zcode-acp-server 0.22.0 → 0.23.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/dist/config/user-config.d.ts +48 -0
- package/dist/config/user-config.d.ts.map +1 -0
- package/dist/config/user-config.js +101 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/handlers/session.d.ts +16 -2
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +65 -10
- package/dist/handlers/session.js.map +1 -1
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +2 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/remote/config.d.ts +36 -24
- package/dist/remote/config.d.ts.map +1 -1
- package/dist/remote/config.js +70 -36
- package/dist/remote/config.js.map +1 -1
- package/dist/remote/hub-server.d.ts +18 -7
- package/dist/remote/hub-server.d.ts.map +1 -1
- package/dist/remote/hub-server.js +46 -31
- package/dist/remote/hub-server.js.map +1 -1
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +10 -0
- package/dist/utils.js.map +1 -1
- package/docs/REMOTE-CLIENTS.md +19 -17
- package/docs/REMOTE.md +55 -13
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global user configuration (~/.config/zcode-acp/config.json).
|
|
3
|
+
*
|
|
4
|
+
* The file is the AUTHORITATIVE source for user preferences that must not
|
|
5
|
+
* depend on how a process was launched: the hub daemon is idle-exited and
|
|
6
|
+
* re-spawned by whichever bridge needs it next, so its birth env rotates
|
|
7
|
+
* between GUI-launched editors (no shell rc vars) and interactive shells.
|
|
8
|
+
* Every read is live (no cache) — editing the file takes effect on the next
|
|
9
|
+
* use (e.g. the next remote incubation) without restarting the hub.
|
|
10
|
+
*
|
|
11
|
+
* Precedence everywhere: config file > environment variable > built-in
|
|
12
|
+
* default. Env vars remain fully supported as a fallback for setups without
|
|
13
|
+
* a file and for one-off/test overrides of unspecified fields.
|
|
14
|
+
*
|
|
15
|
+
* Per-process plumbing (ZCODE_ACP_REMOTE_ORIGIN, _PIN_CWD,
|
|
16
|
+
* ZCODE_ACP_RESUME_SESSION) is deliberately NOT file-configurable — those
|
|
17
|
+
* carry per-request/per-role state, not user preference.
|
|
18
|
+
*/
|
|
19
|
+
/** Terminal incubation preferences for remote session-create (ADR-0016). */
|
|
20
|
+
export interface TerminalPrefs {
|
|
21
|
+
/** false → remote session-create stays headless (no visible window). */
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
/** Terminal app name (Terminal, iTerm, wezterm, kitty, alacritty, ghostty, …). */
|
|
24
|
+
app?: string;
|
|
25
|
+
/** Shell command template; `{script}` is replaced with the quoted script path. */
|
|
26
|
+
command?: string;
|
|
27
|
+
}
|
|
28
|
+
/** The `remote` section of the user config file. */
|
|
29
|
+
export interface RemoteUserConfig {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
token?: string;
|
|
32
|
+
hubPort?: number;
|
|
33
|
+
hubHost?: string;
|
|
34
|
+
bridgePort?: number;
|
|
35
|
+
terminal?: TerminalPrefs;
|
|
36
|
+
}
|
|
37
|
+
export interface UserConfig {
|
|
38
|
+
remote?: RemoteUserConfig;
|
|
39
|
+
}
|
|
40
|
+
/** Resolve the config file path: $XDG_CONFIG_HOME/zcode-acp or ~/.config/zcode-acp. */
|
|
41
|
+
export declare function userConfigPath(env?: NodeJS.ProcessEnv): string;
|
|
42
|
+
/**
|
|
43
|
+
* Read and validate the user config. Best-effort: a missing file is the
|
|
44
|
+
* normal no-file path ({}), anything unreadable/malformed warns once and
|
|
45
|
+
* reads as absent so the env fallback keeps the process working.
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadUserConfig(env?: NodeJS.ProcessEnv): UserConfig;
|
|
48
|
+
//# sourceMappingURL=user-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../../src/config/user-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG3E;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAqD/E"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global user configuration (~/.config/zcode-acp/config.json).
|
|
3
|
+
*
|
|
4
|
+
* The file is the AUTHORITATIVE source for user preferences that must not
|
|
5
|
+
* depend on how a process was launched: the hub daemon is idle-exited and
|
|
6
|
+
* re-spawned by whichever bridge needs it next, so its birth env rotates
|
|
7
|
+
* between GUI-launched editors (no shell rc vars) and interactive shells.
|
|
8
|
+
* Every read is live (no cache) — editing the file takes effect on the next
|
|
9
|
+
* use (e.g. the next remote incubation) without restarting the hub.
|
|
10
|
+
*
|
|
11
|
+
* Precedence everywhere: config file > environment variable > built-in
|
|
12
|
+
* default. Env vars remain fully supported as a fallback for setups without
|
|
13
|
+
* a file and for one-off/test overrides of unspecified fields.
|
|
14
|
+
*
|
|
15
|
+
* Per-process plumbing (ZCODE_ACP_REMOTE_ORIGIN, _PIN_CWD,
|
|
16
|
+
* ZCODE_ACP_RESUME_SESSION) is deliberately NOT file-configurable — those
|
|
17
|
+
* carry per-request/per-role state, not user preference.
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { warn } from "../utils.js";
|
|
23
|
+
/** Resolve the config file path: $XDG_CONFIG_HOME/zcode-acp or ~/.config/zcode-acp. */
|
|
24
|
+
export function userConfigPath(env = process.env) {
|
|
25
|
+
const base = (env.XDG_CONFIG_HOME ?? "").trim() || path.join(homedir(), ".config");
|
|
26
|
+
return path.join(base, "zcode-acp", "config.json");
|
|
27
|
+
}
|
|
28
|
+
function isPlainObject(v) {
|
|
29
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read and validate the user config. Best-effort: a missing file is the
|
|
33
|
+
* normal no-file path ({}), anything unreadable/malformed warns once and
|
|
34
|
+
* reads as absent so the env fallback keeps the process working.
|
|
35
|
+
*/
|
|
36
|
+
export function loadUserConfig(env = process.env) {
|
|
37
|
+
const file = userConfigPath(env);
|
|
38
|
+
let raw;
|
|
39
|
+
try {
|
|
40
|
+
raw = readFileSync(file, "utf8");
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return {}; // missing (or unreadable) file — env fallback applies
|
|
44
|
+
}
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
warn(`config: ${file} is not valid JSON — ignoring (${e instanceof Error ? e.message : String(e)})`);
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
if (!isPlainObject(parsed)) {
|
|
54
|
+
warn(`config: ${file} is not a JSON object — ignoring`);
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
const remote = parsed["remote"];
|
|
58
|
+
if (remote === undefined)
|
|
59
|
+
return {};
|
|
60
|
+
if (!isPlainObject(remote)) {
|
|
61
|
+
warn(`config: "remote" in ${file} is not an object — ignoring the section`);
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
const out = {};
|
|
65
|
+
if (typeof remote["enabled"] === "boolean")
|
|
66
|
+
out.enabled = remote["enabled"];
|
|
67
|
+
if (typeof remote["token"] === "string" && remote["token"].trim())
|
|
68
|
+
out.token = remote["token"].trim();
|
|
69
|
+
for (const key of ["hubPort", "bridgePort"]) {
|
|
70
|
+
const v = remote[key];
|
|
71
|
+
if (v === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 1 || v > 65535) {
|
|
74
|
+
warn(`config: remote.${key}=${JSON.stringify(v)} is not a valid port — ignoring`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
out[key] = v;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (typeof remote["hubHost"] === "string" && remote["hubHost"].trim()) {
|
|
81
|
+
out.hubHost = remote["hubHost"].trim();
|
|
82
|
+
}
|
|
83
|
+
const terminal = remote["terminal"];
|
|
84
|
+
if (isPlainObject(terminal)) {
|
|
85
|
+
const t = {};
|
|
86
|
+
if (typeof terminal["enabled"] === "boolean")
|
|
87
|
+
t.enabled = terminal["enabled"];
|
|
88
|
+
if (typeof terminal["app"] === "string" && terminal["app"].trim())
|
|
89
|
+
t.app = terminal["app"].trim();
|
|
90
|
+
if (typeof terminal["command"] === "string" && terminal["command"].trim()) {
|
|
91
|
+
t.command = terminal["command"].trim();
|
|
92
|
+
}
|
|
93
|
+
if (Object.keys(t).length > 0)
|
|
94
|
+
out.terminal = t;
|
|
95
|
+
}
|
|
96
|
+
else if (terminal !== undefined) {
|
|
97
|
+
warn(`config: remote.terminal in ${file} is not an object — ignoring`);
|
|
98
|
+
}
|
|
99
|
+
return { remote: out };
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=user-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-config.js","sourceRoot":"","sources":["../../src/config/user-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AA0BnC,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACnF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,CAAU;IAC/B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,sDAAsD;IACnE,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,WAAW,IAAI,kCAAkC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACrG,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,IAAI,kCAAkC,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,uBAAuB,IAAI,0CAA0C,CAAC,CAAC;QAC5E,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5E,IAAI,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACtG,KAAK,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAU,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS;QAC9B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC;YACxE,IAAI,CAAC,kBAAkB,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC;QACpF,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACtE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACpC,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAkB,EAAE,CAAC;QAC5B,IAAI,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,SAAS;YAAE,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC9E,IAAI,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;YAAE,CAAC,CAAC,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAClG,IAAI,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1E,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC,8BAA8B,IAAI,8BAA8B,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -20,6 +20,20 @@ import type { PendingTurn, ZcodeAcpServer } from "../server.js";
|
|
|
20
20
|
* (the TUI's /new) create fresh sessions. Whitespace-only counts as unset.
|
|
21
21
|
*/
|
|
22
22
|
export declare function consumeBootResumeTarget(): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Banner-handshake trigger for a boot-resumed TUI (ADR-0017 follow-up).
|
|
25
|
+
* Martty's welcome banner paints INSTEAD of the transcript and only dives
|
|
26
|
+
* when text is submitted — so a boot-resumed TUI showed its replayed history
|
|
27
|
+
* only after the user's first message. The hub therefore also incubates the
|
|
28
|
+
* resume TUI with `DSH_TUI_AUTOPROMPT=<this string>`: martty auto-submits it
|
|
29
|
+
* at boot (banner dives immediately, the text queues until the boot bind),
|
|
30
|
+
* and the prompt path answers it with a one-line ack instead of a model
|
|
31
|
+
* turn — scoped to the booting connection (see bootResumeTriggerConnection).
|
|
32
|
+
* Plain text on purpose: a leading `/` would route through martty's
|
|
33
|
+
* slash dispatch (whose gates run before agent capabilities are known) and
|
|
34
|
+
* `!` would run a local shell command.
|
|
35
|
+
*/
|
|
36
|
+
export declare const BOOT_RESUME_TRIGGER = "resume session";
|
|
23
37
|
/**
|
|
24
38
|
* `session/new` → local placeholder id. The real zcode `session/create` is
|
|
25
39
|
* deferred to first use (`ensureRealSession`) so an editor startup that never
|
|
@@ -34,7 +48,7 @@ export declare function consumeBootResumeTarget(): string | null;
|
|
|
34
48
|
* back to a fresh session so the window still lands on a usable prompt
|
|
35
49
|
* (the retired in-house REPL had the same fallback).
|
|
36
50
|
*/
|
|
37
|
-
export declare function newSession(server: ZcodeAcpServer, params: acp.NewSessionRequest): Promise<acp.NewSessionResponse>;
|
|
51
|
+
export declare function newSession(server: ZcodeAcpServer, params: acp.NewSessionRequest, client?: acp.AgentContext): Promise<acp.NewSessionResponse>;
|
|
38
52
|
/**
|
|
39
53
|
* Materialize a lazy `session/new` placeholder into a real backend session on
|
|
40
54
|
* first use (prompt / set_config_option / extension methods). Idempotent:
|
|
@@ -68,7 +82,7 @@ export declare function loadSession(server: ZcodeAcpServer, params: acp.LoadSess
|
|
|
68
82
|
* the continuation for real, and only the session/load replay later surfaced
|
|
69
83
|
* the orphaned continuation bubble.
|
|
70
84
|
*/
|
|
71
|
-
export declare function prompt(server: ZcodeAcpServer, params: acp.PromptRequest, cx: acp.AgentContext, requestId: number | string): Promise<acp.PromptResponse>;
|
|
85
|
+
export declare function prompt(server: ZcodeAcpServer, params: acp.PromptRequest, cx: acp.AgentContext, requestId: number | string, client?: acp.AgentContext): Promise<acp.PromptResponse>;
|
|
72
86
|
/**
|
|
73
87
|
* `session/set_config_option` → dispatch model/mode/thought and emit the
|
|
74
88
|
* resulting config_option_update (+ current_mode_update for mode).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/handlers/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAuB1E,OAAO,EAML,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAkHhE;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAIvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/handlers/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAuB1E,OAAO,EAML,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAkHhE;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAIvD;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,iBAAiB,EAC7B,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,GACxB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAgFjC;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2H/F;AAED,6CAA6C;AAC7C,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,mBAAmB,GAC9B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAuBnC;AAkHD,6EAA6E;AAC7E,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,oBAAoB,EAChC,EAAE,EAAE,GAAG,CAAC,YAAY,GACnB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAqFpC;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,kBAAkB,EAC9B,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACrC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CA8GlC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,aAAa,EACzB,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,GACxB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAwD7B;AAuaD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,6BAA6B,EACzC,EAAE,EAAE,GAAG,CAAC,YAAY,GACnB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAa7C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,GAAG,CAAC,kBAAkB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAsBf;AAyJD,qEAAqE;AACrE,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,EAAE,EAAE,GAAG,CAAC,YAAY,CAAC;IACrB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,SAAS,GACd,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA0ClC;AAyCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,MAAM,GAC7B,OAAO,CAsBT;AAID;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,SAAS,GAAG,MAAM,CA2ChF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,SAAS,GAAG,eAAe,EAAE,CAwC5F;AA4JD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG7D;AAcD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,gBAAgB,EACxB,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CA4gB7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iCAAiC,CAC/C,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACpB,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,OAAO,GACjB,OAAO,CAET;AAkDD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,EAC5B,UAAU,EAAE,KAAK,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,GAAG,SAAS,GACxE,OAAO,EAAE,CASX;AAkBD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,EACtB,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAmBf;AAiED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,cAAc,EACtB,EAAE,EAAE,GAAG,CAAC,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,MAAM,EAClB,OAAO,UAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
package/dist/handlers/session.js
CHANGED
|
@@ -24,7 +24,7 @@ import { buildResumeRuntimeModel } from "../config/runtime-model.js";
|
|
|
24
24
|
import { messages } from "../i18n.js";
|
|
25
25
|
import { lookupLazySession, recordMaterializedSession, rememberLazySession, } from "../lazy-sessions.js";
|
|
26
26
|
import { buildDiffContent, EventTranslator, extractLocations, formatTurnError, isTransientTurnError, ProjectionDiffer, } from "../translators/index.js";
|
|
27
|
-
import { log, warn } from "../utils.js";
|
|
27
|
+
import { clientConnectionRoot, log, warn } from "../utils.js";
|
|
28
28
|
import { dispatchEvent } from "./dispatch.js";
|
|
29
29
|
import { sendSessionUpdate, sendTextChunk, withReplayBatch } from "./io.js";
|
|
30
30
|
import { fetchMessages, fullSlice, readTailLimit, replayMessages, sliceTail } from "./replay.js";
|
|
@@ -131,6 +131,20 @@ export function consumeBootResumeTarget() {
|
|
|
131
131
|
delete process.env[BOOT_RESUME_ENV];
|
|
132
132
|
return target || null;
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Banner-handshake trigger for a boot-resumed TUI (ADR-0017 follow-up).
|
|
136
|
+
* Martty's welcome banner paints INSTEAD of the transcript and only dives
|
|
137
|
+
* when text is submitted — so a boot-resumed TUI showed its replayed history
|
|
138
|
+
* only after the user's first message. The hub therefore also incubates the
|
|
139
|
+
* resume TUI with `DSH_TUI_AUTOPROMPT=<this string>`: martty auto-submits it
|
|
140
|
+
* at boot (banner dives immediately, the text queues until the boot bind),
|
|
141
|
+
* and the prompt path answers it with a one-line ack instead of a model
|
|
142
|
+
* turn — scoped to the booting connection (see bootResumeTriggerConnection).
|
|
143
|
+
* Plain text on purpose: a leading `/` would route through martty's
|
|
144
|
+
* slash dispatch (whose gates run before agent capabilities are known) and
|
|
145
|
+
* `!` would run a local shell command.
|
|
146
|
+
*/
|
|
147
|
+
export const BOOT_RESUME_TRIGGER = "resume session";
|
|
134
148
|
/**
|
|
135
149
|
* `session/new` → local placeholder id. The real zcode `session/create` is
|
|
136
150
|
* deferred to first use (`ensureRealSession`) so an editor startup that never
|
|
@@ -145,16 +159,36 @@ export function consumeBootResumeTarget() {
|
|
|
145
159
|
* back to a fresh session so the window still lands on a usable prompt
|
|
146
160
|
* (the retired in-house REPL had the same fallback).
|
|
147
161
|
*/
|
|
148
|
-
export async function newSession(server, params) {
|
|
162
|
+
export async function newSession(server, params, client) {
|
|
149
163
|
const bootResume = consumeBootResumeTarget();
|
|
150
164
|
if (bootResume) {
|
|
151
165
|
try {
|
|
152
|
-
// replayHistory:false —
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
// stall the boot.
|
|
166
|
+
// replayHistory:false — a PRE-response replay would arrive for a session
|
|
167
|
+
// id the client has not adopted yet and be dropped; the tail replay is
|
|
168
|
+
// deferred past the response instead (below).
|
|
156
169
|
const loaded = await loadSession(server, { sessionId: bootResume }, server.clients.broadcast(), { replayHistory: false });
|
|
157
170
|
log(`session/new: boot-resume → ${bootResume}`);
|
|
171
|
+
// Deferred tail replay, same as the TUI's /resume (see resumeSession):
|
|
172
|
+
// once the session/new response is written the booting martty has
|
|
173
|
+
// adopted the returned session id, so post-response chunks can fold
|
|
174
|
+
// into its transcript. The chunks sit BEHIND martty's welcome banner
|
|
175
|
+
// until text is submitted — the DSH_TUI_AUTOPROMPT handshake armed
|
|
176
|
+
// below drops the banner for the user.
|
|
177
|
+
if (isMarttyClient(server)) {
|
|
178
|
+
// Scope the handshake to THIS connection: a phone app attached to the
|
|
179
|
+
// same bridge may prompt during the boot window and must not disarm it.
|
|
180
|
+
server.bootResumeTriggerConnection = clientConnectionRoot(client);
|
|
181
|
+
const cx = server.clients.broadcast();
|
|
182
|
+
const zcodeSid = server.resolveSid(bootResume);
|
|
183
|
+
if (zcodeSid) {
|
|
184
|
+
setImmediate(() => {
|
|
185
|
+
replayResumeHistory(server, cx, bootResume, zcodeSid).catch((e) => {
|
|
186
|
+
warn(`session/new boot-resume: TUI history replay failed (non-fatal): ` +
|
|
187
|
+
`${e instanceof Error ? e.message : String(e)}`);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
158
192
|
return {
|
|
159
193
|
sessionId: bootResume,
|
|
160
194
|
modes: loaded.modes,
|
|
@@ -431,7 +465,9 @@ async function resolveResumeTarget(server, acpSid) {
|
|
|
431
465
|
const MARTTY_RESUME_TAIL = 200;
|
|
432
466
|
/** True when the connecting client is Martty, the bundled TUI frontend. */
|
|
433
467
|
function isMarttyClient(server) {
|
|
434
|
-
|
|
468
|
+
// Sticky flag first: clientName is last-write-wins and a remote client's
|
|
469
|
+
// initialize can land between the TUI's initialize and its session/new.
|
|
470
|
+
return server.marttyClientSeen || (server.clientName ?? "").toLowerCase().includes("martty");
|
|
435
471
|
}
|
|
436
472
|
/**
|
|
437
473
|
* Stream a resumed session's history to the TUI as chunk updates — the same
|
|
@@ -654,8 +690,8 @@ export async function loadSession(server, params, cx, opts = {}) {
|
|
|
654
690
|
* the continuation for real, and only the session/load replay later surfaced
|
|
655
691
|
* the orphaned continuation bubble.
|
|
656
692
|
*/
|
|
657
|
-
export async function prompt(server, params, cx, requestId) {
|
|
658
|
-
let result = await runPrompt(server, params, cx, requestId);
|
|
693
|
+
export async function prompt(server, params, cx, requestId, client) {
|
|
694
|
+
let result = await runPrompt(server, params, cx, requestId, false, client);
|
|
659
695
|
// Sandbox-allow continuation chaining, BOUNDED: each batched restart
|
|
660
696
|
// cancels the in-flight round and queues a continuation for prompt() to
|
|
661
697
|
// consume. A LATER batch (the user approving a second popup seconds after
|
|
@@ -703,7 +739,7 @@ export async function prompt(server, params, cx, requestId) {
|
|
|
703
739
|
return result;
|
|
704
740
|
}
|
|
705
741
|
/** One prompt round: subscribe-before-send, run the event-driven turn loop. */
|
|
706
|
-
async function runPrompt(server, params, cx, requestId, continuationRound = false) {
|
|
742
|
+
async function runPrompt(server, params, cx, requestId, continuationRound = false, client) {
|
|
707
743
|
// Project-level sandbox flip (ADR-0011): a .zcode/acp/sandbox.json created
|
|
708
744
|
// after the backend spawned unsandboxed must arm on THIS prompt, not on the
|
|
709
745
|
// next bridge restart — kill the old process; the ensureBackend() below
|
|
@@ -718,6 +754,25 @@ async function runPrompt(server, params, cx, requestId, continuationRound = fals
|
|
|
718
754
|
// may drag in an image with no accompanying text).
|
|
719
755
|
if (!text && attachments.length === 0)
|
|
720
756
|
throw new Error("empty prompt");
|
|
757
|
+
// Boot-resume banner handshake (see BOOT_RESUME_TRIGGER): martty queues the
|
|
758
|
+
// auto-submitted trigger until its boot bind, so it arrives as the FIRST
|
|
759
|
+
// prompt OF THAT CONNECTION — ack with one chunk and end the turn without
|
|
760
|
+
// touching the backend. Scoped to the arming connection's identity: a
|
|
761
|
+
// phone app attached to the same bridge may prompt inside the boot window
|
|
762
|
+
// and must neither spend nor disarm the handshake (observed live: its
|
|
763
|
+
// prompt raced ahead and the trigger leaked to the model). The same
|
|
764
|
+
// connection submitting anything else first means the auto-submit was
|
|
765
|
+
// lost — disarm so the trigger text typed manually later stays a normal
|
|
766
|
+
// prompt.
|
|
767
|
+
if (server.bootResumeTriggerConnection !== null &&
|
|
768
|
+
clientConnectionRoot(client) === server.bootResumeTriggerConnection) {
|
|
769
|
+
server.bootResumeTriggerConnection = null;
|
|
770
|
+
if (text === BOOT_RESUME_TRIGGER) {
|
|
771
|
+
await sendTextChunk(cx, params.sessionId, messages().bootResumeAck, randomUUID());
|
|
772
|
+
log("session/prompt: boot-resume banner handshake acknowledged");
|
|
773
|
+
return { stopReason: "end_turn" };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
721
776
|
// Materialize a lazy session/new placeholder on first use. Placed after the
|
|
722
777
|
// empty-prompt check so an invalid request doesn't create a backend session.
|
|
723
778
|
const zcodeSid = await ensureRealSession(server, params.sessionId);
|