Skip to content

Commit c898cdb

Browse files
authored
Merge pull request #26 from nextlevelbuilder/codex/trace-search-filter-traces
feat(traces): add search filter flags
2 parents 04ffc17 + c3a33bd commit c898cdb

14 files changed

Lines changed: 589 additions & 12 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4747
- `goclaw agents evolution update` now maps `--action=accept|reject` to the server-compatible `status=approved|rejected` payload.
4848

4949
**P6 — Backend-unblocked surfaces (gateway `v3.12.0-beta.20`+)**
50+
- `goclaw traces list --q --agent-query --channel-query --from --to --min-input-tokens --max-input-tokens --min-output-tokens --max-output-tokens --min-tool-calls --max-tool-calls --tool-name --has-tool-calls` — forwards the trace search and advanced filters added by server PR #155 while preserving existing `--agent` as `agent_id`.
5051
- `goclaw traces follow --session-key|--agent [--since RFC3339] [--limit N]` — one-shot incremental trace polling (`GET /v1/traces/follow`). Re-invoke with returned cursor to advance; no WS stream, no watch loop.
5152
- `goclaw traces timeline <run-id> [--session-key K] [--limit N] [--offset N]` — read archived run timeline items (`GET /v1/runs/{runID}/timeline`) without replaying or mutating a run.
5253
- `goclaw providers reconnect <provider-id>` — hot-reconnect a provider, bumping the registry without touching credentials (`POST /v1/providers/{id}/reconnect`).

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ plus the run timeline archive endpoint:
101101
# Paginated trace listing with server-supported filters
102102
goclaw traces list [--agent <id>] [--user <id>] [--session-key <key>] \
103103
[--status <status>] [--channel <name>] [--limit <n>] [--offset <n>]
104+
goclaw traces list --q "refund error" [--agent-query <text>] [--channel-query <text>] \
105+
[--from <RFC3339>] [--to <RFC3339>] [--min-input-tokens <n>] \
106+
[--max-input-tokens <n>] [--min-output-tokens <n>] [--max-output-tokens <n>] \
107+
[--min-tool-calls <n>] [--max-tool-calls <n>] [--tool-name <text>] \
108+
[--has-tool-calls <true|false>]
104109

105110
# Incremental trace polling (one shot; rerun with returned cursor)
106111
goclaw traces follow --session-key <key> [--since <RFC3339>] [--limit <n>]

