specrails-desktop 2.60.0 → 2.62.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/client/dist/assets/ActivityFeedPage-C_pqc281.js +1 -0
- package/client/dist/assets/{AgentBrowserCapture-D4a3wYM1.js → AgentBrowserCapture-vz6rbDtu.js} +1 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BHCcUahS.js +2 -0
- package/client/dist/assets/AgentModeCodePane-DhmxgG3W.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-25tQlLpF.js → AgentModeJobsPane-99fBBLri.js} +1 -1
- package/client/dist/assets/{AgentsPage-CUDp_sfw.js → AgentsPage-CTwm_6T8.js} +3 -3
- package/client/dist/assets/{AnalyticsPage-DXWMGn8b.js → AnalyticsPage-YD8MLoPa.js} +1 -1
- package/client/dist/assets/CodePage-CLJn2F2e.js +3 -0
- package/client/dist/assets/{DesktopAnalyticsPage-BkFBq0E6.js → DesktopAnalyticsPage-MWGQVbwp.js} +1 -1
- package/client/dist/assets/{DocsDialog-BL7uSbIJ.js → DocsDialog-XUCYKoq0.js} +1 -1
- package/client/dist/assets/{DocsPage-CsDLbmZX.js → DocsPage-dOLC8bXA.js} +1 -1
- package/client/dist/assets/{ExportDropdown-B4Olg1Fk.js → ExportDropdown-BvKQgwM9.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-DxOvwmBF.js +19 -0
- package/client/dist/assets/{JobDetailModal-DS_Owxs6.js → JobDetailModal-CkiubrkZ.js} +1 -1
- package/client/dist/assets/JobDetailPage-CNe4Wn7l.js +1 -0
- package/client/dist/assets/JobsPage-DSts_L_d.js +1 -0
- package/client/dist/assets/LoopBuilderPage-C9D9eer5.js +1 -0
- package/client/dist/assets/{LoopPreviewModal-CH1r-KhU.js → LoopPreviewModal-Dv3dxHr_.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-CiZdI6aw.js → LoopWindowSurface-DN2lbSnL.js} +1 -1
- package/client/dist/assets/{LoopsPage-BwFujdGi.js → LoopsPage-DHgIreEW.js} +2 -2
- package/client/dist/assets/MinimizedChatsContext-Ds2-R6qJ.js +1 -0
- package/client/dist/assets/PluginsPage-D1EJ9oja.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-DKpa1wKk.js +1 -0
- package/client/dist/assets/{RepositoryDeliveries-NNorcGzT.js → RepositoryDeliveries-CW4eKG3x.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-D-jb4bI_.js → RepositoryScopeSelector-DrtPqvc_.js} +1 -1
- package/client/dist/assets/ReviewPacketPage-BtjPI0Cc.js +4 -0
- package/client/dist/assets/{TemplatePreviewModal-5CZdAZ0G.js → TemplatePreviewModal-DcFlhUxy.js} +1 -1
- package/client/dist/assets/TicketDetailModalContext-Dea22CI-.js +16 -0
- package/client/dist/assets/{Trans-wmTUDVyC.js → Trans-CAY2Rd2f.js} +1 -1
- package/client/dist/assets/agent-5bSixVzz.js +1 -0
- package/client/dist/assets/agent-BR4EHfhq.js +1 -0
- package/client/dist/assets/agent-C0IKkJP8.js +1 -0
- package/client/dist/assets/agent-C7li9ayC.js +1 -0
- package/client/dist/assets/agent-D0dRmEen.js +1 -0
- package/client/dist/assets/{agent-BjttZLIl.js → agent-DZVGHm7H.js} +1 -1
- package/client/dist/assets/agent-Dip4h2YE.js +1 -0
- package/client/dist/assets/agent-Pp-P-n1p.js +1 -0
- package/client/dist/assets/agentRuntime-B-myNBUP.js +1 -0
- package/client/dist/assets/agentRuntime-Bjt7Ij6_.js +1 -0
- package/client/dist/assets/agentRuntime-C_yLPKDy.js +1 -0
- package/client/dist/assets/agentRuntime-D2kYQqNO.js +1 -0
- package/client/dist/assets/agentRuntime-DvhEe1um.js +1 -0
- package/client/dist/assets/agentRuntime-DxC6-9rN.js +1 -0
- package/client/dist/assets/agentRuntime-TKQyg137.js +1 -0
- package/client/dist/assets/agentRuntime-WSv8G0pW.js +1 -0
- package/client/dist/assets/ban-E4EaZe3a.js +1 -0
- package/client/dist/assets/chevron-up-CwGUHd6D.js +1 -0
- package/client/dist/assets/dist-Bq10nS2m.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-Dax4zqPF.js → formatDistanceToNow-DlQY1Lm_.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-dpIo42JZ.js → getTimezoneOffsetInMilliseconds-COUeq65G.js} +1 -1
- package/client/dist/assets/index-BacrO_ub.css +2 -0
- package/client/dist/assets/index-CONHvEJw.js +83 -0
- package/client/dist/assets/{jira-api-CLrIvXFm.js → jira-api-C94VNHNY.js} +1 -1
- package/client/dist/assets/layers-eDMjG41I.js +1 -0
- package/client/dist/assets/{loops-Cm55CXJ4.js → loops-B2PhQLKR.js} +1 -1
- package/client/dist/assets/{loops-3IutojWt.js → loops-BqbF46st.js} +1 -1
- package/client/dist/assets/{loops-BAosWtXE.js → loops-CINnHbfI.js} +1 -1
- package/client/dist/assets/{loops-kIJJkaxf.js → loops-D-3XhH04.js} +1 -1
- package/client/dist/assets/{loops-DHQtnfze.js → loops-DKkEEjZ_.js} +1 -1
- package/client/dist/assets/{loops-D7tL_Saw.js → loops-DzWR5-v6.js} +1 -1
- package/client/dist/assets/{loops--KWr0X-w.js → loops-WyWDkhok.js} +1 -1
- package/client/dist/assets/{loops-BISDssL5.js → loops-scD5fIBf.js} +1 -1
- package/client/dist/assets/{narration-DvLQBW9U.js → narration-BEKMVxGm.js} +1 -1
- package/client/dist/assets/{narration-CHI06czm.js → narration-CjLwGKap.js} +1 -1
- package/client/dist/assets/{narration-DjKB3pQZ.js → narration-DANWuGsW.js} +1 -1
- package/client/dist/assets/{narration-eSx3gR0W.js → narration-DGeEDWiT.js} +1 -1
- package/client/dist/assets/{narration-DtCYvRLM.js → narration-DJgpGwIP.js} +1 -1
- package/client/dist/assets/{narration-n3hOA7LY.js → narration-DbHnz0qV.js} +1 -1
- package/client/dist/assets/{narration-thJOeT2-.js → narration-bZSsTZG8.js} +1 -1
- package/client/dist/assets/{narration--hUhjM5v.js → narration-i6aKJKEc.js} +1 -1
- package/client/dist/assets/{project-repositories-BkyT8AjU.js → project-repositories-Cc3oNv8X.js} +1 -1
- package/client/dist/assets/save-B6wGWwBR.js +1 -0
- package/client/dist/assets/{settings-Dn-pXukr.js → settings-BKJZ2KEM.js} +1 -1
- package/client/dist/assets/{settings-BBj_kZaD.js → settings-BV3jocIx.js} +1 -1
- package/client/dist/assets/{settings-COlH_P33.js → settings-BjM2Dw8_.js} +1 -1
- package/client/dist/assets/settings-CKu0LTib.js +1 -0
- package/client/dist/assets/{settings-D77mC_Lb.js → settings-DA77M48m.js} +1 -1
- package/client/dist/assets/{settings-ByVE7jdZ.js → settings-LfFnl1UH.js} +1 -1
- package/client/dist/assets/{settings-DZKy7MqN.js → settings-P1IQvhnL.js} +1 -1
- package/client/dist/assets/{settings-B5Hy_Kol.js → settings-eIMJjyK_.js} +1 -1
- package/client/dist/assets/{spending-Oov7p52A.js → spending-DemeFfrH.js} +1 -1
- package/client/dist/assets/{useDesktop-DpiS8Uov.js → useDesktop-9Lwoel_8.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-DBm1mul0.js → useRuntimeRuns-DL0xYoZ2.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-BZxKaVNT.js → useSharedWebSocket-BEHPeU1Y.js} +2 -2
- package/client/dist/assets/zap-CHo3aCD1.js +1 -0
- package/client/dist/index.html +26 -22
- package/docs/guide/de/integrations/6-agent-chat.md +11 -0
- package/docs/guide/en/integrations/6-agent-chat.md +11 -0
- package/docs/guide/es/integrations/6-agent-chat.md +11 -0
- package/docs/guide/fr/integrations/6-agent-chat.md +11 -0
- package/docs/guide/it/integrations/6-agent-chat.md +11 -0
- package/docs/guide/ja/integrations/6-agent-chat.md +11 -0
- package/docs/guide/pt/integrations/6-agent-chat.md +11 -0
- package/docs/guide/zh/integrations/6-agent-chat.md +11 -0
- package/docs/internals/README.md +1 -0
- package/docs/internals/agent-sessions.md +157 -0
- package/docs/internals/api-reference.md +33 -4
- package/docs/internals/configuration.md +36 -0
- package/docs/internals/core-runtime-updates.md +10 -3
- package/docs/internals/operations-runbook.md +11 -0
- package/docs/internals/programmatic-agent-runtime.md +2 -0
- package/docs/internals/source-map.md +40 -1
- package/mcp-bridge/dist/specrails-mcp.js +82 -15
- package/package.json +1 -1
- package/server/dist/agent-mcp-config.js +41 -0
- package/server/dist/core-compat.js +2 -0
- package/server/dist/desktop-db.js +101 -4
- package/server/dist/desktop-router.js +31 -2
- package/server/dist/index.js +58 -0
- package/server/dist/mcp/tools/app-settings.js +11 -0
- package/server/dist/mcp/tools/mission.js +45 -2
- package/server/dist/mcp/tools/specs.js +2 -2
- package/server/dist/modules/accounting/runtime/result-event.js +9 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +12 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +25 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-effective-config.js +2 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +6 -2
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +18 -6
- package/server/dist/modules/agent-sessions/adapters/host-process.js +73 -0
- package/server/dist/modules/agent-sessions/adapters/http.js +31 -0
- package/server/dist/modules/agent-sessions/adapters/rpc-client.js +147 -0
- package/server/dist/modules/agent-sessions/application/session-event-pump.js +99 -0
- package/server/dist/modules/agent-sessions/domain/availability.js +25 -0
- package/server/dist/modules/agent-sessions/domain/errors.js +23 -0
- package/server/dist/modules/agent-sessions/domain/host-state.js +80 -0
- package/server/dist/modules/agent-sessions/domain/projection.js +147 -0
- package/server/dist/modules/agent-sessions/domain/protocol.js +59 -0
- package/server/dist/modules/agent-sessions/domain/subagent-policy.js +34 -0
- package/server/dist/modules/agent-sessions/index.js +31 -0
- package/server/dist/modules/agent-sessions/ports.js +2 -0
- package/server/dist/modules/agent-sessions/runtime/core-sessions-availability.js +43 -0
- package/server/dist/modules/agent-sessions/runtime/session-host-registry.js +226 -0
- package/server/dist/modules/agents/runtime/agent-store.js +3 -1
- package/server/dist/modules/loops/runtime/loop-core-factory.js +2 -2
- package/server/dist/modules/loops/runtime/loop-definition.js +1 -0
- package/server/dist/modules/loops/runtime/loop-factory.js +1 -1
- package/server/dist/modules/loops/runtime/loop-graph.js +8 -1
- package/server/dist/modules/loops/runtime/loop-implement-recipe.js +41 -11
- package/server/dist/modules/loops/runtime/loop-run-manager.js +4 -0
- package/server/dist/modules/loops/runtime/loop-templates.js +3 -3
- package/server/dist/modules/missions/runtime/agent-chat-manager.js +137 -40
- package/server/dist/modules/missions/runtime/agent-chat-router.js +68 -0
- package/server/dist/modules/missions/runtime/agent-operator-prompt.js +8 -9
- package/server/dist/modules/missions/runtime/agent-session-store.js +133 -0
- package/server/dist/modules/missions/runtime/core-session-runner.js +204 -0
- package/server/dist/modules/missions/runtime/mission-core-sessions.js +286 -0
- package/server/dist/modules/missions/runtime/mission-session-projector.js +168 -0
- package/server/dist/modules/project-settings/adapters/http.js +14 -2
- package/server/dist/modules/project-settings/adapters/sqlite.js +30 -2
- package/server/dist/modules/project-settings/domain.js +24 -0
- package/server/dist/modules/project-settings/index.js +2 -1
- package/server/dist/modules/specs/runtime/spec-models.js +1 -1
- package/server/dist/plugins/codex-spawn.js +15 -7
- package/server/dist/project-registry.js +32 -0
- package/server/dist/project-router-settings.js +7 -1
- package/server/dist/providers/claude-adapter.js +6 -1
- package/server/dist/schemas/agent-runtime.schema.json +9 -7
- package/client/dist/assets/ActivityFeedPage-3J7fR8yB.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-Dps30tk9.js +0 -2
- package/client/dist/assets/AgentModeCodePane-CvLimey8.js +0 -2
- package/client/dist/assets/CodePage-D981zRHy.js +0 -3
- package/client/dist/assets/InteractiveJobComposer-Z6fl4791.js +0 -19
- package/client/dist/assets/JobDetailPage-1TUQ9vlE.js +0 -1
- package/client/dist/assets/JobsPage-BSrl8P7u.js +0 -1
- package/client/dist/assets/LoopBuilderPage-Dh5uk2Qy.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-BD2bsRBe.js +0 -1
- package/client/dist/assets/PluginsPage-B8f49Mvn.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-D7q-O6h3.js +0 -1
- package/client/dist/assets/ReviewPacketPage-DpZSzF-Q.js +0 -4
- package/client/dist/assets/TicketDetailModalContext-BHmtj1As.js +0 -16
- package/client/dist/assets/agent--8QAfoXL.js +0 -1
- package/client/dist/assets/agent-3HxXkIWX.js +0 -1
- package/client/dist/assets/agent-CpFCz32T.js +0 -1
- package/client/dist/assets/agent-DVnw6ybO.js +0 -1
- package/client/dist/assets/agent-Z5s_GTP0.js +0 -1
- package/client/dist/assets/agent-hv57-zyG.js +0 -1
- package/client/dist/assets/agent-xGrZ_aN1.js +0 -1
- package/client/dist/assets/agentRuntime-7Ydcvr6P.js +0 -1
- package/client/dist/assets/agentRuntime-BHVw9orA.js +0 -1
- package/client/dist/assets/agentRuntime-DCPDaQcV.js +0 -1
- package/client/dist/assets/agentRuntime-DKTw4Ajg.js +0 -1
- package/client/dist/assets/agentRuntime-DVhij_i_.js +0 -1
- package/client/dist/assets/agentRuntime-Doss-T8o.js +0 -1
- package/client/dist/assets/agentRuntime-eZSMUlkM.js +0 -1
- package/client/dist/assets/agentRuntime-qSSU7fQk.js +0 -1
- package/client/dist/assets/index-Bfunvi6w.css +0 -2
- package/client/dist/assets/index-g8S62tpS.js +0 -83
- package/client/dist/assets/save-OsUqVn99.js +0 -1
- package/client/dist/assets/settings-Dbyd6gmj.js +0 -1
- package/client/dist/assets/zap-CUOlvtT1.js +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StdioSessionHostClient = exports.MAX_RESPONSE_BYTES = void 0;
|
|
4
|
+
const node_string_decoder_1 = require("node:string_decoder");
|
|
5
|
+
const errors_1 = require("../domain/errors");
|
|
6
|
+
/** Longest accepted response line from the host. */
|
|
7
|
+
exports.MAX_RESPONSE_BYTES = 16 * 1024 * 1024;
|
|
8
|
+
/**
|
|
9
|
+
* JSON-RPC 2.0 client for the Core session host over NDJSON stdio.
|
|
10
|
+
* Requests time out; every pending request is rejected when the connection
|
|
11
|
+
* ends; notifications fan out to subscribers (session.event, session.lagged,
|
|
12
|
+
* host.leaseLost).
|
|
13
|
+
*/
|
|
14
|
+
class StdioSessionHostClient {
|
|
15
|
+
transport;
|
|
16
|
+
options;
|
|
17
|
+
nextId = 1;
|
|
18
|
+
pending = new Map();
|
|
19
|
+
eventListeners = new Set();
|
|
20
|
+
lagListeners = new Set();
|
|
21
|
+
closeListeners = new Set();
|
|
22
|
+
closedReason = null;
|
|
23
|
+
closedCode = null;
|
|
24
|
+
constructor(transport, options = {}) {
|
|
25
|
+
this.transport = transport;
|
|
26
|
+
this.options = options;
|
|
27
|
+
const decoder = new node_string_decoder_1.StringDecoder('utf8');
|
|
28
|
+
let buffer = '';
|
|
29
|
+
transport.stdout.on('data', (chunk) => {
|
|
30
|
+
buffer += typeof chunk === 'string' ? chunk : decoder.write(chunk);
|
|
31
|
+
for (;;) {
|
|
32
|
+
const newline = buffer.indexOf('\n');
|
|
33
|
+
if (newline < 0)
|
|
34
|
+
break;
|
|
35
|
+
const line = buffer.slice(0, newline).trim();
|
|
36
|
+
buffer = buffer.slice(newline + 1);
|
|
37
|
+
if (line)
|
|
38
|
+
this.receive(line);
|
|
39
|
+
}
|
|
40
|
+
if (Buffer.byteLength(buffer) > exports.MAX_RESPONSE_BYTES)
|
|
41
|
+
this.markClosed('Session host sent an oversized frame');
|
|
42
|
+
});
|
|
43
|
+
transport.stdout.on('end', () => this.markClosed('Session host closed its output'));
|
|
44
|
+
transport.stdin.on('error', () => this.markClosed('Session host input closed'));
|
|
45
|
+
}
|
|
46
|
+
get closed() {
|
|
47
|
+
return this.closedReason !== null;
|
|
48
|
+
}
|
|
49
|
+
request(method, params = {}) {
|
|
50
|
+
if (this.closedReason)
|
|
51
|
+
return Promise.reject(new errors_1.SessionRequestError(this.closedReason, this.closedCode ? { code: this.closedCode, retryable: false } : { code: 'host_unavailable', retryable: true }));
|
|
52
|
+
const id = this.nextId++;
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const timer = setTimeout(() => {
|
|
55
|
+
this.pending.delete(id);
|
|
56
|
+
reject(new errors_1.SessionRequestError(`Session host did not answer ${method} in time`, { code: 'host_timeout', retryable: true }));
|
|
57
|
+
}, this.options.timeoutMs ?? 60_000);
|
|
58
|
+
timer.unref?.();
|
|
59
|
+
this.pending.set(id, { method, resolve: resolve, reject, timer });
|
|
60
|
+
this.transport.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id, method, params })}\n`);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
onEvent(listener) {
|
|
64
|
+
this.eventListeners.add(listener);
|
|
65
|
+
return () => { this.eventListeners.delete(listener); };
|
|
66
|
+
}
|
|
67
|
+
onLagged(listener) {
|
|
68
|
+
this.lagListeners.add(listener);
|
|
69
|
+
return () => { this.lagListeners.delete(listener); };
|
|
70
|
+
}
|
|
71
|
+
onClose(listener) {
|
|
72
|
+
if (this.closedReason) {
|
|
73
|
+
listener(this.closedReason, this.closedCode ?? undefined);
|
|
74
|
+
return () => { };
|
|
75
|
+
}
|
|
76
|
+
this.closeListeners.add(listener);
|
|
77
|
+
return () => { this.closeListeners.delete(listener); };
|
|
78
|
+
}
|
|
79
|
+
async close() {
|
|
80
|
+
if (this.closedReason)
|
|
81
|
+
return;
|
|
82
|
+
await this.options.onClose?.();
|
|
83
|
+
this.markClosed('Session host closed');
|
|
84
|
+
}
|
|
85
|
+
/** The process ended (called by the launcher). A known `code` (e.g. `journal_locked`) travels to pending requests and close listeners. */
|
|
86
|
+
markClosed(reason, code) {
|
|
87
|
+
if (this.closedReason)
|
|
88
|
+
return;
|
|
89
|
+
this.closedReason = reason;
|
|
90
|
+
this.closedCode = code ?? null;
|
|
91
|
+
for (const [id, request] of this.pending) {
|
|
92
|
+
clearTimeout(request.timer);
|
|
93
|
+
request.reject(new errors_1.SessionRequestError(reason, code ? { code, retryable: false } : { code: 'host_unavailable', retryable: true }));
|
|
94
|
+
this.pending.delete(id);
|
|
95
|
+
}
|
|
96
|
+
for (const listener of this.closeListeners)
|
|
97
|
+
listener(reason, code);
|
|
98
|
+
this.closeListeners.clear();
|
|
99
|
+
}
|
|
100
|
+
receive(line) {
|
|
101
|
+
let message;
|
|
102
|
+
try {
|
|
103
|
+
message = JSON.parse(line);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (typeof message.method === 'string') {
|
|
109
|
+
const params = (message.params ?? {});
|
|
110
|
+
if (message.method === 'session.event') {
|
|
111
|
+
const envelope = { sessionId: String(params.sessionId), seq: Number(params.seq), event: params.event };
|
|
112
|
+
for (const listener of this.eventListeners)
|
|
113
|
+
listener(envelope);
|
|
114
|
+
}
|
|
115
|
+
else if (message.method === 'session.lagged') {
|
|
116
|
+
for (const listener of this.lagListeners)
|
|
117
|
+
listener(String(params.sessionId), Number(params.deliveredSeq));
|
|
118
|
+
}
|
|
119
|
+
else if (message.method === 'host.leaseLost') {
|
|
120
|
+
this.markClosed('Another session host took over this scope', 'journal_locked');
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// The host could not start and printed its fatal result (e.g. another host owns the journal).
|
|
125
|
+
if (message.type === 'runtime-result' && message.status === 'failed') {
|
|
126
|
+
const error = message.error;
|
|
127
|
+
const detail = typeof error === 'object' && error !== null ? error : { message: error };
|
|
128
|
+
this.markClosed(typeof detail.message === 'string' ? detail.message : 'The session host failed to start', typeof detail.code === 'string' ? detail.code : undefined);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (typeof message.id !== 'number')
|
|
132
|
+
return;
|
|
133
|
+
const request = this.pending.get(message.id);
|
|
134
|
+
if (!request)
|
|
135
|
+
return;
|
|
136
|
+
this.pending.delete(message.id);
|
|
137
|
+
clearTimeout(request.timer);
|
|
138
|
+
if (message.error && typeof message.error === 'object') {
|
|
139
|
+
const error = message.error;
|
|
140
|
+
request.reject(new errors_1.SessionRequestError(error.message ?? `Session host rejected ${request.method}`, error.data ?? { code: 'internal', retryable: false }, error.code));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
request.resolve(message.result ?? {});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.StdioSessionHostClient = StdioSessionHostClient;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionEventPump = void 0;
|
|
4
|
+
const projection_1 = require("../domain/projection");
|
|
5
|
+
const PAGE = 500;
|
|
6
|
+
/**
|
|
7
|
+
* Applies one Core session's committed events to a surface sink exactly once
|
|
8
|
+
* and in order. Duplicates are ignored, gaps are filled from the host's
|
|
9
|
+
* journal (`session.events`), and `attach` rebuilds the in-memory projection
|
|
10
|
+
* by replaying up to the sink's cursor without re-applying anything.
|
|
11
|
+
*/
|
|
12
|
+
class SessionEventPump {
|
|
13
|
+
sessionId;
|
|
14
|
+
client;
|
|
15
|
+
sink;
|
|
16
|
+
listener;
|
|
17
|
+
state;
|
|
18
|
+
chain = Promise.resolve();
|
|
19
|
+
detached = false;
|
|
20
|
+
constructor(sessionId, client, sink, listener = () => { }) {
|
|
21
|
+
this.sessionId = sessionId;
|
|
22
|
+
this.client = client;
|
|
23
|
+
this.sink = sink;
|
|
24
|
+
this.listener = listener;
|
|
25
|
+
this.state = (0, projection_1.initialProjection)(sessionId);
|
|
26
|
+
}
|
|
27
|
+
get projection() {
|
|
28
|
+
return this.state;
|
|
29
|
+
}
|
|
30
|
+
/** Rebuild state up to the sink cursor, then apply everything after it. */
|
|
31
|
+
attach() {
|
|
32
|
+
return this.enqueue(async () => {
|
|
33
|
+
const cursor = this.sink.cursor(this.sessionId);
|
|
34
|
+
this.state = (0, projection_1.initialProjection)(this.sessionId);
|
|
35
|
+
await this.fetchFrom(0, cursor);
|
|
36
|
+
await this.fetchFrom(this.state.lastSeq);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** A live notification. */
|
|
40
|
+
push(envelope) {
|
|
41
|
+
if (envelope.sessionId !== this.sessionId)
|
|
42
|
+
return Promise.resolve();
|
|
43
|
+
return this.enqueue(async () => {
|
|
44
|
+
const result = (0, projection_1.reduceEnvelope)(this.state, envelope);
|
|
45
|
+
if (result.status === 'duplicate')
|
|
46
|
+
return;
|
|
47
|
+
if (result.status === 'gap') {
|
|
48
|
+
await this.fetchFrom(this.state.lastSeq);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.commit(envelope.seq, result.state, result.ops);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** The host dropped queued notifications; catch up from the journal. */
|
|
55
|
+
catchUp() {
|
|
56
|
+
return this.enqueue(() => this.fetchFrom(this.state.lastSeq));
|
|
57
|
+
}
|
|
58
|
+
detach() {
|
|
59
|
+
this.detached = true;
|
|
60
|
+
}
|
|
61
|
+
/** Page through the journal; events up to `rebuildUntil` only rebuild state. */
|
|
62
|
+
async fetchFrom(afterSeq, rebuildUntil = 0) {
|
|
63
|
+
let cursor = afterSeq;
|
|
64
|
+
for (;;) {
|
|
65
|
+
if (this.detached)
|
|
66
|
+
return;
|
|
67
|
+
if (rebuildUntil > 0 && cursor >= rebuildUntil)
|
|
68
|
+
return;
|
|
69
|
+
const page = await this.client.request('session.events', { sessionId: this.sessionId, afterSeq: cursor, limit: PAGE });
|
|
70
|
+
for (const envelope of page.events) {
|
|
71
|
+
const result = (0, projection_1.reduceEnvelope)(this.state, envelope);
|
|
72
|
+
if (result.status !== 'applied')
|
|
73
|
+
continue;
|
|
74
|
+
if (envelope.seq <= rebuildUntil)
|
|
75
|
+
this.state = result.state;
|
|
76
|
+
else
|
|
77
|
+
this.commit(envelope.seq, result.state, result.ops);
|
|
78
|
+
cursor = envelope.seq;
|
|
79
|
+
if (rebuildUntil > 0 && cursor >= rebuildUntil)
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!page.hasMore || page.events.length === 0)
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
commit(seq, state, ops) {
|
|
87
|
+
// The sink owns the transaction (ops + cursor); state advances only after it committed.
|
|
88
|
+
this.sink.apply(this.sessionId, seq, ops);
|
|
89
|
+
this.state = state;
|
|
90
|
+
if (ops.length > 0)
|
|
91
|
+
this.listener(this.sessionId, ops, state);
|
|
92
|
+
}
|
|
93
|
+
enqueue(task) {
|
|
94
|
+
const run = this.chain.then(task);
|
|
95
|
+
this.chain = run.catch(() => undefined);
|
|
96
|
+
return run;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.SessionEventPump = SessionEventPump;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CORE_SESSIONS_FLAG = void 0;
|
|
4
|
+
exports.parseCoreSessionsFlag = parseCoreSessionsFlag;
|
|
5
|
+
exports.resolveCoreSessionsAvailability = resolveCoreSessionsAvailability;
|
|
6
|
+
exports.DEFAULT_CORE_SESSIONS_FLAG = 'auto';
|
|
7
|
+
function parseCoreSessionsFlag(raw) {
|
|
8
|
+
const value = raw?.trim().toLowerCase();
|
|
9
|
+
if (value === 'on' || value === '1' || value === 'true')
|
|
10
|
+
return 'on';
|
|
11
|
+
if (value === 'auto')
|
|
12
|
+
return 'auto';
|
|
13
|
+
if (value === 'off' || value === '0' || value === 'false')
|
|
14
|
+
return 'off';
|
|
15
|
+
return exports.DEFAULT_CORE_SESSIONS_FLAG;
|
|
16
|
+
}
|
|
17
|
+
function resolveCoreSessionsAvailability(flag, capabilities, contractCompatible = true) {
|
|
18
|
+
if (flag === 'off')
|
|
19
|
+
return { enabled: false, flag, reason: 'Core sessions are disabled (SPECRAILS_CORE_SESSIONS=off)' };
|
|
20
|
+
if (capabilities?.sessions !== 1)
|
|
21
|
+
return { enabled: false, flag, reason: 'The selected Core does not advertise the sessions capability; using legacy transports' };
|
|
22
|
+
if (!contractCompatible)
|
|
23
|
+
return { enabled: false, flag, reason: 'The selected Core session contract is not compatible with this Desktop; using legacy transports' };
|
|
24
|
+
return { enabled: true, flag, reason: flag === 'on' ? 'Core sessions forced on' : 'Core sessions available' };
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionRequestError = void 0;
|
|
4
|
+
exports.isSessionRequestError = isSessionRequestError;
|
|
5
|
+
/** A session protocol request failed; `code` is Core's stable error code. */
|
|
6
|
+
class SessionRequestError extends Error {
|
|
7
|
+
rpcCode;
|
|
8
|
+
code;
|
|
9
|
+
retryable;
|
|
10
|
+
detail;
|
|
11
|
+
constructor(message, data, rpcCode) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.rpcCode = rpcCode;
|
|
14
|
+
this.name = 'SessionRequestError';
|
|
15
|
+
this.code = data.code;
|
|
16
|
+
this.retryable = data.retryable;
|
|
17
|
+
this.detail = data.detail;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.SessionRequestError = SessionRequestError;
|
|
21
|
+
function isSessionRequestError(error, code) {
|
|
22
|
+
return error instanceof SessionRequestError && (code === undefined || error.code === code);
|
|
23
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INITIAL_HOST_STATE = exports.DEFAULT_SUPERVISION = void 0;
|
|
4
|
+
exports.canTransition = canTransition;
|
|
5
|
+
exports.onDemand = onDemand;
|
|
6
|
+
exports.onReady = onReady;
|
|
7
|
+
exports.onFailure = onFailure;
|
|
8
|
+
exports.onFatal = onFatal;
|
|
9
|
+
exports.onRestartDue = onRestartDue;
|
|
10
|
+
exports.onRetry = onRetry;
|
|
11
|
+
exports.onStop = onStop;
|
|
12
|
+
exports.acceptsSessions = acceptsSessions;
|
|
13
|
+
exports.DEFAULT_SUPERVISION = Object.freeze({
|
|
14
|
+
baseDelayMs: 500,
|
|
15
|
+
maxDelayMs: 30_000,
|
|
16
|
+
maxFailures: 5,
|
|
17
|
+
failureWindowMs: 5 * 60_000,
|
|
18
|
+
});
|
|
19
|
+
exports.INITIAL_HOST_STATE = Object.freeze({ status: 'absent', failures: [] });
|
|
20
|
+
const TRANSITIONS = Object.freeze({
|
|
21
|
+
absent: ['starting', 'stopped'],
|
|
22
|
+
starting: ['ready', 'restarting', 'degraded', 'stopped'],
|
|
23
|
+
ready: ['restarting', 'degraded', 'stopped'],
|
|
24
|
+
restarting: ['starting', 'degraded', 'stopped'],
|
|
25
|
+
degraded: ['starting', 'stopped'],
|
|
26
|
+
stopped: [],
|
|
27
|
+
});
|
|
28
|
+
function canTransition(from, to) {
|
|
29
|
+
return from === to || TRANSITIONS[from].includes(to);
|
|
30
|
+
}
|
|
31
|
+
function transition(state, to, failures = state.failures) {
|
|
32
|
+
if (!canTransition(state.status, to))
|
|
33
|
+
throw new Error(`Session host: ${state.status} → ${to} is not allowed`);
|
|
34
|
+
return { status: to, failures };
|
|
35
|
+
}
|
|
36
|
+
/** A session is needed in this scope. */
|
|
37
|
+
function onDemand(state) {
|
|
38
|
+
if (state.status === 'absent')
|
|
39
|
+
return { state: transition(state, 'starting'), decision: { action: 'start' } };
|
|
40
|
+
return { state, decision: { action: 'none' } };
|
|
41
|
+
}
|
|
42
|
+
function onReady(state) {
|
|
43
|
+
return transition(state, 'ready');
|
|
44
|
+
}
|
|
45
|
+
/** The host exited unexpectedly or failed to start. */
|
|
46
|
+
function onFailure(state, now, policy = exports.DEFAULT_SUPERVISION) {
|
|
47
|
+
if (state.status === 'stopped')
|
|
48
|
+
return { state, decision: { action: 'none' } };
|
|
49
|
+
const failures = [...state.failures.filter((at) => now - at < policy.failureWindowMs), now];
|
|
50
|
+
if (failures.length >= policy.maxFailures) {
|
|
51
|
+
return { state: transition(state, 'degraded', failures), decision: { action: 'degrade', reason: `${failures.length} host failures within ${Math.round(policy.failureWindowMs / 1000)}s` } };
|
|
52
|
+
}
|
|
53
|
+
const delayMs = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** (failures.length - 1));
|
|
54
|
+
return { state: transition(state, 'restarting', failures), decision: { action: 'restart', delayMs } };
|
|
55
|
+
}
|
|
56
|
+
/** A failure no restart can fix (e.g. another host owns the scope's journal): degrade at once. */
|
|
57
|
+
function onFatal(state, reason) {
|
|
58
|
+
if (state.status === 'stopped' || state.status === 'degraded')
|
|
59
|
+
return { state, decision: { action: 'none' } };
|
|
60
|
+
return { state: transition(state, 'degraded'), decision: { action: 'degrade', reason } };
|
|
61
|
+
}
|
|
62
|
+
/** The restart delay elapsed. */
|
|
63
|
+
function onRestartDue(state) {
|
|
64
|
+
if (state.status !== 'restarting')
|
|
65
|
+
return { state, decision: { action: 'none' } };
|
|
66
|
+
return { state: transition(state, 'starting'), decision: { action: 'start' } };
|
|
67
|
+
}
|
|
68
|
+
/** A user explicitly retries a degraded scope (e.g. after fixing Core). */
|
|
69
|
+
function onRetry(state) {
|
|
70
|
+
if (state.status !== 'degraded')
|
|
71
|
+
return { state, decision: { action: 'none' } };
|
|
72
|
+
return { state: transition(state, 'starting', []), decision: { action: 'start' } };
|
|
73
|
+
}
|
|
74
|
+
function onStop(state) {
|
|
75
|
+
return state.status === 'stopped' ? state : transition(state, 'stopped');
|
|
76
|
+
}
|
|
77
|
+
/** Whether new turns in this scope should use Core sessions right now. */
|
|
78
|
+
function acceptsSessions(state) {
|
|
79
|
+
return state.status !== 'degraded' && state.status !== 'stopped';
|
|
80
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initialProjection = initialProjection;
|
|
4
|
+
exports.reduceEnvelope = reduceEnvelope;
|
|
5
|
+
exports.liveSubagentCount = liveSubagentCount;
|
|
6
|
+
const protocol_1 = require("./protocol");
|
|
7
|
+
function initialProjection(sessionId, lastSeq = 0) {
|
|
8
|
+
return { sessionId, lastSeq, phase: 'idle', processAlive: false, openTurn: null, liveSubagents: 0, subagents: {} };
|
|
9
|
+
}
|
|
10
|
+
const KNOWN = new Set(protocol_1.KNOWN_EVENT_TYPES);
|
|
11
|
+
function phaseOp(state) {
|
|
12
|
+
return { kind: 'resident.phase', phase: state.phase, liveSubagents: state.liveSubagents, processAlive: state.processAlive };
|
|
13
|
+
}
|
|
14
|
+
function reduceEnvelope(state, envelope) {
|
|
15
|
+
if (envelope.sessionId !== state.sessionId)
|
|
16
|
+
throw new Error(`Event for ${envelope.sessionId} applied to ${state.sessionId}`);
|
|
17
|
+
if (envelope.seq <= state.lastSeq)
|
|
18
|
+
return { status: 'duplicate', state, ops: [] };
|
|
19
|
+
if (envelope.seq !== state.lastSeq + 1)
|
|
20
|
+
return { status: 'gap', state, ops: [], expected: state.lastSeq + 1 };
|
|
21
|
+
const next = { ...state, lastSeq: envelope.seq };
|
|
22
|
+
const event = envelope.event;
|
|
23
|
+
if (!KNOWN.has(event.type))
|
|
24
|
+
return { status: 'applied', state: next, ops: [] };
|
|
25
|
+
const ops = [];
|
|
26
|
+
switch (event.type) {
|
|
27
|
+
case 'session.phase':
|
|
28
|
+
next.phase = event.phase;
|
|
29
|
+
ops.push(phaseOp(next));
|
|
30
|
+
break;
|
|
31
|
+
case 'session.process':
|
|
32
|
+
next.processAlive = event.state === 'started';
|
|
33
|
+
ops.push(phaseOp(next));
|
|
34
|
+
break;
|
|
35
|
+
case 'session.provider-ref':
|
|
36
|
+
ops.push({ kind: 'provider.ref', providerSessionRef: event.providerSessionRef });
|
|
37
|
+
break;
|
|
38
|
+
case 'session.updated':
|
|
39
|
+
ops.push({ kind: 'session.updated', changes: event.changes, outcome: event.outcome });
|
|
40
|
+
break;
|
|
41
|
+
case 'session.closed':
|
|
42
|
+
ops.push({ kind: 'session.closed', reason: event.reason });
|
|
43
|
+
break;
|
|
44
|
+
case 'input.accepted':
|
|
45
|
+
ops.push({ kind: 'input.state', inputId: event.inputId, state: 'accepted', turnId: null, reason: null });
|
|
46
|
+
break;
|
|
47
|
+
case 'input.state':
|
|
48
|
+
ops.push({ kind: 'input.state', inputId: event.inputId, state: event.state, turnId: event.turnId ?? null, reason: event.reason ?? null });
|
|
49
|
+
break;
|
|
50
|
+
case 'turn.started':
|
|
51
|
+
next.openTurn = { turnId: event.turnId, origin: event.origin, startedAt: event.at };
|
|
52
|
+
ops.push({ kind: 'turn.opened', turnId: event.turnId, origin: event.origin, inputIds: [...event.inputIds], triggeredBy: [...(event.trigger?.subagentIds ?? [])], at: event.at });
|
|
53
|
+
break;
|
|
54
|
+
case 'turn.output':
|
|
55
|
+
ops.push({ kind: 'turn.delta', turnId: event.turnId, channel: event.channel, delta: event.delta });
|
|
56
|
+
break;
|
|
57
|
+
case 'turn.tool': {
|
|
58
|
+
const { type: _type, at: _at, turnId, ...tool } = event;
|
|
59
|
+
ops.push({ kind: 'turn.tool', turnId, tool });
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'turn.completed': {
|
|
63
|
+
const open = next.openTurn;
|
|
64
|
+
next.openTurn = null;
|
|
65
|
+
ops.push({ kind: 'turn.closed', turnId: event.turnId, origin: open?.origin ?? 'user', status: event.status, text: event.text, error: event.error ?? null, usage: event.usage, startedAt: open?.startedAt ?? event.at, at: event.at });
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case 'subagent.started': {
|
|
69
|
+
const existing = next.subagents[event.subagentId];
|
|
70
|
+
const node = existing
|
|
71
|
+
? { ...existing, phase: 'running', reason: null, endedAt: null, restarts: existing.restarts + (existing.phase === 'idle' ? 1 : 0) }
|
|
72
|
+
: {
|
|
73
|
+
subagentId: event.subagentId,
|
|
74
|
+
parentId: event.parentId,
|
|
75
|
+
kind: event.kind,
|
|
76
|
+
agentType: event.agentType ?? null,
|
|
77
|
+
description: event.description,
|
|
78
|
+
phase: 'running',
|
|
79
|
+
reason: null,
|
|
80
|
+
restarts: 0,
|
|
81
|
+
startedAt: event.at,
|
|
82
|
+
endedAt: null,
|
|
83
|
+
usage: null,
|
|
84
|
+
toolUses: null,
|
|
85
|
+
durationMs: null,
|
|
86
|
+
resultSummary: null,
|
|
87
|
+
launchedInTurnId: next.openTurn?.turnId ?? null,
|
|
88
|
+
delegated: event.delegated ?? null,
|
|
89
|
+
};
|
|
90
|
+
next.subagents = { ...next.subagents, [node.subagentId]: node };
|
|
91
|
+
ops.push({ kind: 'subagent.upsert', subagent: node });
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'subagent.phase': {
|
|
95
|
+
const node = next.subagents[event.subagentId];
|
|
96
|
+
if (!node)
|
|
97
|
+
break;
|
|
98
|
+
const live = event.phase === 'running';
|
|
99
|
+
const updated = { ...node, phase: event.phase, reason: event.reason ?? (live ? null : node.reason), endedAt: live ? null : event.at };
|
|
100
|
+
next.subagents = { ...next.subagents, [node.subagentId]: updated };
|
|
101
|
+
ops.push({ kind: 'subagent.upsert', subagent: updated });
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'subagent.usage': {
|
|
105
|
+
const node = next.subagents[event.subagentId];
|
|
106
|
+
if (!node)
|
|
107
|
+
break;
|
|
108
|
+
const updated = { ...node, usage: event.usage, toolUses: event.toolUses ?? node.toolUses, durationMs: event.durationMs ?? node.durationMs };
|
|
109
|
+
next.subagents = { ...next.subagents, [node.subagentId]: updated };
|
|
110
|
+
ops.push({ kind: 'subagent.upsert', subagent: updated });
|
|
111
|
+
if (event.billing === 'separate' && node.delegated)
|
|
112
|
+
ops.push({ kind: 'subagent.billed', subagentId: node.subagentId, driver: node.delegated.driver, model: event.usage.model ?? node.delegated.model, usage: event.usage, seq: envelope.seq, at: event.at });
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'subagent.result': {
|
|
116
|
+
const node = next.subagents[event.subagentId];
|
|
117
|
+
if (!node)
|
|
118
|
+
break;
|
|
119
|
+
const updated = { ...node, resultSummary: event.summary };
|
|
120
|
+
next.subagents = { ...next.subagents, [node.subagentId]: updated };
|
|
121
|
+
ops.push({ kind: 'subagent.upsert', subagent: updated });
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case 'subagent.output':
|
|
125
|
+
ops.push({ kind: 'subagent.output', subagentId: event.subagentId, channel: event.channel, delta: event.delta ?? null, tool: event.tool ?? null, seq: envelope.seq });
|
|
126
|
+
break;
|
|
127
|
+
case 'subagents.settled':
|
|
128
|
+
next.liveSubagents = event.live;
|
|
129
|
+
ops.push(phaseOp(next));
|
|
130
|
+
break;
|
|
131
|
+
case 'output.truncated':
|
|
132
|
+
ops.push({ kind: 'notice', level: 'info', code: 'output_truncated', message: `${event.droppedEvents} output chunks (${event.droppedBytes} bytes) were not stored` });
|
|
133
|
+
break;
|
|
134
|
+
case 'provider.diagnostic':
|
|
135
|
+
if (event.level === 'warning')
|
|
136
|
+
ops.push({ kind: 'notice', level: 'warning', code: event.code, message: event.message });
|
|
137
|
+
break;
|
|
138
|
+
case 'session.opened':
|
|
139
|
+
case 'notice.interruption':
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
return { status: 'applied', state: next, ops };
|
|
143
|
+
}
|
|
144
|
+
/** Number of sub-agents the provider is still running. */
|
|
145
|
+
function liveSubagentCount(state) {
|
|
146
|
+
return Object.values(state.subagents).filter((node) => node.phase === 'running').length;
|
|
147
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session protocol v1 wire types, as Desktop consumes them from the Core
|
|
4
|
+
* session host (`specrails-core runtime host --stdio`). This is Desktop's
|
|
5
|
+
* anti-corruption layer for the protocol: Core's internals never leak in.
|
|
6
|
+
* Drift is checked against the resolved Core's integration contract
|
|
7
|
+
* (`agentRuntime.sessions`) by `checkSessionContract` and check-core-compat.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.KNOWN_EVENT_TYPES = exports.SUPPORTED_PROTOCOL_VERSIONS = void 0;
|
|
11
|
+
exports.checkSessionContract = checkSessionContract;
|
|
12
|
+
exports.SUPPORTED_PROTOCOL_VERSIONS = Object.freeze([1]);
|
|
13
|
+
/** Event types this Desktop understands; newer Cores may send more (ignored). */
|
|
14
|
+
exports.KNOWN_EVENT_TYPES = Object.freeze([
|
|
15
|
+
'session.opened',
|
|
16
|
+
'session.phase',
|
|
17
|
+
'session.process',
|
|
18
|
+
'session.provider-ref',
|
|
19
|
+
'session.updated',
|
|
20
|
+
'session.closed',
|
|
21
|
+
'input.accepted',
|
|
22
|
+
'input.state',
|
|
23
|
+
'turn.started',
|
|
24
|
+
'turn.output',
|
|
25
|
+
'turn.tool',
|
|
26
|
+
'turn.completed',
|
|
27
|
+
'subagent.started',
|
|
28
|
+
'subagent.phase',
|
|
29
|
+
'subagent.output',
|
|
30
|
+
'subagent.usage',
|
|
31
|
+
'subagent.result',
|
|
32
|
+
'subagents.settled',
|
|
33
|
+
'output.truncated',
|
|
34
|
+
'notice.interruption',
|
|
35
|
+
'provider.diagnostic',
|
|
36
|
+
]);
|
|
37
|
+
/** Compare a Core contract block with what this Desktop speaks. Pure. */
|
|
38
|
+
function checkSessionContract(block) {
|
|
39
|
+
if (!block)
|
|
40
|
+
return { compatible: false, protocolVersion: null, unknownEventTypes: [], missingEventTypes: [], reasons: ['Core does not declare agentRuntime.sessions'] };
|
|
41
|
+
const common = block.protocolVersions.filter((version) => exports.SUPPORTED_PROTOCOL_VERSIONS.includes(version));
|
|
42
|
+
const declared = new Set(block.eventTypes);
|
|
43
|
+
const known = new Set(exports.KNOWN_EVENT_TYPES);
|
|
44
|
+
const missingEventTypes = exports.KNOWN_EVENT_TYPES.filter((type) => !declared.has(type));
|
|
45
|
+
const reasons = [];
|
|
46
|
+
if (common.length === 0)
|
|
47
|
+
reasons.push(`No common protocol version (Core ${block.protocolVersions.join(', ')}; Desktop ${exports.SUPPORTED_PROTOCOL_VERSIONS.join(', ')})`);
|
|
48
|
+
if (block.cliOperation !== 'host')
|
|
49
|
+
reasons.push(`Unexpected session CLI operation "${block.cliOperation}"`);
|
|
50
|
+
if (missingEventTypes.length)
|
|
51
|
+
reasons.push(`Core no longer declares: ${missingEventTypes.join(', ')}`);
|
|
52
|
+
return {
|
|
53
|
+
compatible: reasons.length === 0,
|
|
54
|
+
protocolVersion: common.length ? Math.max(...common) : null,
|
|
55
|
+
unknownEventTypes: block.eventTypes.filter((type) => !known.has(type)),
|
|
56
|
+
missingEventTypes,
|
|
57
|
+
reasons,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveSubagentPolicy = resolveSubagentPolicy;
|
|
4
|
+
exports.resolveSubagentRuntime = resolveSubagentRuntime;
|
|
5
|
+
/** The project setting decides; conversations without a project use the global one. */
|
|
6
|
+
function resolveSubagentPolicy(input) {
|
|
7
|
+
const allowed = input.projectAllows ?? input.globalAllows;
|
|
8
|
+
return allowed ? 'enabled' : 'disabled';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Hybrid runtime (core-agent-sessions-host D9): native unless the user chose a
|
|
12
|
+
* provider other than the mission's; then Core launches them. Overrides a
|
|
13
|
+
* driver cannot apply are dropped (the UI never offers them).
|
|
14
|
+
*/
|
|
15
|
+
function resolveSubagentRuntime(input) {
|
|
16
|
+
const { choice } = input;
|
|
17
|
+
if (!choice)
|
|
18
|
+
return { runtime: { mode: 'native' } };
|
|
19
|
+
if (choice.provider === input.missionProvider) {
|
|
20
|
+
const caps = input.drivers.find((driver) => driver.id === input.missionProvider)?.capabilities;
|
|
21
|
+
return {
|
|
22
|
+
runtime: {
|
|
23
|
+
mode: 'native',
|
|
24
|
+
...(caps?.subagentModel ? { model: choice.model } : {}),
|
|
25
|
+
...(caps?.subagentEffort && choice.effort ? { effort: choice.effort } : {}),
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (!input.delegation)
|
|
30
|
+
return { runtime: { mode: 'native' }, unavailable: 'delegation_unsupported' };
|
|
31
|
+
if (!input.drivers.some((driver) => driver.id === choice.provider))
|
|
32
|
+
return { runtime: { mode: 'native' }, unavailable: 'driver_unavailable' };
|
|
33
|
+
return { runtime: { mode: 'delegated', driver: choice.provider, model: choice.model, ...(choice.effort ? { effort: choice.effort } : {}) } };
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionEventPump = exports.resolveCoreSessionsAvailability = exports.parseCoreSessionsFlag = exports.DEFAULT_CORE_SESSIONS_FLAG = exports.acceptsSessions = exports.DEFAULT_SUPERVISION = exports.resolveSubagentRuntime = exports.resolveSubagentPolicy = exports.reduceEnvelope = exports.liveSubagentCount = exports.initialProjection = exports.isSessionRequestError = exports.SessionRequestError = exports.checkSessionContract = exports.KNOWN_EVENT_TYPES = exports.SUPPORTED_PROTOCOL_VERSIONS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Agent sessions: Desktop as the host of Core's agent session runtime.
|
|
6
|
+
* Public API for conversational features (missions first). Adapters and the
|
|
7
|
+
* runtime registry are exposed through focused subpaths, not this barrel.
|
|
8
|
+
*/
|
|
9
|
+
var protocol_1 = require("./domain/protocol");
|
|
10
|
+
Object.defineProperty(exports, "SUPPORTED_PROTOCOL_VERSIONS", { enumerable: true, get: function () { return protocol_1.SUPPORTED_PROTOCOL_VERSIONS; } });
|
|
11
|
+
Object.defineProperty(exports, "KNOWN_EVENT_TYPES", { enumerable: true, get: function () { return protocol_1.KNOWN_EVENT_TYPES; } });
|
|
12
|
+
Object.defineProperty(exports, "checkSessionContract", { enumerable: true, get: function () { return protocol_1.checkSessionContract; } });
|
|
13
|
+
var errors_1 = require("./domain/errors");
|
|
14
|
+
Object.defineProperty(exports, "SessionRequestError", { enumerable: true, get: function () { return errors_1.SessionRequestError; } });
|
|
15
|
+
Object.defineProperty(exports, "isSessionRequestError", { enumerable: true, get: function () { return errors_1.isSessionRequestError; } });
|
|
16
|
+
var projection_1 = require("./domain/projection");
|
|
17
|
+
Object.defineProperty(exports, "initialProjection", { enumerable: true, get: function () { return projection_1.initialProjection; } });
|
|
18
|
+
Object.defineProperty(exports, "liveSubagentCount", { enumerable: true, get: function () { return projection_1.liveSubagentCount; } });
|
|
19
|
+
Object.defineProperty(exports, "reduceEnvelope", { enumerable: true, get: function () { return projection_1.reduceEnvelope; } });
|
|
20
|
+
var subagent_policy_1 = require("./domain/subagent-policy");
|
|
21
|
+
Object.defineProperty(exports, "resolveSubagentPolicy", { enumerable: true, get: function () { return subagent_policy_1.resolveSubagentPolicy; } });
|
|
22
|
+
Object.defineProperty(exports, "resolveSubagentRuntime", { enumerable: true, get: function () { return subagent_policy_1.resolveSubagentRuntime; } });
|
|
23
|
+
var host_state_1 = require("./domain/host-state");
|
|
24
|
+
Object.defineProperty(exports, "DEFAULT_SUPERVISION", { enumerable: true, get: function () { return host_state_1.DEFAULT_SUPERVISION; } });
|
|
25
|
+
Object.defineProperty(exports, "acceptsSessions", { enumerable: true, get: function () { return host_state_1.acceptsSessions; } });
|
|
26
|
+
var availability_1 = require("./domain/availability");
|
|
27
|
+
Object.defineProperty(exports, "DEFAULT_CORE_SESSIONS_FLAG", { enumerable: true, get: function () { return availability_1.DEFAULT_CORE_SESSIONS_FLAG; } });
|
|
28
|
+
Object.defineProperty(exports, "parseCoreSessionsFlag", { enumerable: true, get: function () { return availability_1.parseCoreSessionsFlag; } });
|
|
29
|
+
Object.defineProperty(exports, "resolveCoreSessionsAvailability", { enumerable: true, get: function () { return availability_1.resolveCoreSessionsAvailability; } });
|
|
30
|
+
var session_event_pump_1 = require("./application/session-event-pump");
|
|
31
|
+
Object.defineProperty(exports, "SessionEventPump", { enumerable: true, get: function () { return session_event_pump_1.SessionEventPump; } });
|