@oh-my-pi/pi-coding-agent 16.1.23 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/dist/cli.js +3817 -3735
- package/dist/types/advisor/__tests__/emission-guard.test.d.ts +1 -0
- package/dist/types/advisor/emission-guard.d.ts +73 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/runtime.d.ts +7 -0
- package/dist/types/advisor/watchdog.d.ts +2 -0
- package/dist/types/cli/flag-tables.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +1 -1
- package/dist/types/collab/display-name.d.ts +3 -0
- package/dist/types/collab/host.d.ts +0 -2
- package/dist/types/commands/worktree.d.ts +0 -3
- package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
- package/dist/types/config/model-discovery.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +6 -2
- package/dist/types/config/model-resolver.d.ts +12 -0
- package/dist/types/config/models-config-schema.d.ts +29 -2
- package/dist/types/config/models-config.d.ts +22 -1
- package/dist/types/config/settings-schema.d.ts +91 -21
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/dap/config.d.ts +1 -1
- package/dist/types/edit/hashline/filesystem.d.ts +4 -2
- package/dist/types/edit/renderer.d.ts +4 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -17
- package/dist/types/extensibility/hooks/types.d.ts +10 -10
- package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
- package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
- package/dist/types/internal-urls/router.d.ts +1 -1
- package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
- package/dist/types/internal-urls/types.d.ts +19 -2
- package/dist/types/irc/bus.d.ts +6 -0
- package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +7 -0
- package/dist/types/modes/components/move-overlay.d.ts +23 -0
- package/dist/types/modes/components/plugin-selector.d.ts +2 -1
- package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
- package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
- package/dist/types/modes/components/settings-defs.d.ts +4 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/show-images-selector.d.ts +2 -1
- package/dist/types/modes/components/status-line/component.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/theme-selector.d.ts +2 -1
- package/dist/types/modes/components/thinking-selector.d.ts +2 -1
- package/dist/types/modes/controllers/command-controller.d.ts +10 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -1
- package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
- package/dist/types/modes/running-subagent-badge.d.ts +6 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +9 -1
- package/dist/types/sdk.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +2 -1
- package/dist/types/session/session-manager.d.ts +13 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
- package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
- package/dist/types/ssh/connection-manager.d.ts +2 -0
- package/dist/types/ssh/file-transfer.d.ts +79 -0
- package/dist/types/ssh/utils.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +3 -0
- package/dist/types/task/executor.d.ts +16 -0
- package/dist/types/tiny/text.d.ts +1 -1
- package/dist/types/tools/builtin-names.d.ts +5 -1
- package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
- package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tools/path-utils.d.ts +29 -0
- package/dist/types/tools/read.d.ts +2 -2
- package/dist/types/tools/render-utils.d.ts +8 -0
- package/dist/types/tools/renderers.d.ts +11 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo.d.ts +0 -16
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/utils/active-repo-context.d.ts +8 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/utils/markit-cache.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +5 -1
- package/dist/types/utils/prompt-path.d.ts +1 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
- package/dist/types/web/search/providers/xai.d.ts +13 -0
- package/dist/types/web/search/types.d.ts +18 -2
- package/package.json +30 -15
- package/scripts/build-binary.ts +9 -9
- package/scripts/bundle-dist.ts +2 -2
- package/src/advisor/__tests__/advisor.test.ts +40 -4
- package/src/advisor/__tests__/emission-guard.test.ts +147 -0
- package/src/advisor/advise-tool.ts +1 -1
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +11 -0
- package/src/advisor/watchdog.ts +12 -1
- package/src/cli/args.ts +5 -2
- package/src/cli/auth-broker-cli.ts +17 -0
- package/src/cli/config-cli.ts +4 -0
- package/src/cli/flag-tables.ts +7 -4
- package/src/cli/gallery-cli.ts +14 -2
- package/src/cli/gallery-fixtures/edit.ts +60 -0
- package/src/cli/gallery-fixtures/fs.ts +17 -17
- package/src/cli/gallery-fixtures/search.ts +4 -4
- package/src/cli/usage-cli.ts +20 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +5 -1
- package/src/collab/host.ts +2 -13
- package/src/commands/worktree.ts +6 -0
- package/src/config/inline-tool-descriptors-mode.ts +27 -0
- package/src/config/model-discovery.ts +77 -8
- package/src/config/model-registry.ts +89 -11
- package/src/config/model-resolver.ts +39 -1
- package/src/config/models-config-schema.ts +41 -5
- package/src/config/models-config.ts +4 -1
- package/src/config/settings-schema.ts +85 -27
- package/src/config/settings.ts +172 -1
- package/src/cursor.ts +1 -1
- package/src/dap/config.ts +152 -8
- package/src/dap/session.ts +1 -1
- package/src/discovery/helpers.ts +3 -1
- package/src/edit/hashline/diff.ts +14 -3
- package/src/edit/hashline/execute.ts +42 -6
- package/src/edit/hashline/filesystem.ts +38 -2
- package/src/edit/index.ts +1 -0
- package/src/edit/modes/patch.ts +11 -1
- package/src/edit/renderer.ts +140 -13
- package/src/eval/__tests__/agent-bridge.test.ts +101 -0
- package/src/eval/agent-bridge.ts +26 -3
- package/src/eval/js/tool-bridge.ts +2 -2
- package/src/export/html/index.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/export/html/tool-views.generated.js +20 -20
- package/src/extensibility/extensions/types.ts +22 -22
- package/src/extensibility/hooks/types.ts +11 -11
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +8 -3
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +25 -10
- package/src/extensibility/plugins/marketplace/manager.ts +22 -0
- package/src/extensibility/plugins/marketplace/types.ts +1 -0
- package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
- package/src/internal-urls/docs-index.generated.txt +2 -2
- package/src/internal-urls/docs-index.ts +2 -3
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/registry-helpers.ts +19 -4
- package/src/internal-urls/router.ts +5 -3
- package/src/internal-urls/ssh-protocol.ts +367 -0
- package/src/internal-urls/types.ts +19 -2
- package/src/irc/bus.ts +11 -3
- package/src/mcp/tool-bridge.ts +32 -2
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +2 -2
- package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
- package/src/modes/components/agent-hub.ts +3 -0
- package/src/modes/components/agent-transcript-viewer.ts +9 -7
- package/src/modes/components/custom-editor.ts +90 -5
- package/src/modes/components/move-overlay.ts +282 -0
- package/src/modes/components/plan-review-overlay.ts +35 -35
- package/src/modes/components/plugin-selector.ts +6 -1
- package/src/modes/components/queue-mode-selector.ts +6 -1
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/session-selector.ts +11 -10
- package/src/modes/components/settings-defs.ts +14 -1
- package/src/modes/components/settings-selector.ts +196 -29
- package/src/modes/components/show-images-selector.ts +6 -1
- package/src/modes/components/status-line/component.ts +108 -28
- package/src/modes/components/status-line/segments.ts +5 -1
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/theme-selector.ts +6 -1
- package/src/modes/components/thinking-selector.ts +6 -1
- package/src/modes/components/tool-execution.ts +25 -9
- package/src/modes/components/tree-selector.ts +5 -5
- package/src/modes/controllers/command-controller.ts +140 -47
- package/src/modes/controllers/event-controller.ts +59 -3
- package/src/modes/controllers/input-controller.ts +68 -2
- package/src/modes/controllers/selector-controller.ts +5 -2
- package/src/modes/controllers/streaming-reveal.ts +17 -0
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +116 -50
- package/src/modes/internal-url-autocomplete.ts +17 -2
- package/src/modes/prompt-action-autocomplete.ts +3 -1
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
- package/src/modes/setup-wizard/scenes/providers.ts +2 -1
- package/src/modes/setup-wizard/scenes/theme.ts +6 -12
- package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
- package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
- package/src/modes/theme/theme.ts +9 -1
- package/src/modes/types.ts +9 -1
- package/src/modes/utils/interactive-context-helpers.ts +1 -1
- package/src/priority.json +15 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/system.md +21 -7
- package/src/prompts/agents/designer.md +1 -1
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +2 -2
- package/src/prompts/agents/plan.md +2 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/agents/task.md +2 -2
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/plan-mode-active.md +3 -3
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/system-prompt.md +6 -6
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/{find.md → glob.md} +1 -1
- package/src/prompts/tools/{search.md → grep.md} +3 -3
- package/src/prompts/tools/read.md +4 -2
- package/src/sdk.ts +55 -25
- package/src/session/agent-session.ts +508 -132
- package/src/session/session-history-format.ts +2 -2
- package/src/session/session-manager.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +138 -20
- package/src/slash-commands/helpers/usage-report.ts +23 -2
- package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
- package/src/ssh/connection-manager.ts +12 -15
- package/src/ssh/file-transfer.ts +209 -0
- package/src/ssh/utils.ts +24 -0
- package/src/system-prompt.ts +56 -28
- package/src/task/executor.ts +96 -36
- package/src/task/index.ts +3 -3
- package/src/task/render.ts +14 -13
- package/src/task/worktree.ts +38 -1
- package/src/tiny/text.ts +49 -4
- package/src/tiny/worker.ts +3 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/builtin-names.ts +26 -2
- package/src/tools/{find.ts → glob.ts} +48 -42
- package/src/tools/{search.ts → grep.ts} +311 -129
- package/src/tools/index.ts +13 -14
- package/src/tools/irc.ts +6 -2
- package/src/tools/path-utils.ts +64 -1
- package/src/tools/read.ts +35 -6
- package/src/tools/render-utils.ts +14 -0
- package/src/tools/renderers.ts +15 -4
- package/src/tools/ssh.ts +17 -2
- package/src/tools/todo.ts +4 -26
- package/src/tools/write.ts +25 -9
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/image-resize.ts +88 -7
- package/src/utils/lang-from-path.ts +3 -3
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +86 -18
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/provider.ts +54 -34
- package/src/web/search/providers/duckduckgo.ts +140 -0
- package/src/web/search/providers/firecrawl.ts +144 -0
- package/src/web/search/providers/tinyfish.ts +159 -0
- package/src/web/search/providers/xai.ts +292 -0
- package/src/web/search/providers/zai.ts +142 -56
- package/src/web/search/types.ts +6 -2
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session policy gate for advisor `advise()` calls.
|
|
3
|
+
*
|
|
4
|
+
* The advisor system prompt tells the watcher model:
|
|
5
|
+
*
|
|
6
|
+
* > at most one `advise` per update
|
|
7
|
+
* > NEVER repeat advice you already gave, and NEVER send the same advice twice
|
|
8
|
+
*
|
|
9
|
+
* Real advisor models violate this. Issue #3520 captured a session where
|
|
10
|
+
* `__advisor.jsonl` recorded 309 `advise` calls covering 92 unique notes —
|
|
11
|
+
* 114× `Stop.`, 52× `No issue; continue.`, 41× `Done.` — flooding the primary
|
|
12
|
+
* transcript with `<advisory severity="blocker">Stop.</advisory>` after the
|
|
13
|
+
* task was already complete. The fix is to make the rules load-bearing in code
|
|
14
|
+
* instead of prose: silently drop duplicates, content-free self-talk, and
|
|
15
|
+
* over-budget calls at the `enqueueAdvice` boundary so the primary stays
|
|
16
|
+
* clean even when the advisor misbehaves.
|
|
17
|
+
*
|
|
18
|
+
* The gate is intentionally invisible to the advisor model — `AdviseTool`
|
|
19
|
+
* still returns `Recorded.` for a suppressed call. Surfacing "suppressed"
|
|
20
|
+
* back into advisor context risks the model rephrasing the same useless note
|
|
21
|
+
* to bypass the dedupe ("Stop.", then "Halt." then "Stop now.").
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Case-insensitive, punctuation-folded normalization. Collapses every run of
|
|
26
|
+
* non-letter / non-digit characters into a single space and trims, so
|
|
27
|
+
* `"Stop."`, `"*Stop*"`, and `" stop "` all key to `stop`, while
|
|
28
|
+
* `"No issue; continue."` keys to `no issue continue`.
|
|
29
|
+
*
|
|
30
|
+
* Exported for tests.
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeAdvisorNote(note: string): string {
|
|
33
|
+
return note
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.normalize("NFKC")
|
|
36
|
+
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
37
|
+
.trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Normalized phrases the advisor occasionally emits that carry no concrete
|
|
42
|
+
* actionable content. Each must be the output of {@link normalizeAdvisorNote}
|
|
43
|
+
* so a single membership check covers every punctuation/casing variant
|
|
44
|
+
* (`"Stop."`, `"stop"`, `"STOP!"`).
|
|
45
|
+
*
|
|
46
|
+
* The list is conservative — only short, content-free filler the reporter
|
|
47
|
+
* observed driving primary-transcript pollution. A genuine `blocker` like
|
|
48
|
+
* `"Stop: 'await' missing on writeStream.end() will lose buffered writes."`
|
|
49
|
+
* does not match.
|
|
50
|
+
*/
|
|
51
|
+
const SUPPRESSED_NORMALIZED_PHRASES: Record<string, true> = {
|
|
52
|
+
// Self-stop noise — telling the agent to "stop" without a reason is useless.
|
|
53
|
+
stop: true,
|
|
54
|
+
"stop here": true,
|
|
55
|
+
"stop now": true,
|
|
56
|
+
halt: true,
|
|
57
|
+
abort: true,
|
|
58
|
+
// Completion self-talk — the agent already finished the task.
|
|
59
|
+
done: true,
|
|
60
|
+
"task done": true,
|
|
61
|
+
"task complete": true,
|
|
62
|
+
complete: true,
|
|
63
|
+
finished: true,
|
|
64
|
+
ok: true,
|
|
65
|
+
okay: true,
|
|
66
|
+
"ok done": true,
|
|
67
|
+
// "Nothing to flag" — silence is the correct expression of "no concerns".
|
|
68
|
+
"no issue": true,
|
|
69
|
+
"no issues": true,
|
|
70
|
+
"no issue continue": true,
|
|
71
|
+
"no concerns": true,
|
|
72
|
+
"no concern": true,
|
|
73
|
+
"nothing to add": true,
|
|
74
|
+
"nothing to flag": true,
|
|
75
|
+
"nothing to report": true,
|
|
76
|
+
"no notes": true,
|
|
77
|
+
"no further input": true,
|
|
78
|
+
"no further input needed": true,
|
|
79
|
+
"no further input required": true,
|
|
80
|
+
"no further watcher input": true,
|
|
81
|
+
"no further watcher input needed": true,
|
|
82
|
+
"no further advice": true,
|
|
83
|
+
"no further advice needed": true,
|
|
84
|
+
// Endorsements — equivalent to silence.
|
|
85
|
+
lgtm: true,
|
|
86
|
+
"looks good": true,
|
|
87
|
+
"all good": true,
|
|
88
|
+
"agent is on track": true,
|
|
89
|
+
"agent on track": true,
|
|
90
|
+
"on track": true,
|
|
91
|
+
continue: true,
|
|
92
|
+
"carry on": true,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Bounds the dedupe history. Sessions with very long advisor activity could
|
|
97
|
+
* otherwise grow the set without bound. The reporter's pathological session
|
|
98
|
+
* had 92 unique notes; 4096 leaves headroom while staying tiny (≤ ~256 KB of
|
|
99
|
+
* normalized strings even at long max).
|
|
100
|
+
*/
|
|
101
|
+
const DEFAULT_HISTORY_CAPACITY = 4096;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Decides whether an advisor `advise()` call should reach the primary agent.
|
|
105
|
+
*
|
|
106
|
+
* Enforces — in this order — the noise filter, session-scoped exact-text
|
|
107
|
+
* dedupe (FIFO-evicted at {@link DEFAULT_HISTORY_CAPACITY}), and a per-update
|
|
108
|
+
* rate limit of one accepted note per advisor model prompt. Suppressed calls
|
|
109
|
+
* never consume the per-update budget — a noise call doesn't burn the slot
|
|
110
|
+
* for a real concern that follows in the same update.
|
|
111
|
+
*
|
|
112
|
+
* Reset on advisor reset (compaction, session switch, `/new`) via
|
|
113
|
+
* {@link reset}. Per-update gate is cleared at the start of every advisor
|
|
114
|
+
* `agent.prompt()` cycle via {@link beginUpdate}.
|
|
115
|
+
*/
|
|
116
|
+
export class AdvisorEmissionGuard {
|
|
117
|
+
#seen = new Set<string>();
|
|
118
|
+
/** Insertion-order log to drive FIFO eviction without an extra Map. */
|
|
119
|
+
#seenOrder: string[] = [];
|
|
120
|
+
#consumedThisUpdate = false;
|
|
121
|
+
readonly #capacity: number;
|
|
122
|
+
|
|
123
|
+
constructor(opts: { capacity?: number } = {}) {
|
|
124
|
+
this.#capacity = opts.capacity ?? DEFAULT_HISTORY_CAPACITY;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Drop all dedupe and per-update state. Called from
|
|
129
|
+
* `AgentSession#resetAdvisorSessionState()` whenever the advisor runtime is
|
|
130
|
+
* reset — same boundary as `yieldQueue.clear("advisor")`, so a re-primed
|
|
131
|
+
* advisor can re-raise old issues (the primary transcript was rewritten).
|
|
132
|
+
*/
|
|
133
|
+
reset(): void {
|
|
134
|
+
this.#seen.clear();
|
|
135
|
+
this.#seenOrder.length = 0;
|
|
136
|
+
this.#consumedThisUpdate = false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Clear the per-update rate-limit gate. Called by `AdvisorRuntime` right
|
|
141
|
+
* before each `agent.prompt(batch)` invocation so the next advisor model
|
|
142
|
+
* cycle starts with a fresh budget of one advise.
|
|
143
|
+
*/
|
|
144
|
+
beginUpdate(): void {
|
|
145
|
+
this.#consumedThisUpdate = false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Whether the proposed note should reach the primary. On `true` the gate
|
|
150
|
+
* has already recorded the note (consumed the per-update budget and added
|
|
151
|
+
* it to the dedupe history) — caller delivers the note. On `false` the
|
|
152
|
+
* caller drops it.
|
|
153
|
+
*
|
|
154
|
+
* Empty / whitespace-only notes are suppressed; the model's
|
|
155
|
+
* tool-args contract still requires a non-empty string but defense-in-depth.
|
|
156
|
+
*/
|
|
157
|
+
accept(note: string): boolean {
|
|
158
|
+
const key = normalizeAdvisorNote(note);
|
|
159
|
+
if (!key) return false;
|
|
160
|
+
if (SUPPRESSED_NORMALIZED_PHRASES[key]) return false;
|
|
161
|
+
if (this.#seen.has(key)) return false;
|
|
162
|
+
if (this.#consumedThisUpdate) return false;
|
|
163
|
+
this.#consumedThisUpdate = true;
|
|
164
|
+
this.#seen.add(key);
|
|
165
|
+
this.#seenOrder.push(key);
|
|
166
|
+
if (this.#seenOrder.length > this.#capacity) {
|
|
167
|
+
const stale = this.#seenOrder.shift();
|
|
168
|
+
if (stale !== undefined) this.#seen.delete(stale);
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
package/src/advisor/index.ts
CHANGED
package/src/advisor/runtime.ts
CHANGED
|
@@ -30,6 +30,13 @@ export interface AdvisorRuntimeHost {
|
|
|
30
30
|
* the primary's next compaction triggers {@link AdvisorRuntime.reset}).
|
|
31
31
|
*/
|
|
32
32
|
maintainContext?(incomingTokens: number): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Called immediately before each `agent.prompt(batch)` cycle. Lets the host
|
|
35
|
+
* clear per-update advisor state — currently the one-advise-per-update gate
|
|
36
|
+
* in {@link AdvisorEmissionGuard}, which the host owns because it is the
|
|
37
|
+
* one that routes `advise()` results back to the primary.
|
|
38
|
+
*/
|
|
39
|
+
beginAdvisorUpdate?(): void;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
interface PendingDelta {
|
|
@@ -275,6 +282,10 @@ export class AdvisorRuntime {
|
|
|
275
282
|
|
|
276
283
|
let success = false;
|
|
277
284
|
try {
|
|
285
|
+
// Reset the host's per-update advisor state (one-advise-per-update
|
|
286
|
+
// gate) before each model cycle, so the new batch starts with a
|
|
287
|
+
// fresh budget. Dedupe history persists across cycles.
|
|
288
|
+
this.host.beginAdvisorUpdate?.();
|
|
278
289
|
await this.agent.prompt(batch);
|
|
279
290
|
success = true;
|
|
280
291
|
this.#consecutiveFailures = 0;
|
package/src/advisor/watchdog.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import * as os from "node:os";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { getAgentDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import { getAgentDir, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { expandAtImports } from "../discovery/at-imports";
|
|
5
|
+
import activeRepoWatchdogTemplate from "../prompts/advisor/active-repo-watchdog.md" with { type: "text" };
|
|
6
|
+
import type { ActiveRepoContext } from "../utils/active-repo-context";
|
|
5
7
|
import { repo } from "../utils/git";
|
|
8
|
+
import { normalizePromptPath } from "../utils/prompt-path";
|
|
9
|
+
|
|
10
|
+
export function formatActiveRepoWatchdogPrompt(activeRepoContext: ActiveRepoContext): string {
|
|
11
|
+
return prompt
|
|
12
|
+
.render(activeRepoWatchdogTemplate, {
|
|
13
|
+
relativeRepoRoot: normalizePromptPath(activeRepoContext.relativeRepoRoot),
|
|
14
|
+
})
|
|
15
|
+
.trim();
|
|
16
|
+
}
|
|
6
17
|
|
|
7
18
|
/**
|
|
8
19
|
* Discover and load WATCHDOG.md files walking up from cwd, project .omp folder, and user agent dir.
|
package/src/cli/args.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { APP_NAME, CONFIG_DIR_NAME, logger } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { CLI_THINKING_LEVELS, type ConfiguredThinkingLevel, parseCliThinkingLevel } from "../thinking";
|
|
7
|
-
import { BUILTIN_TOOL_NAMES } from "../tools/builtin-names";
|
|
7
|
+
import { BUILTIN_TOOL_NAMES, normalizeToolNames } from "../tools/builtin-names";
|
|
8
8
|
import {
|
|
9
9
|
OPTIONAL_FLAGS,
|
|
10
10
|
OPTIONAL_VALUE_FLAGS,
|
|
@@ -90,6 +90,7 @@ const PARSE_DEPS: ParseDeps = {
|
|
|
90
90
|
logger,
|
|
91
91
|
parseThinking: parseCliThinkingLevel,
|
|
92
92
|
builtinToolNames: BUILTIN_TOOL_NAMES,
|
|
93
|
+
normalizeToolNames,
|
|
93
94
|
thinkingEfforts: CLI_THINKING_LEVELS,
|
|
94
95
|
};
|
|
95
96
|
|
|
@@ -310,6 +311,8 @@ export function getExtraHelpText(): string {
|
|
|
310
311
|
PERPLEXITY_API_KEY - Perplexity web search API key (optional; anonymous fallback)
|
|
311
312
|
PERPLEXITY_COOKIES - Perplexity web search (session cookie)
|
|
312
313
|
TAVILY_API_KEY - Tavily web search
|
|
314
|
+
TINYFISH_API_KEY - TinyFish web search
|
|
315
|
+
FIRECRAWL_API_KEY - Firecrawl web search
|
|
313
316
|
ANTHROPIC_SEARCH_API_KEY - Anthropic web search (override; isolates search from main ANTHROPIC_API_KEY)
|
|
314
317
|
ANTHROPIC_SEARCH_BASE_URL - Anthropic web search base URL (override; pairs with ANTHROPIC_SEARCH_API_KEY)
|
|
315
318
|
|
|
@@ -330,7 +333,7 @@ ${chalk.bold("Available Tools (default-enabled unless noted):")}
|
|
|
330
333
|
edit - Edit files with find/replace
|
|
331
334
|
write - Write files (creates/overwrites)
|
|
332
335
|
grep - Search file contents
|
|
333
|
-
|
|
336
|
+
glob - Find files by glob pattern
|
|
334
337
|
lsp - Language server protocol (code intelligence)
|
|
335
338
|
python - Execute Python code (requires: ${APP_NAME} setup python)
|
|
336
339
|
notebook - Edit Jupyter notebooks
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type OAuthCredential,
|
|
29
29
|
type OAuthProvider,
|
|
30
30
|
type OAuthProviderInfo,
|
|
31
|
+
PASTE_CODE_LOGIN_PROVIDERS,
|
|
31
32
|
PROVIDER_REGISTRY,
|
|
32
33
|
SqliteAuthCredentialStore,
|
|
33
34
|
} from "@oh-my-pi/pi-ai";
|
|
@@ -211,6 +212,15 @@ async function runLocalLogin(provider: OAuthProvider): Promise<void> {
|
|
|
211
212
|
const storage = new AuthStorage(store);
|
|
212
213
|
await storage.reload();
|
|
213
214
|
try {
|
|
215
|
+
// Only paste-code providers (fixed non-loopback redirect, e.g. GitLab Duo
|
|
216
|
+
// Agent's vscode:// URI) get the manual paste fallback. An explicit
|
|
217
|
+
// `onManualCodeInput` is honored for ANY provider (the storage escape hatch),
|
|
218
|
+
// so for loopback providers we must not pass it: it would make
|
|
219
|
+
// `OAuthCallbackFlow` race a readline prompt against the HTTP callback and, if
|
|
220
|
+
// the callback wins, leave that prompt outstanding (dirty/blocked terminal).
|
|
221
|
+
// `AuthStorage.login` independently refuses to synthesize the default prompt
|
|
222
|
+
// for non-paste-code providers, so this is defense-in-depth on the same gate.
|
|
223
|
+
const usesManualInput = PASTE_CODE_LOGIN_PROVIDERS.has(provider);
|
|
214
224
|
await storage.login(provider, {
|
|
215
225
|
onAuth({ url, instructions }) {
|
|
216
226
|
process.stdout.write(`\nOpen this URL in your browser:\n${url}\n`);
|
|
@@ -223,6 +233,13 @@ async function runLocalLogin(provider: OAuthProvider): Promise<void> {
|
|
|
223
233
|
onPrompt(p) {
|
|
224
234
|
return ask(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
|
|
225
235
|
},
|
|
236
|
+
...(usesManualInput
|
|
237
|
+
? {
|
|
238
|
+
onManualCodeInput() {
|
|
239
|
+
return ask("Paste the authorization code (or full redirect URL):");
|
|
240
|
+
},
|
|
241
|
+
}
|
|
242
|
+
: undefined),
|
|
226
243
|
});
|
|
227
244
|
process.stdout.write(`\nCredentials saved to ${getAgentDbPath()}\n`);
|
|
228
245
|
} finally {
|
package/src/cli/config-cli.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
Settings,
|
|
17
17
|
type SettingValue,
|
|
18
18
|
settings,
|
|
19
|
+
validateProviderMaxInFlightRequests,
|
|
19
20
|
} from "../config/settings";
|
|
20
21
|
import { SETTINGS_SCHEMA } from "../config/settings-schema";
|
|
21
22
|
import { theme } from "../modes/theme/theme";
|
|
@@ -218,6 +219,9 @@ function parseAndSetValue(path: SettingPath, rawValue: string): void {
|
|
|
218
219
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
219
220
|
throw new Error(`Invalid record JSON: ${rawValue}`);
|
|
220
221
|
}
|
|
222
|
+
if (path === "providers.maxInFlightRequests") {
|
|
223
|
+
parsed = validateProviderMaxInFlightRequests(parsed);
|
|
224
|
+
}
|
|
221
225
|
parsedValue = parsed;
|
|
222
226
|
break;
|
|
223
227
|
}
|
package/src/cli/flag-tables.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface ParseDeps {
|
|
|
46
46
|
logger: { warn: (message: string, meta?: Record<string, unknown>) => void };
|
|
47
47
|
parseThinking: (value: string | null | undefined) => ConfiguredThinkingLevel | undefined;
|
|
48
48
|
builtinToolNames: readonly string[];
|
|
49
|
+
normalizeToolNames: (values: Iterable<string>) => string[];
|
|
49
50
|
thinkingEfforts: readonly string[];
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -147,10 +148,12 @@ export const STRING_SETTERS: Record<string, StringSetter> = {
|
|
|
147
148
|
result.models = value.split(",").map(s => s.trim());
|
|
148
149
|
},
|
|
149
150
|
"--tools": (result, value, deps) => {
|
|
150
|
-
const names =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
const names = deps.normalizeToolNames(
|
|
152
|
+
value
|
|
153
|
+
.split(",")
|
|
154
|
+
.map(s => s.trim())
|
|
155
|
+
.filter(Boolean),
|
|
156
|
+
);
|
|
154
157
|
const valid: string[] = [];
|
|
155
158
|
for (const name of names) {
|
|
156
159
|
if (deps.builtinToolNames.includes(name)) {
|
package/src/cli/gallery-cli.ts
CHANGED
|
@@ -138,14 +138,26 @@ export async function renderGalleryState(
|
|
|
138
138
|
return await fixture.renderState(state, width, expanded);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
// A non-customRendered fixture may borrow another tool's built-in renderer
|
|
142
|
+
// (e.g. `edit_delete` → `edit`): drive the component under that real tool
|
|
143
|
+
// name so the sample exercises the exact production branch, not the
|
|
144
|
+
// custom-tool one (which tints/pads non-framed result rows).
|
|
145
|
+
const componentName = fixture.customRendered ? name : (fixture.renderer ?? name);
|
|
146
|
+
const tool = fakeToolFor(componentName, fixture);
|
|
142
147
|
const streamingArgs = state === "streaming" ? (fixture.streamingArgs ?? fixture.args) : fixture.args;
|
|
143
148
|
// The component only calls `requestRender`/`requestComponentRender` (via
|
|
144
149
|
// its loader) during a static render; `imageBudget` is consulted solely
|
|
145
150
|
// when images render, which the gallery disables. A cast avoids
|
|
146
151
|
// constructing a real terminal.
|
|
147
152
|
const ui = { requestRender() {}, requestComponentRender() {} } as unknown as TUI;
|
|
148
|
-
const component = new ToolExecutionComponent(
|
|
153
|
+
const component = new ToolExecutionComponent(
|
|
154
|
+
componentName,
|
|
155
|
+
streamingArgs,
|
|
156
|
+
{ showImages: false },
|
|
157
|
+
tool,
|
|
158
|
+
ui,
|
|
159
|
+
getProjectDir(),
|
|
160
|
+
);
|
|
149
161
|
component.setExpanded(expanded);
|
|
150
162
|
|
|
151
163
|
if (state !== "streaming") {
|
|
@@ -62,6 +62,66 @@ export const editFixtures: Record<string, GalleryFixture> = {
|
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
64
|
|
|
65
|
+
edit_delete: {
|
|
66
|
+
label: "Delete",
|
|
67
|
+
// The registry has no `edit_delete` key, so `renderer: "edit"` routes this
|
|
68
|
+
// fixture through the real built-in edit renderer (see the harness in
|
|
69
|
+
// `gallery-cli`), keeping the sample identical to a production delete.
|
|
70
|
+
renderer: "edit",
|
|
71
|
+
streamingArgs: { file_path: "scripts/prune-changelogs.ts", op: "delete" },
|
|
72
|
+
args: { file_path: "scripts/prune-changelogs.ts", op: "delete" },
|
|
73
|
+
result: {
|
|
74
|
+
content: [{ type: "text", text: "Deleted scripts/prune-changelogs.ts" }],
|
|
75
|
+
details: {
|
|
76
|
+
op: "delete",
|
|
77
|
+
path: "scripts/prune-changelogs.ts",
|
|
78
|
+
diff: "",
|
|
79
|
+
oldText: "#!/usr/bin/env bun\n// obsolete changelog pruning helper\n",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
errorResult: {
|
|
83
|
+
content: [{ type: "text", text: "Edit failed: scripts/prune-changelogs.ts not found" }],
|
|
84
|
+
isError: true,
|
|
85
|
+
details: {
|
|
86
|
+
op: "delete",
|
|
87
|
+
path: "scripts/prune-changelogs.ts",
|
|
88
|
+
diff: "",
|
|
89
|
+
errorText: "Cannot delete scripts/prune-changelogs.ts: the file does not exist.",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
edit_move: {
|
|
95
|
+
label: "Move",
|
|
96
|
+
renderer: "edit",
|
|
97
|
+
streamingArgs: { file_path: "scripts/prune-changelogs.ts", rename: "scripts/archived/prune-changelogs.ts" },
|
|
98
|
+
args: { file_path: "scripts/prune-changelogs.ts", rename: "scripts/archived/prune-changelogs.ts" },
|
|
99
|
+
result: {
|
|
100
|
+
content: [{ type: "text", text: "Moved scripts/prune-changelogs.ts to scripts/archived/prune-changelogs.ts" }],
|
|
101
|
+
details: {
|
|
102
|
+
op: "update",
|
|
103
|
+
path: "scripts/archived/prune-changelogs.ts",
|
|
104
|
+
move: "scripts/archived/prune-changelogs.ts",
|
|
105
|
+
sourcePath: "scripts/prune-changelogs.ts",
|
|
106
|
+
diff: "",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
errorResult: {
|
|
110
|
+
content: [
|
|
111
|
+
{ type: "text", text: "Edit failed: destination scripts/archived/prune-changelogs.ts already exists" },
|
|
112
|
+
],
|
|
113
|
+
isError: true,
|
|
114
|
+
details: {
|
|
115
|
+
op: "update",
|
|
116
|
+
path: "scripts/archived/prune-changelogs.ts",
|
|
117
|
+
move: "scripts/archived/prune-changelogs.ts",
|
|
118
|
+
sourcePath: "scripts/prune-changelogs.ts",
|
|
119
|
+
diff: "",
|
|
120
|
+
errorText: "MV destination scripts/archived/prune-changelogs.ts already exists.",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
|
|
65
125
|
apply_patch: {
|
|
66
126
|
label: "Apply Patch",
|
|
67
127
|
editMode: "apply_patch",
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// biome-ignore-all lint/suspicious/noTemplateCurlyInString: sample source-code strings (read fixtures) intentionally contain literal ${...}.
|
|
2
|
-
// Gallery fixtures for the filesystem tools (read, write,
|
|
2
|
+
// Gallery fixtures for the filesystem tools (read, write, glob).
|
|
3
3
|
import { ReadToolGroupComponent } from "../../modes/components/read-tool-group";
|
|
4
4
|
import type { GalleryFixture, GalleryFixtureState, GalleryResult } from "./types";
|
|
5
5
|
|
|
6
6
|
const readSnippet = [
|
|
7
|
-
"export const
|
|
7
|
+
"export const globToolRenderer = {",
|
|
8
8
|
"\tinline: true,",
|
|
9
|
-
"\trenderCall(args:
|
|
9
|
+
"\trenderCall(args: GlobRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {",
|
|
10
10
|
"\t\tconst meta: string[] = [];",
|
|
11
11
|
"\t\tif (args.limit !== undefined) meta.push(`limit:${args.limit}`);",
|
|
12
12
|
"",
|
|
13
13
|
"\t\tconst text = renderStatusLine(",
|
|
14
|
-
'\t\t\t{ icon: "pending", title: "
|
|
14
|
+
'\t\t\t{ icon: "pending", title: "Glob", description: formatGlobRenderPaths(args.paths) || "*", meta },',
|
|
15
15
|
"\t\t\tuiTheme,",
|
|
16
16
|
"\t\t);",
|
|
17
17
|
"\t\treturn new Text(text, 0, 0);",
|
|
@@ -99,22 +99,22 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
99
99
|
read: {
|
|
100
100
|
label: "Read",
|
|
101
101
|
// Streaming: path still being typed, selector not yet appended.
|
|
102
|
-
streamingArgs: { path: "packages/coding-agent/src/tools/
|
|
103
|
-
args: { path: "packages/coding-agent/src/tools/
|
|
102
|
+
streamingArgs: { path: "packages/coding-agent/src/tools/glob" },
|
|
103
|
+
args: { path: "packages/coding-agent/src/tools/glob.ts:437-448" },
|
|
104
104
|
result: {
|
|
105
105
|
content: [
|
|
106
106
|
{
|
|
107
107
|
type: "text",
|
|
108
108
|
text: [
|
|
109
|
-
"[packages/coding-agent/src/tools/
|
|
110
|
-
"437:export const
|
|
109
|
+
"[packages/coding-agent/src/tools/glob.ts#E48E]",
|
|
110
|
+
"437:export const globToolRenderer = {",
|
|
111
111
|
"438:\tinline: true,",
|
|
112
|
-
"439:\trenderCall(args:
|
|
112
|
+
"439:\trenderCall(args: GlobRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {",
|
|
113
113
|
"440:\t\tconst meta: string[] = [];",
|
|
114
114
|
"441:\t\tif (args.limit !== undefined) meta.push(`limit:${args.limit}`);",
|
|
115
115
|
"442:",
|
|
116
116
|
"443:\t\tconst text = renderStatusLine(",
|
|
117
|
-
'444:\t\t\t{ icon: "pending", title: "
|
|
117
|
+
'444:\t\t\t{ icon: "pending", title: "Glob", description: formatGlobRenderPaths(args.paths) || "*", meta },',
|
|
118
118
|
"445:\t\t\tuiTheme,",
|
|
119
119
|
"446:\t\t);",
|
|
120
120
|
"447:\t\treturn new Text(text, 0, 0);",
|
|
@@ -124,7 +124,7 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
124
124
|
],
|
|
125
125
|
details: {
|
|
126
126
|
kind: "file",
|
|
127
|
-
resolvedPath: "/Users/dev/Projects/pi/packages/coding-agent/src/tools/
|
|
127
|
+
resolvedPath: "/Users/dev/Projects/pi/packages/coding-agent/src/tools/glob.ts",
|
|
128
128
|
contentType: "text/typescript",
|
|
129
129
|
displayContent: { text: readSnippet, startLine: 437 },
|
|
130
130
|
},
|
|
@@ -134,7 +134,7 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
134
134
|
content: [
|
|
135
135
|
{
|
|
136
136
|
type: "text",
|
|
137
|
-
text: "Error: ENOENT: no such file or directory, open 'packages/coding-agent/src/tools/
|
|
137
|
+
text: "Error: ENOENT: no such file or directory, open 'packages/coding-agent/src/tools/glob.ts'",
|
|
138
138
|
},
|
|
139
139
|
],
|
|
140
140
|
},
|
|
@@ -179,8 +179,8 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
179
179
|
},
|
|
180
180
|
},
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
label: "
|
|
182
|
+
glob: {
|
|
183
|
+
label: "Glob",
|
|
184
184
|
// Streaming: glob half-typed, no limit yet.
|
|
185
185
|
streamingArgs: { paths: ["packages/coding-agent/src/tools/*-render"] },
|
|
186
186
|
args: { paths: ["packages/coding-agent/src/**/*.test.ts"], limit: 50 },
|
|
@@ -191,7 +191,7 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
191
191
|
text: [
|
|
192
192
|
"packages/coding-agent/src/tools/read.test.ts",
|
|
193
193
|
"packages/coding-agent/src/tools/write.test.ts",
|
|
194
|
-
"packages/coding-agent/src/tools/
|
|
194
|
+
"packages/coding-agent/src/tools/glob.test.ts",
|
|
195
195
|
"packages/coding-agent/src/cli/gallery-cli.test.ts",
|
|
196
196
|
"packages/coding-agent/src/edit/edit.test.ts",
|
|
197
197
|
].join("\n"),
|
|
@@ -205,7 +205,7 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
205
205
|
files: [
|
|
206
206
|
"packages/coding-agent/src/cli/gallery-cli.test.ts",
|
|
207
207
|
"packages/coding-agent/src/edit/edit.test.ts",
|
|
208
|
-
"packages/coding-agent/src/tools/
|
|
208
|
+
"packages/coding-agent/src/tools/glob.test.ts",
|
|
209
209
|
"packages/coding-agent/src/tools/read.test.ts",
|
|
210
210
|
"packages/coding-agent/src/tools/write.test.ts",
|
|
211
211
|
],
|
|
@@ -213,7 +213,7 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
213
213
|
},
|
|
214
214
|
errorResult: {
|
|
215
215
|
isError: true,
|
|
216
|
-
content: [{ type: "text", text: "
|
|
216
|
+
content: [{ type: "text", text: "Glob failed: invalid glob pattern '[unclosed'." }],
|
|
217
217
|
details: { error: "invalid glob pattern '[unclosed'" },
|
|
218
218
|
},
|
|
219
219
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/** Gallery fixtures for the search tools (
|
|
1
|
+
/** Gallery fixtures for the search tools (grep, search_tool_bm25, ast_grep). */
|
|
2
2
|
import type { GalleryFixture } from "./types";
|
|
3
3
|
|
|
4
4
|
export const searchFixtures: Record<string, GalleryFixture> = {
|
|
5
|
-
|
|
6
|
-
label: "
|
|
5
|
+
grep: {
|
|
6
|
+
label: "Grep",
|
|
7
7
|
streamingArgs: {
|
|
8
8
|
pattern: "useState",
|
|
9
9
|
},
|
|
@@ -101,7 +101,7 @@ export const searchFixtures: Record<string, GalleryFixture> = {
|
|
|
101
101
|
limit: 5,
|
|
102
102
|
total_tools: 142,
|
|
103
103
|
activated_tools: ["docling_extract_tables", "docling_convert", "pdf_read_text"],
|
|
104
|
-
active_selected_tools: ["read", "
|
|
104
|
+
active_selected_tools: ["read", "grep", "edit", "bash"],
|
|
105
105
|
tools: [
|
|
106
106
|
{
|
|
107
107
|
name: "docling_extract_tables",
|
package/src/cli/usage-cli.ts
CHANGED
|
@@ -326,7 +326,26 @@ function formatAccountHeader(
|
|
|
326
326
|
const planType = report.metadata?.planType;
|
|
327
327
|
if (typeof planType === "string" && planType) header += chalk.dim(` · plan: ${planType}`);
|
|
328
328
|
const savedResets = report.resetCredits?.availableCount ?? 0;
|
|
329
|
-
if (savedResets > 0)
|
|
329
|
+
if (savedResets > 0) {
|
|
330
|
+
header += chalk.cyan(` · ✦ ${savedResets} saved reset${savedResets === 1 ? "" : "s"}`);
|
|
331
|
+
const credits = report.resetCredits?.credits;
|
|
332
|
+
if (credits) {
|
|
333
|
+
const expiries = credits
|
|
334
|
+
.filter(c => c.expiresAt)
|
|
335
|
+
.map(c => ({ date: c.expiresAt!, ms: Date.parse(c.expiresAt!) }))
|
|
336
|
+
.filter(c => !Number.isNaN(c.ms))
|
|
337
|
+
.sort((a, b) => a.ms - b.ms);
|
|
338
|
+
const upcoming = expiries.find(c => c.ms > nowMs);
|
|
339
|
+
if (upcoming) {
|
|
340
|
+
header += chalk.dim(
|
|
341
|
+
` · soonest expires in ${formatDuration(upcoming.ms - nowMs)} (${upcoming.date.slice(0, 10)})`,
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
const lastExpired = expiries.at(-1);
|
|
345
|
+
if (lastExpired) header += chalk.dim(` · expired (${lastExpired.date.slice(0, 10)})`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
330
349
|
if (report.fetchedAt && nowMs - report.fetchedAt > 90_000) {
|
|
331
350
|
header += chalk.dim(` · fetched ${formatDuration(nowMs - report.fetchedAt)} ago`);
|
|
332
351
|
}
|
|
@@ -120,7 +120,7 @@ ${chalk.bold("Arguments:")}
|
|
|
120
120
|
|
|
121
121
|
${chalk.bold("Options:")}
|
|
122
122
|
--provider <name> Provider: ${PROVIDERS.join(", ")}
|
|
123
|
-
--recency <value> Recency filter (
|
|
123
|
+
--recency <value> Recency filter (when supported): ${RECENCY_OPTIONS.join(", ")}
|
|
124
124
|
-l, --limit <n> Max results to return
|
|
125
125
|
--compact Render condensed output
|
|
126
126
|
-h, --help Show this help
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import type { InteractiveModeContext } from "../modes/types";
|
|
3
|
+
|
|
4
|
+
/** Display name for this process's user in collab sessions. */
|
|
5
|
+
export function collabDisplayName(ctx: InteractiveModeContext): string {
|
|
6
|
+
const configured = (ctx.settings.get("collab.displayName") ?? "").trim();
|
|
7
|
+
if (configured) return configured;
|
|
8
|
+
try {
|
|
9
|
+
return os.userInfo().username;
|
|
10
|
+
} catch {
|
|
11
|
+
return "anonymous";
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/collab/guest.ts
CHANGED
|
@@ -24,7 +24,7 @@ import type { SessionEntry } from "../session/session-entries";
|
|
|
24
24
|
import { shouldDisableReasoning, toReasoningEffort } from "../thinking";
|
|
25
25
|
import { setSessionTerminalTitle } from "../utils/title-generator";
|
|
26
26
|
import { importRoomKey } from "./crypto";
|
|
27
|
-
import { collabDisplayName } from "./
|
|
27
|
+
import { collabDisplayName } from "./display-name";
|
|
28
28
|
import {
|
|
29
29
|
type AgentSnapshot,
|
|
30
30
|
COLLAB_PROTO,
|
|
@@ -276,6 +276,7 @@ export class CollabGuestLink {
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
this.#ctx.collabGuest = this;
|
|
279
|
+
this.#ctx.syncRunningSubagentBadge();
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
/** User-initiated leave (or post-disconnect cleanup): restore the previous session. */
|
|
@@ -357,6 +358,7 @@ export class CollabGuestLink {
|
|
|
357
358
|
this.#applyHostState(pending.state);
|
|
358
359
|
this.#ctx.resetObserverRegistry();
|
|
359
360
|
this.#applyAgentSnapshots(pending.agents);
|
|
361
|
+
this.#ctx.syncRunningSubagentBadge();
|
|
360
362
|
this.#assistantStreamSynced = false;
|
|
361
363
|
setSessionTerminalTitle(pending.state.sessionName ?? pending.header.title, pending.state.cwd);
|
|
362
364
|
this.#ctx.chatContainer.clear();
|
|
@@ -440,6 +442,7 @@ export class CollabGuestLink {
|
|
|
440
442
|
break;
|
|
441
443
|
case "agents":
|
|
442
444
|
this.#applyAgentSnapshots(frame.agents);
|
|
445
|
+
this.#ctx.syncRunningSubagentBadge();
|
|
443
446
|
break;
|
|
444
447
|
case "transcript": {
|
|
445
448
|
const resolve = this.#pendingTranscripts.get(frame.reqId);
|
|
@@ -573,6 +576,7 @@ export class CollabGuestLink {
|
|
|
573
576
|
this.#ctx.statusLine.setCollabStatus(null);
|
|
574
577
|
this.#flushPendingTranscripts();
|
|
575
578
|
this.#clearAgentMirror();
|
|
579
|
+
this.#ctx.syncRunningSubagentBadge();
|
|
576
580
|
this.#ctx.resetObserverRegistry();
|
|
577
581
|
this.#clearTransientUi();
|
|
578
582
|
// Replica file stays on disk: it is a valid session file outside the
|