cmd/traces.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ var tracesListCmd = &cobra.Command{
3939
if v, _ := cmd.Flags().GetString("channel"); v != "" {
4040
q.Set("channel", v)
4141
}
42+
setStringQueryFlag(cmd, q, "q", "q")
43+
setStringQueryFlag(cmd, q, "agent-query", "agent")
44+
setStringQueryFlag(cmd, q, "channel-query", "channel_query")
45+
setStringQueryFlag(cmd, q, "from", "from")
46+
setStringQueryFlag(cmd, q, "to", "to")
47+
setChangedIntQueryFlag(cmd, q, "min-input-tokens", "min_input_tokens")
48+
setChangedIntQueryFlag(cmd, q, "max-input-tokens", "max_input_tokens")
49+
setChangedIntQueryFlag(cmd, q, "min-output-tokens", "min_output_tokens")
50+
setChangedIntQueryFlag(cmd, q, "max-output-tokens", "max_output_tokens")
51+
setChangedIntQueryFlag(cmd, q, "min-tool-calls", "min_tool_calls")
52+
setChangedIntQueryFlag(cmd, q, "max-tool-calls", "max_tool_calls")
53+
setStringQueryFlag(cmd, q, "tool-name", "tool_name")
54+
setStringQueryFlag(cmd, q, "has-tool-calls", "has_tool_calls")
4255
if v, _ := cmd.Flags().GetInt("limit"); v > 0 {
4356
q.Set("limit", fmt.Sprintf("%d", v))
4457
}
@@ -115,6 +128,21 @@ func validateTraceID(id string) error {
115128
return nil
116129
}
117130

131+
func setStringQueryFlag(cmd *cobra.Command, q url.Values, flagName, queryName string) {
132+
v, _ := cmd.Flags().GetString(flagName)
133+
if v != "" {
134+
q.Set(queryName, v)
135+
}
136+
}
137+
138+
func setChangedIntQueryFlag(cmd *cobra.Command, q url.Values, flagName, queryName string) {
139+
if !cmd.Flags().Changed(flagName) {
140+
return
141+
}
142+
v, _ := cmd.Flags().GetInt(flagName)
143+
q.Set(queryName, fmt.Sprintf("%d", v))
144+
}
145+
118146
var tracesExportCmd = &cobra.Command{
119147
Use: "export <traceID>", Short: "Export trace to file", Args: cobra.ExactArgs(1),
120148
RunE: func(cmd *cobra.Command, args []string) error {
@@ -308,6 +336,19 @@ func init() {
308336
tracesListCmd.Flags().String("session-key", "", "Filter by session key")
309337
tracesListCmd.Flags().String("status", "", "Filter: running, success, error")
310338
tracesListCmd.Flags().String("channel", "", "Filter by channel")
339+
tracesListCmd.Flags().String("q", "", "Search trace id, previews, session/channel labels, agent/channel labels, and span previews")
340+
tracesListCmd.Flags().String("agent-query", "", "Search agent display name or key")
341+
tracesListCmd.Flags().String("channel-query", "", "Search channel instance name, display name, or type")
342+
tracesListCmd.Flags().String("from", "", "Filter traces from RFC3339 start time")
343+
tracesListCmd.Flags().String("to", "", "Filter traces up to RFC3339 start time")
344+
tracesListCmd.Flags().Int("min-input-tokens", 0, "Minimum total input tokens")
345+
tracesListCmd.Flags().Int("max-input-tokens", 0, "Maximum total input tokens")
346+
tracesListCmd.Flags().Int("min-output-tokens", 0, "Minimum total output tokens")
347+
tracesListCmd.Flags().Int("max-output-tokens", 0, "Maximum total output tokens")
348+
tracesListCmd.Flags().Int("min-tool-calls", 0, "Minimum tool-call count")
349+
tracesListCmd.Flags().Int("max-tool-calls", 0, "Maximum tool-call count")
350+
tracesListCmd.Flags().String("tool-name", "", "Search span tool names")
351+
tracesListCmd.Flags().String("has-tool-calls", "", "Filter traces by tool-call presence: true or false")
311352
tracesListCmd.Flags().Int("limit", 20, "Max results")
312353
tracesListCmd.Flags().Int("offset", 0, "Pagination offset")
313354
tracesListCmd.Flags().String("since", "", "Deprecated: use traces follow --since")

cmd/traces_contract_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,22 @@ func rawJSON(t *testing.T, w http.ResponseWriter, payload any) {
1616

1717
func resetTracesListFlags(t *testing.T) {
1818
t.Helper()
19-
for _, name := range []string{"agent", "user", "session-key", "status", "channel", "since"} {
19+
for _, name := range []string{
20+
"agent", "user", "session-key", "status", "channel", "since",
21+
"q", "agent-query", "channel-query", "from", "to", "tool-name", "has-tool-calls",
22+
} {
2023
resetTestFlag(tracesListCmd, name, "")
2124
}
2225
resetTestFlag(tracesListCmd, "root-only", "false")
2326
resetTestFlag(tracesListCmd, "limit", "20")
2427
resetTestFlag(tracesListCmd, "offset", "0")
28+
for _, name := range []string{
29+
"min-input-tokens", "max-input-tokens",
30+
"min-output-tokens", "max-output-tokens",
31+
"min-tool-calls", "max-tool-calls",
32+
} {
33+
resetTestFlag(tracesListCmd, name, "0")
34+
}
2535
}
2636

2737
func assertNoTracesReplayCommand(t *testing.T) {

cmd/traces_list_test.go

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,46 @@ func TestTracesList_ServerFilters(t *testing.T) {
8787
"--session-key=session-1",
8888
"--status=failed",
8989
"--channel=telegram",
90+
"--q=trace_%",
91+
"--agent-query=helper",
92+
"--channel-query=ops",
93+
"--from=2026-06-10T01:02:03Z",
94+
"--to=2026-06-11T04:05:06Z",
95+
"--min-input-tokens=10",
96+
"--max-input-tokens=20",
97+
"--min-output-tokens=30",
98+
"--max-output-tokens=40",
99+
"--min-tool-calls=1",
100+
"--max-tool-calls=3",
101+
"--tool-name=web_%",
102+
"--has-tool-calls=true",
90103
"--limit=5",
91104
"--offset=10",
92105
)
93106
if err != nil {
94107
t.Fatalf("traces list: %v", err)
95108
}
96109
want := map[string]string{
97-
"agent_id": "agent-1",
98-
"user_id": "user-1",
99-
"session_key": "session-1",
100-
"status": "failed",
101-
"channel": "telegram",
102-
"limit": "5",
103-
"offset": "10",
110+
"agent_id": "agent-1",
111+
"user_id": "user-1",
112+
"session_key": "session-1",
113+
"status": "failed",
114+
"channel": "telegram",
115+
"q": "trace_%",
116+
"agent": "helper",
117+
"channel_query": "ops",
118+
"from": "2026-06-10T01:02:03Z",
119+
"to": "2026-06-11T04:05:06Z",
120+
"min_input_tokens": "10",
121+
"max_input_tokens": "20",
122+
"min_output_tokens": "30",
123+
"max_output_tokens": "40",
124+
"min_tool_calls": "1",
125+
"max_tool_calls": "3",
126+
"tool_name": "web_%",
127+
"has_tool_calls": "true",
128+
"limit": "5",
129+
"offset": "10",
104130
}
105131
for key, val := range want {
106132
if got := query.Get(key); got != val {
@@ -112,6 +138,38 @@ func TestTracesList_ServerFilters(t *testing.T) {
112138
}
113139
}
114140

141+
func TestTracesList_ServerFiltersForwardExplicitFalseAndZero(t *testing.T) {
142+
t.Cleanup(func() { resetTracesListFlags(t) })
143+
var query url.Values
144+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
145+
query = r.URL.Query()
146+
rawJSON(t, w, traceListFixture())
147+
}))
148+
defer srv.Close()
149+
t.Setenv("GOCLAW_SERVER", srv.URL)
150+
t.Setenv("GOCLAW_TOKEN", "test-token")
151+
t.Setenv("GOCLAW_OUTPUT", "json")
152+
153+
err := runCmd(t, "traces", "list",
154+
"--min-input-tokens=0",
155+
"--max-tool-calls=0",
156+
"--has-tool-calls=false",
157+
)
158+
if err != nil {
159+
t.Fatalf("traces list: %v", err)
160+
}
161+
want := map[string]string{
162+
"min_input_tokens": "0",
163+
"max_tool_calls": "0",
164+
"has_tool_calls": "false",
165+
}
166+
for key, val := range want {
167+
if got := query.Get(key); got != val {
168+
t.Fatalf("%s = %q, want %q; query=%s", key, got, val, query.Encode())
169+
}
170+
}
171+
}
172+
115173
func TestTracesReplayCommandAbsent(t *testing.T) {
116174
assertNoTracesReplayCommand(t)
117175
}

docs/codebase-summary.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# GoClaw CLI - Codebase Summary
22

33
**Generated from:** `repomix-output.xml` (2026-04-15), updated manually 2026-06-12
4-
**Phase Status:** P0-P4 Complete (AI-First Expansion); Super Admin API Parity Complete; Domain Coverage P5 + P6 (Backend-Unblocked) Implemented; Runtime & Packages CLI Parity Implemented
4+
**Phase Status:** P0-P4 Complete (AI-First Expansion); Super Admin API Parity Complete; Domain Coverage P5 + P6 (Backend-Unblocked) Implemented; Runtime & Packages CLI Parity Implemented; Trace Search/Filter CLI Implemented
55
**Total Files:** 80+
66
**Estimated Tokens:** 80,000+
77
**Total Size:** 220+ KB
@@ -10,7 +10,7 @@
1010

1111
## Overview
1212

13-
GoClaw CLI is a production-ready Go application providing comprehensive command-line management for GoClaw AI agent gateway servers. Built with Cobra framework, it supports 30+ command groups across modular command files with dual modes: interactive (human) and automation (CI/agent). Phases 0-4 (AI-first expansion) add AI ergonomics, admin/ops, migration, vault, and advanced agent/team/memory support. The 2026-05-18 super-admin parity work adds gateway upgrade, package updates, workstations, webhooks, MCP user credentials, secure env reveal, media/TTS/storage/channel fillers, and focused route-contract tests. The 2026-05-19 P3/P4 filler pass adds first-class profile commands, `GOCLAW_PROFILE`, `sessions compact`, WS health, trace filter polish, `codex-pool`, `api-keys rotate`, `config defaults`, chat session convenience wrappers, and `tools invoke --args`. The 2026-05-20 P5 filler pass adds team attachment download, skill-specific evolution suggestion apply, and fixes evolution update payload compatibility. The 2026-05-27 P6 backend-unblocked pass adds seven new surfaces wired to backend PRs `#37` and `#44`: `traces follow`, `providers reconnect`, `sessions branch`, `sessions follow`, `channels writers test`, `activity aggregate`, and `logs aggregate` — all one-shot HTTP commands (no new watch loops; reuse the existing `client.FollowStream` only for true streaming surfaces). The 2026-06-11 traces contract pass aligns `traces list/get/follow/export` with server `dev` envelopes and adds `traces timeline`. The 2026-06-12 Runtime & Packages parity pass aligns `packages` and secure `credentials` command envelopes with server `dev`, adds `credentials agent-credentials`, and preserves raw object payloads for machine output.
13+
GoClaw CLI is a production-ready Go application providing comprehensive command-line management for GoClaw AI agent gateway servers. Built with Cobra framework, it supports 30+ command groups across modular command files with dual modes: interactive (human) and automation (CI/agent). Phases 0-4 (AI-first expansion) add AI ergonomics, admin/ops, migration, vault, and advanced agent/team/memory support. The 2026-05-18 super-admin parity work adds gateway upgrade, package updates, workstations, webhooks, MCP user credentials, secure env reveal, media/TTS/storage/channel fillers, and focused route-contract tests. The 2026-05-19 P3/P4 filler pass adds first-class profile commands, `GOCLAW_PROFILE`, `sessions compact`, WS health, trace filter polish, `codex-pool`, `api-keys rotate`, `config defaults`, chat session convenience wrappers, and `tools invoke --args`. The 2026-05-20 P5 filler pass adds team attachment download, skill-specific evolution suggestion apply, and fixes evolution update payload compatibility. The 2026-05-27 P6 backend-unblocked pass adds seven new surfaces wired to backend PRs `#37` and `#44`: `traces follow`, `providers reconnect`, `sessions branch`, `sessions follow`, `channels writers test`, `activity aggregate`, and `logs aggregate` — all one-shot HTTP commands (no new watch loops; reuse the existing `client.FollowStream` only for true streaming surfaces). The 2026-06-11 traces contract pass aligns `traces list/get/follow/export` with server `dev` envelopes and adds `traces timeline`. The 2026-06-12 Runtime & Packages parity pass aligns `packages` and secure `credentials` command envelopes with server `dev`, adds `credentials agent-credentials`, and preserves raw object payloads for machine output. The 2026-06-12 trace search/filter pass exposes server PR #155 filters on `traces list` with `--q`, `--agent-query`, `--channel-query`, date ranges, token ranges, tool-call ranges, `--tool-name`, and `--has-tool-calls`.
1414

1515
**Key Metrics:**
1616
- **70+ command files** in `cmd/` (modularized for maintainability)

docs/project-roadmap.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,22 @@
22

33
**Last Updated:** 2026-06-12
44
**Phase Structure:** Legacy Phases 1-9 (bootstrap → CI/CD) + AI-First Expansion Phases 0-5 (2026-04-15)
5-
**Current Status:** Legacy Phases 1-9 ✓ COMPLETE; P0-P4 ✓ COMPLETE; Super Admin API Parity ✓ COMPLETE; Domain Coverage P5/P6 ✓ COMPLETE; Runtime & Packages CLI parity implemented pending beta ship.
6-
**Next Phase:** Ship Runtime & Packages parity PR to `dev` and verify beta release.
5+
**Current Status:** Legacy Phases 1-9 ✓ COMPLETE; P0-P6 ✓ COMPLETE; Super Admin API Parity ✓ COMPLETE; Runtime & Packages CLI parity implemented; trace contract and trace search/filter CLI support implemented.
6+
**Next Phase:** Ship trace search/filter CLI PR to `dev` and verify beta release.
7+
8+
---
9+
10+
## 2026-06-12: Trace Search/Filter CLI IMPLEMENTED
11+
12+
**Objective:** Expose server PR #155 trace search and advanced filters through `goclaw traces list`.
13+
14+
**Deliverables:**
15+
- [x] Added `traces list --q --agent-query --channel-query --from --to --min-input-tokens --max-input-tokens --min-output-tokens --max-output-tokens --min-tool-calls --max-tool-calls --tool-name --has-tool-calls`.
16+
- [x] Preserved existing `--agent` as `agent_id` for backward compatibility; `--agent-query` maps to the server `agent` text search.
17+
- [x] Added focused tests for full query forwarding, explicit `false`, explicit zero values, and replay absence.
18+
- [x] Synced README, changelog, codebase summary, and plan artifacts.
19+
20+
**Validation:** `/usr/local/go/bin/go test -count=1 ./cmd -run 'TestTracesList|TestTracesReplayCommandAbsent'`; `/usr/local/go/bin/go test -count=1 ./cmd`; `/usr/local/go/bin/go test -count=1 ./...`; `/usr/local/go/bin/go vet ./...`; `/usr/local/go/bin/go build ./...`.
721

822
---
923

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
phase: 1
3+
title: Contract Lock
4+
status: completed
5+
priority: P1
6+
effort: 1h
7+
dependencies: []
8+
---
9+
10+
# Phase 1: Contract Lock
11+
12+
## Overview
13+
14+
Lock the merged server contract from `digitopvn/goclaw#155` before implementation. The output of this phase is a testable list of query mappings and explicit exclusions.
15+
16+
## Requirements
17+
18+
- Functional: map every new server list filter to one CLI flag.
19+
- Non-functional: preserve existing `traces list` automation contract and avoid speculative server behavior.
20+
21+
## Architecture
22+
23+
`traces list` remains a one-shot `GET /v1/traces` command. The CLI only builds `url.Values`; all matching semantics, wildcard escaping, date parsing, and tenant scoping stay server-side.
24+
25+
## Related Code Files
26+
27+
- Modify: `cmd/traces_contract_test.go`
28+
- Read: `cmd/traces.go`
29+
- Read: `cmd/traces_list_test.go`
30+
- Read: upstream `digitopvn/goclaw#155`
31+
32+
## Implementation Steps
33+
34+
1. Verify the server PR is merged and capture exact query names.
35+
2. Add or update a contract test helper that resets all trace list flags, including new ones.
36+
3. Confirm existing replay-absence coverage stays in place.
37+
38+
## Todo List
39+
40+
- [x] Capture exact query parameter names from server PR #155.
41+
- [x] Update `resetTracesListFlags` for all new flags.
42+
- [x] Keep `TestTracesReplayCommandAbsent` green.
43+
44+
## Success Criteria
45+
46+
- [x] Contract test names all new query params.
47+
- [x] No plan or test implies `traces replay` exists.
48+
- [x] No unresolved scope questions remain.
49+
50+
## Risk Assessment
51+
52+
Misnaming `agent` as `agent_id` would silently change semantics. The test must assert both `--agent` and `--agent-query` mappings to keep ID and query search distinct.
53+
54+
## Security Considerations
55+
56+
No credentials or live traces are used. Tests use synthetic `httptest` requests and server-shaped fixtures only.
57+
58+
## Next Steps
59+
60+
Proceed to Phase 2 only after focused contract tests fail for missing flags.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
phase: 2
3+
title: TDD Implementation
4+
status: completed
5+
priority: P1
6+
effort: 2h
7+
dependencies:
8+
- 1
9+
---
10+
11+
# Phase 2: TDD Implementation
12+
13+
## Overview
14+
15+
Add focused failing tests for the new trace list filters, then implement the smallest CLI change in `cmd/traces.go`.
16+
17+
## Requirements
18+
19+
- Functional: `traces list` forwards all PR #155 filter flags when supplied.
20+
- Functional: zero-valued numeric filters can be forwarded when the user explicitly sets them.
21+
- Functional: `--has-tool-calls=false` is forwarded, not dropped.
22+
- Non-functional: no output shape or command behavior changes outside list query construction.
23+
24+
## Architecture
25+
26+
The command keeps using `url.Values`. A small helper may copy changed string/int/bool-like string flags to query params if it reduces repetitive flag handling without creating a generic framework.
27+
28+
## Related Code Files
29+
30+
- Modify: `cmd/traces.go`
31+
- Modify: `cmd/traces_list_test.go`
32+
- Modify: `cmd/traces_contract_test.go`
33+
34+
## Implementation Steps
35+
36+
1. Extend `TestTracesList_ServerFilters` to include all new flags and expected query names.
37+
2. Add a focused test for false/zero explicit forwarding:
38+
`--has-tool-calls=false`, `--min-tool-calls=0`, `--min-input-tokens=0`.
39+
3. Run focused tests and confirm failure before implementation.
40+
4. Add flags to `tracesListCmd`:
41+
`--q`, `--agent-query`, `--channel-query`, `--from`, `--to`,
42+
`--min-input-tokens`, `--max-input-tokens`,
43+
`--min-output-tokens`, `--max-output-tokens`,
44+
`--min-tool-calls`, `--max-tool-calls`,
45+
`--tool-name`, `--has-tool-calls`.
46+
5. Map flags to server params:
47+
`q -> q`, `agent-query -> agent`, `channel-query -> channel_query`,
48+
ranges to snake_case names, `has-tool-calls -> has_tool_calls`.
49+
6. Run focused tests again and fix only failures in this scope.
50+
51+
## Todo List
52+
53+
- [x] Add red tests for new filter forwarding.
54+
- [x] Add explicit false/zero forwarding test.
55+
- [x] Implement flag registration and query mapping.
56+
- [x] Keep existing basic filter test green.
57+
58+
## Success Criteria
59+
60+
- [x] Focused trace list tests pass.
61+
- [x] No changes to `traces follow`, `get`, `export`, or `timeline`.
62+
- [x] `cmd/traces.go` remains compilable and has no duplicated large blocks.
63+
64+
## Risk Assessment
65+
66+
The main regression risk is accidentally changing `--agent` from agent ID to label search. Keep `--agent` as `agent_id` for backward compatibility and add `--agent-query` for the server `agent` query.
67+
68+
## Security Considerations
69+
70+
User-provided filter strings are only URL query values encoded through `url.Values`. No shell execution, path usage, or local file reads are introduced.
71+
72+
## Next Steps
73+
74+
After focused tests pass, update README and docs in Phase 3.

0 commit comments

Comments
 (0)