@oh-my-pi/pi-coding-agent 15.10.11 → 15.11.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 +103 -2
- package/dist/cli.js +5790 -5731
- package/dist/types/async/index.d.ts +0 -1
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
- package/dist/types/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +18 -0
- package/dist/types/config/settings-schema.d.ts +85 -34
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- package/dist/types/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/history-protocol.d.ts +14 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/irc/bus.d.ts +66 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- package/dist/types/modes/components/agent-hub.d.ts +30 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
- package/dist/types/modes/components/welcome.d.ts +3 -9
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +10 -4
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +5 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-lifecycle.d.ts +51 -0
- package/dist/types/registry/agent-registry.d.ts +16 -5
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +49 -32
- package/dist/types/session/messages.d.ts +2 -4
- package/dist/types/session/session-history-format.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +21 -3
- package/dist/types/session/streaming-output.d.ts +46 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +12 -2
- package/dist/types/task/index.d.ts +13 -6
- package/dist/types/task/output-manager.d.ts +0 -7
- package/dist/types/task/repair-args.d.ts +8 -7
- package/dist/types/task/types.d.ts +63 -51
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/find.d.ts +0 -11
- package/dist/types/tools/grouped-file-output.d.ts +0 -49
- package/dist/types/tools/index.d.ts +7 -3
- package/dist/types/tools/irc.d.ts +76 -38
- package/dist/types/tools/job.d.ts +7 -1
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- package/examples/extensions/with-deps/package.json +1 -0
- package/package.json +11 -10
- package/scripts/bundle-dist.ts +28 -19
- package/src/async/index.ts +0 -1
- package/src/auto-thinking/classifier.ts +1 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/agentic.ts +230 -115
- package/src/cli/gallery-fixtures/types.ts +5 -0
- package/src/cli-commands.ts +29 -0
- package/src/cli.ts +28 -15
- package/src/commands/launch.ts +4 -0
- package/src/commit/agentic/tools/analyze-file.ts +38 -19
- package/src/commit/model-selection.ts +3 -2
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/keybindings.ts +6 -1
- package/src/config/model-registry.ts +97 -30
- package/src/config/model-resolver.ts +60 -0
- package/src/config/settings-schema.ts +99 -55
- package/src/config/settings.ts +68 -3
- package/src/edit/hashline/execute.ts +39 -2
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/eval/__tests__/agent-bridge.test.ts +5 -3
- package/src/eval/agent-bridge.ts +3 -16
- package/src/eval/completion-bridge.ts +1 -0
- package/src/eval/js/shared/prelude.txt +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -1
- package/src/eval/py/kernel.ts +31 -11
- package/src/eval/py/prelude.py +5 -6
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +82 -3
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +38 -13
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/extensibility/shared-events.ts +2 -2
- package/src/hindsight/bank.ts +17 -2
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +3 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/irc/bus.ts +292 -0
- package/src/main.ts +26 -66
- package/src/memories/index.ts +2 -0
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +151 -4
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
- package/src/modes/components/assistant-message.ts +19 -21
- package/src/modes/components/compaction-summary-message.ts +68 -32
- package/src/modes/components/custom-editor.ts +10 -0
- package/src/modes/components/footer.ts +3 -1
- package/src/modes/components/status-line/component.ts +118 -34
- package/src/modes/components/tool-execution.ts +31 -1
- package/src/modes/components/ttsr-notification.ts +72 -30
- package/src/modes/components/welcome.ts +9 -33
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +65 -0
- package/src/modes/controllers/extension-ui-controller.ts +8 -8
- package/src/modes/controllers/input-controller.ts +19 -2
- package/src/modes/controllers/mcp-command-controller.ts +38 -3
- package/src/modes/controllers/selector-controller.ts +21 -17
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +47 -22
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/theme/theme.ts +18 -5
- package/src/modes/types.ts +5 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +51 -49
- package/src/prompts/system/irc-incoming.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +0 -5
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/irc.md +29 -19
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/task-summary.md +5 -16
- package/src/prompts/tools/task.md +38 -29
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +16 -5
- package/src/sdk.ts +37 -10
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +422 -291
- package/src/session/messages.ts +11 -78
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +59 -5
- package/src/session/streaming-output.ts +226 -10
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/system-prompt.ts +14 -0
- package/src/task/executor.ts +851 -461
- package/src/task/index.ts +721 -796
- package/src/task/output-manager.ts +0 -11
- package/src/task/render.ts +148 -63
- package/src/task/repair-args.ts +21 -9
- package/src/task/types.ts +82 -66
- package/src/thinking.ts +7 -0
- package/src/tiny/title-client.ts +34 -5
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- package/src/tools/ask.ts +4 -2
- package/src/tools/bash.ts +61 -10
- package/src/tools/browser/tab-worker.ts +26 -7
- package/src/tools/browser.ts +28 -1
- package/src/tools/find.ts +2 -27
- package/src/tools/grouped-file-output.ts +1 -118
- package/src/tools/image-gen.ts +11 -4
- package/src/tools/index.ts +17 -13
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +596 -171
- package/src/tools/job.ts +41 -7
- package/src/tools/read.ts +57 -1
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +4 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/src/async/support.ts +0 -5
- package/src/task/simple-mode.ts +0 -27
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol handler for history:// URLs.
|
|
3
|
+
*
|
|
4
|
+
* Exposes agent transcripts as concise markdown. Live refs render from the
|
|
5
|
+
* in-memory message array; parked refs (session disposed, sessionFile
|
|
6
|
+
* retained) load read-only from the JSONL session file — no writer, no lock.
|
|
7
|
+
*
|
|
8
|
+
* URL forms:
|
|
9
|
+
* - history:// - Index of all registry agents (id, status, kind, last activity)
|
|
10
|
+
* - history://<agentId> - Concise markdown transcript of that agent
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentRef } from "../registry/agent-registry";
|
|
13
|
+
import { AgentRegistry } from "../registry/agent-registry";
|
|
14
|
+
import { formatSessionHistoryMarkdown } from "../session/session-history-format";
|
|
15
|
+
import { loadSessionMessagesReadOnly } from "../session/session-manager";
|
|
16
|
+
import type { InternalResource, InternalUrl, ProtocolHandler, UrlCompletion } from "./types";
|
|
17
|
+
|
|
18
|
+
/** Humanize a last-activity timestamp as `Ns/Nm/Nh/Nd ago`. */
|
|
19
|
+
function formatAgo(timestamp: number): string {
|
|
20
|
+
const diffMs = Math.max(0, Date.now() - timestamp);
|
|
21
|
+
const secs = Math.floor(diffMs / 1000);
|
|
22
|
+
if (secs < 60) return `${secs}s ago`;
|
|
23
|
+
const mins = Math.floor(secs / 60);
|
|
24
|
+
if (mins < 60) return `${mins}m ago`;
|
|
25
|
+
const hours = Math.floor(mins / 60);
|
|
26
|
+
if (hours < 24) return `${hours}h ago`;
|
|
27
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Handler for history:// URLs.
|
|
32
|
+
*
|
|
33
|
+
* Resolves agent ids against the global AgentRegistry, serving transcripts
|
|
34
|
+
* for both live and parked agents.
|
|
35
|
+
*/
|
|
36
|
+
export class HistoryProtocolHandler implements ProtocolHandler {
|
|
37
|
+
readonly scheme = "history";
|
|
38
|
+
readonly immutable = false;
|
|
39
|
+
|
|
40
|
+
async resolve(url: InternalUrl): Promise<InternalResource> {
|
|
41
|
+
const agentId = url.rawHost || url.hostname;
|
|
42
|
+
const registry = AgentRegistry.global();
|
|
43
|
+
|
|
44
|
+
if (!agentId) {
|
|
45
|
+
const content = this.#renderIndex(registry.list());
|
|
46
|
+
return {
|
|
47
|
+
url: url.href,
|
|
48
|
+
content,
|
|
49
|
+
contentType: "text/markdown",
|
|
50
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let ref = registry.get(agentId);
|
|
55
|
+
if (!ref) {
|
|
56
|
+
// Case-insensitive fallback: agent ids are human-typed (e.g. AuthLoader).
|
|
57
|
+
const lower = agentId.toLowerCase();
|
|
58
|
+
ref = registry.list().find(candidate => candidate.id.toLowerCase() === lower);
|
|
59
|
+
}
|
|
60
|
+
if (!ref) {
|
|
61
|
+
const known = registry.list().map(candidate => candidate.id);
|
|
62
|
+
const knownStr = known.length > 0 ? known.join(", ") : "none";
|
|
63
|
+
throw new Error(`Unknown agent: ${agentId}\nKnown agents: ${knownStr}\nList all with history://`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const notes: string[] = [];
|
|
67
|
+
let messages: unknown[];
|
|
68
|
+
if (ref.session) {
|
|
69
|
+
messages = ref.session.messages;
|
|
70
|
+
notes.push("Source: live session");
|
|
71
|
+
} else if (ref.sessionFile) {
|
|
72
|
+
messages = await loadSessionMessagesReadOnly(ref.sessionFile);
|
|
73
|
+
notes.push(`Source: session file (read-only, ${ref.status})`);
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error(`Agent ${ref.id} has no transcript: session is gone and no session file was retained`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const content = formatSessionHistoryMarkdown(messages, { title: `${ref.id} (${ref.status})` });
|
|
79
|
+
return {
|
|
80
|
+
url: url.href,
|
|
81
|
+
content,
|
|
82
|
+
contentType: "text/markdown",
|
|
83
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
84
|
+
sourcePath: ref.sessionFile ?? undefined,
|
|
85
|
+
notes,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#renderIndex(refs: AgentRef[]): string {
|
|
90
|
+
const lines: string[] = ["# Agents", ""];
|
|
91
|
+
if (refs.length === 0) {
|
|
92
|
+
lines.push("No agents registered.");
|
|
93
|
+
return `${lines.join("\n")}\n`;
|
|
94
|
+
}
|
|
95
|
+
lines.push("| id | status | kind | parent | last activity |", "|---|---|---|---|---|");
|
|
96
|
+
for (const ref of refs) {
|
|
97
|
+
lines.push(
|
|
98
|
+
`| ${ref.id} | ${ref.status} | ${ref.kind} | ${ref.parentId ?? "—"} | ${formatAgo(ref.lastActivity)} |`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
lines.push("", "Read a transcript with `read history://<id>`.");
|
|
102
|
+
return `${lines.join("\n")}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async complete(): Promise<UrlCompletion[]> {
|
|
106
|
+
return AgentRegistry.global()
|
|
107
|
+
.list()
|
|
108
|
+
.map(ref => ({
|
|
109
|
+
value: ref.id,
|
|
110
|
+
description: `${ref.status} · ${ref.kind}${ref.parentId ? ` · parent ${ref.parentId}` : ""}`,
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal URL router for internal protocols (`agent://`, `artifact://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`).
|
|
2
|
+
* Internal URL router for internal protocols (`agent://`, `artifact://`, `history://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`).
|
|
3
3
|
*
|
|
4
4
|
* One process-global router with one handler per scheme. Access via
|
|
5
5
|
* `InternalUrlRouter.instance()`. Handlers are stateless; per-session and
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { AgentProtocolHandler } from "./agent-protocol";
|
|
9
9
|
import { ArtifactProtocolHandler } from "./artifact-protocol";
|
|
10
|
+
import { HistoryProtocolHandler } from "./history-protocol";
|
|
10
11
|
import { IssueProtocolHandler, PrProtocolHandler } from "./issue-pr-protocol";
|
|
11
12
|
import { LocalProtocolHandler } from "./local-protocol";
|
|
12
13
|
import { McpProtocolHandler } from "./mcp-protocol";
|
|
@@ -35,6 +36,7 @@ export class InternalUrlRouter {
|
|
|
35
36
|
this.register(new McpProtocolHandler());
|
|
36
37
|
this.register(new IssueProtocolHandler());
|
|
37
38
|
this.register(new PrProtocolHandler());
|
|
39
|
+
this.register(new HistoryProtocolHandler());
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/** Process-global router instance. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the internal URL routing system.
|
|
3
3
|
*
|
|
4
|
-
* Internal URLs (`agent://`, `artifact://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`) are resolved by tools like read,
|
|
4
|
+
* Internal URLs (`agent://`, `artifact://`, `history://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`) are resolved by tools like read,
|
|
5
5
|
* providing access to agent outputs and server resources without exposing filesystem paths.
|
|
6
6
|
*/
|
|
7
7
|
|
package/src/irc/bus.ts
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IrcBus - Process-global mailbox bus for agent-to-agent messaging.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the old auto-reply model: a `send` never blocks on the recipient
|
|
5
|
+
* generating anything. Delivery resolves the recipient via the global
|
|
6
|
+
* AgentRegistry — parked agents are revived through the
|
|
7
|
+
* AgentLifecycleManager, idle agents are woken with a real turn, and busy
|
|
8
|
+
* agents receive the message as a non-interrupting aside at the next step
|
|
9
|
+
* boundary (see AgentSession.deliverIrcMessage). Replies are real turns by
|
|
10
|
+
* the recipient, observed via `wait`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { logger, Snowflake } from "@oh-my-pi/pi-utils";
|
|
14
|
+
import { AgentLifecycleManager } from "../registry/agent-lifecycle";
|
|
15
|
+
import { AgentRegistry, MAIN_AGENT_ID } from "../registry/agent-registry";
|
|
16
|
+
import type { CustomMessage } from "../session/messages";
|
|
17
|
+
|
|
18
|
+
export interface IrcMessage {
|
|
19
|
+
id: string;
|
|
20
|
+
/** Sender agent id. */
|
|
21
|
+
from: string;
|
|
22
|
+
/** Recipient agent id (resolved; "all" is expanded by the tool, not stored). */
|
|
23
|
+
to: string;
|
|
24
|
+
body: string;
|
|
25
|
+
ts: number;
|
|
26
|
+
/** Message id being answered. */
|
|
27
|
+
replyTo?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IrcDeliveryReceipt {
|
|
31
|
+
to: string;
|
|
32
|
+
outcome: "injected" | "woken" | "revived" | "failed";
|
|
33
|
+
error?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IrcWaiter {
|
|
37
|
+
from?: string;
|
|
38
|
+
resolve: (msg: IrcMessage) => void;
|
|
39
|
+
cancel: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Mailbox cap per agent; oldest messages are dropped beyond it. */
|
|
43
|
+
const MAILBOX_CAP = 100;
|
|
44
|
+
|
|
45
|
+
export class IrcBus {
|
|
46
|
+
static #global: IrcBus | undefined;
|
|
47
|
+
|
|
48
|
+
static global(): IrcBus {
|
|
49
|
+
if (!IrcBus.#global) {
|
|
50
|
+
IrcBus.#global = new IrcBus();
|
|
51
|
+
}
|
|
52
|
+
return IrcBus.#global;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Reset the global bus. Test-only. */
|
|
56
|
+
static resetGlobalForTests(): void {
|
|
57
|
+
IrcBus.#global = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
readonly #registry: AgentRegistry;
|
|
61
|
+
readonly #lifecycle: () => AgentLifecycleManager;
|
|
62
|
+
readonly #mailboxes = new Map<string, IrcMessage[]>();
|
|
63
|
+
readonly #waiters = new Map<string, IrcWaiter[]>();
|
|
64
|
+
|
|
65
|
+
constructor(registry: AgentRegistry = AgentRegistry.global(), lifecycle?: AgentLifecycleManager) {
|
|
66
|
+
this.#registry = registry;
|
|
67
|
+
// Lazy: the lifecycle global self-constructs against the global registry,
|
|
68
|
+
// so only touch it when a parked recipient actually needs reviving.
|
|
69
|
+
this.#lifecycle = () => lifecycle ?? AgentLifecycleManager.global();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Fire-and-forget delivery. Never blocks on the recipient generating
|
|
74
|
+
* anything: the receipt reports how the message reached the recipient
|
|
75
|
+
* (waiter/aside = "injected", idle wake = "woken", park revival =
|
|
76
|
+
* "revived"), not what they did with it.
|
|
77
|
+
*
|
|
78
|
+
* Mailbox semantics: a successfully delivered message never lingers in
|
|
79
|
+
* the recipient's mailbox — injection/wake puts the full body into their
|
|
80
|
+
* context, so buffering it too would double-deliver via a later
|
|
81
|
+
* `wait`/`inbox` and inflate unread counts. Only a failed live hand-off
|
|
82
|
+
* is buffered for the recipient to drain later.
|
|
83
|
+
*/
|
|
84
|
+
async send(msg: Omit<IrcMessage, "id" | "ts">): Promise<IrcDeliveryReceipt> {
|
|
85
|
+
const message: IrcMessage = { ...msg, id: Snowflake.next(), ts: Date.now() };
|
|
86
|
+
const ref = this.#registry.get(message.to);
|
|
87
|
+
if (!ref || ref.status === "aborted") {
|
|
88
|
+
return { to: message.to, outcome: "failed", error: `Unknown or terminated agent "${message.to}".` };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let revived = false;
|
|
92
|
+
if (ref.status === "parked") {
|
|
93
|
+
try {
|
|
94
|
+
await this.#lifecycle().ensureLive(message.to);
|
|
95
|
+
revived = true;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
to: message.to,
|
|
99
|
+
outcome: "failed",
|
|
100
|
+
error: error instanceof Error ? error.message : String(error),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// A pending `wait` from the recipient consumes the message directly —
|
|
106
|
+
// it is returned from their irc tool call and never hits the inbox or
|
|
107
|
+
// the session injection path.
|
|
108
|
+
const waiter = this.#takeMatchingWaiter(message.to, message.from);
|
|
109
|
+
if (waiter) {
|
|
110
|
+
waiter.resolve(message);
|
|
111
|
+
this.#relayToMainUi(message);
|
|
112
|
+
return { to: message.to, outcome: revived ? "revived" : "injected" };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const session = this.#registry.get(message.to)?.session;
|
|
116
|
+
if (!session) {
|
|
117
|
+
return { to: message.to, outcome: "failed", error: `Agent "${message.to}" has no live session.` };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const delivery = await session.deliverIrcMessage(message);
|
|
122
|
+
this.#relayToMainUi(message);
|
|
123
|
+
return { to: message.to, outcome: revived ? "revived" : delivery };
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// Live hand-off failed (e.g. recipient disposed mid-shutdown): buffer
|
|
126
|
+
// the message so a later `wait`/`inbox` from the recipient can still
|
|
127
|
+
// pick it up. The receipt stays "failed" — the recipient has not
|
|
128
|
+
// seen it.
|
|
129
|
+
this.#enqueue(message);
|
|
130
|
+
return {
|
|
131
|
+
to: message.to,
|
|
132
|
+
outcome: "failed",
|
|
133
|
+
error: error instanceof Error ? error.message : String(error),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Block until a message for `agentId` (optionally from `filter.from`)
|
|
140
|
+
* arrives; consume + return it. Null on timeout (`timeoutMs <= 0` waits
|
|
141
|
+
* forever). Rejects when `signal` aborts. By default, already-buffered
|
|
142
|
+
* mail satisfies the wait before parking a future waiter; callers that
|
|
143
|
+
* need a strictly future reply can disable that drain.
|
|
144
|
+
*/
|
|
145
|
+
async wait(
|
|
146
|
+
agentId: string,
|
|
147
|
+
filter: { from?: string },
|
|
148
|
+
timeoutMs: number,
|
|
149
|
+
signal?: AbortSignal,
|
|
150
|
+
options?: { drainPending?: boolean },
|
|
151
|
+
): Promise<IrcMessage | null> {
|
|
152
|
+
if (signal?.aborted) {
|
|
153
|
+
throw signal.reason instanceof Error ? signal.reason : new Error("IRC wait aborted");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (options?.drainPending !== false) {
|
|
157
|
+
// Already-pending mail satisfies the wait without parking a waiter.
|
|
158
|
+
const pending = this.#takeFromMailbox(agentId, filter.from);
|
|
159
|
+
if (pending) return pending;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const { promise, resolve, reject } = Promise.withResolvers<IrcMessage | null>();
|
|
163
|
+
let timer: NodeJS.Timeout | undefined;
|
|
164
|
+
let onAbort: (() => void) | undefined;
|
|
165
|
+
|
|
166
|
+
const waiter: IrcWaiter = {
|
|
167
|
+
from: filter.from,
|
|
168
|
+
resolve: msg => {
|
|
169
|
+
cleanup();
|
|
170
|
+
resolve(msg);
|
|
171
|
+
},
|
|
172
|
+
cancel: () => {
|
|
173
|
+
cleanup();
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
const cleanup = (): void => {
|
|
177
|
+
this.#removeWaiter(agentId, waiter);
|
|
178
|
+
clearTimeout(timer);
|
|
179
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (signal) {
|
|
183
|
+
onAbort = () => {
|
|
184
|
+
cleanup();
|
|
185
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error("IRC wait aborted"));
|
|
186
|
+
};
|
|
187
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
188
|
+
}
|
|
189
|
+
if (timeoutMs > 0) {
|
|
190
|
+
timer = setTimeout(() => {
|
|
191
|
+
cleanup();
|
|
192
|
+
resolve(null);
|
|
193
|
+
}, timeoutMs);
|
|
194
|
+
timer.unref?.();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let waiters = this.#waiters.get(agentId);
|
|
198
|
+
if (!waiters) {
|
|
199
|
+
waiters = [];
|
|
200
|
+
this.#waiters.set(agentId, waiters);
|
|
201
|
+
}
|
|
202
|
+
waiters.push(waiter);
|
|
203
|
+
return promise;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Drain (or peek) pending messages for `agentId`. */
|
|
207
|
+
inbox(agentId: string, opts?: { peek?: boolean }): IrcMessage[] {
|
|
208
|
+
const mailbox = this.#mailboxes.get(agentId);
|
|
209
|
+
if (!mailbox || mailbox.length === 0) return [];
|
|
210
|
+
if (opts?.peek) return [...mailbox];
|
|
211
|
+
this.#mailboxes.delete(agentId);
|
|
212
|
+
return mailbox;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
unreadCount(agentId: string): number {
|
|
216
|
+
return this.#mailboxes.get(agentId)?.length ?? 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#enqueue(message: IrcMessage): void {
|
|
220
|
+
let mailbox = this.#mailboxes.get(message.to);
|
|
221
|
+
if (!mailbox) {
|
|
222
|
+
mailbox = [];
|
|
223
|
+
this.#mailboxes.set(message.to, mailbox);
|
|
224
|
+
}
|
|
225
|
+
mailbox.push(message);
|
|
226
|
+
if (mailbox.length > MAILBOX_CAP) {
|
|
227
|
+
const dropped = mailbox.shift();
|
|
228
|
+
logger.debug("IrcBus: mailbox full, dropped oldest message", {
|
|
229
|
+
agentId: message.to,
|
|
230
|
+
droppedId: dropped?.id,
|
|
231
|
+
droppedFrom: dropped?.from,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Resolve the OLDEST waiter for `agentId` whose from-filter accepts `from`. */
|
|
237
|
+
#takeMatchingWaiter(agentId: string, from: string): IrcWaiter | undefined {
|
|
238
|
+
const waiters = this.#waiters.get(agentId);
|
|
239
|
+
if (!waiters) return undefined;
|
|
240
|
+
const index = waiters.findIndex(waiter => !waiter.from || waiter.from === from);
|
|
241
|
+
if (index === -1) return undefined;
|
|
242
|
+
const [waiter] = waiters.splice(index, 1);
|
|
243
|
+
if (waiters.length === 0) this.#waiters.delete(agentId);
|
|
244
|
+
return waiter;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#removeWaiter(agentId: string, waiter: IrcWaiter): void {
|
|
248
|
+
const waiters = this.#waiters.get(agentId);
|
|
249
|
+
if (!waiters) return;
|
|
250
|
+
const index = waiters.indexOf(waiter);
|
|
251
|
+
if (index !== -1) waiters.splice(index, 1);
|
|
252
|
+
if (waiters.length === 0) this.#waiters.delete(agentId);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#takeFromMailbox(agentId: string, from?: string): IrcMessage | undefined {
|
|
256
|
+
const mailbox = this.#mailboxes.get(agentId);
|
|
257
|
+
if (!mailbox) return undefined;
|
|
258
|
+
const index = from ? mailbox.findIndex(msg => msg.from === from) : 0;
|
|
259
|
+
if (index === -1 || mailbox.length === 0) return undefined;
|
|
260
|
+
const [message] = mailbox.splice(index, 1);
|
|
261
|
+
if (mailbox.length === 0) this.#mailboxes.delete(agentId);
|
|
262
|
+
return message;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Surface agent↔agent traffic as a display-only card on the main session
|
|
267
|
+
* UI. Skipped when the main agent is either endpoint: as recipient its
|
|
268
|
+
* own `deliverIrcMessage` (or `wait` tool result) already shows the
|
|
269
|
+
* message, and as sender the irc send tool call already rendered the
|
|
270
|
+
* outbound body — relaying it again would duplicate it in the transcript.
|
|
271
|
+
*/
|
|
272
|
+
#relayToMainUi(message: IrcMessage): void {
|
|
273
|
+
if (message.to === MAIN_AGENT_ID || message.from === MAIN_AGENT_ID) return;
|
|
274
|
+
const mainSession = this.#registry.get(MAIN_AGENT_ID)?.session;
|
|
275
|
+
if (!mainSession) return;
|
|
276
|
+
const record: CustomMessage = {
|
|
277
|
+
role: "custom",
|
|
278
|
+
customType: "irc:relay",
|
|
279
|
+
content: `[IRC \`${message.from}\` → \`${message.to}\`]\n\n${message.body}`,
|
|
280
|
+
display: true,
|
|
281
|
+
details: { from: message.from, to: message.to, body: message.body },
|
|
282
|
+
attribution: "agent",
|
|
283
|
+
timestamp: message.ts,
|
|
284
|
+
};
|
|
285
|
+
try {
|
|
286
|
+
mainSession.emitIrcRelayObservation(record);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
// Display-only forwarding must never affect delivery semantics.
|
|
289
|
+
logger.debug("IrcBus: main UI relay failed", { to: message.to, error: String(error) });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -51,7 +51,6 @@ import { ExtensionRunner } from "./extensibility/extensions/runner";
|
|
|
51
51
|
import type { ExtensionUIContext } from "./extensibility/extensions/types";
|
|
52
52
|
import { scheduleMarketplaceAutoUpdate } from "./extensibility/plugins/marketplace-auto-update";
|
|
53
53
|
import type { MCPManager } from "./mcp";
|
|
54
|
-
import { WelcomeComponent } from "./modes/components/welcome";
|
|
55
54
|
import { InteractiveMode } from "./modes/interactive-mode";
|
|
56
55
|
import type { PrintModeOptions } from "./modes/print-mode";
|
|
57
56
|
import { CURRENT_SETUP_VERSION } from "./modes/setup-version";
|
|
@@ -67,10 +66,10 @@ import {
|
|
|
67
66
|
import type { AgentSession } from "./session/agent-session";
|
|
68
67
|
import type { AuthStorage } from "./session/auth-storage";
|
|
69
68
|
import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
|
|
70
|
-
import { resolvePromptInput } from "./system-prompt";
|
|
69
|
+
import { discoverTitleSystemPromptFile, resolvePromptInput } from "./system-prompt";
|
|
71
70
|
import { initTelemetryExport, isTelemetryExportEnabled } from "./telemetry-export";
|
|
72
71
|
import { AUTO_THINKING } from "./thinking";
|
|
73
|
-
import {
|
|
72
|
+
import type { LspStartupServerInfo } from "./tools";
|
|
74
73
|
import {
|
|
75
74
|
getChangelogPath,
|
|
76
75
|
getNewEntries,
|
|
@@ -85,6 +84,7 @@ type RunPrintMode = (session: AgentSession, options: PrintModeOptions) => Promis
|
|
|
85
84
|
type RunRpcMode = (
|
|
86
85
|
session: AgentSession,
|
|
87
86
|
setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
|
|
87
|
+
eventBus?: EventBus,
|
|
88
88
|
) => Promise<never>;
|
|
89
89
|
|
|
90
90
|
function maybeShowStartupSplash(options: {
|
|
@@ -92,37 +92,12 @@ function maybeShowStartupSplash(options: {
|
|
|
92
92
|
resuming: boolean;
|
|
93
93
|
quiet: boolean;
|
|
94
94
|
version: string;
|
|
95
|
-
setupPending: boolean;
|
|
96
|
-
modelName?: string;
|
|
97
|
-
providerName?: string;
|
|
98
|
-
lspServers?: LspStartupServerInfo[];
|
|
99
95
|
}): void {
|
|
100
96
|
if (!options.isInteractive) return;
|
|
101
97
|
if (options.resuming || options.quiet) return;
|
|
102
98
|
if ($env.PI_TIMING) return;
|
|
103
99
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
104
|
-
//
|
|
105
|
-
// splash — keep the minimal two-line notice there.
|
|
106
|
-
if (options.setupPending) {
|
|
107
|
-
process.stdout.write(`${chalk.dim(`omp ${options.version}`)}\n${chalk.dim("Initializing session…")}\n`);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
// Render the same welcome box the TUI paints first: recent sessions as a
|
|
111
|
-
// loading placeholder (the fixed slot count keeps the box height stable) and
|
|
112
|
-
// the logo held on the intro animation's first frame so the in-TUI intro
|
|
113
|
-
// continues from the frame shown here. Clearing the screen first puts the
|
|
114
|
-
// box at the same origin the TUI's first full paint (clearScrollback) uses,
|
|
115
|
-
// so the live welcome replaces this frame in place without shifting.
|
|
116
|
-
const welcome = new WelcomeComponent(
|
|
117
|
-
options.version,
|
|
118
|
-
options.modelName ?? "",
|
|
119
|
-
options.providerName ?? "",
|
|
120
|
-
null,
|
|
121
|
-
options.lspServers ?? [],
|
|
122
|
-
);
|
|
123
|
-
welcome.holdIntroFirstFrame();
|
|
124
|
-
const lines = welcome.render(process.stdout.columns || 80);
|
|
125
|
-
process.stdout.write(`\x1b[2J\x1b[H\x1b[3J\n${lines.join("\n")}\n`);
|
|
100
|
+
//process.stdout.write(`${chalk.dim(`omp ${options.version}`)}\n${chalk.dim("Initializing session…")}\n`);
|
|
126
101
|
}
|
|
127
102
|
|
|
128
103
|
async function checkForNewVersion(currentVersion: string): Promise<string | undefined> {
|
|
@@ -155,7 +130,7 @@ const HOST_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
|
|
155
130
|
"task.isolation.merge",
|
|
156
131
|
"task.isolation.commits",
|
|
157
132
|
"task.eager",
|
|
158
|
-
"task.
|
|
133
|
+
"task.batch",
|
|
159
134
|
"task.maxConcurrency",
|
|
160
135
|
"task.maxRecursionDepth",
|
|
161
136
|
"task.disabledAgents",
|
|
@@ -370,6 +345,7 @@ async function runInteractiveMode(
|
|
|
370
345
|
eventBus?: EventBus,
|
|
371
346
|
initialMessage?: string,
|
|
372
347
|
initialImages?: ImageContent[],
|
|
348
|
+
titleSystemPrompt?: string,
|
|
373
349
|
): Promise<void> {
|
|
374
350
|
const mode = new InteractiveMode(
|
|
375
351
|
session,
|
|
@@ -379,6 +355,7 @@ async function runInteractiveMode(
|
|
|
379
355
|
lspServers,
|
|
380
356
|
mcpManager,
|
|
381
357
|
eventBus,
|
|
358
|
+
titleSystemPrompt,
|
|
382
359
|
);
|
|
383
360
|
|
|
384
361
|
// Cold-launch gate: the full setup wizard (every scene + the overlay and
|
|
@@ -422,7 +399,7 @@ async function runInteractiveMode(
|
|
|
422
399
|
// Every in-process session load also uses `clearTerminalHistory`; cold launch
|
|
423
400
|
// follows the same clean-cutover path instead of preserving a previous run's
|
|
424
401
|
// transcript above the fresh one.
|
|
425
|
-
mode.renderInitialMessages(
|
|
402
|
+
mode.renderInitialMessages({ preserveExistingChat: true, clearTerminalHistory: true });
|
|
426
403
|
|
|
427
404
|
for (const notify of notifs) {
|
|
428
405
|
if (!notify) {
|
|
@@ -724,7 +701,7 @@ async function buildSessionOptions(
|
|
|
724
701
|
sessionManager: SessionManager | undefined,
|
|
725
702
|
modelRegistry: ModelRegistry,
|
|
726
703
|
activeSettings: Settings,
|
|
727
|
-
): Promise<{ options: CreateAgentSessionOptions }> {
|
|
704
|
+
): Promise<{ options: CreateAgentSessionOptions; titleSystemPrompt?: string }> {
|
|
728
705
|
const options: CreateAgentSessionOptions = {
|
|
729
706
|
cwd: parsed.cwd ?? getProjectDir(),
|
|
730
707
|
autoApprove: parsed.autoApprove ?? false,
|
|
@@ -735,6 +712,8 @@ async function buildSessionOptions(
|
|
|
735
712
|
const resolvedSystemPrompt = await resolvePromptInput(systemPromptSource, "system prompt");
|
|
736
713
|
const appendPromptSource = parsed.appendSystemPrompt ?? discoverAppendSystemPromptFile();
|
|
737
714
|
const resolvedAppendPrompt = await resolvePromptInput(appendPromptSource, "append system prompt");
|
|
715
|
+
const titleSystemPromptSource = discoverTitleSystemPromptFile();
|
|
716
|
+
const titleSystemPrompt = await resolvePromptInput(titleSystemPromptSource, "title system prompt");
|
|
738
717
|
|
|
739
718
|
if (sessionManager) {
|
|
740
719
|
options.sessionManager = sessionManager;
|
|
@@ -880,7 +859,7 @@ async function buildSessionOptions(
|
|
|
880
859
|
options.additionalExtensionPaths = [];
|
|
881
860
|
}
|
|
882
861
|
|
|
883
|
-
return { options };
|
|
862
|
+
return { options, titleSystemPrompt };
|
|
884
863
|
}
|
|
885
864
|
|
|
886
865
|
interface RunRootCommandDependencies {
|
|
@@ -920,6 +899,7 @@ export async function runRootCommand(
|
|
|
920
899
|
if (parsedArgs.listModels !== undefined) {
|
|
921
900
|
const settingsInstance = await logger.time("settings:init:list-models", Settings.init, {
|
|
922
901
|
cwd: getProjectDir(),
|
|
902
|
+
configFiles: parsedArgs.config,
|
|
923
903
|
});
|
|
924
904
|
await modelRegistry.refresh("online");
|
|
925
905
|
const cliExtensionPaths = parsedArgs.noExtensions
|
|
@@ -983,11 +963,16 @@ export async function runRootCommand(
|
|
|
983
963
|
}
|
|
984
964
|
|
|
985
965
|
let cwd = getProjectDir();
|
|
986
|
-
const settingsInstance =
|
|
966
|
+
const settingsInstance =
|
|
967
|
+
deps.settings ?? (await logger.time("settings:init", Settings.init, { cwd, configFiles: parsedArgs.config }));
|
|
987
968
|
if (parsedArgs.approvalMode) {
|
|
988
969
|
// Runtime override (not persisted): every settings.get("tools.approvalMode") downstream
|
|
989
970
|
// sees this value. The wrapper still honours --auto-approve / --yolo on top of it.
|
|
990
971
|
settingsInstance.override("tools.approvalMode", parsedArgs.approvalMode);
|
|
972
|
+
} else if (parsedArgs.autoApprove) {
|
|
973
|
+
// --auto-approve / --yolo without an explicit --approval-mode: reflect in settings so
|
|
974
|
+
// setup-time checks (e.g. #wrapToolForAcpPermission) also see the yolo intent.
|
|
975
|
+
settingsInstance.override("tools.approvalMode", "yolo");
|
|
991
976
|
}
|
|
992
977
|
if (parsedArgs.mode === "rpc" || parsedArgs.mode === "rpc-ui") {
|
|
993
978
|
applyRpcDefaultSettingOverrides(settingsInstance);
|
|
@@ -1133,7 +1118,7 @@ export async function runRootCommand(
|
|
|
1133
1118
|
clearPluginRootsCache: clearPluginRootsAndCaches,
|
|
1134
1119
|
});
|
|
1135
1120
|
|
|
1136
|
-
const { options: sessionOptions } = await logger.time(
|
|
1121
|
+
const { options: sessionOptions, titleSystemPrompt } = await logger.time(
|
|
1137
1122
|
"buildSessionOptions",
|
|
1138
1123
|
buildSessionOptions,
|
|
1139
1124
|
parsedArgs,
|
|
@@ -1228,40 +1213,11 @@ export async function runRootCommand(
|
|
|
1228
1213
|
stdinContent: pipedInput,
|
|
1229
1214
|
});
|
|
1230
1215
|
|
|
1231
|
-
// Resolve the model the session will most likely start with so the splash
|
|
1232
|
-
// box matches the final welcome screen (the raw role selector, e.g.
|
|
1233
|
-
// "anthropic/claude-fable-5:high", is wider than the left column and would
|
|
1234
|
-
// collapse the box into the single-column layout).
|
|
1235
|
-
let splashModel = sessionOptions.model;
|
|
1236
|
-
if (!splashModel) {
|
|
1237
|
-
const remembered = settingsInstance.getModelRole("default");
|
|
1238
|
-
if (remembered) {
|
|
1239
|
-
splashModel = resolveModelRoleValue(remembered, modelRegistry.getAll(), {
|
|
1240
|
-
settings: settingsInstance,
|
|
1241
|
-
matchPreferences: modelMatchPreferences,
|
|
1242
|
-
modelRegistry,
|
|
1243
|
-
}).model;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
// Mirror createAgentSession's startup LSP discovery (sync and cheap: root
|
|
1247
|
-
// markers + binary lookup) so the splash lists the same servers the live
|
|
1248
|
-
// welcome screen will show.
|
|
1249
|
-
const splashLspServers =
|
|
1250
|
-
(sessionOptions.enableLsp ?? true)
|
|
1251
|
-
? discoverStartupLspServers(
|
|
1252
|
-
sessionOptions.cwd ?? cwd,
|
|
1253
|
-
settingsInstance.get("lsp.lazy") ? "available" : "connecting",
|
|
1254
|
-
)
|
|
1255
|
-
: [];
|
|
1256
1216
|
maybeShowStartupSplash({
|
|
1257
1217
|
isInteractive,
|
|
1258
1218
|
resuming: Boolean(parsedArgs.continue || parsedArgs.resume || parsedArgs.fork),
|
|
1259
1219
|
quiet: settingsInstance.get("startup.quiet"),
|
|
1260
1220
|
version: VERSION,
|
|
1261
|
-
setupPending: deps.forceSetupWizard === true || settingsInstance.get("setupVersion") < CURRENT_SETUP_VERSION,
|
|
1262
|
-
modelName: splashModel?.name,
|
|
1263
|
-
providerName: splashModel?.provider,
|
|
1264
|
-
lspServers: splashLspServers,
|
|
1265
1221
|
});
|
|
1266
1222
|
|
|
1267
1223
|
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager } = await createSession({
|
|
@@ -1298,7 +1254,7 @@ export async function runRootCommand(
|
|
|
1298
1254
|
// Branch-only protocol runner: keep RPC host code out of normal interactive startup.
|
|
1299
1255
|
const runRpcMode: RunRpcMode = (await import("./modes/rpc/rpc-mode")).runRpcMode;
|
|
1300
1256
|
stopStartupWatchdog();
|
|
1301
|
-
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined);
|
|
1257
|
+
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined, eventBus);
|
|
1302
1258
|
} else if (isInteractive) {
|
|
1303
1259
|
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
|
|
1304
1260
|
const changelogMarkdown = await logger.time("main:getChangelogForDisplay", getChangelogForDisplay, parsedArgs);
|
|
@@ -1311,7 +1267,10 @@ export async function runRootCommand(
|
|
|
1311
1267
|
return `${scopedModel.model.id}${thinkingStr}`;
|
|
1312
1268
|
})
|
|
1313
1269
|
.join(", ");
|
|
1314
|
-
|
|
1270
|
+
// Routed through the TUI (not stdout): the startup capture owns the
|
|
1271
|
+
// terminal in raw mode here, and the TUI's first clearScrollback paint
|
|
1272
|
+
// would wipe a pre-TUI line anyway.
|
|
1273
|
+
notifs.push({ kind: "info", message: `Model scope: ${modelList} (Ctrl+P to cycle)` });
|
|
1315
1274
|
}
|
|
1316
1275
|
|
|
1317
1276
|
if ($env.PI_TIMING) {
|
|
@@ -1338,6 +1297,7 @@ export async function runRootCommand(
|
|
|
1338
1297
|
eventBus,
|
|
1339
1298
|
initialMessage,
|
|
1340
1299
|
initialImages,
|
|
1300
|
+
titleSystemPrompt,
|
|
1341
1301
|
);
|
|
1342
1302
|
} else {
|
|
1343
1303
|
// Branch-only single-shot runner: keep print-mode code out of normal interactive startup.
|
package/src/memories/index.ts
CHANGED
|
@@ -276,6 +276,7 @@ async function runPhase1(options: {
|
|
|
276
276
|
apiKey: modelRegistry.resolver(phase1Model.provider, {
|
|
277
277
|
sessionId: session.sessionId,
|
|
278
278
|
baseUrl: phase1Model.baseUrl,
|
|
279
|
+
modelId: phase1Model.id,
|
|
279
280
|
}),
|
|
280
281
|
modelMaxTokens: computeModelTokenBudget(phase1Model, config),
|
|
281
282
|
config,
|
|
@@ -436,6 +437,7 @@ async function runPhase2(options: {
|
|
|
436
437
|
apiKey: modelRegistry.resolver(phase2Model.provider, {
|
|
437
438
|
sessionId: session.sessionId,
|
|
438
439
|
baseUrl: phase2Model.baseUrl,
|
|
440
|
+
modelId: phase2Model.id,
|
|
439
441
|
}),
|
|
440
442
|
metadata: session.agent?.metadataForProvider(phase2Model.provider),
|
|
441
443
|
});
|