@super-one/cli 0.74.0-alpha.2 → 0.75.0-alpha
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/MANIFEST.json +2 -2
- package/lib/cli.mjs +734 -543
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -137,8 +137,8 @@ import { fileURLToPath } from "node:url";
|
|
|
137
137
|
function resolveCliReleaseVersion() {
|
|
138
138
|
const fromEnv = process.env.SUPERONE_CLI_VERSION?.trim();
|
|
139
139
|
if (fromEnv) return fromEnv;
|
|
140
|
-
if ("0.
|
|
141
|
-
return "0.
|
|
140
|
+
if ("0.75.0-alpha".trim()) {
|
|
141
|
+
return "0.75.0-alpha".trim();
|
|
142
142
|
}
|
|
143
143
|
const fromDist = readDistManifestVersion();
|
|
144
144
|
if (fromDist) return fromDist;
|
|
@@ -510,7 +510,7 @@ var init_superone_tool_descriptions = __esm({
|
|
|
510
510
|
ENVIRONMENT_GET_INFO_INCLUDE_DESCRIPTION = 'Detail groups to add. Default ["hardware"]; [] returns IDs, labels and state only.';
|
|
511
511
|
ARCHIVE_ENVIRONMENT_DESCRIPTION = "Target environment ID from environment_get_info. Omit or use localhost for the calling session host.";
|
|
512
512
|
LAYOUT_SNAPSHOT_DESCRIPTION = "Read the SuperOne desktop layout around your session: whether the user can see your chat, the sidebar, your activity panel (open, side, maximized), its tab groups (index and px box, so you can tell top/bottom from side by side) and tabs (id, kind, title, active, group, openedBy), your bottom terminal, the split view, chatMode and chatPosition, and sizes: px widths of the window, sidebar, panel and chat, with the min/max each column can take. Call it for tab and group ids before layout_act, for sizes before a resize, or to check what the user is looking at.";
|
|
513
|
-
LAYOUT_ACT_DESCRIPTION = "Arrange the SuperOne desktop for the user: open a file as a tab; focus or close tabs, move one beside or into a group or reorder it (move_tab), resize a group; show, hide, maximize, move or resize your activity panel; show or hide your terminal; show, hide or resize the sidebar (widen a column to narrow the chat); dock, float or iconify the chat; put
|
|
513
|
+
LAYOUT_ACT_DESCRIPTION = "Arrange the SuperOne desktop for the user: open a file as a tab; focus or close tabs, move one beside or into a group or reorder it (move_tab), resize a group; show, hide, maximize, move or resize your activity panel; show or hide your terminal; show, hide or resize the sidebar (widen a column to narrow the chat); dock, float or iconify the chat; put any other session beside yours. Open browser and terminal tabs with browser_tabs / terminal_tabs. It never moves the user: in the background your own layout changes wait for them (status queued) and window-wide ones are skipped. `visible` says whether the user sees the result now.";
|
|
514
514
|
LAYOUT_ACT_PATH_DESCRIPTION = "open_file: the file, absolute or relative to your working folder.";
|
|
515
515
|
LAYOUT_ACT_LINE_DESCRIPTION = "open_file: 1-based line to reveal.";
|
|
516
516
|
LAYOUT_ACT_TAB_DESCRIPTION = "Tab id from layout_snapshot (browser_tabs / terminal_tabs ids work too). focus_tab, close_tab, move_tab; maximize_panel defaults to the active tab.";
|
|
@@ -521,7 +521,7 @@ var init_superone_tool_descriptions = __esm({
|
|
|
521
521
|
LAYOUT_ACT_HEIGHT_DESCRIPTION = "resize_group: target height in px.";
|
|
522
522
|
LAYOUT_ACT_MODE_DESCRIPTION = "chat_mode: docked (a column beside the panel), floating (a movable window over the panel, which then fills the window) or icon (that window collapsed).";
|
|
523
523
|
LAYOUT_ACT_POSITION_DESCRIPTION = "chat_mode: where the floating chat or its icon snaps. Alone it moves the chat without changing mode; while docked it is kept for the next float.";
|
|
524
|
-
LAYOUT_ACT_SESSION_DESCRIPTION = "split_session / close_split: a session
|
|
524
|
+
LAYOUT_ACT_SESSION_DESCRIPTION = "split_session / close_split: a session id from session_list, session_search or session_collab_*.";
|
|
525
525
|
}
|
|
526
526
|
});
|
|
527
527
|
|
|
@@ -15634,6 +15634,23 @@ var init_session_runtime = __esm({
|
|
|
15634
15634
|
}
|
|
15635
15635
|
this.systemPromptAppends.set(sessionId, prompt.trim());
|
|
15636
15636
|
}
|
|
15637
|
+
/**
|
|
15638
|
+
* `to` continues `from`, a collaboration child of a session on another
|
|
15639
|
+
* machine: `to` becomes that child, with its parent and prompt, and `from`
|
|
15640
|
+
* a plain session. No-op when `from` has no such parent.
|
|
15641
|
+
*/
|
|
15642
|
+
moveExternalParent(from, to2) {
|
|
15643
|
+
const source = this.live.get(from);
|
|
15644
|
+
const target = this.live.get(to2);
|
|
15645
|
+
const parent = source?.externalParent;
|
|
15646
|
+
if (!source || !target || !parent) return;
|
|
15647
|
+
target.externalParent = { sessionId: parent.sessionId };
|
|
15648
|
+
source.externalParent = null;
|
|
15649
|
+
this.systemPromptAppends.set(to2, collaborationSystemPrompt(parent.sessionId));
|
|
15650
|
+
this.systemPromptAppends.delete(from);
|
|
15651
|
+
this.persist(target);
|
|
15652
|
+
this.persist(source);
|
|
15653
|
+
}
|
|
15637
15654
|
/** Set agent cwd (project root or worktree). Null clears to project default. */
|
|
15638
15655
|
setCwd(sessionId, cwd) {
|
|
15639
15656
|
const session = this.live.get(sessionId);
|
|
@@ -20939,11 +20956,246 @@ var init_sqlite_session_store = __esm({
|
|
|
20939
20956
|
}
|
|
20940
20957
|
});
|
|
20941
20958
|
|
|
20959
|
+
// ../../packages/shared/src/harness/acp-brand.ts
|
|
20960
|
+
function isGrokAcpAgent(agentId) {
|
|
20961
|
+
if (!agentId) return false;
|
|
20962
|
+
const id = agentId.toLowerCase();
|
|
20963
|
+
return id.includes("grok");
|
|
20964
|
+
}
|
|
20965
|
+
function isOpenCodeAcpAgent(agentId) {
|
|
20966
|
+
if (!agentId) return false;
|
|
20967
|
+
return agentId.toLowerCase().includes("opencode");
|
|
20968
|
+
}
|
|
20969
|
+
function resolveHarnessBrandKey(harnessId, acpAgentId) {
|
|
20970
|
+
if (!harnessId) return "claude";
|
|
20971
|
+
if (harnessId !== "acp") return harnessId;
|
|
20972
|
+
if (isGrokAcpAgent(acpAgentId)) return "acp-grok";
|
|
20973
|
+
if (isOpenCodeAcpAgent(acpAgentId)) return "acp-opencode";
|
|
20974
|
+
if (acpAgentId?.trim()) {
|
|
20975
|
+
const short = acpAgentId.trim().toLowerCase().replace(/-build$/, "").replace(/[^a-z0-9]+/g, "-");
|
|
20976
|
+
return short ? `acp-${short}` : "acp";
|
|
20977
|
+
}
|
|
20978
|
+
return "acp";
|
|
20979
|
+
}
|
|
20980
|
+
function acpAgentDisplayName(agentId, catalogName) {
|
|
20981
|
+
if (catalogName?.trim()) {
|
|
20982
|
+
return catalogName.trim().replace(/\s+Build$/i, "");
|
|
20983
|
+
}
|
|
20984
|
+
if (isGrokAcpAgent(agentId)) return "Grok";
|
|
20985
|
+
if (isOpenCodeAcpAgent(agentId)) return "OpenCode";
|
|
20986
|
+
if (agentId?.trim()) {
|
|
20987
|
+
return agentId.trim().replace(/-build$/i, "").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
20988
|
+
}
|
|
20989
|
+
return "ACP";
|
|
20990
|
+
}
|
|
20991
|
+
var init_acp_brand = __esm({
|
|
20992
|
+
"../../packages/shared/src/harness/acp-brand.ts"() {
|
|
20993
|
+
"use strict";
|
|
20994
|
+
}
|
|
20995
|
+
});
|
|
20996
|
+
|
|
20997
|
+
// ../../packages/shared/src/harness/harness-capabilities.ts
|
|
20998
|
+
function supportsShortWidgetResult(harnessId) {
|
|
20999
|
+
return !!harnessId && Object.hasOwn(HARNESS_CAPABILITIES, harnessId) && HARNESS_CAPABILITIES[harnessId].supportsShortWidgetResult;
|
|
21000
|
+
}
|
|
21001
|
+
var HARNESS_CAPABILITIES, ALL_GOAL_LIFECYCLE_ARGS;
|
|
21002
|
+
var init_harness_capabilities = __esm({
|
|
21003
|
+
"../../packages/shared/src/harness/harness-capabilities.ts"() {
|
|
21004
|
+
"use strict";
|
|
21005
|
+
init_acp_brand();
|
|
21006
|
+
HARNESS_CAPABILITIES = {
|
|
21007
|
+
claude: {
|
|
21008
|
+
supportsMcp: true,
|
|
21009
|
+
supportsPlanMode: true,
|
|
21010
|
+
supportsTodos: true,
|
|
21011
|
+
supportsSubagents: true,
|
|
21012
|
+
supportsCompact: true,
|
|
21013
|
+
supportsStreamingToolInput: true,
|
|
21014
|
+
supportsQueuedSteer: true,
|
|
21015
|
+
supportsQueuedSteerSoon: true,
|
|
21016
|
+
// SDK `additionalDirectories`.
|
|
21017
|
+
supportsAdditionalDirs: true,
|
|
21018
|
+
// SDK `forkSession()` copies + remaps the transcript jsonl.
|
|
21019
|
+
supportsFork: true,
|
|
21020
|
+
supportsContextUsage: true,
|
|
21021
|
+
softCancel: false,
|
|
21022
|
+
// Built-in `/goal <condition>`: a Stop hook re-checks the condition after
|
|
21023
|
+
// each turn and keeps going until it is met, so there is no paused state —
|
|
21024
|
+
// the goal is either live or cleared.
|
|
21025
|
+
goal: { lifecycleArgs: ["clear"], canPause: false, transport: "slash", semantics: "condition" },
|
|
21026
|
+
terminalCommandApproval: "harness",
|
|
21027
|
+
modUi: true,
|
|
21028
|
+
supportsShortWidgetResult: true,
|
|
21029
|
+
// `ultrathink` adds a deeper-reasoning reminder to the turn (effort is
|
|
21030
|
+
// untouched); `ultracode` opts it into the Workflow tool, on messages stamped
|
|
21031
|
+
// human — see `claudeMessageOrigin`.
|
|
21032
|
+
promptKeywords: ["ultrathink", "ultracode"],
|
|
21033
|
+
displayName: "Claude"
|
|
21034
|
+
},
|
|
21035
|
+
codex: {
|
|
21036
|
+
supportsRealtimeVoice: true,
|
|
21037
|
+
supportsMcp: true,
|
|
21038
|
+
supportsPlanMode: true,
|
|
21039
|
+
supportsTodos: false,
|
|
21040
|
+
supportsSubagents: false,
|
|
21041
|
+
supportsCompact: true,
|
|
21042
|
+
supportsStreamingToolInput: false,
|
|
21043
|
+
supportsQueuedSteer: true,
|
|
21044
|
+
// Core's queue item either interrupts the turn or waits it out.
|
|
21045
|
+
supportsQueuedSteerSoon: false,
|
|
21046
|
+
// sandbox_workspace_write.writable_roots, re-sent every turn.
|
|
21047
|
+
supportsAdditionalDirs: true,
|
|
21048
|
+
// app-server thread fork, truncatable at a turn id.
|
|
21049
|
+
supportsFork: true,
|
|
21050
|
+
// Token usage arrives on the session snapshot only.
|
|
21051
|
+
supportsContextUsage: false,
|
|
21052
|
+
softCancel: false,
|
|
21053
|
+
// `thread/goal/{get,set,clear}` over the app server; SuperOne drives the
|
|
21054
|
+
// follow-up turns itself, so every transition is an explicit RPC.
|
|
21055
|
+
goal: { lifecycleArgs: [], canPause: true, transport: "rpc", semantics: "objective" },
|
|
21056
|
+
terminalCommandApproval: "harness",
|
|
21057
|
+
modUi: false,
|
|
21058
|
+
supportsShortWidgetResult: true,
|
|
21059
|
+
promptKeywords: [],
|
|
21060
|
+
displayName: "Codex"
|
|
21061
|
+
},
|
|
21062
|
+
acp: {
|
|
21063
|
+
// Host injects SuperOne MCP on every ACP session; user MCP attach is a separate gap.
|
|
21064
|
+
supportsMcp: true,
|
|
21065
|
+
// Agent-driven plan + x.ai/exit_plan_mode approval is shipped (not host enter-plan).
|
|
21066
|
+
supportsPlanMode: true,
|
|
21067
|
+
// ACP session/update plan entries map to todo_write-style UI events.
|
|
21068
|
+
supportsTodos: true,
|
|
21069
|
+
// Subagent_* notifications already map to task rows, but there is no nested
|
|
21070
|
+
// child chrome for ACP. Keep false until Task UI can host those sessions.
|
|
21071
|
+
supportsSubagents: false,
|
|
21072
|
+
// Host intercepts `/compact` → `x.ai/compact_conversation`.
|
|
21073
|
+
supportsCompact: true,
|
|
21074
|
+
supportsStreamingToolInput: false,
|
|
21075
|
+
// Mid-turn send queues. Steer (`now`) is a new `session/prompt` with
|
|
21076
|
+
// `_meta.sendNow`, which cancels the live turn and runs the message next.
|
|
21077
|
+
// Steer soon is `x.ai/interject`: next safe point, the tool in flight stays.
|
|
21078
|
+
supportsQueuedSteer: true,
|
|
21079
|
+
supportsQueuedSteerSoon: true,
|
|
21080
|
+
// session/new additionalDirectories, gated per agent capability.
|
|
21081
|
+
supportsAdditionalDirs: true,
|
|
21082
|
+
// Cold `x.ai/session/fork` copies Grok session files; SuperOne then resumes the child.
|
|
21083
|
+
supportsFork: true,
|
|
21084
|
+
supportsContextUsage: true,
|
|
21085
|
+
softCancel: true,
|
|
21086
|
+
// Grok only — see `resolveGoalCapability`. The agent owns the loop and
|
|
21087
|
+
// reports back over `goal_updated`; the host just posts `/goal …` lines.
|
|
21088
|
+
goal: {
|
|
21089
|
+
lifecycleArgs: ["status", "pause", "resume", "clear"],
|
|
21090
|
+
canPause: true,
|
|
21091
|
+
transport: "slash",
|
|
21092
|
+
semantics: "objective"
|
|
21093
|
+
},
|
|
21094
|
+
terminalCommandApproval: "harness",
|
|
21095
|
+
modUi: false,
|
|
21096
|
+
supportsShortWidgetResult: false,
|
|
21097
|
+
promptKeywords: [],
|
|
21098
|
+
displayName: "Others"
|
|
21099
|
+
},
|
|
21100
|
+
opencode: {
|
|
21101
|
+
supportsMcp: true,
|
|
21102
|
+
supportsPlanMode: true,
|
|
21103
|
+
supportsTodos: true,
|
|
21104
|
+
supportsSubagents: true,
|
|
21105
|
+
supportsCompact: true,
|
|
21106
|
+
supportsStreamingToolInput: false,
|
|
21107
|
+
supportsQueuedSteer: false,
|
|
21108
|
+
supportsQueuedSteerSoon: false,
|
|
21109
|
+
// Single `directory` only.
|
|
21110
|
+
supportsAdditionalDirs: false,
|
|
21111
|
+
// Server-side `forkSession(id, anchor)` + `moveSession`.
|
|
21112
|
+
supportsFork: true,
|
|
21113
|
+
supportsContextUsage: true,
|
|
21114
|
+
softCancel: true,
|
|
21115
|
+
goal: null,
|
|
21116
|
+
terminalCommandApproval: "harness",
|
|
21117
|
+
modUi: false,
|
|
21118
|
+
supportsShortWidgetResult: false,
|
|
21119
|
+
promptKeywords: [],
|
|
21120
|
+
displayName: "OpenCode"
|
|
21121
|
+
},
|
|
21122
|
+
cursor: {
|
|
21123
|
+
supportsMcp: true,
|
|
21124
|
+
supportsPlanMode: true,
|
|
21125
|
+
// updateTodos / task tool deltas when event map lands (PR5+)
|
|
21126
|
+
supportsTodos: true,
|
|
21127
|
+
supportsSubagents: true,
|
|
21128
|
+
supportsCompact: false,
|
|
21129
|
+
supportsStreamingToolInput: true,
|
|
21130
|
+
supportsQueuedSteer: false,
|
|
21131
|
+
supportsQueuedSteerSoon: false,
|
|
21132
|
+
// Single cwd; multi-root parked in the harness design doc.
|
|
21133
|
+
supportsAdditionalDirs: false,
|
|
21134
|
+
// SDK has no transcript-fork API; the adapter creates a blank agent.
|
|
21135
|
+
supportsFork: false,
|
|
21136
|
+
supportsContextUsage: true,
|
|
21137
|
+
softCancel: false,
|
|
21138
|
+
goal: null,
|
|
21139
|
+
// The @cursor/sdk local executor auto-approves custom tools with no host hook.
|
|
21140
|
+
terminalCommandApproval: "executor",
|
|
21141
|
+
modUi: false,
|
|
21142
|
+
supportsShortWidgetResult: false,
|
|
21143
|
+
promptKeywords: [],
|
|
21144
|
+
displayName: "Cursor"
|
|
21145
|
+
},
|
|
21146
|
+
dsh: {
|
|
21147
|
+
// In-process dsh Cordis tree (docs/harness/dsh/deepseek-harness-integration.md).
|
|
21148
|
+
// Flags flip only when the corresponding event path is wired.
|
|
21149
|
+
// `supportsStreamingToolInput`: live `tool-call-delta` frames open the row
|
|
21150
|
+
// and stream its arguments before the durable `tool/call` completes it.
|
|
21151
|
+
// `supportsCompact`: `compaction-basic` compacts automatically at context
|
|
21152
|
+
// pressure and on provider overflow; `/compact` drives `compactNow()`.
|
|
21153
|
+
// `supportsMcp` covers both SuperOne's own tools (native dsh rows) and
|
|
21154
|
+
// third-party servers read from dsh's own profile patch layer.
|
|
21155
|
+
// `supportsSubagents`: foreground delegation (`dsh-tool-subagent` over the
|
|
21156
|
+
// in-process spawn provider), rendered as a Task block with the child's
|
|
21157
|
+
// steps nested under it. Background and continuable children are not wired.
|
|
21158
|
+
// `supportsPlanMode`: `plan` also turns on dsh plan mode; `exit_plan_mode`
|
|
21159
|
+
// reaches the plan-approval card through the user-questions seam.
|
|
21160
|
+
// `supportsQueuedSteer`: `agent.steer()` lands at the next step boundary
|
|
21161
|
+
// and never cancels a tool, so there is one steer and no separate "soon".
|
|
21162
|
+
supportsMcp: true,
|
|
21163
|
+
supportsPlanMode: true,
|
|
21164
|
+
supportsTodos: true,
|
|
21165
|
+
supportsSubagents: true,
|
|
21166
|
+
supportsCompact: true,
|
|
21167
|
+
supportsStreamingToolInput: true,
|
|
21168
|
+
supportsQueuedSteer: true,
|
|
21169
|
+
supportsQueuedSteerSoon: false,
|
|
21170
|
+
// Single cwd.
|
|
21171
|
+
supportsAdditionalDirs: false,
|
|
21172
|
+
// `runtime.forkSession` copies the log prefix up to an event seq.
|
|
21173
|
+
supportsFork: true,
|
|
21174
|
+
supportsContextUsage: false,
|
|
21175
|
+
softCancel: false,
|
|
21176
|
+
goal: null,
|
|
21177
|
+
terminalCommandApproval: "harness",
|
|
21178
|
+
modUi: false,
|
|
21179
|
+
supportsShortWidgetResult: false,
|
|
21180
|
+
promptKeywords: [],
|
|
21181
|
+
displayName: "DeepSeek"
|
|
21182
|
+
}
|
|
21183
|
+
};
|
|
21184
|
+
ALL_GOAL_LIFECYCLE_ARGS = [...new Set(
|
|
21185
|
+
Object.values(HARNESS_CAPABILITIES).flatMap((caps) => caps.goal?.lifecycleArgs ?? [])
|
|
21186
|
+
)];
|
|
21187
|
+
}
|
|
21188
|
+
});
|
|
21189
|
+
|
|
20942
21190
|
// ../../packages/shared/src/session-provider-definitions.ts
|
|
21191
|
+
function sessionProviderDisplayName(row2) {
|
|
21192
|
+
return row2.isBase ? HARNESS_CAPABILITIES[row2.harnessId].displayName : row2.name;
|
|
21193
|
+
}
|
|
20943
21194
|
var BASE_SESSION_PROVIDERS, BASE_SESSION_PROVIDER_DEFINITIONS;
|
|
20944
21195
|
var init_session_provider_definitions = __esm({
|
|
20945
21196
|
"../../packages/shared/src/session-provider-definitions.ts"() {
|
|
20946
21197
|
"use strict";
|
|
21198
|
+
init_harness_capabilities();
|
|
20947
21199
|
BASE_SESSION_PROVIDERS = {
|
|
20948
21200
|
claude: { id: "claude-base", name: "Claude (Base)", config: {} },
|
|
20949
21201
|
codex: { id: "codex-base", name: "Codex (Base)", config: {} },
|
|
@@ -36579,237 +36831,6 @@ var init_slash_commands = __esm({
|
|
|
36579
36831
|
}
|
|
36580
36832
|
});
|
|
36581
36833
|
|
|
36582
|
-
// ../../packages/shared/src/harness/acp-brand.ts
|
|
36583
|
-
function isGrokAcpAgent(agentId) {
|
|
36584
|
-
if (!agentId) return false;
|
|
36585
|
-
const id = agentId.toLowerCase();
|
|
36586
|
-
return id.includes("grok");
|
|
36587
|
-
}
|
|
36588
|
-
function isOpenCodeAcpAgent(agentId) {
|
|
36589
|
-
if (!agentId) return false;
|
|
36590
|
-
return agentId.toLowerCase().includes("opencode");
|
|
36591
|
-
}
|
|
36592
|
-
function resolveHarnessBrandKey(harnessId, acpAgentId) {
|
|
36593
|
-
if (!harnessId) return "claude";
|
|
36594
|
-
if (harnessId !== "acp") return harnessId;
|
|
36595
|
-
if (isGrokAcpAgent(acpAgentId)) return "acp-grok";
|
|
36596
|
-
if (isOpenCodeAcpAgent(acpAgentId)) return "acp-opencode";
|
|
36597
|
-
if (acpAgentId?.trim()) {
|
|
36598
|
-
const short = acpAgentId.trim().toLowerCase().replace(/-build$/, "").replace(/[^a-z0-9]+/g, "-");
|
|
36599
|
-
return short ? `acp-${short}` : "acp";
|
|
36600
|
-
}
|
|
36601
|
-
return "acp";
|
|
36602
|
-
}
|
|
36603
|
-
function acpAgentDisplayName(agentId, catalogName) {
|
|
36604
|
-
if (catalogName?.trim()) {
|
|
36605
|
-
return catalogName.trim().replace(/\s+Build$/i, "");
|
|
36606
|
-
}
|
|
36607
|
-
if (isGrokAcpAgent(agentId)) return "Grok";
|
|
36608
|
-
if (isOpenCodeAcpAgent(agentId)) return "OpenCode";
|
|
36609
|
-
if (agentId?.trim()) {
|
|
36610
|
-
return agentId.trim().replace(/-build$/i, "").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
36611
|
-
}
|
|
36612
|
-
return "ACP";
|
|
36613
|
-
}
|
|
36614
|
-
var init_acp_brand = __esm({
|
|
36615
|
-
"../../packages/shared/src/harness/acp-brand.ts"() {
|
|
36616
|
-
"use strict";
|
|
36617
|
-
}
|
|
36618
|
-
});
|
|
36619
|
-
|
|
36620
|
-
// ../../packages/shared/src/harness/harness-capabilities.ts
|
|
36621
|
-
function supportsShortWidgetResult(harnessId) {
|
|
36622
|
-
return !!harnessId && Object.hasOwn(HARNESS_CAPABILITIES, harnessId) && HARNESS_CAPABILITIES[harnessId].supportsShortWidgetResult;
|
|
36623
|
-
}
|
|
36624
|
-
var HARNESS_CAPABILITIES, ALL_GOAL_LIFECYCLE_ARGS;
|
|
36625
|
-
var init_harness_capabilities = __esm({
|
|
36626
|
-
"../../packages/shared/src/harness/harness-capabilities.ts"() {
|
|
36627
|
-
"use strict";
|
|
36628
|
-
init_acp_brand();
|
|
36629
|
-
HARNESS_CAPABILITIES = {
|
|
36630
|
-
claude: {
|
|
36631
|
-
supportsMcp: true,
|
|
36632
|
-
supportsPlanMode: true,
|
|
36633
|
-
supportsTodos: true,
|
|
36634
|
-
supportsSubagents: true,
|
|
36635
|
-
supportsCompact: true,
|
|
36636
|
-
supportsStreamingToolInput: true,
|
|
36637
|
-
supportsQueuedSteer: true,
|
|
36638
|
-
supportsQueuedSteerSoon: true,
|
|
36639
|
-
// SDK `additionalDirectories`.
|
|
36640
|
-
supportsAdditionalDirs: true,
|
|
36641
|
-
// SDK `forkSession()` copies + remaps the transcript jsonl.
|
|
36642
|
-
supportsFork: true,
|
|
36643
|
-
supportsContextUsage: true,
|
|
36644
|
-
softCancel: false,
|
|
36645
|
-
// Built-in `/goal <condition>`: a Stop hook re-checks the condition after
|
|
36646
|
-
// each turn and keeps going until it is met, so there is no paused state —
|
|
36647
|
-
// the goal is either live or cleared.
|
|
36648
|
-
goal: { lifecycleArgs: ["clear"], canPause: false, transport: "slash", semantics: "condition" },
|
|
36649
|
-
terminalCommandApproval: "harness",
|
|
36650
|
-
modUi: true,
|
|
36651
|
-
supportsShortWidgetResult: true,
|
|
36652
|
-
// `ultrathink` adds a deeper-reasoning reminder to the turn (effort is
|
|
36653
|
-
// untouched); `ultracode` opts it into the Workflow tool, on messages stamped
|
|
36654
|
-
// human — see `claudeMessageOrigin`.
|
|
36655
|
-
promptKeywords: ["ultrathink", "ultracode"],
|
|
36656
|
-
displayName: "Claude"
|
|
36657
|
-
},
|
|
36658
|
-
codex: {
|
|
36659
|
-
supportsRealtimeVoice: true,
|
|
36660
|
-
supportsMcp: true,
|
|
36661
|
-
supportsPlanMode: true,
|
|
36662
|
-
supportsTodos: false,
|
|
36663
|
-
supportsSubagents: false,
|
|
36664
|
-
supportsCompact: true,
|
|
36665
|
-
supportsStreamingToolInput: false,
|
|
36666
|
-
supportsQueuedSteer: true,
|
|
36667
|
-
// Core's queue item either interrupts the turn or waits it out.
|
|
36668
|
-
supportsQueuedSteerSoon: false,
|
|
36669
|
-
// sandbox_workspace_write.writable_roots, re-sent every turn.
|
|
36670
|
-
supportsAdditionalDirs: true,
|
|
36671
|
-
// app-server thread fork, truncatable at a turn id.
|
|
36672
|
-
supportsFork: true,
|
|
36673
|
-
// Token usage arrives on the session snapshot only.
|
|
36674
|
-
supportsContextUsage: false,
|
|
36675
|
-
softCancel: false,
|
|
36676
|
-
// `thread/goal/{get,set,clear}` over the app server; SuperOne drives the
|
|
36677
|
-
// follow-up turns itself, so every transition is an explicit RPC.
|
|
36678
|
-
goal: { lifecycleArgs: [], canPause: true, transport: "rpc", semantics: "objective" },
|
|
36679
|
-
terminalCommandApproval: "harness",
|
|
36680
|
-
modUi: false,
|
|
36681
|
-
supportsShortWidgetResult: true,
|
|
36682
|
-
promptKeywords: [],
|
|
36683
|
-
displayName: "Codex"
|
|
36684
|
-
},
|
|
36685
|
-
acp: {
|
|
36686
|
-
// Host injects SuperOne MCP on every ACP session; user MCP attach is a separate gap.
|
|
36687
|
-
supportsMcp: true,
|
|
36688
|
-
// Agent-driven plan + x.ai/exit_plan_mode approval is shipped (not host enter-plan).
|
|
36689
|
-
supportsPlanMode: true,
|
|
36690
|
-
// ACP session/update plan entries map to todo_write-style UI events.
|
|
36691
|
-
supportsTodos: true,
|
|
36692
|
-
// Subagent_* notifications already map to task rows, but there is no nested
|
|
36693
|
-
// child chrome for ACP. Keep false until Task UI can host those sessions.
|
|
36694
|
-
supportsSubagents: false,
|
|
36695
|
-
// Host intercepts `/compact` → `x.ai/compact_conversation`.
|
|
36696
|
-
supportsCompact: true,
|
|
36697
|
-
supportsStreamingToolInput: false,
|
|
36698
|
-
// Mid-turn send queues. Steer (`now`) is a new `session/prompt` with
|
|
36699
|
-
// `_meta.sendNow`, which cancels the live turn and runs the message next.
|
|
36700
|
-
// Steer soon is `x.ai/interject`: next safe point, the tool in flight stays.
|
|
36701
|
-
supportsQueuedSteer: true,
|
|
36702
|
-
supportsQueuedSteerSoon: true,
|
|
36703
|
-
// session/new additionalDirectories, gated per agent capability.
|
|
36704
|
-
supportsAdditionalDirs: true,
|
|
36705
|
-
// Cold `x.ai/session/fork` copies Grok session files; SuperOne then resumes the child.
|
|
36706
|
-
supportsFork: true,
|
|
36707
|
-
supportsContextUsage: true,
|
|
36708
|
-
softCancel: true,
|
|
36709
|
-
// Grok only — see `resolveGoalCapability`. The agent owns the loop and
|
|
36710
|
-
// reports back over `goal_updated`; the host just posts `/goal …` lines.
|
|
36711
|
-
goal: {
|
|
36712
|
-
lifecycleArgs: ["status", "pause", "resume", "clear"],
|
|
36713
|
-
canPause: true,
|
|
36714
|
-
transport: "slash",
|
|
36715
|
-
semantics: "objective"
|
|
36716
|
-
},
|
|
36717
|
-
terminalCommandApproval: "harness",
|
|
36718
|
-
modUi: false,
|
|
36719
|
-
supportsShortWidgetResult: false,
|
|
36720
|
-
promptKeywords: [],
|
|
36721
|
-
displayName: "Others"
|
|
36722
|
-
},
|
|
36723
|
-
opencode: {
|
|
36724
|
-
supportsMcp: true,
|
|
36725
|
-
supportsPlanMode: true,
|
|
36726
|
-
supportsTodos: true,
|
|
36727
|
-
supportsSubagents: true,
|
|
36728
|
-
supportsCompact: true,
|
|
36729
|
-
supportsStreamingToolInput: false,
|
|
36730
|
-
supportsQueuedSteer: false,
|
|
36731
|
-
supportsQueuedSteerSoon: false,
|
|
36732
|
-
// Single `directory` only.
|
|
36733
|
-
supportsAdditionalDirs: false,
|
|
36734
|
-
// Server-side `forkSession(id, anchor)` + `moveSession`.
|
|
36735
|
-
supportsFork: true,
|
|
36736
|
-
supportsContextUsage: true,
|
|
36737
|
-
softCancel: true,
|
|
36738
|
-
goal: null,
|
|
36739
|
-
terminalCommandApproval: "harness",
|
|
36740
|
-
modUi: false,
|
|
36741
|
-
supportsShortWidgetResult: false,
|
|
36742
|
-
promptKeywords: [],
|
|
36743
|
-
displayName: "OpenCode"
|
|
36744
|
-
},
|
|
36745
|
-
cursor: {
|
|
36746
|
-
supportsMcp: true,
|
|
36747
|
-
supportsPlanMode: true,
|
|
36748
|
-
// updateTodos / task tool deltas when event map lands (PR5+)
|
|
36749
|
-
supportsTodos: true,
|
|
36750
|
-
supportsSubagents: true,
|
|
36751
|
-
supportsCompact: false,
|
|
36752
|
-
supportsStreamingToolInput: true,
|
|
36753
|
-
supportsQueuedSteer: false,
|
|
36754
|
-
supportsQueuedSteerSoon: false,
|
|
36755
|
-
// Single cwd; multi-root parked in the harness design doc.
|
|
36756
|
-
supportsAdditionalDirs: false,
|
|
36757
|
-
// SDK has no transcript-fork API; the adapter creates a blank agent.
|
|
36758
|
-
supportsFork: false,
|
|
36759
|
-
supportsContextUsage: true,
|
|
36760
|
-
softCancel: false,
|
|
36761
|
-
goal: null,
|
|
36762
|
-
// The @cursor/sdk local executor auto-approves custom tools with no host hook.
|
|
36763
|
-
terminalCommandApproval: "executor",
|
|
36764
|
-
modUi: false,
|
|
36765
|
-
supportsShortWidgetResult: false,
|
|
36766
|
-
promptKeywords: [],
|
|
36767
|
-
displayName: "Cursor"
|
|
36768
|
-
},
|
|
36769
|
-
dsh: {
|
|
36770
|
-
// In-process dsh Cordis tree (docs/harness/dsh/deepseek-harness-integration.md).
|
|
36771
|
-
// Flags flip only when the corresponding event path is wired.
|
|
36772
|
-
// `supportsStreamingToolInput`: live `tool-call-delta` frames open the row
|
|
36773
|
-
// and stream its arguments before the durable `tool/call` completes it.
|
|
36774
|
-
// `supportsCompact`: `compaction-basic` compacts automatically at context
|
|
36775
|
-
// pressure and on provider overflow; `/compact` drives `compactNow()`.
|
|
36776
|
-
// `supportsMcp` covers both SuperOne's own tools (native dsh rows) and
|
|
36777
|
-
// third-party servers read from dsh's own profile patch layer.
|
|
36778
|
-
// `supportsSubagents`: foreground delegation (`dsh-tool-subagent` over the
|
|
36779
|
-
// in-process spawn provider), rendered as a Task block with the child's
|
|
36780
|
-
// steps nested under it. Background and continuable children are not wired.
|
|
36781
|
-
// `supportsPlanMode`: `plan` also turns on dsh plan mode; `exit_plan_mode`
|
|
36782
|
-
// reaches the plan-approval card through the user-questions seam.
|
|
36783
|
-
// `supportsQueuedSteer`: `agent.steer()` lands at the next step boundary
|
|
36784
|
-
// and never cancels a tool, so there is one steer and no separate "soon".
|
|
36785
|
-
supportsMcp: true,
|
|
36786
|
-
supportsPlanMode: true,
|
|
36787
|
-
supportsTodos: true,
|
|
36788
|
-
supportsSubagents: true,
|
|
36789
|
-
supportsCompact: true,
|
|
36790
|
-
supportsStreamingToolInput: true,
|
|
36791
|
-
supportsQueuedSteer: true,
|
|
36792
|
-
supportsQueuedSteerSoon: false,
|
|
36793
|
-
// Single cwd.
|
|
36794
|
-
supportsAdditionalDirs: false,
|
|
36795
|
-
// `runtime.forkSession` copies the log prefix up to an event seq.
|
|
36796
|
-
supportsFork: true,
|
|
36797
|
-
supportsContextUsage: false,
|
|
36798
|
-
softCancel: false,
|
|
36799
|
-
goal: null,
|
|
36800
|
-
terminalCommandApproval: "harness",
|
|
36801
|
-
modUi: false,
|
|
36802
|
-
supportsShortWidgetResult: false,
|
|
36803
|
-
promptKeywords: [],
|
|
36804
|
-
displayName: "DeepSeek"
|
|
36805
|
-
}
|
|
36806
|
-
};
|
|
36807
|
-
ALL_GOAL_LIFECYCLE_ARGS = [...new Set(
|
|
36808
|
-
Object.values(HARNESS_CAPABILITIES).flatMap((caps) => caps.goal?.lifecycleArgs ?? [])
|
|
36809
|
-
)];
|
|
36810
|
-
}
|
|
36811
|
-
});
|
|
36812
|
-
|
|
36813
36834
|
// ../../packages/shared/src/session-goal.ts
|
|
36814
36835
|
function sessionGoalFromClaudeActive(raw) {
|
|
36815
36836
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
@@ -36911,6 +36932,22 @@ var init_plugin_notice_wire = __esm({
|
|
|
36911
36932
|
}
|
|
36912
36933
|
});
|
|
36913
36934
|
|
|
36935
|
+
// ../../packages/claude/src/artifact-result.ts
|
|
36936
|
+
function artifactResultTitle(toolName, toolUseResult) {
|
|
36937
|
+
if (toolName !== "Artifact" || !toolUseResult || typeof toolUseResult !== "object") return void 0;
|
|
36938
|
+
const result3 = toolUseResult;
|
|
36939
|
+
const read5 = result3.read && typeof result3.read === "object" ? result3.read : null;
|
|
36940
|
+
return nonEmpty2(result3.title) ?? nonEmpty2(read5?.title);
|
|
36941
|
+
}
|
|
36942
|
+
function nonEmpty2(value) {
|
|
36943
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
36944
|
+
}
|
|
36945
|
+
var init_artifact_result = __esm({
|
|
36946
|
+
"../../packages/claude/src/artifact-result.ts"() {
|
|
36947
|
+
"use strict";
|
|
36948
|
+
}
|
|
36949
|
+
});
|
|
36950
|
+
|
|
36914
36951
|
// ../../packages/claude/src/agent-event-mapper.ts
|
|
36915
36952
|
function emptyResult(sessionId) {
|
|
36916
36953
|
return {
|
|
@@ -37001,6 +37038,8 @@ function buildClaudeResultMetadata(result3, startedAt, pausedMs, lastAssistantUs
|
|
|
37001
37038
|
cacheReadInputTokens: usage2.cache_read_input_tokens ?? 0,
|
|
37002
37039
|
cacheCreationInputTokens: usage2.cache_creation_input_tokens ?? 0
|
|
37003
37040
|
};
|
|
37041
|
+
const cacheTtl = writtenCacheTtl(usage2.cache_creation);
|
|
37042
|
+
if (cacheTtl) metadata.usage.cacheTtl = cacheTtl;
|
|
37004
37043
|
}
|
|
37005
37044
|
if (result3.modelUsage) {
|
|
37006
37045
|
metadata.modelUsage = {};
|
|
@@ -37022,6 +37061,11 @@ function buildClaudeResultMetadata(result3, startedAt, pausedMs, lastAssistantUs
|
|
|
37022
37061
|
}
|
|
37023
37062
|
return metadata;
|
|
37024
37063
|
}
|
|
37064
|
+
function writtenCacheTtl(cacheCreation) {
|
|
37065
|
+
if ((cacheCreation?.ephemeral_1h_input_tokens ?? 0) > 0) return "1h";
|
|
37066
|
+
if ((cacheCreation?.ephemeral_5m_input_tokens ?? 0) > 0) return "5m";
|
|
37067
|
+
return void 0;
|
|
37068
|
+
}
|
|
37025
37069
|
function createClaudeAgentEventMapper(options) {
|
|
37026
37070
|
const now = options.now ?? Date.now;
|
|
37027
37071
|
const startedAt = options.startedAt ?? now();
|
|
@@ -37064,6 +37108,7 @@ function createClaudeAgentEventMapper(options) {
|
|
|
37064
37108
|
const outputPath = isBash ? extractBashOutputPath(text2) : void 0;
|
|
37065
37109
|
const isTimedOut = isBash ? extractBashKilled(toolUseResult) : void 0;
|
|
37066
37110
|
const taskCreateTodo = extractTaskCreateTodo(toolName, toolUseResult, text2);
|
|
37111
|
+
const resultTitle = artifactResultTitle(toolName, toolUseResult);
|
|
37067
37112
|
const isError = isClaudeToolLayerError(toolName, sdkIsError, text2);
|
|
37068
37113
|
const app = options.toolApps?.toolResult(toolUseId, toolUseResult, sdkIsError, content);
|
|
37069
37114
|
emit({
|
|
@@ -37077,6 +37122,7 @@ function createClaudeAgentEventMapper(options) {
|
|
|
37077
37122
|
...isTimedOut ? { isTimedOut } : {},
|
|
37078
37123
|
...isError ? { isError: true } : {},
|
|
37079
37124
|
...taskCreateTodo ?? {},
|
|
37125
|
+
...resultTitle ? { resultTitle } : {},
|
|
37080
37126
|
parentToolUseId,
|
|
37081
37127
|
...app ? { app } : {}
|
|
37082
37128
|
},
|
|
@@ -37638,6 +37684,7 @@ var init_agent_event_mapper2 = __esm({
|
|
|
37638
37684
|
init_session_goal();
|
|
37639
37685
|
init_result_failure();
|
|
37640
37686
|
init_plugin_notice_wire();
|
|
37687
|
+
init_artifact_result();
|
|
37641
37688
|
}
|
|
37642
37689
|
});
|
|
37643
37690
|
|
|
@@ -98425,7 +98472,10 @@ var BUILTIN_TOOL_INPUT_FIELDS = {
|
|
|
98425
98472
|
TaskList: ["status"],
|
|
98426
98473
|
TodoList: ["total", "completed"],
|
|
98427
98474
|
UpdateGoal: ["message", "blocked_reason"],
|
|
98428
|
-
Artifact: ["action", "scope", "title"],
|
|
98475
|
+
Artifact: ["action", "scope", "title", "file_path", "url"],
|
|
98476
|
+
ArtifactComments: ["action"],
|
|
98477
|
+
ArtifactData: ["action", "collection", "doc_id"],
|
|
98478
|
+
ArtifactCheck: ["action"],
|
|
98429
98479
|
SandboxNetworkAccess: ["host"],
|
|
98430
98480
|
ToolSearch: ["query"],
|
|
98431
98481
|
SearchTools: ["query"],
|
|
@@ -102062,6 +102112,10 @@ async function handleSessionCreate(payload, ctx) {
|
|
|
102062
102112
|
if (p3.externalParent != null && !externalParentSessionId) {
|
|
102063
102113
|
return { error: { code: "invalid_argument", message: "externalParent.sessionId is required" } };
|
|
102064
102114
|
}
|
|
102115
|
+
if (p3.continuesFrom != null && typeof p3.continuesFrom !== "string") {
|
|
102116
|
+
return { error: { code: "invalid_argument", message: "continuesFrom must be a string" } };
|
|
102117
|
+
}
|
|
102118
|
+
const continuesFrom = typeof p3.continuesFrom === "string" && p3.continuesFrom.trim() ? p3.continuesFrom.trim() : null;
|
|
102065
102119
|
try {
|
|
102066
102120
|
const agentSettings = loadNodeAgentSettings(ctx.settingsConfigPath);
|
|
102067
102121
|
const defaults2 = resolveAgentTurnDefaults(agentSettings, harnessId);
|
|
@@ -102122,7 +102176,18 @@ async function handleSessionCreate(payload, ctx) {
|
|
|
102122
102176
|
ctx.drafts.assertControl(selections.draftId, ctx.client.clientSessionId, selections.draftLeaseId);
|
|
102123
102177
|
};
|
|
102124
102178
|
assertCreate();
|
|
102125
|
-
|
|
102179
|
+
const continuation = continuesFrom && !ctx.sessions.createOnHost ? ctx.collaboration : void 0;
|
|
102180
|
+
if (continuesFrom && !ctx.sessions.createOnHost) {
|
|
102181
|
+
if (!continuation?.assertContinuable || !continuation.continueSession) {
|
|
102182
|
+
throw Object.assign(new Error("This host cannot continue a session in a new one"), { code: "unsupported" });
|
|
102183
|
+
}
|
|
102184
|
+
continuation.assertContinuable(continuesFrom);
|
|
102185
|
+
}
|
|
102186
|
+
let session = ctx.sessions.createOnHost ? await ctx.sessions.createOnHost(
|
|
102187
|
+
{ ...createInput, ...selections, ...continuesFrom ? { continuesFrom } : {} },
|
|
102188
|
+
{ settings: initialSettings, assertCreate }
|
|
102189
|
+
) : ctx.sessions.create(createInput);
|
|
102190
|
+
if (continuesFrom && continuation) continuation.continueSession(continuesFrom, session.sessionId);
|
|
102126
102191
|
if (!ctx.sessions.createOnHost && (model || effort || permissionMode || sandboxMode || Object.keys(extras).length)) {
|
|
102127
102192
|
session = await ctx.sessions.patchSettings(session.sessionId, {
|
|
102128
102193
|
...extras,
|
|
@@ -109019,6 +109084,364 @@ import Database5 from "better-sqlite3";
|
|
|
109019
109084
|
import { mkdirSync as mkdirSync24 } from "node:fs";
|
|
109020
109085
|
import { dirname as dirname20 } from "node:path";
|
|
109021
109086
|
|
|
109087
|
+
// ../../packages/runtime/src/collaboration/store.ts
|
|
109088
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
109089
|
+
|
|
109090
|
+
// ../../packages/runtime/src/collaboration/errors.ts
|
|
109091
|
+
var CollaborationError = class extends Error {
|
|
109092
|
+
constructor(message, code) {
|
|
109093
|
+
super(message);
|
|
109094
|
+
this.code = code;
|
|
109095
|
+
this.name = "CollaborationError";
|
|
109096
|
+
}
|
|
109097
|
+
code;
|
|
109098
|
+
};
|
|
109099
|
+
|
|
109100
|
+
// ../../packages/runtime/src/collaboration/store.ts
|
|
109101
|
+
var SESSION_CONTINUATION_TABLE_DDL = `
|
|
109102
|
+
CREATE TABLE IF NOT EXISTS session_continuations (
|
|
109103
|
+
from_session_id TEXT PRIMARY KEY NOT NULL,
|
|
109104
|
+
to_session_id TEXT NOT NULL UNIQUE,
|
|
109105
|
+
created_at TEXT NOT NULL
|
|
109106
|
+
);
|
|
109107
|
+
`;
|
|
109108
|
+
var MAX_CONTINUATION_HOPS = 64;
|
|
109109
|
+
var GRANT_COLUMNS = `credential_hash AS grant_id, parent_session_id, child_session_id,
|
|
109110
|
+
agent_id, task, config_json, task_sent, COALESCE(kind, 'spawn') AS kind, started_at`;
|
|
109111
|
+
var MESSAGE_COLUMNS = `id, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at`;
|
|
109112
|
+
function nowIso() {
|
|
109113
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
109114
|
+
}
|
|
109115
|
+
var CollaborationStore = class {
|
|
109116
|
+
constructor(db) {
|
|
109117
|
+
this.db = db;
|
|
109118
|
+
}
|
|
109119
|
+
db;
|
|
109120
|
+
transaction(fn) {
|
|
109121
|
+
return this.db.transaction(fn)();
|
|
109122
|
+
}
|
|
109123
|
+
// --- grants --------------------------------------------------------------
|
|
109124
|
+
grantById(grantId) {
|
|
109125
|
+
return this.db.prepare(`SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants WHERE credential_hash = ?`).get(grantId) ?? null;
|
|
109126
|
+
}
|
|
109127
|
+
/**
|
|
109128
|
+
* The grant an approved launch created. launchIds are agent-chosen and may
|
|
109129
|
+
* repeat across requests, so the latest approval wins.
|
|
109130
|
+
*/
|
|
109131
|
+
grantForLaunch(parentSessionId, launchId) {
|
|
109132
|
+
return this.db.prepare(`
|
|
109133
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109134
|
+
WHERE parent_session_id = ? AND json_extract(config_json, '$.launchId') = ?
|
|
109135
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
109136
|
+
`).get(parentSessionId, launchId) ?? null;
|
|
109137
|
+
}
|
|
109138
|
+
/** True when `sessionId` is a spawn child (nested collaboration is unsupported). */
|
|
109139
|
+
isSpawnChild(sessionId) {
|
|
109140
|
+
return Boolean(this.db.prepare(`
|
|
109141
|
+
SELECT 1 FROM session_collaboration_grants
|
|
109142
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn' LIMIT 1
|
|
109143
|
+
`).get(sessionId));
|
|
109144
|
+
}
|
|
109145
|
+
spawnGrantForChild(childSessionId) {
|
|
109146
|
+
return this.db.prepare(`
|
|
109147
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109148
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn'
|
|
109149
|
+
`).get(childSessionId) ?? null;
|
|
109150
|
+
}
|
|
109151
|
+
startedSpawnGrants() {
|
|
109152
|
+
return this.db.prepare(`
|
|
109153
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109154
|
+
WHERE child_session_id IS NOT NULL AND COALESCE(kind, 'spawn') = 'spawn'
|
|
109155
|
+
`).all();
|
|
109156
|
+
}
|
|
109157
|
+
/** Started handoff grants; the session each created is `config_json.handoffSessionId`. */
|
|
109158
|
+
startedHandoffGrants() {
|
|
109159
|
+
return this.db.prepare(`
|
|
109160
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109161
|
+
WHERE kind = 'handoff' AND started_at IS NOT NULL
|
|
109162
|
+
`).all();
|
|
109163
|
+
}
|
|
109164
|
+
/**
|
|
109165
|
+
* Open mailbox channels of `sessionId`: spawn grants with a bound child and
|
|
109166
|
+
* started link grants, oldest first. Handoff grants are never channels.
|
|
109167
|
+
*/
|
|
109168
|
+
channelsFor(sessionId) {
|
|
109169
|
+
return this.db.prepare(`
|
|
109170
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109171
|
+
WHERE (parent_session_id = ? OR child_session_id = ?)
|
|
109172
|
+
AND child_session_id IS NOT NULL
|
|
109173
|
+
AND (COALESCE(kind, 'spawn') = 'spawn' OR (kind = 'link' AND started_at IS NOT NULL))
|
|
109174
|
+
ORDER BY created_at, rowid
|
|
109175
|
+
`).all(sessionId, sessionId);
|
|
109176
|
+
}
|
|
109177
|
+
/** True when one of the two sessions was created by a handoff from the other. */
|
|
109178
|
+
isHandoffPair(a, b3) {
|
|
109179
|
+
return Boolean(this.db.prepare(`
|
|
109180
|
+
SELECT 1 FROM session_collaboration_grants
|
|
109181
|
+
WHERE kind = 'handoff' AND (
|
|
109182
|
+
(parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
109183
|
+
OR (parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
109184
|
+
) LIMIT 1
|
|
109185
|
+
`).get(a, b3, b3, a));
|
|
109186
|
+
}
|
|
109187
|
+
findLinkGrant(initiatorSessionId, peerSessionId) {
|
|
109188
|
+
return this.db.prepare(`
|
|
109189
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
109190
|
+
WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'
|
|
109191
|
+
`).get(initiatorSessionId, peerSessionId) ?? null;
|
|
109192
|
+
}
|
|
109193
|
+
/**
|
|
109194
|
+
* Persist an approved launch. Link grants bind the peer at once; spawn and
|
|
109195
|
+
* handoff grants bind their session on start. The brief arrives on start.
|
|
109196
|
+
*/
|
|
109197
|
+
createGrant(input) {
|
|
109198
|
+
const grantId = randomUUID17();
|
|
109199
|
+
try {
|
|
109200
|
+
this.db.prepare(`
|
|
109201
|
+
INSERT INTO session_collaboration_grants
|
|
109202
|
+
(credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id,
|
|
109203
|
+
agent_id, task, config_json, created_at, kind)
|
|
109204
|
+
VALUES (?, NULL, '', ?, ?, ?, '', ?, ?, ?)
|
|
109205
|
+
`).run(
|
|
109206
|
+
grantId,
|
|
109207
|
+
input.parentSessionId,
|
|
109208
|
+
input.childSessionId ?? null,
|
|
109209
|
+
input.agentId,
|
|
109210
|
+
JSON.stringify(input.config),
|
|
109211
|
+
nowIso(),
|
|
109212
|
+
input.kind
|
|
109213
|
+
);
|
|
109214
|
+
} catch (error51) {
|
|
109215
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
109216
|
+
if (input.childSessionId && /UNIQUE|unique/i.test(message)) {
|
|
109217
|
+
throw new CollaborationError(
|
|
109218
|
+
`Session ${input.parentSessionId} already has a link to ${input.childSessionId}; request it again to reuse it.`,
|
|
109219
|
+
"failed_precondition"
|
|
109220
|
+
);
|
|
109221
|
+
}
|
|
109222
|
+
throw error51;
|
|
109223
|
+
}
|
|
109224
|
+
return grantId;
|
|
109225
|
+
}
|
|
109226
|
+
updateConfig(grantId, config2) {
|
|
109227
|
+
this.db.prepare("UPDATE session_collaboration_grants SET config_json = ? WHERE credential_hash = ?").run(JSON.stringify(config2), grantId);
|
|
109228
|
+
}
|
|
109229
|
+
/** Record the brief of a grant whose task has not been delivered yet. */
|
|
109230
|
+
setTask(grantId, task) {
|
|
109231
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task = ? WHERE credential_hash = ? AND task_sent = 0").run(task, grantId);
|
|
109232
|
+
}
|
|
109233
|
+
markTaskSent(grantId) {
|
|
109234
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?").run(grantId);
|
|
109235
|
+
}
|
|
109236
|
+
/** Link start. Returns false when the grant had already been started. */
|
|
109237
|
+
markStarted(grantId) {
|
|
109238
|
+
return this.db.prepare(`
|
|
109239
|
+
UPDATE session_collaboration_grants SET started_at = ? WHERE credential_hash = ? AND started_at IS NULL
|
|
109240
|
+
`).run(nowIso(), grantId).changes === 1;
|
|
109241
|
+
}
|
|
109242
|
+
/**
|
|
109243
|
+
* Record the session a spawn/handoff start created. Throws when a concurrent
|
|
109244
|
+
* start already consumed the grant.
|
|
109245
|
+
*
|
|
109246
|
+
* A handoff session is deliberately *not* written to child_session_id: that
|
|
109247
|
+
* column holds channel endpoints (spawn children, link peers), and a handoff
|
|
109248
|
+
* sibling is neither. The created id lives in config_json instead.
|
|
109249
|
+
*/
|
|
109250
|
+
bindStartedSession(grant, sessionId, config2) {
|
|
109251
|
+
const changes = grant.kind === "handoff" ? this.db.prepare(`
|
|
109252
|
+
UPDATE session_collaboration_grants SET config_json = ?, started_at = ?
|
|
109253
|
+
WHERE credential_hash = ? AND started_at IS NULL
|
|
109254
|
+
`).run(JSON.stringify({ ...config2, handoffSessionId: sessionId }), nowIso(), grant.grant_id).changes : this.db.prepare(`
|
|
109255
|
+
UPDATE session_collaboration_grants SET child_session_id = ?, started_at = ?
|
|
109256
|
+
WHERE credential_hash = ? AND child_session_id IS NULL
|
|
109257
|
+
`).run(sessionId, nowIso(), grant.grant_id).changes;
|
|
109258
|
+
if (changes !== 1) throw new CollaborationError("This launch was already started", "failed_precondition");
|
|
109259
|
+
}
|
|
109260
|
+
// --- continuation --------------------------------------------------------
|
|
109261
|
+
/** The session `sessionId` was continued in; null when it was not. */
|
|
109262
|
+
successorOf(sessionId) {
|
|
109263
|
+
const row2 = this.db.prepare("SELECT to_session_id FROM session_continuations WHERE from_session_id = ?").get(sessionId);
|
|
109264
|
+
return row2?.to_session_id ?? null;
|
|
109265
|
+
}
|
|
109266
|
+
/** The session `sessionId` continues; null when it continues none. */
|
|
109267
|
+
predecessorOf(sessionId) {
|
|
109268
|
+
const row2 = this.db.prepare("SELECT from_session_id FROM session_continuations WHERE to_session_id = ?").get(sessionId);
|
|
109269
|
+
return row2?.from_session_id ?? null;
|
|
109270
|
+
}
|
|
109271
|
+
/** The newest session in `sessionId`'s continuation chain (itself when never continued). */
|
|
109272
|
+
currentSessionFor(sessionId) {
|
|
109273
|
+
let current = sessionId;
|
|
109274
|
+
for (let hop = 0; hop < MAX_CONTINUATION_HOPS; hop++) {
|
|
109275
|
+
const next = this.successorOf(current);
|
|
109276
|
+
if (!next) return current;
|
|
109277
|
+
current = next;
|
|
109278
|
+
}
|
|
109279
|
+
return current;
|
|
109280
|
+
}
|
|
109281
|
+
/**
|
|
109282
|
+
* Record that `to` continues `from`, and move `from`'s mailbox channels
|
|
109283
|
+
* (spawn grants with a bound child, started link grants) with their
|
|
109284
|
+
* messages and read cursors to `to`. Handoff and not-yet-started grants
|
|
109285
|
+
* stay: their launch ids live in `from`'s transcript.
|
|
109286
|
+
*
|
|
109287
|
+
* Messages move on both sides so idempotency keys and report history follow
|
|
109288
|
+
* the session, and deleting `from` no longer cascades them away. Returns the
|
|
109289
|
+
* moved grants as they are now.
|
|
109290
|
+
*/
|
|
109291
|
+
continueSession(from, to2) {
|
|
109292
|
+
return this.transaction(() => {
|
|
109293
|
+
const successor = this.successorOf(from);
|
|
109294
|
+
if (successor) {
|
|
109295
|
+
throw new CollaborationError(`Session ${from} was already continued in ${successor}`, "failed_precondition");
|
|
109296
|
+
}
|
|
109297
|
+
this.db.prepare("INSERT INTO session_continuations (from_session_id, to_session_id, created_at) VALUES (?, ?, ?)").run(from, to2, nowIso());
|
|
109298
|
+
const grantIds = this.channelsFor(from).map((grant) => grant.grant_id);
|
|
109299
|
+
for (const grantId of grantIds) {
|
|
109300
|
+
for (const column of ["parent_session_id", "child_session_id"]) {
|
|
109301
|
+
this.db.prepare(`UPDATE session_collaboration_grants SET ${column} = ? WHERE credential_hash = ? AND ${column} = ?`).run(to2, grantId, from);
|
|
109302
|
+
}
|
|
109303
|
+
for (const column of ["sender_session_id", "recipient_session_id"]) {
|
|
109304
|
+
this.db.prepare(`UPDATE session_collaboration_messages SET ${column} = ? WHERE credential_hash = ? AND ${column} = ?`).run(to2, grantId, from);
|
|
109305
|
+
}
|
|
109306
|
+
this.db.prepare("UPDATE session_collaboration_cursors SET session_id = ? WHERE credential_hash = ? AND session_id = ?").run(to2, grantId, from);
|
|
109307
|
+
}
|
|
109308
|
+
return grantIds.map((grantId) => this.grantById(grantId));
|
|
109309
|
+
});
|
|
109310
|
+
}
|
|
109311
|
+
// --- mailbox -------------------------------------------------------------
|
|
109312
|
+
nextSequence(grantId) {
|
|
109313
|
+
const row2 = this.db.prepare(`
|
|
109314
|
+
SELECT COALESCE(MAX(sequence), 0) + 1 AS next_sequence
|
|
109315
|
+
FROM session_collaboration_messages WHERE credential_hash = ?
|
|
109316
|
+
`).get(grantId);
|
|
109317
|
+
return row2.next_sequence;
|
|
109318
|
+
}
|
|
109319
|
+
insertMessage(grantId, input) {
|
|
109320
|
+
const row2 = {
|
|
109321
|
+
id: randomUUID17(),
|
|
109322
|
+
sequence: this.nextSequence(grantId),
|
|
109323
|
+
sender_session_id: input.senderSessionId,
|
|
109324
|
+
recipient_session_id: input.recipientSessionId,
|
|
109325
|
+
client_message_id: input.clientMessageId,
|
|
109326
|
+
content: input.content,
|
|
109327
|
+
created_at: nowIso()
|
|
109328
|
+
};
|
|
109329
|
+
this.db.prepare(`
|
|
109330
|
+
INSERT INTO session_collaboration_messages
|
|
109331
|
+
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
109332
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
109333
|
+
`).run(
|
|
109334
|
+
row2.id,
|
|
109335
|
+
grantId,
|
|
109336
|
+
row2.sequence,
|
|
109337
|
+
row2.sender_session_id,
|
|
109338
|
+
row2.recipient_session_id,
|
|
109339
|
+
row2.client_message_id,
|
|
109340
|
+
row2.content,
|
|
109341
|
+
row2.created_at
|
|
109342
|
+
);
|
|
109343
|
+
return row2;
|
|
109344
|
+
}
|
|
109345
|
+
/** Append one message; a repeated `clientMessageId` returns the stored row. */
|
|
109346
|
+
appendMessage(input) {
|
|
109347
|
+
return this.transaction(() => {
|
|
109348
|
+
if (input.clientMessageId) {
|
|
109349
|
+
const existing = this.db.prepare(`
|
|
109350
|
+
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
109351
|
+
WHERE credential_hash = ? AND sender_session_id = ? AND client_message_id = ?
|
|
109352
|
+
`).get(input.grantId, input.senderSessionId, input.clientMessageId);
|
|
109353
|
+
if (existing) return { row: existing, reused: true };
|
|
109354
|
+
}
|
|
109355
|
+
const row2 = this.insertMessage(input.grantId, { ...input, clientMessageId: input.clientMessageId ?? null });
|
|
109356
|
+
return { row: row2, reused: false };
|
|
109357
|
+
});
|
|
109358
|
+
}
|
|
109359
|
+
/** When `sessionId` last sent and last received a mailbox message (ISO), across all its channels. */
|
|
109360
|
+
lastMessageTimes(sessionId) {
|
|
109361
|
+
return this.db.prepare(`
|
|
109362
|
+
SELECT
|
|
109363
|
+
(SELECT MAX(created_at) FROM session_collaboration_messages WHERE sender_session_id = ?) AS sentAt,
|
|
109364
|
+
(SELECT MAX(created_at) FROM session_collaboration_messages WHERE recipient_session_id = ?) AS receivedAt
|
|
109365
|
+
`).get(sessionId, sessionId);
|
|
109366
|
+
}
|
|
109367
|
+
/** Queue a link grant's opening body for the peer exactly once, then mark the task sent. */
|
|
109368
|
+
appendLinkOpening(grant, recipientSessionId, content) {
|
|
109369
|
+
this.transaction(() => {
|
|
109370
|
+
try {
|
|
109371
|
+
this.insertMessage(grant.grant_id, {
|
|
109372
|
+
senderSessionId: grant.parent_session_id,
|
|
109373
|
+
recipientSessionId,
|
|
109374
|
+
clientMessageId: `link-opening:${grant.grant_id}`,
|
|
109375
|
+
content
|
|
109376
|
+
});
|
|
109377
|
+
} catch {
|
|
109378
|
+
}
|
|
109379
|
+
this.markTaskSent(grant.grant_id);
|
|
109380
|
+
});
|
|
109381
|
+
}
|
|
109382
|
+
/**
|
|
109383
|
+
* Up to `limit` unread messages addressed to `sessionId` across grants,
|
|
109384
|
+
* without marking them read: one per grant in turn, grants with the oldest
|
|
109385
|
+
* unread message first, so successive reads reach every grant even when
|
|
109386
|
+
* there are more grants than `limit`. Each batch is a prefix of its grant's
|
|
109387
|
+
* unread messages. Selection reads only the mailbox index; bodies load for
|
|
109388
|
+
* the selected messages alone.
|
|
109389
|
+
*/
|
|
109390
|
+
peekMailbox(sessionId, grantIds, limit2) {
|
|
109391
|
+
const unread = this.db.prepare(`
|
|
109392
|
+
SELECT rowid AS rid FROM session_collaboration_messages
|
|
109393
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence > ?
|
|
109394
|
+
ORDER BY sequence LIMIT ?
|
|
109395
|
+
`);
|
|
109396
|
+
const createdAt = this.db.prepare("SELECT created_at FROM session_collaboration_messages WHERE rowid = ?");
|
|
109397
|
+
const queues2 = grantIds.flatMap((grantId) => {
|
|
109398
|
+
const cursor = this.db.prepare(`
|
|
109399
|
+
SELECT last_sequence FROM session_collaboration_cursors WHERE credential_hash = ? AND session_id = ?
|
|
109400
|
+
`).get(grantId, sessionId);
|
|
109401
|
+
const rids = unread.all(grantId, sessionId, cursor?.last_sequence ?? 0, limit2).map((row2) => row2.rid);
|
|
109402
|
+
if (rids.length === 0) return [];
|
|
109403
|
+
const oldest = createdAt.get(rids[0]).created_at;
|
|
109404
|
+
return [{ grantId, rids, oldest, taken: 0 }];
|
|
109405
|
+
}).sort((a, b3) => a.oldest.localeCompare(b3.oldest));
|
|
109406
|
+
let budget = limit2;
|
|
109407
|
+
while (budget > 0) {
|
|
109408
|
+
let took = false;
|
|
109409
|
+
for (const queue of queues2) {
|
|
109410
|
+
if (budget === 0) break;
|
|
109411
|
+
if (queue.taken === queue.rids.length) continue;
|
|
109412
|
+
queue.taken++;
|
|
109413
|
+
budget--;
|
|
109414
|
+
took = true;
|
|
109415
|
+
}
|
|
109416
|
+
if (!took) break;
|
|
109417
|
+
}
|
|
109418
|
+
const load = this.db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages WHERE rowid = ?`);
|
|
109419
|
+
return queues2.filter((queue) => queue.taken > 0).map(({ grantId, rids, taken }) => ({
|
|
109420
|
+
grantId,
|
|
109421
|
+
rows: rids.slice(0, taken).map((rid) => load.get(rid))
|
|
109422
|
+
}));
|
|
109423
|
+
}
|
|
109424
|
+
/**
|
|
109425
|
+
* Mark messages read up to each grant's `lastSequence`. A cursor never
|
|
109426
|
+
* moves back, so acknowledgements may arrive in any order.
|
|
109427
|
+
*/
|
|
109428
|
+
ackMailbox(sessionId, acks) {
|
|
109429
|
+
this.transaction(() => {
|
|
109430
|
+
for (const { grantId, lastSequence } of acks) {
|
|
109431
|
+
this.db.prepare(`
|
|
109432
|
+
INSERT INTO session_collaboration_cursors (credential_hash, session_id, last_sequence)
|
|
109433
|
+
VALUES (?, ?, ?)
|
|
109434
|
+
ON CONFLICT(credential_hash, session_id) DO UPDATE SET last_sequence = MAX(last_sequence, excluded.last_sequence)
|
|
109435
|
+
`).run(grantId, sessionId, lastSequence);
|
|
109436
|
+
this.db.prepare(`
|
|
109437
|
+
UPDATE session_collaboration_messages SET delivered_at = COALESCE(delivered_at, ?)
|
|
109438
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence <= ?
|
|
109439
|
+
`).run(nowIso(), grantId, sessionId, lastSequence);
|
|
109440
|
+
}
|
|
109441
|
+
});
|
|
109442
|
+
}
|
|
109443
|
+
};
|
|
109444
|
+
|
|
109022
109445
|
// ../../packages/shared/src/session-interruption.ts
|
|
109023
109446
|
function isNodeRestartInterruption(reason) {
|
|
109024
109447
|
return reason === "node_restart" || reason === "node_restart_non_reattachable";
|
|
@@ -109105,6 +109528,26 @@ var CollaborationWakeQueue = class {
|
|
|
109105
109528
|
this.start();
|
|
109106
109529
|
}
|
|
109107
109530
|
}
|
|
109531
|
+
/** True while a wake for `sessionId` is being prepared or handed to its harness. */
|
|
109532
|
+
hasInFlight(sessionId) {
|
|
109533
|
+
if (this.delivering.has(sessionId)) return true;
|
|
109534
|
+
return Boolean(this.db.prepare(`SELECT 1 FROM collaboration_wakes
|
|
109535
|
+
WHERE session_id = ? AND state IN ('preparing', 'dispatching') LIMIT 1`).get(sessionId));
|
|
109536
|
+
}
|
|
109537
|
+
/**
|
|
109538
|
+
* Hand `from`'s wakes to `to`, the session that continues it. Settled rows
|
|
109539
|
+
* move too, so deduplication by notification id keeps holding for the new
|
|
109540
|
+
* recipient. `to` starts paused: its own first output (see {@link observe})
|
|
109541
|
+
* releases the moved wakes, so none runs ahead of the turn the user starts.
|
|
109542
|
+
* Throws while a wake for `from` is in flight.
|
|
109543
|
+
*/
|
|
109544
|
+
migrate(from, to2) {
|
|
109545
|
+
if (this.hasInFlight(from)) throw new Error(`A collaboration wake for ${from} is being delivered`);
|
|
109546
|
+
this.db.prepare("UPDATE collaboration_wakes SET session_id = ? WHERE session_id = ?").run(to2, from);
|
|
109547
|
+
this.db.prepare("DELETE FROM collaboration_wake_recipients WHERE session_id = ?").run(from);
|
|
109548
|
+
this.paused.delete(from);
|
|
109549
|
+
this.pause(to2);
|
|
109550
|
+
}
|
|
109108
109551
|
pause(sessionId) {
|
|
109109
109552
|
if (this.paused.has(sessionId)) return;
|
|
109110
109553
|
this.db.prepare(`INSERT INTO collaboration_wake_recipients (session_id, paused)
|
|
@@ -109204,6 +109647,7 @@ var CollaborationWakeQueue = class {
|
|
|
109204
109647
|
// ../../packages/runtime/src/db/schema.ts
|
|
109205
109648
|
var SCHEMA_SQL = `
|
|
109206
109649
|
${COLLABORATION_WAKE_TABLE_DDL}
|
|
109650
|
+
${SESSION_CONTINUATION_TABLE_DDL}
|
|
109207
109651
|
PRAGMA foreign_keys = ON;
|
|
109208
109652
|
|
|
109209
109653
|
CREATE TABLE IF NOT EXISTS meta (
|
|
@@ -111381,7 +111825,7 @@ function dispatchAutomationRpc(method, payload, ctx) {
|
|
|
111381
111825
|
// ../../packages/runtime/src/server/rpc-codex.ts
|
|
111382
111826
|
init_environment();
|
|
111383
111827
|
init_oauth_loopback();
|
|
111384
|
-
import { randomUUID as
|
|
111828
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
111385
111829
|
function invalid9(message) {
|
|
111386
111830
|
throw Object.assign(new Error(message), { code: "invalid_argument" });
|
|
111387
111831
|
}
|
|
@@ -111408,7 +111852,7 @@ async function startMcpOauth(client3, login) {
|
|
|
111408
111852
|
const result3 = login((url2) => opened(url2));
|
|
111409
111853
|
const first = await Promise.race([authorization, result3]);
|
|
111410
111854
|
if (typeof first !== "string") return first;
|
|
111411
|
-
const loginId =
|
|
111855
|
+
const loginId = randomUUID18();
|
|
111412
111856
|
pendingMcpOauth.set(loginId, { clientSessionId: client3.clientSessionId, authorizationUrl: first, result: result3 });
|
|
111413
111857
|
const forget = () => {
|
|
111414
111858
|
setTimeout(() => pendingMcpOauth.delete(loginId), MCP_OAUTH_COLLECT_MS).unref?.();
|
|
@@ -112353,7 +112797,7 @@ var NodeTerminalManager = class {
|
|
|
112353
112797
|
init_session();
|
|
112354
112798
|
|
|
112355
112799
|
// ../../packages/runtime/src/lease/control-lease.ts
|
|
112356
|
-
import { randomUUID as
|
|
112800
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
112357
112801
|
var DEFAULT_TTL_MS = 3e4;
|
|
112358
112802
|
var MAX_TTL_MS = 15 * 60 * 1e3;
|
|
112359
112803
|
var YIELD_LEVEL = { none: 0, holder: 1, any: 2 };
|
|
@@ -112381,7 +112825,7 @@ function clampTtlMs(ttlMs) {
|
|
|
112381
112825
|
var ControlLeaseService = class {
|
|
112382
112826
|
constructor(db) {
|
|
112383
112827
|
this.db = db;
|
|
112384
|
-
this.leaseEpoch =
|
|
112828
|
+
this.leaseEpoch = randomUUID19();
|
|
112385
112829
|
}
|
|
112386
112830
|
db;
|
|
112387
112831
|
leaseEpoch;
|
|
@@ -112468,7 +112912,7 @@ var ControlLeaseService = class {
|
|
|
112468
112912
|
code: "failed_precondition"
|
|
112469
112913
|
});
|
|
112470
112914
|
}
|
|
112471
|
-
const leaseId =
|
|
112915
|
+
const leaseId = randomUUID19();
|
|
112472
112916
|
const generation = existing && existing.epoch === this.leaseEpoch ? String(Number(existing.generation || "0") + 1) : "1";
|
|
112473
112917
|
const expiresAt = now + ttl;
|
|
112474
112918
|
this.db.prepare(
|
|
@@ -112589,7 +113033,7 @@ import { dirname as dirname21, join as join51 } from "node:path";
|
|
|
112589
113033
|
// ../../packages/runtime/src/mcp-apps/resource-store.ts
|
|
112590
113034
|
init_mcp_apps();
|
|
112591
113035
|
init_mcp_app_resource();
|
|
112592
|
-
import { createHash as createHash21, randomUUID as
|
|
113036
|
+
import { createHash as createHash21, randomUUID as randomUUID20 } from "node:crypto";
|
|
112593
113037
|
import { existsSync as existsSync50, mkdirSync as mkdirSync25, readFileSync as readFileSync26, readdirSync as readdirSync16, renameSync as renameSync9, rmSync as rmSync11, statSync as statSync17, utimesSync, writeFileSync as writeFileSync20 } from "node:fs";
|
|
112594
113038
|
import { join as join50 } from "node:path";
|
|
112595
113039
|
function createMcpAppResourceStore(directory) {
|
|
@@ -112605,7 +113049,7 @@ function createMcpAppResourceStore(directory) {
|
|
|
112605
113049
|
const hash2 = digest(snapshot.html), file2 = path(hash2);
|
|
112606
113050
|
if (!existsSync50(file2) || statSync17(file2).size > MCP_APP_HTML_MAX_BYTES || digest(readFileSync26(file2, "utf8")) !== hash2) {
|
|
112607
113051
|
mkdirSync25(directory, { recursive: true });
|
|
112608
|
-
const temporary = join50(directory, `${hash2}.${
|
|
113052
|
+
const temporary = join50(directory, `${hash2}.${randomUUID20()}.tmp`);
|
|
112609
113053
|
try {
|
|
112610
113054
|
writeFileSync20(temporary, snapshot.html, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
112611
113055
|
renameSync9(temporary, file2);
|
|
@@ -112685,15 +113129,23 @@ init_harness();
|
|
|
112685
113129
|
// src/session/collaboration.ts
|
|
112686
113130
|
init_environment();
|
|
112687
113131
|
|
|
112688
|
-
// ../../packages/runtime/src/collaboration/
|
|
112689
|
-
|
|
112690
|
-
|
|
112691
|
-
|
|
112692
|
-
|
|
112693
|
-
|
|
112694
|
-
|
|
112695
|
-
|
|
112696
|
-
}
|
|
113132
|
+
// ../../packages/runtime/src/collaboration/continuation.ts
|
|
113133
|
+
function assertContinuable(store, wakes, from, state) {
|
|
113134
|
+
if (!state) throw new CollaborationError(`Session ${from} no longer exists`, "not_found");
|
|
113135
|
+
const successor = store.successorOf(from);
|
|
113136
|
+
if (successor) throw new CollaborationError(`Session ${from} was already continued in ${successor}`, "failed_precondition");
|
|
113137
|
+
if (state.busy || wakes.hasInFlight(from)) {
|
|
113138
|
+
throw new CollaborationError(`Session ${from} is working; continue it once it is idle`, "failed_precondition");
|
|
113139
|
+
}
|
|
113140
|
+
}
|
|
113141
|
+
function continueCollaboration(store, wakes, from, to2, state) {
|
|
113142
|
+
return store.transaction(() => {
|
|
113143
|
+
assertContinuable(store, wakes, from, state);
|
|
113144
|
+
const moved = store.continueSession(from, to2);
|
|
113145
|
+
wakes.migrate(from, to2);
|
|
113146
|
+
return moved;
|
|
113147
|
+
});
|
|
113148
|
+
}
|
|
112697
113149
|
|
|
112698
113150
|
// ../../packages/runtime/src/collaboration/launch.ts
|
|
112699
113151
|
init_agent_types();
|
|
@@ -112881,7 +113333,7 @@ function formatPeers(peers) {
|
|
|
112881
113333
|
function resolveSendChannel(store, callerSessionId, to2, sessionTitle2) {
|
|
112882
113334
|
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
112883
113335
|
const peers = peersOf(channels);
|
|
112884
|
-
const target = to2?.trim();
|
|
113336
|
+
const target = to2?.trim() ? store.currentSessionFor(to2.trim()) : void 0;
|
|
112885
113337
|
if (!target) {
|
|
112886
113338
|
if (peers.length === 0) throw new CollaborationError(NO_PEERS_HINT, "failed_precondition");
|
|
112887
113339
|
const parent = channels.find((channel) => channel.peer.relation === "parent");
|
|
@@ -112915,7 +113367,7 @@ function normalizeMailboxContent(raw) {
|
|
|
112915
113367
|
function readCallerMailbox(store, callerSessionId, options, sessionTitle2) {
|
|
112916
113368
|
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
112917
113369
|
const peers = peersOf(channels);
|
|
112918
|
-
const from = options.from?.map((id) => id.trim()).filter(Boolean) ?? [];
|
|
113370
|
+
const from = options.from?.map((id) => id.trim()).filter(Boolean).map((id) => store.currentSessionFor(id)) ?? [];
|
|
112919
113371
|
if (from.length > 0) {
|
|
112920
113372
|
const known = new Set(peers.map((peer) => peer.sessionId));
|
|
112921
113373
|
const unknown2 = from.filter((id) => !known.has(id));
|
|
@@ -113039,293 +113491,6 @@ function prepareLaunchStart(store, callerSessionId, input) {
|
|
|
113039
113491
|
return { ...grant, task };
|
|
113040
113492
|
}
|
|
113041
113493
|
|
|
113042
|
-
// ../../packages/runtime/src/collaboration/store.ts
|
|
113043
|
-
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
113044
|
-
var GRANT_COLUMNS = `credential_hash AS grant_id, parent_session_id, child_session_id,
|
|
113045
|
-
agent_id, task, config_json, task_sent, COALESCE(kind, 'spawn') AS kind, started_at`;
|
|
113046
|
-
var MESSAGE_COLUMNS = `id, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at`;
|
|
113047
|
-
function nowIso() {
|
|
113048
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
113049
|
-
}
|
|
113050
|
-
var CollaborationStore = class {
|
|
113051
|
-
constructor(db) {
|
|
113052
|
-
this.db = db;
|
|
113053
|
-
}
|
|
113054
|
-
db;
|
|
113055
|
-
transaction(fn) {
|
|
113056
|
-
return this.db.transaction(fn)();
|
|
113057
|
-
}
|
|
113058
|
-
// --- grants --------------------------------------------------------------
|
|
113059
|
-
grantById(grantId) {
|
|
113060
|
-
return this.db.prepare(`SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants WHERE credential_hash = ?`).get(grantId) ?? null;
|
|
113061
|
-
}
|
|
113062
|
-
/**
|
|
113063
|
-
* The grant an approved launch created. launchIds are agent-chosen and may
|
|
113064
|
-
* repeat across requests, so the latest approval wins.
|
|
113065
|
-
*/
|
|
113066
|
-
grantForLaunch(parentSessionId, launchId) {
|
|
113067
|
-
return this.db.prepare(`
|
|
113068
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113069
|
-
WHERE parent_session_id = ? AND json_extract(config_json, '$.launchId') = ?
|
|
113070
|
-
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
113071
|
-
`).get(parentSessionId, launchId) ?? null;
|
|
113072
|
-
}
|
|
113073
|
-
/** True when `sessionId` is a spawn child (nested collaboration is unsupported). */
|
|
113074
|
-
isSpawnChild(sessionId) {
|
|
113075
|
-
return Boolean(this.db.prepare(`
|
|
113076
|
-
SELECT 1 FROM session_collaboration_grants
|
|
113077
|
-
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn' LIMIT 1
|
|
113078
|
-
`).get(sessionId));
|
|
113079
|
-
}
|
|
113080
|
-
spawnGrantForChild(childSessionId) {
|
|
113081
|
-
return this.db.prepare(`
|
|
113082
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113083
|
-
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn'
|
|
113084
|
-
`).get(childSessionId) ?? null;
|
|
113085
|
-
}
|
|
113086
|
-
startedSpawnGrants() {
|
|
113087
|
-
return this.db.prepare(`
|
|
113088
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113089
|
-
WHERE child_session_id IS NOT NULL AND COALESCE(kind, 'spawn') = 'spawn'
|
|
113090
|
-
`).all();
|
|
113091
|
-
}
|
|
113092
|
-
/** Started handoff grants; the session each created is `config_json.handoffSessionId`. */
|
|
113093
|
-
startedHandoffGrants() {
|
|
113094
|
-
return this.db.prepare(`
|
|
113095
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113096
|
-
WHERE kind = 'handoff' AND started_at IS NOT NULL
|
|
113097
|
-
`).all();
|
|
113098
|
-
}
|
|
113099
|
-
/**
|
|
113100
|
-
* Open mailbox channels of `sessionId`: spawn grants with a bound child and
|
|
113101
|
-
* started link grants, oldest first. Handoff grants are never channels.
|
|
113102
|
-
*/
|
|
113103
|
-
channelsFor(sessionId) {
|
|
113104
|
-
return this.db.prepare(`
|
|
113105
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113106
|
-
WHERE (parent_session_id = ? OR child_session_id = ?)
|
|
113107
|
-
AND child_session_id IS NOT NULL
|
|
113108
|
-
AND (COALESCE(kind, 'spawn') = 'spawn' OR (kind = 'link' AND started_at IS NOT NULL))
|
|
113109
|
-
ORDER BY created_at, rowid
|
|
113110
|
-
`).all(sessionId, sessionId);
|
|
113111
|
-
}
|
|
113112
|
-
/** True when one of the two sessions was created by a handoff from the other. */
|
|
113113
|
-
isHandoffPair(a, b3) {
|
|
113114
|
-
return Boolean(this.db.prepare(`
|
|
113115
|
-
SELECT 1 FROM session_collaboration_grants
|
|
113116
|
-
WHERE kind = 'handoff' AND (
|
|
113117
|
-
(parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
113118
|
-
OR (parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
113119
|
-
) LIMIT 1
|
|
113120
|
-
`).get(a, b3, b3, a));
|
|
113121
|
-
}
|
|
113122
|
-
findLinkGrant(initiatorSessionId, peerSessionId) {
|
|
113123
|
-
return this.db.prepare(`
|
|
113124
|
-
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
113125
|
-
WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'
|
|
113126
|
-
`).get(initiatorSessionId, peerSessionId) ?? null;
|
|
113127
|
-
}
|
|
113128
|
-
/**
|
|
113129
|
-
* Persist an approved launch. Link grants bind the peer at once; spawn and
|
|
113130
|
-
* handoff grants bind their session on start. The brief arrives on start.
|
|
113131
|
-
*/
|
|
113132
|
-
createGrant(input) {
|
|
113133
|
-
const grantId = randomUUID20();
|
|
113134
|
-
try {
|
|
113135
|
-
this.db.prepare(`
|
|
113136
|
-
INSERT INTO session_collaboration_grants
|
|
113137
|
-
(credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id,
|
|
113138
|
-
agent_id, task, config_json, created_at, kind)
|
|
113139
|
-
VALUES (?, NULL, '', ?, ?, ?, '', ?, ?, ?)
|
|
113140
|
-
`).run(
|
|
113141
|
-
grantId,
|
|
113142
|
-
input.parentSessionId,
|
|
113143
|
-
input.childSessionId ?? null,
|
|
113144
|
-
input.agentId,
|
|
113145
|
-
JSON.stringify(input.config),
|
|
113146
|
-
nowIso(),
|
|
113147
|
-
input.kind
|
|
113148
|
-
);
|
|
113149
|
-
} catch (error51) {
|
|
113150
|
-
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
113151
|
-
if (input.childSessionId && /UNIQUE|unique/i.test(message)) {
|
|
113152
|
-
throw new CollaborationError(
|
|
113153
|
-
`Session ${input.parentSessionId} already has a link to ${input.childSessionId}; request it again to reuse it.`,
|
|
113154
|
-
"failed_precondition"
|
|
113155
|
-
);
|
|
113156
|
-
}
|
|
113157
|
-
throw error51;
|
|
113158
|
-
}
|
|
113159
|
-
return grantId;
|
|
113160
|
-
}
|
|
113161
|
-
updateConfig(grantId, config2) {
|
|
113162
|
-
this.db.prepare("UPDATE session_collaboration_grants SET config_json = ? WHERE credential_hash = ?").run(JSON.stringify(config2), grantId);
|
|
113163
|
-
}
|
|
113164
|
-
/** Record the brief of a grant whose task has not been delivered yet. */
|
|
113165
|
-
setTask(grantId, task) {
|
|
113166
|
-
this.db.prepare("UPDATE session_collaboration_grants SET task = ? WHERE credential_hash = ? AND task_sent = 0").run(task, grantId);
|
|
113167
|
-
}
|
|
113168
|
-
markTaskSent(grantId) {
|
|
113169
|
-
this.db.prepare("UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?").run(grantId);
|
|
113170
|
-
}
|
|
113171
|
-
/** Link start. Returns false when the grant had already been started. */
|
|
113172
|
-
markStarted(grantId) {
|
|
113173
|
-
return this.db.prepare(`
|
|
113174
|
-
UPDATE session_collaboration_grants SET started_at = ? WHERE credential_hash = ? AND started_at IS NULL
|
|
113175
|
-
`).run(nowIso(), grantId).changes === 1;
|
|
113176
|
-
}
|
|
113177
|
-
/**
|
|
113178
|
-
* Record the session a spawn/handoff start created. Throws when a concurrent
|
|
113179
|
-
* start already consumed the grant.
|
|
113180
|
-
*
|
|
113181
|
-
* A handoff session is deliberately *not* written to child_session_id: that
|
|
113182
|
-
* column holds channel endpoints (spawn children, link peers), and a handoff
|
|
113183
|
-
* sibling is neither. The created id lives in config_json instead.
|
|
113184
|
-
*/
|
|
113185
|
-
bindStartedSession(grant, sessionId, config2) {
|
|
113186
|
-
const changes = grant.kind === "handoff" ? this.db.prepare(`
|
|
113187
|
-
UPDATE session_collaboration_grants SET config_json = ?, started_at = ?
|
|
113188
|
-
WHERE credential_hash = ? AND started_at IS NULL
|
|
113189
|
-
`).run(JSON.stringify({ ...config2, handoffSessionId: sessionId }), nowIso(), grant.grant_id).changes : this.db.prepare(`
|
|
113190
|
-
UPDATE session_collaboration_grants SET child_session_id = ?, started_at = ?
|
|
113191
|
-
WHERE credential_hash = ? AND child_session_id IS NULL
|
|
113192
|
-
`).run(sessionId, nowIso(), grant.grant_id).changes;
|
|
113193
|
-
if (changes !== 1) throw new CollaborationError("This launch was already started", "failed_precondition");
|
|
113194
|
-
}
|
|
113195
|
-
// --- mailbox -------------------------------------------------------------
|
|
113196
|
-
nextSequence(grantId) {
|
|
113197
|
-
const row2 = this.db.prepare(`
|
|
113198
|
-
SELECT COALESCE(MAX(sequence), 0) + 1 AS next_sequence
|
|
113199
|
-
FROM session_collaboration_messages WHERE credential_hash = ?
|
|
113200
|
-
`).get(grantId);
|
|
113201
|
-
return row2.next_sequence;
|
|
113202
|
-
}
|
|
113203
|
-
insertMessage(grantId, input) {
|
|
113204
|
-
const row2 = {
|
|
113205
|
-
id: randomUUID20(),
|
|
113206
|
-
sequence: this.nextSequence(grantId),
|
|
113207
|
-
sender_session_id: input.senderSessionId,
|
|
113208
|
-
recipient_session_id: input.recipientSessionId,
|
|
113209
|
-
client_message_id: input.clientMessageId,
|
|
113210
|
-
content: input.content,
|
|
113211
|
-
created_at: nowIso()
|
|
113212
|
-
};
|
|
113213
|
-
this.db.prepare(`
|
|
113214
|
-
INSERT INTO session_collaboration_messages
|
|
113215
|
-
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
113216
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
113217
|
-
`).run(
|
|
113218
|
-
row2.id,
|
|
113219
|
-
grantId,
|
|
113220
|
-
row2.sequence,
|
|
113221
|
-
row2.sender_session_id,
|
|
113222
|
-
row2.recipient_session_id,
|
|
113223
|
-
row2.client_message_id,
|
|
113224
|
-
row2.content,
|
|
113225
|
-
row2.created_at
|
|
113226
|
-
);
|
|
113227
|
-
return row2;
|
|
113228
|
-
}
|
|
113229
|
-
/** Append one message; a repeated `clientMessageId` returns the stored row. */
|
|
113230
|
-
appendMessage(input) {
|
|
113231
|
-
return this.transaction(() => {
|
|
113232
|
-
if (input.clientMessageId) {
|
|
113233
|
-
const existing = this.db.prepare(`
|
|
113234
|
-
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
113235
|
-
WHERE credential_hash = ? AND sender_session_id = ? AND client_message_id = ?
|
|
113236
|
-
`).get(input.grantId, input.senderSessionId, input.clientMessageId);
|
|
113237
|
-
if (existing) return { row: existing, reused: true };
|
|
113238
|
-
}
|
|
113239
|
-
const row2 = this.insertMessage(input.grantId, { ...input, clientMessageId: input.clientMessageId ?? null });
|
|
113240
|
-
return { row: row2, reused: false };
|
|
113241
|
-
});
|
|
113242
|
-
}
|
|
113243
|
-
/** When `sessionId` last sent and last received a mailbox message (ISO), across all its channels. */
|
|
113244
|
-
lastMessageTimes(sessionId) {
|
|
113245
|
-
return this.db.prepare(`
|
|
113246
|
-
SELECT
|
|
113247
|
-
(SELECT MAX(created_at) FROM session_collaboration_messages WHERE sender_session_id = ?) AS sentAt,
|
|
113248
|
-
(SELECT MAX(created_at) FROM session_collaboration_messages WHERE recipient_session_id = ?) AS receivedAt
|
|
113249
|
-
`).get(sessionId, sessionId);
|
|
113250
|
-
}
|
|
113251
|
-
/** Queue a link grant's opening body for the peer exactly once, then mark the task sent. */
|
|
113252
|
-
appendLinkOpening(grant, recipientSessionId, content) {
|
|
113253
|
-
this.transaction(() => {
|
|
113254
|
-
try {
|
|
113255
|
-
this.insertMessage(grant.grant_id, {
|
|
113256
|
-
senderSessionId: grant.parent_session_id,
|
|
113257
|
-
recipientSessionId,
|
|
113258
|
-
clientMessageId: `link-opening:${grant.grant_id}`,
|
|
113259
|
-
content
|
|
113260
|
-
});
|
|
113261
|
-
} catch {
|
|
113262
|
-
}
|
|
113263
|
-
this.markTaskSent(grant.grant_id);
|
|
113264
|
-
});
|
|
113265
|
-
}
|
|
113266
|
-
/**
|
|
113267
|
-
* Up to `limit` unread messages addressed to `sessionId` across grants,
|
|
113268
|
-
* without marking them read: one per grant in turn, grants with the oldest
|
|
113269
|
-
* unread message first, so successive reads reach every grant even when
|
|
113270
|
-
* there are more grants than `limit`. Each batch is a prefix of its grant's
|
|
113271
|
-
* unread messages. Selection reads only the mailbox index; bodies load for
|
|
113272
|
-
* the selected messages alone.
|
|
113273
|
-
*/
|
|
113274
|
-
peekMailbox(sessionId, grantIds, limit2) {
|
|
113275
|
-
const unread = this.db.prepare(`
|
|
113276
|
-
SELECT rowid AS rid FROM session_collaboration_messages
|
|
113277
|
-
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence > ?
|
|
113278
|
-
ORDER BY sequence LIMIT ?
|
|
113279
|
-
`);
|
|
113280
|
-
const createdAt = this.db.prepare("SELECT created_at FROM session_collaboration_messages WHERE rowid = ?");
|
|
113281
|
-
const queues2 = grantIds.flatMap((grantId) => {
|
|
113282
|
-
const cursor = this.db.prepare(`
|
|
113283
|
-
SELECT last_sequence FROM session_collaboration_cursors WHERE credential_hash = ? AND session_id = ?
|
|
113284
|
-
`).get(grantId, sessionId);
|
|
113285
|
-
const rids = unread.all(grantId, sessionId, cursor?.last_sequence ?? 0, limit2).map((row2) => row2.rid);
|
|
113286
|
-
if (rids.length === 0) return [];
|
|
113287
|
-
const oldest = createdAt.get(rids[0]).created_at;
|
|
113288
|
-
return [{ grantId, rids, oldest, taken: 0 }];
|
|
113289
|
-
}).sort((a, b3) => a.oldest.localeCompare(b3.oldest));
|
|
113290
|
-
let budget = limit2;
|
|
113291
|
-
while (budget > 0) {
|
|
113292
|
-
let took = false;
|
|
113293
|
-
for (const queue of queues2) {
|
|
113294
|
-
if (budget === 0) break;
|
|
113295
|
-
if (queue.taken === queue.rids.length) continue;
|
|
113296
|
-
queue.taken++;
|
|
113297
|
-
budget--;
|
|
113298
|
-
took = true;
|
|
113299
|
-
}
|
|
113300
|
-
if (!took) break;
|
|
113301
|
-
}
|
|
113302
|
-
const load = this.db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages WHERE rowid = ?`);
|
|
113303
|
-
return queues2.filter((queue) => queue.taken > 0).map(({ grantId, rids, taken }) => ({
|
|
113304
|
-
grantId,
|
|
113305
|
-
rows: rids.slice(0, taken).map((rid) => load.get(rid))
|
|
113306
|
-
}));
|
|
113307
|
-
}
|
|
113308
|
-
/**
|
|
113309
|
-
* Mark messages read up to each grant's `lastSequence`. A cursor never
|
|
113310
|
-
* moves back, so acknowledgements may arrive in any order.
|
|
113311
|
-
*/
|
|
113312
|
-
ackMailbox(sessionId, acks) {
|
|
113313
|
-
this.transaction(() => {
|
|
113314
|
-
for (const { grantId, lastSequence } of acks) {
|
|
113315
|
-
this.db.prepare(`
|
|
113316
|
-
INSERT INTO session_collaboration_cursors (credential_hash, session_id, last_sequence)
|
|
113317
|
-
VALUES (?, ?, ?)
|
|
113318
|
-
ON CONFLICT(credential_hash, session_id) DO UPDATE SET last_sequence = MAX(last_sequence, excluded.last_sequence)
|
|
113319
|
-
`).run(grantId, sessionId, lastSequence);
|
|
113320
|
-
this.db.prepare(`
|
|
113321
|
-
UPDATE session_collaboration_messages SET delivered_at = COALESCE(delivered_at, ?)
|
|
113322
|
-
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence <= ?
|
|
113323
|
-
`).run(nowIso(), grantId, sessionId, lastSequence);
|
|
113324
|
-
}
|
|
113325
|
-
});
|
|
113326
|
-
}
|
|
113327
|
-
};
|
|
113328
|
-
|
|
113329
113494
|
// ../../packages/runtime/src/collaboration/index.ts
|
|
113330
113495
|
init_text();
|
|
113331
113496
|
|
|
@@ -113393,6 +113558,7 @@ function retrieveCollaborationMessages(ctx, input) {
|
|
|
113393
113558
|
init_agent_profile();
|
|
113394
113559
|
init_environment();
|
|
113395
113560
|
init_harness_capabilities();
|
|
113561
|
+
init_session_provider_definitions();
|
|
113396
113562
|
|
|
113397
113563
|
// ../../packages/shared/src/acp-brand.ts
|
|
113398
113564
|
init_acp_brand();
|
|
@@ -113468,7 +113634,7 @@ function listCollaborationProfiles(deps) {
|
|
|
113468
113634
|
pushProfile(
|
|
113469
113635
|
p3.id,
|
|
113470
113636
|
p3.harnessId,
|
|
113471
|
-
p3
|
|
113637
|
+
sessionProviderDisplayName(p3),
|
|
113472
113638
|
p3.isBase ? `${p3.harnessId} harness with the built-in configuration` : `Custom ${p3.harnessId} profile`,
|
|
113473
113639
|
p3.config
|
|
113474
113640
|
);
|
|
@@ -113912,6 +114078,31 @@ var CollaborationService = class {
|
|
|
113912
114078
|
retrieve(input) {
|
|
113913
114079
|
return retrieveCollaborationMessages(this.ctx, input);
|
|
113914
114080
|
}
|
|
114081
|
+
/** The session `sessionId` was continued in; null when it was not. */
|
|
114082
|
+
successorOf(sessionId) {
|
|
114083
|
+
return this.ctx.store.successorOf(sessionId);
|
|
114084
|
+
}
|
|
114085
|
+
/** Throws when `from` cannot be continued in a new session now. */
|
|
114086
|
+
assertContinuable(from) {
|
|
114087
|
+
assertContinuable(this.ctx.store, this.ctx.wakes, from, this.continuedState(from));
|
|
114088
|
+
}
|
|
114089
|
+
/**
|
|
114090
|
+
* `to` was just created to continue `from`: it takes over `from`'s channels
|
|
114091
|
+
* and wakes, and the child prompt moves with a spawn grant.
|
|
114092
|
+
*/
|
|
114093
|
+
continueSession(from, to2) {
|
|
114094
|
+
continueCollaboration(this.ctx.store, this.ctx.wakes, from, to2, this.continuedState(from));
|
|
114095
|
+
this.deps.sessions.moveExternalParent(from, to2);
|
|
114096
|
+
const grant = this.ctx.store.spawnGrantForChild(to2);
|
|
114097
|
+
if (grant) {
|
|
114098
|
+
this.deps.sessions.setSystemPromptAppend(to2, launchedSessionPromptAppend(this.deps, grant));
|
|
114099
|
+
this.deps.sessions.setSystemPromptAppend(from, null);
|
|
114100
|
+
}
|
|
114101
|
+
}
|
|
114102
|
+
continuedState(sessionId) {
|
|
114103
|
+
const session = this.deps.sessions.get(sessionId);
|
|
114104
|
+
return session ? { busy: session.status === "streaming" || session.pendingInteraction != null } : null;
|
|
114105
|
+
}
|
|
113915
114106
|
/** Reconstruct the system-prompt append of launched sessions after restart (never link). */
|
|
113916
114107
|
rehydrateSystemPrompts() {
|
|
113917
114108
|
const launched = [
|