xiaotime 0.3.0 → 0.4.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/__tests__/legacy_compat.test.d.ts +2 -0
- package/dist/__tests__/legacy_compat.test.d.ts.map +1 -0
- package/dist/__tests__/legacy_compat.test.js +36 -0
- package/dist/__tests__/legacy_compat.test.js.map +1 -0
- package/dist/__tests__/tool_result_client.test.d.ts +20 -0
- package/dist/__tests__/tool_result_client.test.d.ts.map +1 -0
- package/dist/__tests__/tool_result_client.test.js +142 -0
- package/dist/__tests__/tool_result_client.test.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/tool_result_client.d.ts +72 -0
- package/dist/tool_result_client.d.ts.map +1 -0
- package/dist/tool_result_client.js +123 -0
- package/dist/tool_result_client.js.map +1 -0
- package/dist/ws.d.ts +10 -0
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +124 -52
- package/dist/ws.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy_compat.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/legacy_compat.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Legacy-compatibility / version-cutover tests.
|
|
5
|
+
*
|
|
6
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR5.
|
|
7
|
+
*
|
|
8
|
+
* The hard cutover (decision D2) rejects CLI <0.4.0 at the WS auth
|
|
9
|
+
* handshake: the CLI sends its `version`, the server gates on it.
|
|
10
|
+
* These tests guard the CLI half of that contract — the version the
|
|
11
|
+
* CLI advertises must be its real package version and at/above the
|
|
12
|
+
* 0.4.0 client-owned-dispatch floor. The server-side rejection logic
|
|
13
|
+
* is covered by tests/test_terminal_orchestrator_cli_version.py.
|
|
14
|
+
*/
|
|
15
|
+
const vitest_1 = require("vitest");
|
|
16
|
+
const config_js_1 = require("../config.js");
|
|
17
|
+
// `require` (not `import`) so the tsc build doesn't flag package.json
|
|
18
|
+
// as outside rootDir (TS6059); vitest resolves it fine at runtime.
|
|
19
|
+
const pkg = require("../../package.json");
|
|
20
|
+
(0, vitest_1.describe)("CLI version cutover (#6565 PR5)", () => {
|
|
21
|
+
(0, vitest_1.test)("CLI_VERSION matches package.json — no drift", () => {
|
|
22
|
+
// The server gates the WS handshake on the version the CLI sends.
|
|
23
|
+
// If CLI_VERSION drifts from package.json the CLI would advertise
|
|
24
|
+
// a version it isn't, silently defeating the cutover gate.
|
|
25
|
+
(0, vitest_1.expect)(config_js_1.CLI_VERSION).toBe(pkg.version);
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.test)("CLI_VERSION is a 3-part semver at or above the 0.4.0 floor", () => {
|
|
28
|
+
const parts = config_js_1.CLI_VERSION.split(".").map((p) => Number.parseInt(p, 10));
|
|
29
|
+
(0, vitest_1.expect)(parts).toHaveLength(3);
|
|
30
|
+
(0, vitest_1.expect)(parts.every((n) => Number.isInteger(n))).toBe(true);
|
|
31
|
+
const [major, minor] = parts;
|
|
32
|
+
// >= 0.4.0 — the client-owned-dispatch cutover floor.
|
|
33
|
+
(0, vitest_1.expect)(major > 0 || (major === 0 && minor >= 4)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=legacy_compat.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy_compat.test.js","sourceRoot":"","sources":["../../src/__tests__/legacy_compat.test.ts"],"names":[],"mappings":";;AAAA;;;;;;;;;;;GAWG;AACH,mCAAgD;AAChD,4CAA2C;AAE3C,sEAAsE;AACtE,mEAAmE;AACnE,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEjE,IAAA,iBAAQ,EAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,IAAA,aAAI,EAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,kEAAkE;QAClE,kEAAkE;QAClE,2DAA2D;QAC3D,IAAA,eAAM,EAAC,uBAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;QACtE,MAAM,KAAK,GAAG,uBAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACxE,IAAA,eAAM,EAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAA,eAAM,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;QAC7B,sDAAsD;QACtD,IAAA,eAAM,EAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST POST client tests.
|
|
3
|
+
*
|
|
4
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3.
|
|
5
|
+
*
|
|
6
|
+
* Verifies the contract between the CLI's drain queue (in ws.ts)
|
|
7
|
+
* and the server's POST /tool_results endpoint (added in PR1,
|
|
8
|
+
* consumed by PR2's dispatch refactor):
|
|
9
|
+
*
|
|
10
|
+
* - Happy path: 200 OK returns parsed body (completed,
|
|
11
|
+
* all_completed, resume_signaled).
|
|
12
|
+
* - Retry policy: 5xx + network errors retry with exponential
|
|
13
|
+
* backoff (100ms → 200ms → 400ms, 3 attempts total).
|
|
14
|
+
* - 4xx is durable — throws immediately, no retry.
|
|
15
|
+
* - Auth header (x-api-key with the cli_token) on every attempt.
|
|
16
|
+
* - Request body shape matches the server's PostToolResultsRequest
|
|
17
|
+
* Pydantic model.
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=tool_result_client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_result_client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tool_result_client.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* REST POST client tests.
|
|
4
|
+
*
|
|
5
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3.
|
|
6
|
+
*
|
|
7
|
+
* Verifies the contract between the CLI's drain queue (in ws.ts)
|
|
8
|
+
* and the server's POST /tool_results endpoint (added in PR1,
|
|
9
|
+
* consumed by PR2's dispatch refactor):
|
|
10
|
+
*
|
|
11
|
+
* - Happy path: 200 OK returns parsed body (completed,
|
|
12
|
+
* all_completed, resume_signaled).
|
|
13
|
+
* - Retry policy: 5xx + network errors retry with exponential
|
|
14
|
+
* backoff (100ms → 200ms → 400ms, 3 attempts total).
|
|
15
|
+
* - 4xx is durable — throws immediately, no retry.
|
|
16
|
+
* - Auth header (x-api-key with the cli_token) on every attempt.
|
|
17
|
+
* - Request body shape matches the server's PostToolResultsRequest
|
|
18
|
+
* Pydantic model.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
const vitest_1 = require("vitest");
|
|
22
|
+
const tool_result_client_js_1 = require("../tool_result_client.js");
|
|
23
|
+
// Mock the auth module so we don't read a real token from
|
|
24
|
+
// ~/.xiaotime/cli_token.json during tests.
|
|
25
|
+
vitest_1.vi.mock("../auth.js", () => ({
|
|
26
|
+
getToken: () => "fake-cli-token-for-tests",
|
|
27
|
+
}));
|
|
28
|
+
// Mock the config so the URL is deterministic.
|
|
29
|
+
vitest_1.vi.mock("../config.js", () => ({
|
|
30
|
+
ORCHESTRATOR_URL: "https://terminal.xiaotime.test",
|
|
31
|
+
}));
|
|
32
|
+
let fetchMock;
|
|
33
|
+
let origFetch;
|
|
34
|
+
(0, vitest_1.beforeEach)(() => {
|
|
35
|
+
fetchMock = vitest_1.vi.fn();
|
|
36
|
+
origFetch = globalThis.fetch;
|
|
37
|
+
globalThis.fetch = fetchMock;
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.afterEach)(() => {
|
|
40
|
+
globalThis.fetch = origFetch;
|
|
41
|
+
vitest_1.vi.restoreAllMocks();
|
|
42
|
+
});
|
|
43
|
+
function _okResponse(body) {
|
|
44
|
+
return new Response(JSON.stringify(body), {
|
|
45
|
+
status: 200,
|
|
46
|
+
headers: { "content-type": "application/json" },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function _errorResponse(status, body) {
|
|
50
|
+
return new Response(body, { status });
|
|
51
|
+
}
|
|
52
|
+
(0, vitest_1.describe)("postToolResults happy path", () => {
|
|
53
|
+
(0, vitest_1.test)("posts to correct URL with auth header + JSON body", async () => {
|
|
54
|
+
fetchMock.mockResolvedValue(_okResponse({ completed: ["toolu_a"], all_completed: true, resume_signaled: true }));
|
|
55
|
+
const result = await (0, tool_result_client_js_1.postToolResults)("session-abc", [
|
|
56
|
+
{ tool_use_id: "toolu_a", content: "ok", is_error: false },
|
|
57
|
+
]);
|
|
58
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
59
|
+
completed: ["toolu_a"],
|
|
60
|
+
all_completed: true,
|
|
61
|
+
resume_signaled: true,
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(1);
|
|
64
|
+
const [url, init] = fetchMock.mock.calls[0];
|
|
65
|
+
(0, vitest_1.expect)(url).toBe("https://terminal.xiaotime.test/terminal/sessions/session-abc/tool_results");
|
|
66
|
+
(0, vitest_1.expect)(init.method).toBe("POST");
|
|
67
|
+
(0, vitest_1.expect)(init.headers["x-api-key"]).toBe("fake-cli-token-for-tests");
|
|
68
|
+
(0, vitest_1.expect)(init.headers["Content-Type"]).toBe("application/json");
|
|
69
|
+
const body = JSON.parse(init.body);
|
|
70
|
+
(0, vitest_1.expect)(body).toEqual({
|
|
71
|
+
results: [{ tool_use_id: "toolu_a", content: "ok", is_error: false }],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
(0, vitest_1.test)("supports multi-result batches", async () => {
|
|
75
|
+
fetchMock.mockResolvedValue(_okResponse({
|
|
76
|
+
completed: ["toolu_a", "toolu_b"],
|
|
77
|
+
all_completed: false,
|
|
78
|
+
resume_signaled: false,
|
|
79
|
+
}));
|
|
80
|
+
await (0, tool_result_client_js_1.postToolResults)("sess", [
|
|
81
|
+
{ tool_use_id: "toolu_a", content: "1", is_error: false },
|
|
82
|
+
{ tool_use_id: "toolu_b", content: "2", is_error: true },
|
|
83
|
+
]);
|
|
84
|
+
const body = JSON.parse(fetchMock.mock.calls[0][1].body);
|
|
85
|
+
(0, vitest_1.expect)(body.results).toHaveLength(2);
|
|
86
|
+
(0, vitest_1.expect)(body.results[1].is_error).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
(0, vitest_1.describe)("postToolResults retry behavior", () => {
|
|
90
|
+
(0, vitest_1.test)("4xx error throws immediately without retrying", async () => {
|
|
91
|
+
// Use mockImplementation (fresh Response per call) instead of
|
|
92
|
+
// mockResolvedValue (single Response instance) — the latter's
|
|
93
|
+
// body stream gets consumed on the first .text() read and
|
|
94
|
+
// subsequent reads fail with "Body is unusable".
|
|
95
|
+
fetchMock.mockImplementation(() => Promise.resolve(_errorResponse(400, "unknown_tool_use_id")));
|
|
96
|
+
await (0, vitest_1.expect)((0, tool_result_client_js_1.postToolResults)("sess", [
|
|
97
|
+
{ tool_use_id: "toolu_a", content: "x", is_error: false },
|
|
98
|
+
])).rejects.toThrow(/rejected \(400\)/);
|
|
99
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(1);
|
|
100
|
+
});
|
|
101
|
+
(0, vitest_1.test)("4xx error includes server body in the thrown message", async () => {
|
|
102
|
+
fetchMock.mockImplementation(() => Promise.resolve(_errorResponse(400, '{"code":"unknown_tool_use_id","unknown":["toolu_x"]}')));
|
|
103
|
+
await (0, vitest_1.expect)((0, tool_result_client_js_1.postToolResults)("sess", [
|
|
104
|
+
{ tool_use_id: "toolu_x", content: "x", is_error: false },
|
|
105
|
+
])).rejects.toThrow(/unknown_tool_use_id/);
|
|
106
|
+
});
|
|
107
|
+
(0, vitest_1.test)("5xx error retries up to 3 attempts then throws", async () => {
|
|
108
|
+
fetchMock.mockImplementation(() => Promise.resolve(_errorResponse(503, "service unavailable")));
|
|
109
|
+
await (0, vitest_1.expect)((0, tool_result_client_js_1.postToolResults)("sess", [
|
|
110
|
+
{ tool_use_id: "toolu_a", content: "x", is_error: false },
|
|
111
|
+
])).rejects.toThrow(/503/);
|
|
112
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
|
|
113
|
+
});
|
|
114
|
+
(0, vitest_1.test)("5xx then success on retry → returns success", async () => {
|
|
115
|
+
fetchMock
|
|
116
|
+
.mockResolvedValueOnce(_errorResponse(500, "internal"))
|
|
117
|
+
.mockResolvedValueOnce(_okResponse({ completed: ["a"], all_completed: true, resume_signaled: true }));
|
|
118
|
+
const result = await (0, tool_result_client_js_1.postToolResults)("sess", [
|
|
119
|
+
{ tool_use_id: "a", content: "x", is_error: false },
|
|
120
|
+
]);
|
|
121
|
+
(0, vitest_1.expect)(result.all_completed).toBe(true);
|
|
122
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(2);
|
|
123
|
+
});
|
|
124
|
+
(0, vitest_1.test)("network error retries up to 3 attempts then throws", async () => {
|
|
125
|
+
fetchMock.mockRejectedValue(new Error("ECONNRESET"));
|
|
126
|
+
await (0, vitest_1.expect)((0, tool_result_client_js_1.postToolResults)("sess", [
|
|
127
|
+
{ tool_use_id: "a", content: "x", is_error: false },
|
|
128
|
+
])).rejects.toThrow(/ECONNRESET/);
|
|
129
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
|
|
130
|
+
});
|
|
131
|
+
(0, vitest_1.test)("network error then success on retry → returns success", async () => {
|
|
132
|
+
fetchMock
|
|
133
|
+
.mockRejectedValueOnce(new Error("ECONNRESET"))
|
|
134
|
+
.mockResolvedValueOnce(_okResponse({ completed: ["a"], all_completed: false, resume_signaled: false }));
|
|
135
|
+
const result = await (0, tool_result_client_js_1.postToolResults)("sess", [
|
|
136
|
+
{ tool_use_id: "a", content: "x", is_error: false },
|
|
137
|
+
]);
|
|
138
|
+
(0, vitest_1.expect)(result.completed).toEqual(["a"]);
|
|
139
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(2);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=tool_result_client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_result_client.test.js","sourceRoot":"","sources":["../../src/__tests__/tool_result_client.test.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -22,4 +22,11 @@
|
|
|
22
22
|
export declare const ORCHESTRATOR_URL: string;
|
|
23
23
|
export declare const AUTH_URL: string;
|
|
24
24
|
export declare const WS_URL: string;
|
|
25
|
+
/**
|
|
26
|
+
* The CLI's own version, sent in the WebSocket auth handshake so the
|
|
27
|
+
* server can reject pre-0.4.0 clients (#6565 PR5 — the client-owned
|
|
28
|
+
* dispatch cutover). MUST stay in sync with `version` in package.json;
|
|
29
|
+
* the drift-guard test in `__tests__/legacy_compat.test.ts` enforces it.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CLI_VERSION = "0.4.0";
|
|
25
32
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,gBAAgB,QAC4C,CAAC;AAK1E,eAAO,MAAM,QAAQ,QACuC,CAAC;AAQ7D,eAAO,MAAM,MAAM,QAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,gBAAgB,QAC4C,CAAC;AAK1E,eAAO,MAAM,QAAQ,QACuC,CAAC;AAQ7D,eAAO,MAAM,MAAM,QAA4B,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UAAU,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* got 404. Restored in 0.2.1 (#6097 PR2 follow-up).
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.WS_URL = exports.AUTH_URL = exports.ORCHESTRATOR_URL = void 0;
|
|
24
|
+
exports.CLI_VERSION = exports.WS_URL = exports.AUTH_URL = exports.ORCHESTRATOR_URL = void 0;
|
|
25
25
|
// Terminal orchestrator — handles dev sessions (WebSocket + REST).
|
|
26
26
|
exports.ORCHESTRATOR_URL = process.env.XIAOTIME_ORCHESTRATOR_URL || "https://terminal.xiaotime.ai";
|
|
27
27
|
// Main orchestrator — handles the OAuth login flow (init + token poll).
|
|
@@ -34,4 +34,11 @@ function toWsUrl(url) {
|
|
|
34
34
|
return parsed.toString().replace(/\/$/, "");
|
|
35
35
|
}
|
|
36
36
|
exports.WS_URL = toWsUrl(exports.ORCHESTRATOR_URL);
|
|
37
|
+
/**
|
|
38
|
+
* The CLI's own version, sent in the WebSocket auth handshake so the
|
|
39
|
+
* server can reject pre-0.4.0 clients (#6565 PR5 — the client-owned
|
|
40
|
+
* dispatch cutover). MUST stay in sync with `version` in package.json;
|
|
41
|
+
* the drift-guard test in `__tests__/legacy_compat.test.ts` enforces it.
|
|
42
|
+
*/
|
|
43
|
+
exports.CLI_VERSION = "0.4.0";
|
|
37
44
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH,mEAAmE;AACtD,QAAA,gBAAgB,GAC3B,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,8BAA8B,CAAC;AAE1E,wEAAwE;AACxE,6EAA6E;AAC7E,4BAA4B;AACf,QAAA,QAAQ,GACnB,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,yBAAyB,CAAC;AAE7D,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;IAChE,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAEY,QAAA,MAAM,GAAG,OAAO,CAAC,wBAAgB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAEH,mEAAmE;AACtD,QAAA,gBAAgB,GAC3B,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,8BAA8B,CAAC;AAE1E,wEAAwE;AACxE,6EAA6E;AAC7E,4BAA4B;AACf,QAAA,QAAQ,GACnB,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,yBAAyB,CAAC;AAE7D,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;IAChE,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAEY,QAAA,MAAM,GAAG,OAAO,CAAC,wBAAgB,CAAC,CAAC;AAEhD;;;;;GAKG;AACU,QAAA,WAAW,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST POST client for tool_result delivery.
|
|
3
|
+
*
|
|
4
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3.
|
|
5
|
+
*
|
|
6
|
+
* Pre-#6565 the CLI sent tool_result back to terminal-orchestrator
|
|
7
|
+
* over the WebSocket — the server awaited each one inline with a
|
|
8
|
+
* 120s timeout. Any approval taking >2 min wedged the session
|
|
9
|
+
* (the wedge class observed live 2026-05-20 during #6509 PR1
|
|
10
|
+
* canary).
|
|
11
|
+
*
|
|
12
|
+
* Post-#6565 the CLI POSTs results to the new REST endpoint:
|
|
13
|
+
*
|
|
14
|
+
* POST <ORCHESTRATOR_URL>/terminal/sessions/{session_id}/tool_results
|
|
15
|
+
* Headers: x-api-key: <cli_token>
|
|
16
|
+
* Body: {"results": [{tool_use_id, content, is_error}, ...]}
|
|
17
|
+
*
|
|
18
|
+
* Server-side: atomic across the batch. Any unknown `tool_use_id`
|
|
19
|
+
* → 400 `unknown_tool_use_id` + no rows mutated. On success,
|
|
20
|
+
* ``mark_completed_batch.all_completed=True`` signals the WS
|
|
21
|
+
* handler (waiting on the per-session resume queue) to assemble
|
|
22
|
+
* tool_result blocks + resume the model stream.
|
|
23
|
+
*
|
|
24
|
+
* Retry policy: 3 attempts with 100ms→200ms→400ms exponential
|
|
25
|
+
* backoff for transient 5xx + network errors. 4xx errors are
|
|
26
|
+
* non-retriable (the server's rejection is durable — the
|
|
27
|
+
* tool_use_id IS unknown, retrying won't change that).
|
|
28
|
+
*
|
|
29
|
+
* Failure mode if all retries exhaust: the tool_result is lost
|
|
30
|
+
* from the server's perspective; the corresponding row stays as
|
|
31
|
+
* `status='pending'` in `terminal_pending_tool_calls` until the
|
|
32
|
+
* future expiry sweep (PR5). The user-visible symptom is a
|
|
33
|
+
* session that appears frozen (model never resumes). Tightening
|
|
34
|
+
* this is the WS-reconnect-replay work in PR4 — on the next WS
|
|
35
|
+
* connect, server re-emits pending tool_call events and the CLI
|
|
36
|
+
* can re-attempt the POST after re-rendering the menu.
|
|
37
|
+
*/
|
|
38
|
+
export interface ToolResultEntry {
|
|
39
|
+
tool_use_id: string;
|
|
40
|
+
content: string;
|
|
41
|
+
is_error: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface PostToolResultsResponse {
|
|
44
|
+
completed: string[];
|
|
45
|
+
all_completed: boolean;
|
|
46
|
+
resume_signaled: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Thrown on durable (4xx) HTTP rejections that retrying won't fix.
|
|
50
|
+
* #6573 CR-r1: replaces the pre-r1 string-prefix matching on the
|
|
51
|
+
* Error message (`startsWith("POST tool_results rejected")`) which
|
|
52
|
+
* was fragile to message-format drift. The retry loop checks
|
|
53
|
+
* `instanceof DurableHttpError` and re-throws without retry; all
|
|
54
|
+
* other thrown errors are treated as transient + retried.
|
|
55
|
+
*/
|
|
56
|
+
export declare class DurableHttpError extends Error {
|
|
57
|
+
readonly status: number;
|
|
58
|
+
readonly body: string;
|
|
59
|
+
constructor(status: number, body: string);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* POST a batch of tool_results to the orchestrator. Retries on
|
|
63
|
+
* 5xx / network errors with exponential backoff; throws on
|
|
64
|
+
* 4xx (non-retriable) or after retries exhaust.
|
|
65
|
+
*
|
|
66
|
+
* Most callers pass a single-result batch (one tool finished, POST
|
|
67
|
+
* the result, move to next). Batch form exists so a future
|
|
68
|
+
* client-side allowlist-auto-execute path could fire multiple
|
|
69
|
+
* results in one POST.
|
|
70
|
+
*/
|
|
71
|
+
export declare function postToolResults(sessionId: string, results: ToolResultEntry[]): Promise<PostToolResultsResponse>;
|
|
72
|
+
//# sourceMappingURL=tool_result_client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_result_client.d.ts","sourceRoot":"","sources":["../src/tool_result_client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAMzC;AAKD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,EAAE,GACzB,OAAO,CAAC,uBAAuB,CAAC,CAmDlC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* REST POST client for tool_result delivery.
|
|
4
|
+
*
|
|
5
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3.
|
|
6
|
+
*
|
|
7
|
+
* Pre-#6565 the CLI sent tool_result back to terminal-orchestrator
|
|
8
|
+
* over the WebSocket — the server awaited each one inline with a
|
|
9
|
+
* 120s timeout. Any approval taking >2 min wedged the session
|
|
10
|
+
* (the wedge class observed live 2026-05-20 during #6509 PR1
|
|
11
|
+
* canary).
|
|
12
|
+
*
|
|
13
|
+
* Post-#6565 the CLI POSTs results to the new REST endpoint:
|
|
14
|
+
*
|
|
15
|
+
* POST <ORCHESTRATOR_URL>/terminal/sessions/{session_id}/tool_results
|
|
16
|
+
* Headers: x-api-key: <cli_token>
|
|
17
|
+
* Body: {"results": [{tool_use_id, content, is_error}, ...]}
|
|
18
|
+
*
|
|
19
|
+
* Server-side: atomic across the batch. Any unknown `tool_use_id`
|
|
20
|
+
* → 400 `unknown_tool_use_id` + no rows mutated. On success,
|
|
21
|
+
* ``mark_completed_batch.all_completed=True`` signals the WS
|
|
22
|
+
* handler (waiting on the per-session resume queue) to assemble
|
|
23
|
+
* tool_result blocks + resume the model stream.
|
|
24
|
+
*
|
|
25
|
+
* Retry policy: 3 attempts with 100ms→200ms→400ms exponential
|
|
26
|
+
* backoff for transient 5xx + network errors. 4xx errors are
|
|
27
|
+
* non-retriable (the server's rejection is durable — the
|
|
28
|
+
* tool_use_id IS unknown, retrying won't change that).
|
|
29
|
+
*
|
|
30
|
+
* Failure mode if all retries exhaust: the tool_result is lost
|
|
31
|
+
* from the server's perspective; the corresponding row stays as
|
|
32
|
+
* `status='pending'` in `terminal_pending_tool_calls` until the
|
|
33
|
+
* future expiry sweep (PR5). The user-visible symptom is a
|
|
34
|
+
* session that appears frozen (model never resumes). Tightening
|
|
35
|
+
* this is the WS-reconnect-replay work in PR4 — on the next WS
|
|
36
|
+
* connect, server re-emits pending tool_call events and the CLI
|
|
37
|
+
* can re-attempt the POST after re-rendering the menu.
|
|
38
|
+
*/
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.DurableHttpError = void 0;
|
|
41
|
+
exports.postToolResults = postToolResults;
|
|
42
|
+
const config_js_1 = require("./config.js");
|
|
43
|
+
const auth_js_1 = require("./auth.js");
|
|
44
|
+
/**
|
|
45
|
+
* Thrown on durable (4xx) HTTP rejections that retrying won't fix.
|
|
46
|
+
* #6573 CR-r1: replaces the pre-r1 string-prefix matching on the
|
|
47
|
+
* Error message (`startsWith("POST tool_results rejected")`) which
|
|
48
|
+
* was fragile to message-format drift. The retry loop checks
|
|
49
|
+
* `instanceof DurableHttpError` and re-throws without retry; all
|
|
50
|
+
* other thrown errors are treated as transient + retried.
|
|
51
|
+
*/
|
|
52
|
+
class DurableHttpError extends Error {
|
|
53
|
+
status;
|
|
54
|
+
body;
|
|
55
|
+
constructor(status, body) {
|
|
56
|
+
super(`POST tool_results rejected (${status}): ${body}`);
|
|
57
|
+
this.name = "DurableHttpError";
|
|
58
|
+
this.status = status;
|
|
59
|
+
this.body = body;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.DurableHttpError = DurableHttpError;
|
|
63
|
+
const MAX_ATTEMPTS = 3;
|
|
64
|
+
const BACKOFF_BASE_MS = 100;
|
|
65
|
+
/**
|
|
66
|
+
* POST a batch of tool_results to the orchestrator. Retries on
|
|
67
|
+
* 5xx / network errors with exponential backoff; throws on
|
|
68
|
+
* 4xx (non-retriable) or after retries exhaust.
|
|
69
|
+
*
|
|
70
|
+
* Most callers pass a single-result batch (one tool finished, POST
|
|
71
|
+
* the result, move to next). Batch form exists so a future
|
|
72
|
+
* client-side allowlist-auto-execute path could fire multiple
|
|
73
|
+
* results in one POST.
|
|
74
|
+
*/
|
|
75
|
+
async function postToolResults(sessionId, results) {
|
|
76
|
+
const url = `${config_js_1.ORCHESTRATOR_URL}/terminal/sessions/${sessionId}/tool_results`;
|
|
77
|
+
const headers = {
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
"x-api-key": (0, auth_js_1.getToken)(),
|
|
80
|
+
};
|
|
81
|
+
const body = JSON.stringify({ results });
|
|
82
|
+
let lastErr = null;
|
|
83
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(url, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers,
|
|
88
|
+
body,
|
|
89
|
+
});
|
|
90
|
+
if (response.ok) {
|
|
91
|
+
return (await response.json());
|
|
92
|
+
}
|
|
93
|
+
// 4xx is durable — server rejected the payload, retrying
|
|
94
|
+
// won't change the outcome. Throw a typed DurableHttpError
|
|
95
|
+
// (caught + re-raised below via instanceof check, never
|
|
96
|
+
// retried).
|
|
97
|
+
if (response.status >= 400 && response.status < 500) {
|
|
98
|
+
const errBody = await response.text();
|
|
99
|
+
throw new DurableHttpError(response.status, errBody);
|
|
100
|
+
}
|
|
101
|
+
// 5xx — retriable.
|
|
102
|
+
lastErr = new Error(`POST tool_results failed (${response.status}): ${await response.text()}`);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// #6573 CR-r1: type-check the rejection class instead of
|
|
106
|
+
// string-matching the Error message. DurableHttpError means
|
|
107
|
+
// "server told us no, retrying won't change that" — re-raise
|
|
108
|
+
// immediately. Everything else (network error, fetch
|
|
109
|
+
// failure, unexpected throw) is treated as transient +
|
|
110
|
+
// retried below.
|
|
111
|
+
if (err instanceof DurableHttpError) {
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
115
|
+
}
|
|
116
|
+
if (attempt < MAX_ATTEMPTS) {
|
|
117
|
+
const delay = BACKOFF_BASE_MS * 2 ** (attempt - 1);
|
|
118
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
throw lastErr ?? new Error("POST tool_results failed (unknown)");
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=tool_result_client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_result_client.js","sourceRoot":"","sources":["../src/tool_result_client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;;;AAiDH,0CAsDC;AArGD,2CAA+C;AAC/C,uCAAqC;AAcrC;;;;;;;GAOG;AACH,MAAa,gBAAiB,SAAQ,KAAK;IAChC,MAAM,CAAS;IACf,IAAI,CAAS;IACtB,YAAY,MAAc,EAAE,IAAY;QACtC,KAAK,CAAC,+BAA+B,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AATD,4CASC;AAED,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B;;;;;;;;;GASG;AACI,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,OAA0B;IAE1B,MAAM,GAAG,GAAG,GAAG,4BAAgB,sBAAsB,SAAS,eAAe,CAAC;IAC9E,MAAM,OAAO,GAAG;QACd,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,IAAA,kBAAQ,GAAE;KACxB,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAEzC,IAAI,OAAO,GAAiB,IAAI,CAAC;IACjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI;aACL,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;YAC5D,CAAC;YACD,yDAAyD;YACzD,2DAA2D;YAC3D,wDAAwD;YACxD,YAAY;YACZ,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACtC,MAAM,IAAI,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACvD,CAAC;YACD,mBAAmB;YACnB,OAAO,GAAG,IAAI,KAAK,CACjB,6BAA6B,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAC1E,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yDAAyD;YACzD,4DAA4D;YAC5D,6DAA6D;YAC7D,qDAAqD;YACrD,uDAAuD;YACvD,iBAAiB;YACjB,IAAI,GAAG,YAAY,gBAAgB,EAAE,CAAC;gBACpC,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,OAAO,GAAG,YAAY,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,eAAe,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YACnD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,MAAM,OAAO,IAAI,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACnE,CAAC"}
|
package/dist/ws.d.ts
CHANGED
|
@@ -5,6 +5,16 @@
|
|
|
5
5
|
* SessionUI state machine from incoming WS events so the user gets
|
|
6
6
|
* a spinner while the model is generating / a tool is running, and
|
|
7
7
|
* approval prompts don't race the main rl on keystrokes.
|
|
8
|
+
*
|
|
9
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3 — tool_result
|
|
10
|
+
* delivery moved off the WebSocket: results POST to
|
|
11
|
+
* /terminal/sessions/{id}/tool_results via the new REST endpoint.
|
|
12
|
+
* The WS now carries only inbound (server→client) `tool_call`
|
|
13
|
+
* events as informational triggers. Tool calls drain serially
|
|
14
|
+
* through a local queue so concurrent menus can't corrupt
|
|
15
|
+
* SessionUI state — the pre-#6565 `inFlightToolUseId` reject-on-
|
|
16
|
+
* overlap guard is gone (it caused the wedge observed in the
|
|
17
|
+
* #6509 PR1 canary).
|
|
8
18
|
*/
|
|
9
19
|
export declare function runSession(sessionId: string): Promise<void>;
|
|
10
20
|
//# sourceMappingURL=ws.d.ts.map
|
package/dist/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAwBH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiQjE"}
|
package/dist/ws.js
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* SessionUI state machine from incoming WS events so the user gets
|
|
7
7
|
* a spinner while the model is generating / a tool is running, and
|
|
8
8
|
* approval prompts don't race the main rl on keystrokes.
|
|
9
|
+
*
|
|
10
|
+
* SPRINT-TERMINAL-CLIENT-OWNED-DISPATCH-1 (#6565) PR3 — tool_result
|
|
11
|
+
* delivery moved off the WebSocket: results POST to
|
|
12
|
+
* /terminal/sessions/{id}/tool_results via the new REST endpoint.
|
|
13
|
+
* The WS now carries only inbound (server→client) `tool_call`
|
|
14
|
+
* events as informational triggers. Tool calls drain serially
|
|
15
|
+
* through a local queue so concurrent menus can't corrupt
|
|
16
|
+
* SessionUI state — the pre-#6565 `inFlightToolUseId` reject-on-
|
|
17
|
+
* overlap guard is gone (it caused the wedge observed in the
|
|
18
|
+
* #6509 PR1 canary).
|
|
9
19
|
*/
|
|
10
20
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -16,8 +26,10 @@ const ws_1 = __importDefault(require("ws"));
|
|
|
16
26
|
const config_js_1 = require("./config.js");
|
|
17
27
|
const auth_js_1 = require("./auth.js");
|
|
18
28
|
const tools_js_1 = require("./tools.js");
|
|
29
|
+
const tool_result_client_js_1 = require("./tool_result_client.js");
|
|
19
30
|
const display_js_1 = require("./display.js");
|
|
20
31
|
const ui_state_js_1 = require("./ui_state.js");
|
|
32
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
21
33
|
async function runSession(sessionId) {
|
|
22
34
|
const wsUrl = `${config_js_1.WS_URL}/terminal/sessions/${sessionId}/stream`;
|
|
23
35
|
return new Promise((resolve, reject) => {
|
|
@@ -25,19 +37,93 @@ async function runSession(sessionId) {
|
|
|
25
37
|
let connected = false;
|
|
26
38
|
const ui = new ui_state_js_1.SessionUI();
|
|
27
39
|
let firstTokenThisTurn = true;
|
|
28
|
-
// #
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
let
|
|
40
|
+
// #6565 PR3 — serial drain queue. The server may emit multiple
|
|
41
|
+
// tool_call events back-to-back (one per tool_use block in the
|
|
42
|
+
// model's batch). Executing concurrently would corrupt
|
|
43
|
+
// SessionUI state (two menus rendering on top of each other);
|
|
44
|
+
// queueing + draining one-at-a-time gives clean approval flows
|
|
45
|
+
// while still parallelizable across the server (server emits
|
|
46
|
+
// all N then awaits the resume signal — order of completion
|
|
47
|
+
// doesn't matter to the server, only that all N land via POST).
|
|
48
|
+
const toolCallQueue = [];
|
|
49
|
+
let drainRunning = false;
|
|
50
|
+
async function drainToolCallQueue() {
|
|
51
|
+
if (drainRunning)
|
|
52
|
+
return;
|
|
53
|
+
drainRunning = true;
|
|
54
|
+
try {
|
|
55
|
+
while (toolCallQueue.length > 0) {
|
|
56
|
+
const tc = toolCallQueue.shift();
|
|
57
|
+
ui.setState("running_tool");
|
|
58
|
+
const result = await (0, tools_js_1.executeTool)({
|
|
59
|
+
tool_use_id: tc.tool_use_id,
|
|
60
|
+
name: tc.name,
|
|
61
|
+
input: tc.input,
|
|
62
|
+
}, ui);
|
|
63
|
+
let postFailed = false;
|
|
64
|
+
try {
|
|
65
|
+
await (0, tool_result_client_js_1.postToolResults)(sessionId, [
|
|
66
|
+
{
|
|
67
|
+
tool_use_id: result.tool_use_id,
|
|
68
|
+
content: result.content,
|
|
69
|
+
is_error: result.is_error,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
// POST exhausted retries (transient 5xx / network) OR
|
|
75
|
+
// 4xx (durable rejection, e.g. unknown_tool_use_id from
|
|
76
|
+
// a server restart that lost the pending row). Tool
|
|
77
|
+
// result is lost from the server's perspective; the
|
|
78
|
+
// pending row stays as `pending` until PR5's expiry
|
|
79
|
+
// sweep. PR4's WS-reconnect replay path will re-emit
|
|
80
|
+
// the tool_call on next session connect for a retry.
|
|
81
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
+
process.stderr.write(chalk_1.default.yellow(` (tool_result POST failed: ${msg})\n`));
|
|
83
|
+
postFailed = true;
|
|
84
|
+
}
|
|
85
|
+
// #6573 CR-r1: on POST failure, the server's resume signal
|
|
86
|
+
// won't fire (server never learns this tool completed).
|
|
87
|
+
// The CLI would sit silently with no further events from
|
|
88
|
+
// the server, looking wedged. Bounce the user back to the
|
|
89
|
+
// prompt with a clear cue + a fresh idle state so they can
|
|
90
|
+
// type `xiaotime dev --session <id>` (resume) or pick a
|
|
91
|
+
// different recovery action. The unfinished pending rows
|
|
92
|
+
// stay durable in the DB for PR4's reconnect replay.
|
|
93
|
+
if (postFailed) {
|
|
94
|
+
ui.setState("idle");
|
|
95
|
+
process.stderr.write(chalk_1.default.yellow(" (session paused — POST failed; reconnect with " +
|
|
96
|
+
"`xiaotime dev --session " +
|
|
97
|
+
sessionId +
|
|
98
|
+
"` to retry pending tool calls)\n"));
|
|
99
|
+
// #6573 CR-r1: a POST failure means the server never got
|
|
100
|
+
// the resume signal — its dispatch state is inconsistent.
|
|
101
|
+
// Close the socket rather than returning to the prompt:
|
|
102
|
+
// the clean recovery is a fresh session, not a retry on a
|
|
103
|
+
// half-dispatched one. ws.close() fires the on("close")
|
|
104
|
+
// handler (rl teardown + resolve), so the user genuinely
|
|
105
|
+
// re-runs `xiaotime dev --session <id>`. Pending rows stay
|
|
106
|
+
// durable in the DB for PR4's reconnect replay; draining
|
|
107
|
+
// stops here because the loop is abandoned with the session.
|
|
108
|
+
ws.close();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Don't transition state back to "generating" here — the
|
|
112
|
+
// model may already be streaming on the next turn (the
|
|
113
|
+
// server's resume happens as soon as all batch results
|
|
114
|
+
// land via POST). The next text_delta clears the spinner
|
|
115
|
+
// via onFirstToken.
|
|
116
|
+
firstTokenThisTurn = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
drainRunning = false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
38
123
|
ws.on("open", () => {
|
|
39
|
-
// Auth
|
|
40
|
-
|
|
124
|
+
// Auth — #6565 PR5: `version` lets the server reject pre-0.4.0
|
|
125
|
+
// clients that would wedge on the old WS tool_result path.
|
|
126
|
+
ws.send(JSON.stringify({ type: "auth", token: (0, auth_js_1.getToken)(), version: config_js_1.CLI_VERSION }));
|
|
41
127
|
// Connect with cwd
|
|
42
128
|
ws.send(JSON.stringify({ type: "connect", cwd: process.cwd() }));
|
|
43
129
|
});
|
|
@@ -48,7 +134,20 @@ async function runSession(sessionId) {
|
|
|
48
134
|
connected = true;
|
|
49
135
|
(0, tools_js_1.setSessionCwd)(process.cwd());
|
|
50
136
|
(0, display_js_1.printWelcome)(msg.session_id, msg.session_name, msg.resumed, msg.message_count);
|
|
51
|
-
|
|
137
|
+
// #6565 PR4b — if the server is replaying tool calls left
|
|
138
|
+
// pending by a prior session (restart / crash mid-approval),
|
|
139
|
+
// defer the prompt loop. The server emits those `tool_call`
|
|
140
|
+
// events next; they drain through the queue, the CLI POSTs
|
|
141
|
+
// results, the server resumes the turn, and that turn's
|
|
142
|
+
// `message_stop` starts the prompt loop. Starting it here
|
|
143
|
+
// would race the replayed approval menus against the prompt.
|
|
144
|
+
if ((msg.pending_tool_calls ?? 0) > 0) {
|
|
145
|
+
process.stderr.write(chalk_1.default.cyan(` (resuming ${msg.pending_tool_calls} pending tool ` +
|
|
146
|
+
`call(s) from a prior session)\n`));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
startUserPromptLoop();
|
|
150
|
+
}
|
|
52
151
|
break;
|
|
53
152
|
case "text_delta":
|
|
54
153
|
if (firstTokenThisTurn) {
|
|
@@ -58,49 +157,22 @@ async function runSession(sessionId) {
|
|
|
58
157
|
(0, display_js_1.printTextDelta)(msg.content);
|
|
59
158
|
break;
|
|
60
159
|
case "tool_call": {
|
|
61
|
-
// #
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`while another tool (id=${inFlightToolUseId}) is still in flight. ` +
|
|
71
|
-
`CLI rejects overlapping executions; the server WS handler should ` +
|
|
72
|
-
`serialize tool dispatch.`;
|
|
73
|
-
console.error(`\n[cli] ${errMsg}\n`);
|
|
74
|
-
ws.send(JSON.stringify({
|
|
75
|
-
type: "tool_result",
|
|
76
|
-
tool_use_id: msg.tool_use_id,
|
|
77
|
-
content: errMsg,
|
|
78
|
-
is_error: true,
|
|
79
|
-
}));
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
inFlightToolUseId = msg.tool_use_id;
|
|
83
|
-
// executeTool drives its own state transitions when an
|
|
84
|
-
// approval is needed (running_tool ↔ awaiting_approval).
|
|
85
|
-
ui.setState("running_tool");
|
|
86
|
-
(0, tools_js_1.executeTool)({
|
|
160
|
+
// #6565 PR3 — informational event now. Server has already
|
|
161
|
+
// persisted the pending row + is awaiting our POST to
|
|
162
|
+
// /terminal/sessions/{id}/tool_results. Queue the call
|
|
163
|
+
// for serial draining (one menu rendering at a time);
|
|
164
|
+
// POST the result after each drain iteration. The
|
|
165
|
+
// pre-#6565 inFlightToolUseId reject-on-overlap guard is
|
|
166
|
+
// gone — concurrent dispatch is now safe by construction
|
|
167
|
+
// via the drain serialization.
|
|
168
|
+
toolCallQueue.push({
|
|
87
169
|
tool_use_id: msg.tool_use_id,
|
|
88
170
|
name: msg.name,
|
|
89
171
|
input: msg.input,
|
|
90
|
-
}, ui).then((result) => {
|
|
91
|
-
inFlightToolUseId = null;
|
|
92
|
-
ws.send(JSON.stringify({
|
|
93
|
-
type: "tool_result",
|
|
94
|
-
tool_use_id: result.tool_use_id,
|
|
95
|
-
content: result.content,
|
|
96
|
-
is_error: result.is_error,
|
|
97
|
-
}));
|
|
98
|
-
// Back to generating; the next text_delta will clear any
|
|
99
|
-
// spinner if onFirstToken hasn't yet been called for this
|
|
100
|
-
// generation segment.
|
|
101
|
-
ui.setState("generating");
|
|
102
|
-
firstTokenThisTurn = true;
|
|
103
172
|
});
|
|
173
|
+
// Fire-and-forget — drainToolCallQueue is idempotent on
|
|
174
|
+
// concurrent invocation (the drainRunning guard inside).
|
|
175
|
+
drainToolCallQueue();
|
|
104
176
|
break;
|
|
105
177
|
}
|
|
106
178
|
case "message_stop":
|
package/dist/ws.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xiaotime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Xiaotime Developer — persistent-memory AI coding assistant",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xiaotime": "./dist/index.js"
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@types/node": "^22.0.0",
|
|
27
27
|
"typescript": "^5.7.0",
|
|
28
28
|
"ts-node": "^10.9.2",
|
|
29
|
-
"vitest": "^
|
|
29
|
+
"vitest": "^4.1.7"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=20.19.0"
|
|
33
33
|
}
|
|
34
34
|
}
|