@super-one/cli 0.68.0-alpha.2 → 0.69.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 +1314 -1227
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -359,7 +359,7 @@ var init_interaction_memory = __esm({
|
|
|
359
359
|
});
|
|
360
360
|
|
|
361
361
|
// ../../packages/shared/src/superone-tool-descriptions.ts
|
|
362
|
-
var MANUAL_READ_DESCRIPTION, SETUP_MINI_APP_DEV_DESCRIPTION, REGISTER_DEV_MINIAPP_DESCRIPTION, PACK_MINI_APP_DESCRIPTION, UPDATE_SUPERONE_TYPES_DESCRIPTION, RENAME_SESSION_DESCRIPTION, SESSION_TAG_DESCRIPTION, SESSION_TAG_LIST_DESCRIPTION, PROJECT_LIST_DESCRIPTION, SESSION_LIST_DESCRIPTION, SESSION_SEARCH_DESCRIPTION, SESSION_READ_DESCRIPTION, SESSION_TAGS_FILTER_DESCRIPTION, SESSION_TAG_MATCH_DESCRIPTION, SESSION_CLEANUP_DESCRIPTION, CONFIG_READ_DESCRIPTION, CONFIG_APPLY_DESCRIPTION, LIST_MEDIA_PROVIDERS_DESCRIPTION, GENERATE_IMAGE_DESCRIPTION, GENERATE_VIDEO_DESCRIPTION, VIDEO_STATUS_DESCRIPTION, SESSION_LIST_AGENTS_DESCRIPTION, SESSION_REQUEST_AGENTS_DESCRIPTION, LAUNCH_SUMMARY_DESCRIPTION, LAUNCH_TASK_DESCRIPTION, LAUNCH_MODE_DESCRIPTION, LAUNCH_SESSION_ID_DESCRIPTION, LAUNCH_PERMISSION_MODE_DESCRIPTION, LAUNCH_CWD_DESCRIPTION, LAUNCH_WORKTREE_DESCRIPTION, LAUNCH_BRANCH_NAME_DESCRIPTION, SESSION_START_DESCRIPTION, SESSION_SEND_DESCRIPTION, SESSION_RETRIEVE_DESCRIPTION, AUTOMATION_LIST_DESCRIPTION, AUTOMATION_APPLY_DESCRIPTION, AUTOMATION_DELETE_DESCRIPTION, TERMINAL_TABS_DESCRIPTION, TERMINAL_SNAPSHOT_DESCRIPTION, TERMINAL_ACT_DESCRIPTION, TERMINAL_WAIT_FOR_DESCRIPTION;
|
|
362
|
+
var MANUAL_READ_DESCRIPTION, SETUP_MINI_APP_DEV_DESCRIPTION, REGISTER_DEV_MINIAPP_DESCRIPTION, PACK_MINI_APP_DESCRIPTION, UPDATE_SUPERONE_TYPES_DESCRIPTION, RENAME_SESSION_DESCRIPTION, SESSION_TAG_DESCRIPTION, SESSION_TAG_LIST_DESCRIPTION, PROJECT_LIST_DESCRIPTION, SESSION_LIST_DESCRIPTION, SESSION_SEARCH_DESCRIPTION, SESSION_READ_DESCRIPTION, SESSION_TAGS_FILTER_DESCRIPTION, SESSION_TAG_MATCH_DESCRIPTION, SESSION_CLEANUP_DESCRIPTION, CONFIG_READ_DESCRIPTION, CONFIG_APPLY_DESCRIPTION, LIST_MEDIA_PROVIDERS_DESCRIPTION, GENERATE_IMAGE_DESCRIPTION, GENERATE_VIDEO_DESCRIPTION, VIDEO_STATUS_DESCRIPTION, SESSION_LIST_AGENTS_DESCRIPTION, SESSION_REQUEST_AGENTS_DESCRIPTION, LAUNCH_SUMMARY_DESCRIPTION, START_LAUNCH_ID_DESCRIPTION, LAUNCH_TASK_DESCRIPTION, LAUNCH_MODE_DESCRIPTION, LAUNCH_SESSION_ID_DESCRIPTION, LAUNCH_PERMISSION_MODE_DESCRIPTION, LAUNCH_CWD_DESCRIPTION, LAUNCH_WORKTREE_DESCRIPTION, LAUNCH_BRANCH_NAME_DESCRIPTION, SESSION_START_DESCRIPTION, SESSION_SEND_DESCRIPTION, SESSION_SEND_TO_DESCRIPTION, SESSION_SEND_CONTENT_DESCRIPTION, SESSION_RETRIEVE_DESCRIPTION, SESSION_RETRIEVE_FROM_DESCRIPTION, AUTOMATION_LIST_DESCRIPTION, AUTOMATION_APPLY_DESCRIPTION, AUTOMATION_DELETE_DESCRIPTION, TERMINAL_TABS_DESCRIPTION, TERMINAL_SNAPSHOT_DESCRIPTION, TERMINAL_ACT_DESCRIPTION, TERMINAL_WAIT_FOR_DESCRIPTION;
|
|
363
363
|
var init_superone_tool_descriptions = __esm({
|
|
364
364
|
"../../packages/shared/src/superone-tool-descriptions.ts"() {
|
|
365
365
|
"use strict";
|
|
@@ -385,18 +385,22 @@ var init_superone_tool_descriptions = __esm({
|
|
|
385
385
|
GENERATE_VIDEO_DESCRIPTION = "Submit a video generation; the tool opens a parameter review for user approval. Stop on cancelled or error; use returned feedback before retrying. Poll media_video_status about every 30s until generated or error. Results display automatically. Use media_list_providers for configured providers and the matching media provider-task manual for unfamiliar options.";
|
|
386
386
|
VIDEO_STATUS_DESCRIPTION = "Advance a submitted video job and read its status: running, generated with savedPaths, or error with message. Poll about every 30 seconds while running: these calls download and save the completed video. Report it ready only after generated; stop on error.";
|
|
387
387
|
SESSION_LIST_AGENTS_DESCRIPTION = "List the agent profiles available for user-approved child sessions. Only launchable agents are returned. Inspect each profile's harness and defaultConfig before session_collab_request. You may reuse one agentId for multiple launches. Skip this call when the user already named an agent with @ \u2014 that mention carries its agentId.";
|
|
388
|
-
SESSION_REQUEST_AGENTS_DESCRIPTION = 'Request user approval for collaboration launches. See the mode field for spawn vs handoff vs link. Spawn/handoff: pick an agentId from session_collab_list_agents; require name, role, summary
|
|
389
|
-
LAUNCH_SUMMARY_DESCRIPTION = "Short 2\u20133 sentence
|
|
390
|
-
|
|
388
|
+
SESSION_REQUEST_AGENTS_DESCRIPTION = 'Request user approval for collaboration launches. See the mode field for spawn vs handoff vs link. Spawn/handoff: pick an agentId from session_collab_list_agents; require name, role, summary. Link: require sessionId + summary. Read read_manual({ domain: "product", topic: "collaboration" }) before the first launch in a session. User must approve; returns a launchId per launch. Pass the task to session_collab_start, not here.';
|
|
389
|
+
LAUNCH_SUMMARY_DESCRIPTION = "Short 2\u20133 sentence summary the user approves in the confirm dialog. The full brief goes to session_collab_start.";
|
|
390
|
+
START_LAUNCH_ID_DESCRIPTION = "A launchId returned by session_collab_request in this session.";
|
|
391
|
+
LAUNCH_TASK_DESCRIPTION = "Full Markdown brief. Required for spawn/handoff, delivered to the new session. A handoff receiver cannot ask you anything back, so make the brief self-contained. Link: optional opening for the peer (mailbox + turn wake, never system prompt).";
|
|
391
392
|
LAUNCH_MODE_DESCRIPTION = '"spawn" (default) = nested child with a two-way mailbox. "handoff" = top-level sibling, not nested: it owns the task from then on, with no mailbox and no reply \u2014 pass work forward rather than supervise it. "link" = connect to an existing session (sessionId required).';
|
|
392
393
|
LAUNCH_SESSION_ID_DESCRIPTION = 'Existing SuperOne session id to link with (mode "link" only). Required for link; ignore for spawn. Prefer ids from @session mentions or session_list \u2014 never invent ids.';
|
|
393
394
|
LAUNCH_PERMISSION_MODE_DESCRIPTION = 'How autonomous the child session is. Nobody watches a child, so prefer the most autonomous mode it can finish under; "plan"/"default" only when stopping for human review is the point. Per-harness mode names, and why requesting autonomy is safe here: See read_manual({ domain: "product", topic: "collaboration" }).';
|
|
394
395
|
LAUNCH_CWD_DESCRIPTION = 'Only for a genuinely different project root; omit for the current project. Never a same-repo worktree leaf \u2014 express isolation with config.worktree. See read_manual({ domain: "product", topic: "collaboration" }).';
|
|
395
396
|
LAUNCH_WORKTREE_DESCRIPTION = 'Host-managed worktree for same-repo isolation; leave cwd unset. For parallel implementers, not for read-only review of the shared checkout. See read_manual({ domain: "product", topic: "collaboration" }).';
|
|
396
397
|
LAUNCH_BRANCH_NAME_DESCRIPTION = 'With mode "branch", create this unique branch. Git cannot check out one branch in two worktrees.';
|
|
397
|
-
SESSION_START_DESCRIPTION = "
|
|
398
|
-
SESSION_SEND_DESCRIPTION = "Send a persistent Markdown message
|
|
399
|
-
|
|
398
|
+
SESSION_START_DESCRIPTION = "Start one approved launch by launchId and give it its task. Spawn: create the child and deliver the task. Handoff: create the sibling session and deliver the task; no mailbox follows. Link: bind the existing peer and wake it via turn injection (not system prompt). Returns the peer sessionId once it begins or is notified; message it with session_collab_send. Retries are idempotent and keep the first task. Start all approved launches back-to-back.";
|
|
399
|
+
SESSION_SEND_DESCRIPTION = "Send a persistent Markdown message to a collaboration peer: your spawn parent or child, or a linked session. The host checks that you may message that session. Use clientMessageId for retry-safe delivery. The host wakes the peer and later wakes you when it replies. After sending, continue other work or end your turn. Never sleep, resend, or poll session_collab_retrieve while waiting.";
|
|
400
|
+
SESSION_SEND_TO_DESCRIPTION = "Peer session id. Omit to message your spawn parent, or your only peer. session_collab_retrieve lists your peers.";
|
|
401
|
+
SESSION_SEND_CONTENT_DESCRIPTION = "Mailbox message body in Markdown. Prefer structured Markdown (headings, lists, code fences) for agent-to-agent handoffs; the SuperOne UI renders it as a Markdown preview.";
|
|
402
|
+
SESSION_RETRIEVE_DESCRIPTION = 'Read queued Markdown messages from your collaboration peers and list those peers (session id, name, relation). Call after a collaboration wake, before acting on peer input, or to find out who you can message. This is a non-blocking read: status "empty" is not a retry signal. Do not sleep or poll; end your turn and wait for the next wake.';
|
|
403
|
+
SESSION_RETRIEVE_FROM_DESCRIPTION = "Only read messages from these peer session ids. Omit to read from every peer.";
|
|
400
404
|
AUTOMATION_LIST_DESCRIPTION = "List project automations with schedule and last/next run. Pass id for full prompt, agentConfig, and schedule; filter by query or enabled. Find unknown ids here before automation_apply or automation_delete; check for duplicates before creation when needed.";
|
|
401
405
|
AUTOMATION_APPLY_DESCRIPTION = 'Create or update a project automation. create needs name, prompt, schedule; update needs id plus any field (pause via enabled=false). Call automation_list first for ids; remove with automation_delete. Always opens a user confirmation dialog and applies nothing without approval. For schedule and agentConfig shapes see read_manual({ domain: "product", topic: "automation" }).';
|
|
402
406
|
AUTOMATION_DELETE_DESCRIPTION = "Permanently delete project automations by id (from automation_list). Always opens a user confirmation dialog. Current project only. Prefer automation_list to choose ids first.";
|
|
@@ -463,10 +467,6 @@ var init_host_action_collaboration_descriptors = __esm({
|
|
|
463
467
|
"minLength": 1,
|
|
464
468
|
"description": LAUNCH_SUMMARY_DESCRIPTION
|
|
465
469
|
},
|
|
466
|
-
"task": {
|
|
467
|
-
"type": "string",
|
|
468
|
-
"description": LAUNCH_TASK_DESCRIPTION
|
|
469
|
-
},
|
|
470
470
|
"name": {
|
|
471
471
|
"type": "string",
|
|
472
472
|
"minLength": 1,
|
|
@@ -583,12 +583,18 @@ var init_host_action_collaboration_descriptors = __esm({
|
|
|
583
583
|
"inputSchema": {
|
|
584
584
|
"type": "object",
|
|
585
585
|
"properties": {
|
|
586
|
-
"
|
|
587
|
-
"type": "string"
|
|
586
|
+
"launchId": {
|
|
587
|
+
"type": "string",
|
|
588
|
+
"minLength": 1,
|
|
589
|
+
"description": START_LAUNCH_ID_DESCRIPTION
|
|
590
|
+
},
|
|
591
|
+
"task": {
|
|
592
|
+
"type": "string",
|
|
593
|
+
"description": LAUNCH_TASK_DESCRIPTION
|
|
588
594
|
}
|
|
589
595
|
},
|
|
590
596
|
"required": [
|
|
591
|
-
"
|
|
597
|
+
"launchId"
|
|
592
598
|
],
|
|
593
599
|
"additionalProperties": false
|
|
594
600
|
}
|
|
@@ -599,20 +605,20 @@ var init_host_action_collaboration_descriptors = __esm({
|
|
|
599
605
|
"inputSchema": {
|
|
600
606
|
"type": "object",
|
|
601
607
|
"properties": {
|
|
602
|
-
"
|
|
603
|
-
"type": "string"
|
|
608
|
+
"to": {
|
|
609
|
+
"type": "string",
|
|
610
|
+
"description": SESSION_SEND_TO_DESCRIPTION
|
|
604
611
|
},
|
|
605
612
|
"content": {
|
|
606
613
|
"type": "string",
|
|
607
614
|
"maxLength": 1e5,
|
|
608
|
-
"description":
|
|
615
|
+
"description": SESSION_SEND_CONTENT_DESCRIPTION
|
|
609
616
|
},
|
|
610
617
|
"clientMessageId": {
|
|
611
618
|
"type": "string"
|
|
612
619
|
}
|
|
613
620
|
},
|
|
614
621
|
"required": [
|
|
615
|
-
"credential",
|
|
616
622
|
"content"
|
|
617
623
|
],
|
|
618
624
|
"additionalProperties": false
|
|
@@ -624,18 +630,15 @@ var init_host_action_collaboration_descriptors = __esm({
|
|
|
624
630
|
"inputSchema": {
|
|
625
631
|
"type": "object",
|
|
626
632
|
"properties": {
|
|
627
|
-
"
|
|
633
|
+
"from": {
|
|
628
634
|
"type": "array",
|
|
629
|
-
"minItems": 1,
|
|
630
635
|
"maxItems": 32,
|
|
631
636
|
"items": {
|
|
632
637
|
"type": "string"
|
|
633
|
-
}
|
|
638
|
+
},
|
|
639
|
+
"description": SESSION_RETRIEVE_FROM_DESCRIPTION
|
|
634
640
|
}
|
|
635
641
|
},
|
|
636
|
-
"required": [
|
|
637
|
-
"credentials"
|
|
638
|
-
],
|
|
639
642
|
"additionalProperties": false
|
|
640
643
|
}
|
|
641
644
|
}
|
|
@@ -3097,11 +3100,12 @@ var init_host_action_computer_descriptors = __esm({
|
|
|
3097
3100
|
});
|
|
3098
3101
|
|
|
3099
3102
|
// ../../packages/shared/src/generative-ui/widget-tool-descriptions.ts
|
|
3100
|
-
var WIDGET_SHOW_DESCRIPTION;
|
|
3103
|
+
var WIDGET_SHOW_DESCRIPTION, WIDGET_LAYOUT_DESCRIPTION;
|
|
3101
3104
|
var init_widget_tool_descriptions = __esm({
|
|
3102
3105
|
"../../packages/shared/src/generative-ui/widget-tool-descriptions.ts"() {
|
|
3103
3106
|
"use strict";
|
|
3104
3107
|
WIDGET_SHOW_DESCRIPTION = `Render SVG, diagrams, charts, interactive HTML, or a SuperOne native surface (media gallery, files previewer) inline in chat. Pass widget_code for new content, or template + data to reuse a saved template. To show media you produced yourself, pass a @native/* template so it renders in SuperOne's own gallery (viewer, download, drag-out) instead of a lookalike you build in widget_code \u2014 call widget_list_templates for the list. To hand the user several files to look at one after another (screenshots, reports, changed sources), pass @native/files-previewer with data.files. Before the first new widget in a session, load the relevant design modules with read_manual({ domain: "widget", modules: [...] }).`;
|
|
3108
|
+
WIDGET_LAYOUT_DESCRIPTION = 'How an HTML widget adapts to a narrow screen such as a phone. "fluid" (default) reflows to the available width. "fixed" lays the widget out at 680px and scales it down as a whole, keeping its proportions \u2014 use it for UI mockups, where the layout itself is what the user is reviewing.';
|
|
3105
3109
|
}
|
|
3106
3110
|
});
|
|
3107
3111
|
|
|
@@ -3131,6 +3135,11 @@ var init_host_action_widget_descriptors = __esm({
|
|
|
3131
3135
|
"type": "string",
|
|
3132
3136
|
"description": "Short snake_case identifier for this widget."
|
|
3133
3137
|
},
|
|
3138
|
+
"layout": {
|
|
3139
|
+
"type": "string",
|
|
3140
|
+
"enum": ["fluid", "fixed"],
|
|
3141
|
+
"description": WIDGET_LAYOUT_DESCRIPTION
|
|
3142
|
+
},
|
|
3134
3143
|
"widget_code": {
|
|
3135
3144
|
"type": "string"
|
|
3136
3145
|
},
|
|
@@ -7411,7 +7420,7 @@ var require_websocket = __commonJS({
|
|
|
7411
7420
|
var http = __require("http");
|
|
7412
7421
|
var net2 = __require("net");
|
|
7413
7422
|
var tls = __require("tls");
|
|
7414
|
-
var { randomBytes:
|
|
7423
|
+
var { randomBytes: randomBytes6, createHash: createHash12 } = __require("crypto");
|
|
7415
7424
|
var { Duplex, Readable: Readable4 } = __require("stream");
|
|
7416
7425
|
var { URL: URL2 } = __require("url");
|
|
7417
7426
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -7949,7 +7958,7 @@ var require_websocket = __commonJS({
|
|
|
7949
7958
|
}
|
|
7950
7959
|
}
|
|
7951
7960
|
const defaultPort = isSecure ? 443 : 80;
|
|
7952
|
-
const key =
|
|
7961
|
+
const key = randomBytes6(16).toString("base64");
|
|
7953
7962
|
const request = isSecure ? https.request : http.request;
|
|
7954
7963
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
7955
7964
|
let perMessageDeflate;
|
|
@@ -8079,7 +8088,7 @@ var require_websocket = __commonJS({
|
|
|
8079
8088
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
8080
8089
|
return;
|
|
8081
8090
|
}
|
|
8082
|
-
const digest =
|
|
8091
|
+
const digest = createHash12("sha1").update(key + GUID).digest("base64");
|
|
8083
8092
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
8084
8093
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
8085
8094
|
return;
|
|
@@ -8448,7 +8457,7 @@ var require_websocket_server = __commonJS({
|
|
|
8448
8457
|
var EventEmitter = __require("events");
|
|
8449
8458
|
var http = __require("http");
|
|
8450
8459
|
var { Duplex } = __require("stream");
|
|
8451
|
-
var { createHash:
|
|
8460
|
+
var { createHash: createHash12 } = __require("crypto");
|
|
8452
8461
|
var extension2 = require_extension();
|
|
8453
8462
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
8454
8463
|
var subprotocol2 = require_subprotocol();
|
|
@@ -8755,7 +8764,7 @@ var require_websocket_server = __commonJS({
|
|
|
8755
8764
|
);
|
|
8756
8765
|
}
|
|
8757
8766
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
8758
|
-
const digest =
|
|
8767
|
+
const digest = createHash12("sha1").update(key + GUID).digest("base64");
|
|
8759
8768
|
const headers = [
|
|
8760
8769
|
"HTTP/1.1 101 Switching Protocols",
|
|
8761
8770
|
"Upgrade: websocket",
|
|
@@ -11799,8 +11808,9 @@ var init_harness_capabilities = __esm({
|
|
|
11799
11808
|
},
|
|
11800
11809
|
dsh: {
|
|
11801
11810
|
// In-process dsh Cordis tree (docs/draft/deepseek-harness-integration.md).
|
|
11802
|
-
// Flags flip only when the corresponding event path is wired
|
|
11803
|
-
//
|
|
11811
|
+
// Flags flip only when the corresponding event path is wired.
|
|
11812
|
+
// `supportsStreamingToolInput`: live `tool-call-delta` frames open the row
|
|
11813
|
+
// and stream its arguments before the durable `tool/call` completes it.
|
|
11804
11814
|
// `supportsCompact`: `compaction-basic` compacts automatically at context
|
|
11805
11815
|
// pressure and on provider overflow; `/compact` drives `compactNow()`.
|
|
11806
11816
|
// `supportsMcp` covers both SuperOne's own tools (native dsh rows) and
|
|
@@ -11808,13 +11818,17 @@ var init_harness_capabilities = __esm({
|
|
|
11808
11818
|
// `supportsSubagents`: foreground delegation (`dsh-tool-subagent` over the
|
|
11809
11819
|
// in-process spawn provider), rendered as a Task block with the child's
|
|
11810
11820
|
// steps nested under it. Background and continuable children are not wired.
|
|
11821
|
+
// `supportsPlanMode`: `plan` also turns on dsh plan mode; `exit_plan_mode`
|
|
11822
|
+
// reaches the plan-approval card through the user-questions seam.
|
|
11823
|
+
// `supportsQueuedSteer`: `agent.steer()` lands at the next step boundary
|
|
11824
|
+
// and never cancels a tool, so there is one steer and no separate "soon".
|
|
11811
11825
|
supportsMcp: true,
|
|
11812
|
-
supportsPlanMode:
|
|
11826
|
+
supportsPlanMode: true,
|
|
11813
11827
|
supportsTodos: true,
|
|
11814
11828
|
supportsSubagents: true,
|
|
11815
11829
|
supportsCompact: true,
|
|
11816
|
-
supportsStreamingToolInput:
|
|
11817
|
-
supportsQueuedSteer:
|
|
11830
|
+
supportsStreamingToolInput: true,
|
|
11831
|
+
supportsQueuedSteer: true,
|
|
11818
11832
|
supportsQueuedSteerSoon: false,
|
|
11819
11833
|
// Single cwd.
|
|
11820
11834
|
supportsAdditionalDirs: false,
|
|
@@ -13068,11 +13082,6 @@ var init_message_bridge = __esm({
|
|
|
13068
13082
|
});
|
|
13069
13083
|
|
|
13070
13084
|
// ../../packages/shared/src/agent-types.ts
|
|
13071
|
-
function resolveLaunchSummary(task, summary2) {
|
|
13072
|
-
const explicit = typeof summary2 === "string" ? summary2.trim() : "";
|
|
13073
|
-
if (explicit) return explicit;
|
|
13074
|
-
return task.split(/\n/, 1)[0]?.trim() || task.trim();
|
|
13075
|
-
}
|
|
13076
13085
|
function hasOneM(id) {
|
|
13077
13086
|
return id.endsWith(ONE_M_SUFFIX);
|
|
13078
13087
|
}
|
|
@@ -13950,23 +13959,23 @@ function mapPlanRequest(payload, fallbackId) {
|
|
|
13950
13959
|
allowedPrompts: Array.isArray(payload.allowedPrompts) ? payload.allowedPrompts : Array.isArray(input.allowedPrompts) ? input.allowedPrompts : []
|
|
13951
13960
|
};
|
|
13952
13961
|
}
|
|
13953
|
-
function userMessage(ctx, blockId, text2,
|
|
13962
|
+
function userMessage(ctx, blockId, text2, nowIso2) {
|
|
13954
13963
|
return {
|
|
13955
13964
|
id: blockId,
|
|
13956
13965
|
role: "user",
|
|
13957
13966
|
status: "complete",
|
|
13958
13967
|
content: text2 ? [{ type: "text", text: text2 }] : [],
|
|
13959
|
-
createdAt:
|
|
13968
|
+
createdAt: nowIso2,
|
|
13960
13969
|
providerId: ctx.providerId ?? "codex"
|
|
13961
13970
|
};
|
|
13962
13971
|
}
|
|
13963
|
-
function assistantMessage(ctx, blockId,
|
|
13972
|
+
function assistantMessage(ctx, blockId, nowIso2) {
|
|
13964
13973
|
return {
|
|
13965
13974
|
id: blockId,
|
|
13966
13975
|
role: "assistant",
|
|
13967
13976
|
status: "streaming",
|
|
13968
13977
|
content: [],
|
|
13969
|
-
createdAt:
|
|
13978
|
+
createdAt: nowIso2,
|
|
13970
13979
|
providerId: ctx.providerId ?? "codex"
|
|
13971
13980
|
};
|
|
13972
13981
|
}
|
|
@@ -13982,7 +13991,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
13982
13991
|
const completedAssistantIds = /* @__PURE__ */ new Set();
|
|
13983
13992
|
let lastAssistantId = null;
|
|
13984
13993
|
let rawTerminalThisTurn = false;
|
|
13985
|
-
const
|
|
13994
|
+
const nowIso2 = ctx.nowIso ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13986
13995
|
const ensureAssistant = (push, preferredId) => {
|
|
13987
13996
|
if (lastAssistantId) return lastAssistantId;
|
|
13988
13997
|
const id = preferredId && preferredId.length > 0 ? preferredId : `assistant-${ctx.sessionId}`;
|
|
@@ -13990,7 +13999,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
13990
13999
|
startedAssistantIds.add(id);
|
|
13991
14000
|
push({
|
|
13992
14001
|
type: "message_start",
|
|
13993
|
-
message: assistantMessage(ctx, id,
|
|
14002
|
+
message: assistantMessage(ctx, id, nowIso2())
|
|
13994
14003
|
});
|
|
13995
14004
|
}
|
|
13996
14005
|
lastAssistantId = id;
|
|
@@ -14012,7 +14021,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
14012
14021
|
const text2 = asString(payload.text) ?? "";
|
|
14013
14022
|
push({
|
|
14014
14023
|
type: "user_message_appended",
|
|
14015
|
-
message: userMessage(ctx, blockId, text2,
|
|
14024
|
+
message: userMessage(ctx, blockId, text2, nowIso2())
|
|
14016
14025
|
});
|
|
14017
14026
|
break;
|
|
14018
14027
|
}
|
|
@@ -15652,9 +15661,6 @@ var init_runtime_policy = __esm({
|
|
|
15652
15661
|
|
|
15653
15662
|
// ../../packages/runtime/src/session/session-runtime.ts
|
|
15654
15663
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15655
|
-
function redactTaskNotificationForDisplay(content) {
|
|
15656
|
-
return content.replace(/\s+with credential\s+(?:"[^"]*"|'[^']*')/gi, "").replace(/\bs1sc_[A-Za-z0-9_-]+/g, "[redacted]").replace(/\s{2,}/g, " ").trim();
|
|
15657
|
-
}
|
|
15658
15664
|
function deriveSessionTitleFromUserText(text2) {
|
|
15659
15665
|
const cleaned = stripMiniAppMarkup(text2).trim().replace(/\s+/g, " ");
|
|
15660
15666
|
if (!cleaned) return null;
|
|
@@ -16422,18 +16428,17 @@ var init_session_runtime = __esm({
|
|
|
16422
16428
|
return { delivered: true };
|
|
16423
16429
|
}
|
|
16424
16430
|
appendUserMessage(session, opts) {
|
|
16425
|
-
const displayText = opts.source === "task-notification" ? redactTaskNotificationForDisplay(opts.text) : opts.text;
|
|
16426
16431
|
const userBlock = {
|
|
16427
16432
|
id: randomUUID5(),
|
|
16428
16433
|
role: "user",
|
|
16429
|
-
text:
|
|
16434
|
+
text: opts.text,
|
|
16430
16435
|
createdAt: Date.now()
|
|
16431
16436
|
};
|
|
16432
16437
|
session.transcript.push(userBlock);
|
|
16433
16438
|
session.updatedAt = Date.now();
|
|
16434
16439
|
let autoTitle = null;
|
|
16435
16440
|
if (!session.title || !session.title.trim()) {
|
|
16436
|
-
autoTitle = deriveSessionTitleFromUserText(
|
|
16441
|
+
autoTitle = deriveSessionTitleFromUserText(opts.text);
|
|
16437
16442
|
if (autoTitle) session.title = autoTitle;
|
|
16438
16443
|
}
|
|
16439
16444
|
this.persist(session);
|
|
@@ -16442,7 +16447,7 @@ var init_session_runtime = __esm({
|
|
|
16442
16447
|
eventType: SESSION_DURABLE_EVENT.userMessage,
|
|
16443
16448
|
payload: {
|
|
16444
16449
|
blockId: userBlock.id,
|
|
16445
|
-
text:
|
|
16450
|
+
text: opts.text,
|
|
16446
16451
|
...opts.source ? { source: opts.source } : {}
|
|
16447
16452
|
},
|
|
16448
16453
|
causationRequestId: opts.requestId
|
|
@@ -19027,10 +19032,10 @@ var init_builtin = __esm({
|
|
|
19027
19032
|
init_effective_endpoints();
|
|
19028
19033
|
init_protocols();
|
|
19029
19034
|
XIAOMI_MODELS = {
|
|
19030
|
-
default: { id: "mimo-v2.
|
|
19031
|
-
opus: { id: "mimo-v2.
|
|
19032
|
-
sonnet: { id: "mimo-v2.
|
|
19033
|
-
haiku: { id: "mimo-v2.
|
|
19035
|
+
default: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
19036
|
+
opus: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
19037
|
+
sonnet: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
19038
|
+
haiku: { id: "mimo-v2.6-pro", name: "MiMo V2.6 Pro" }
|
|
19034
19039
|
};
|
|
19035
19040
|
BAILIAN_CODING_PLAN_MODELS = {
|
|
19036
19041
|
default: { id: "qwen3.7-plus", name: "Qwen 3.7 Plus" },
|
|
@@ -19535,6 +19540,7 @@ var init_builtin = __esm({
|
|
|
19535
19540
|
auth: "api-key",
|
|
19536
19541
|
baseUrl: "https://api.minimax.cn",
|
|
19537
19542
|
apiKeyUrl: "https://platform.minimax.cn/user-center/basic-information/interface-key",
|
|
19543
|
+
catalogProviderId: "minimax-cn",
|
|
19538
19544
|
endpoints: [
|
|
19539
19545
|
anthropic("/anthropic", {
|
|
19540
19546
|
extraEnv: MINIMAX_EXTRA_ENV,
|
|
@@ -19552,6 +19558,7 @@ var init_builtin = __esm({
|
|
|
19552
19558
|
auth: "api-key",
|
|
19553
19559
|
baseUrl: "https://api.minimax.io",
|
|
19554
19560
|
apiKeyUrl: "https://platform.minimax.io/user-center/basic-information/interface-key",
|
|
19561
|
+
catalogProviderId: "minimax",
|
|
19555
19562
|
endpoints: [
|
|
19556
19563
|
anthropic("/anthropic", {
|
|
19557
19564
|
extraEnv: MINIMAX_EXTRA_ENV,
|
|
@@ -19648,6 +19655,7 @@ var init_builtin = __esm({
|
|
|
19648
19655
|
auth: "api-key",
|
|
19649
19656
|
baseUrl: "https://token-plan-cn.xiaomimimo.com",
|
|
19650
19657
|
apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys",
|
|
19658
|
+
catalogProviderId: "xiaomi-token-plan-cn",
|
|
19651
19659
|
endpoints: [
|
|
19652
19660
|
anthropic("/anthropic", {
|
|
19653
19661
|
extraEnv: { ...EMPTY_AUTH_TOKEN },
|
|
@@ -19661,6 +19669,7 @@ var init_builtin = __esm({
|
|
|
19661
19669
|
auth: "api-key",
|
|
19662
19670
|
baseUrl: "https://api.xiaomimimo.com",
|
|
19663
19671
|
apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys",
|
|
19672
|
+
catalogProviderId: "xiaomi",
|
|
19664
19673
|
endpoints: [
|
|
19665
19674
|
anthropic("/anthropic", {
|
|
19666
19675
|
extraEnv: { ...EMPTY_AUTH_TOKEN },
|
|
@@ -19779,6 +19788,7 @@ var init_builtin = __esm({
|
|
|
19779
19788
|
brand: "longcat",
|
|
19780
19789
|
name: "Longcat",
|
|
19781
19790
|
description: "Longcat \u2014 \u957F\u732B AI \u7F16\u7A0B\u52A9\u624B",
|
|
19791
|
+
catalogProviderId: "longcat",
|
|
19782
19792
|
plans: [
|
|
19783
19793
|
{
|
|
19784
19794
|
id: "coding",
|
|
@@ -19883,6 +19893,7 @@ var init_builtin = __esm({
|
|
|
19883
19893
|
brand: "modelscope",
|
|
19884
19894
|
name: "ModelScope",
|
|
19885
19895
|
description: "ModelScope \u9B54\u642D \u2014 \u963F\u91CC\u5DF4\u5DF4\u6A21\u578B\u805A\u5408\u5E73\u53F0",
|
|
19896
|
+
catalogProviderId: "modelscope",
|
|
19886
19897
|
plans: [
|
|
19887
19898
|
{
|
|
19888
19899
|
id: "api",
|
|
@@ -19909,6 +19920,7 @@ var init_builtin = __esm({
|
|
|
19909
19920
|
brand: "siliconcloud",
|
|
19910
19921
|
name: "SiliconFlow",
|
|
19911
19922
|
description: "SiliconFlow \u7845\u57FA\u6D41\u52A8 \u2014 AI \u6A21\u578B\u805A\u5408\u63A8\u7406\u5E73\u53F0",
|
|
19923
|
+
catalogProviderId: "siliconflow-cn",
|
|
19912
19924
|
plans: [
|
|
19913
19925
|
{
|
|
19914
19926
|
id: "api",
|
|
@@ -19958,6 +19970,7 @@ var init_builtin = __esm({
|
|
|
19958
19970
|
brand: "bedrock",
|
|
19959
19971
|
name: "AWS Bedrock",
|
|
19960
19972
|
description: "Amazon Bedrock \u2014 run Claude on AWS infrastructure with IAM authentication",
|
|
19973
|
+
catalogProviderId: "amazon-bedrock",
|
|
19961
19974
|
plans: [
|
|
19962
19975
|
{
|
|
19963
19976
|
id: "aws",
|
|
@@ -19979,6 +19992,7 @@ var init_builtin = __esm({
|
|
|
19979
19992
|
brand: "vertexai",
|
|
19980
19993
|
name: "Google Vertex",
|
|
19981
19994
|
description: "Google Vertex AI \u2014 run Claude on GCP infrastructure with service account authentication",
|
|
19995
|
+
catalogProviderId: "google-vertex-anthropic",
|
|
19982
19996
|
plans: [
|
|
19983
19997
|
{
|
|
19984
19998
|
id: "gcp",
|
|
@@ -71246,8 +71260,8 @@ var require_multipleOf = __commonJS({
|
|
|
71246
71260
|
const { gen, data, schemaCode, it: it2 } = cxt;
|
|
71247
71261
|
const prec = it2.opts.multipleOfPrecision;
|
|
71248
71262
|
const res = gen.let("res");
|
|
71249
|
-
const
|
|
71250
|
-
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${
|
|
71263
|
+
const invalid3 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
|
|
71264
|
+
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid3}))`);
|
|
71251
71265
|
}
|
|
71252
71266
|
};
|
|
71253
71267
|
exports.default = def;
|
|
@@ -73403,8 +73417,8 @@ import { fileURLToPath } from "node:url";
|
|
|
73403
73417
|
function resolveCliReleaseVersion() {
|
|
73404
73418
|
const fromEnv = process.env.SUPERONE_CLI_VERSION?.trim();
|
|
73405
73419
|
if (fromEnv) return fromEnv;
|
|
73406
|
-
if ("0.
|
|
73407
|
-
return "0.
|
|
73420
|
+
if ("0.69.0-alpha".trim()) {
|
|
73421
|
+
return "0.69.0-alpha".trim();
|
|
73408
73422
|
}
|
|
73409
73423
|
const fromDist = readDistManifestVersion();
|
|
73410
73424
|
if (fromDist) return fromDist;
|
|
@@ -74599,7 +74613,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
|
|
|
74599
74613
|
credential_secret TEXT,
|
|
74600
74614
|
credential_hint TEXT NOT NULL,
|
|
74601
74615
|
parent_session_id TEXT NOT NULL,
|
|
74602
|
-
child_session_id TEXT
|
|
74616
|
+
child_session_id TEXT,
|
|
74603
74617
|
agent_id TEXT NOT NULL,
|
|
74604
74618
|
task TEXT NOT NULL,
|
|
74605
74619
|
config_json TEXT NOT NULL,
|
|
@@ -74684,6 +74698,66 @@ CREATE INDEX IF NOT EXISTS idx_session_providers_harness ON session_providers(ha
|
|
|
74684
74698
|
`;
|
|
74685
74699
|
var SCHEMA_GENERATION = 1;
|
|
74686
74700
|
|
|
74701
|
+
// ../../packages/runtime/src/collaboration/schema.ts
|
|
74702
|
+
var GRANTS_TABLE = "session_collaboration_grants";
|
|
74703
|
+
function ensureCollaborationGrantUniqueness(db, { warn = console.warn } = {}) {
|
|
74704
|
+
const rebuiltDdl = hasColumnUniqueChild(db) ? ddlWithoutColumnUnique(db) : null;
|
|
74705
|
+
if (rebuiltDdl === void 0) {
|
|
74706
|
+
warn(`[collaboration] ${GRANTS_TABLE}: unrecognized child_session_id UNIQUE DDL; keeping one link per peer`);
|
|
74707
|
+
}
|
|
74708
|
+
const apply = () => {
|
|
74709
|
+
if (rebuiltDdl) rebuildTable(db, rebuiltDdl);
|
|
74710
|
+
db.exec(`
|
|
74711
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_collaboration_spawn_child
|
|
74712
|
+
ON ${GRANTS_TABLE}(child_session_id) WHERE kind = 'spawn';
|
|
74713
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_collaboration_link_pair
|
|
74714
|
+
ON ${GRANTS_TABLE}(parent_session_id, child_session_id) WHERE kind = 'link';
|
|
74715
|
+
`);
|
|
74716
|
+
};
|
|
74717
|
+
const foreignKeysOn = Boolean(rebuiltDdl) && db.pragma("foreign_keys", { simple: true }) === 1;
|
|
74718
|
+
if (db.inTransaction) {
|
|
74719
|
+
if (foreignKeysOn) {
|
|
74720
|
+
throw new Error(`Rebuilding ${GRANTS_TABLE} inside a transaction requires foreign keys off`);
|
|
74721
|
+
}
|
|
74722
|
+
apply();
|
|
74723
|
+
return;
|
|
74724
|
+
}
|
|
74725
|
+
if (foreignKeysOn) db.pragma("foreign_keys = OFF");
|
|
74726
|
+
try {
|
|
74727
|
+
db.transaction(apply)();
|
|
74728
|
+
} finally {
|
|
74729
|
+
if (foreignKeysOn) db.pragma("foreign_keys = ON");
|
|
74730
|
+
}
|
|
74731
|
+
}
|
|
74732
|
+
function hasColumnUniqueChild(db) {
|
|
74733
|
+
const indexes = db.prepare(`PRAGMA index_list(${GRANTS_TABLE})`).all();
|
|
74734
|
+
return indexes.some((index) => {
|
|
74735
|
+
if (index.origin !== "u") return false;
|
|
74736
|
+
const columns = db.prepare(`PRAGMA index_info(${JSON.stringify(index.name)})`).all();
|
|
74737
|
+
return columns.length === 1 && columns[0].name === "child_session_id";
|
|
74738
|
+
});
|
|
74739
|
+
}
|
|
74740
|
+
var REBUILT_TABLE = `${GRANTS_TABLE}_rebuild`;
|
|
74741
|
+
function ddlWithoutColumnUnique(db) {
|
|
74742
|
+
const { sql } = db.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`).get(GRANTS_TABLE);
|
|
74743
|
+
const withoutUnique = sql.replace(/(\bchild_session_id\s+TEXT)\s+UNIQUE\b/i, "$1");
|
|
74744
|
+
const renamed = withoutUnique.replace(
|
|
74745
|
+
/^CREATE TABLE\s+(?:IF NOT EXISTS\s+)?["`]?session_collaboration_grants["`]?/i,
|
|
74746
|
+
`CREATE TABLE ${REBUILT_TABLE}`
|
|
74747
|
+
);
|
|
74748
|
+
return withoutUnique !== sql && renamed !== withoutUnique ? renamed : void 0;
|
|
74749
|
+
}
|
|
74750
|
+
function rebuildTable(db, ddl) {
|
|
74751
|
+
const indexes = db.prepare(`
|
|
74752
|
+
SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL
|
|
74753
|
+
`).all(GRANTS_TABLE);
|
|
74754
|
+
db.exec(ddl);
|
|
74755
|
+
db.exec(`INSERT INTO ${REBUILT_TABLE} SELECT * FROM ${GRANTS_TABLE}`);
|
|
74756
|
+
db.exec("DROP TABLE session_collaboration_grants");
|
|
74757
|
+
db.exec(`ALTER TABLE ${REBUILT_TABLE} RENAME TO session_collaboration_grants`);
|
|
74758
|
+
for (const index of indexes) db.exec(index.sql);
|
|
74759
|
+
}
|
|
74760
|
+
|
|
74687
74761
|
// ../../packages/runtime/src/db/database.ts
|
|
74688
74762
|
init_session_provider_definitions();
|
|
74689
74763
|
init_platform_registry();
|
|
@@ -74920,7 +74994,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
|
|
|
74920
74994
|
credential_secret TEXT,
|
|
74921
74995
|
credential_hint TEXT NOT NULL,
|
|
74922
74996
|
parent_session_id TEXT NOT NULL,
|
|
74923
|
-
child_session_id TEXT
|
|
74997
|
+
child_session_id TEXT,
|
|
74924
74998
|
agent_id TEXT NOT NULL,
|
|
74925
74999
|
task TEXT NOT NULL,
|
|
74926
75000
|
config_json TEXT NOT NULL,
|
|
@@ -74962,6 +75036,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_cursors (
|
|
|
74962
75036
|
if (!grantCols.some((c) => c.name === "kind")) {
|
|
74963
75037
|
db.exec(`ALTER TABLE session_collaboration_grants ADD COLUMN kind TEXT NOT NULL DEFAULT 'spawn'`);
|
|
74964
75038
|
}
|
|
75039
|
+
ensureCollaborationGrantUniqueness(db);
|
|
74965
75040
|
}
|
|
74966
75041
|
function ensureSessionTagsColumn(db) {
|
|
74967
75042
|
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
@@ -79999,10 +80074,9 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
79999
80074
|
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80000
80075
|
if (denied) return denied;
|
|
80001
80076
|
const p3 = asRecord24(payload);
|
|
80002
|
-
const
|
|
80003
|
-
|
|
80004
|
-
|
|
80005
|
-
return { error: { code: "invalid_argument", message: "credential or grantId required" } };
|
|
80077
|
+
const launchId = typeof p3.launchId === "string" ? p3.launchId.trim() : "";
|
|
80078
|
+
if (!launchId) {
|
|
80079
|
+
return { error: { code: "invalid_argument", message: "launchId required" } };
|
|
80006
80080
|
}
|
|
80007
80081
|
const callerSessionId = typeof p3.callerSessionId === "string" && p3.callerSessionId.trim() ? p3.callerSessionId.trim() : typeof p3.parentSessionId === "string" && p3.parentSessionId.trim() ? p3.parentSessionId.trim() : "";
|
|
80008
80082
|
if (!callerSessionId) {
|
|
@@ -80030,8 +80104,8 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
80030
80104
|
try {
|
|
80031
80105
|
return {
|
|
80032
80106
|
result: await ctx.collaboration.start({
|
|
80033
|
-
|
|
80034
|
-
|
|
80107
|
+
launchId,
|
|
80108
|
+
task: typeof p3.task === "string" ? p3.task : void 0,
|
|
80035
80109
|
formAnswers: p3.formAnswers && typeof p3.formAnswers === "object" ? p3.formAnswers : void 0,
|
|
80036
80110
|
callerSessionId,
|
|
80037
80111
|
controllerClientSessionId: ctx.client.clientSessionId
|
|
@@ -80041,36 +80115,39 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
80041
80115
|
return mapThrown8(err);
|
|
80042
80116
|
}
|
|
80043
80117
|
}
|
|
80118
|
+
function assertMailboxLease(p3, sessionId, ctx) {
|
|
80119
|
+
const leaseId = String(p3.leaseId ?? "").trim();
|
|
80120
|
+
if (!leaseId) return { error: { code: "invalid_argument", message: "leaseId required" } };
|
|
80121
|
+
try {
|
|
80122
|
+
ctx.leases.assertValid({
|
|
80123
|
+
resource: { environmentId: ctx.identity.environmentId, sessionId },
|
|
80124
|
+
leaseId,
|
|
80125
|
+
generation: String(p3.generation ?? ""),
|
|
80126
|
+
holderClientId: ctx.client.clientSessionId
|
|
80127
|
+
});
|
|
80128
|
+
return null;
|
|
80129
|
+
} catch (err) {
|
|
80130
|
+
return mapThrown8(err);
|
|
80131
|
+
}
|
|
80132
|
+
}
|
|
80044
80133
|
function handleCollaborationSend(payload, ctx) {
|
|
80045
80134
|
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80046
80135
|
if (denied) return denied;
|
|
80047
80136
|
const p3 = asRecord24(payload);
|
|
80048
|
-
const credential = String(p3.credential ?? "");
|
|
80049
80137
|
const sessionId = String(p3.sessionId ?? p3.fromSessionId ?? "");
|
|
80050
80138
|
const content = typeof p3.content === "string" ? p3.content : p3.body !== void 0 ? typeof p3.body === "string" ? p3.body : JSON.stringify(p3.body) : "";
|
|
80051
|
-
if (!credential) {
|
|
80052
|
-
return { error: { code: "invalid_argument", message: "credential required" } };
|
|
80053
|
-
}
|
|
80054
80139
|
if (!sessionId) {
|
|
80055
80140
|
return { error: { code: "invalid_argument", message: "sessionId required" } };
|
|
80056
80141
|
}
|
|
80057
|
-
const
|
|
80058
|
-
if (
|
|
80059
|
-
return { error: { code: "invalid_argument", message: "leaseId required" } };
|
|
80060
|
-
}
|
|
80142
|
+
const leaseDenied = assertMailboxLease(p3, sessionId, ctx);
|
|
80143
|
+
if (leaseDenied) return leaseDenied;
|
|
80061
80144
|
try {
|
|
80062
|
-
ctx.leases.assertValid({
|
|
80063
|
-
resource: { environmentId: ctx.identity.environmentId, sessionId },
|
|
80064
|
-
leaseId,
|
|
80065
|
-
generation: String(p3.generation ?? ""),
|
|
80066
|
-
holderClientId: ctx.client.clientSessionId
|
|
80067
|
-
});
|
|
80068
80145
|
return {
|
|
80069
80146
|
result: ctx.collaboration.send({
|
|
80070
|
-
|
|
80147
|
+
sessionId,
|
|
80148
|
+
to: typeof p3.to === "string" ? p3.to : void 0,
|
|
80071
80149
|
content,
|
|
80072
|
-
clientMessageId: typeof p3.clientMessageId === "string" ? p3.clientMessageId : void 0
|
|
80073
|
-
sessionId
|
|
80150
|
+
clientMessageId: typeof p3.clientMessageId === "string" ? p3.clientMessageId : void 0
|
|
80074
80151
|
})
|
|
80075
80152
|
};
|
|
80076
80153
|
} catch (err) {
|
|
@@ -80078,22 +80155,20 @@ function handleCollaborationSend(payload, ctx) {
|
|
|
80078
80155
|
}
|
|
80079
80156
|
}
|
|
80080
80157
|
function handleCollaborationRetrieve(payload, ctx) {
|
|
80081
|
-
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.
|
|
80158
|
+
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80082
80159
|
if (denied) return denied;
|
|
80083
80160
|
const p3 = asRecord24(payload);
|
|
80084
|
-
const credential = String(p3.credential ?? "");
|
|
80085
80161
|
const sessionId = String(p3.sessionId ?? "");
|
|
80086
|
-
if (!credential) {
|
|
80087
|
-
return { error: { code: "invalid_argument", message: "credential required" } };
|
|
80088
|
-
}
|
|
80089
80162
|
if (!sessionId) {
|
|
80090
80163
|
return { error: { code: "invalid_argument", message: "sessionId required" } };
|
|
80091
80164
|
}
|
|
80165
|
+
const leaseDenied = assertMailboxLease(p3, sessionId, ctx);
|
|
80166
|
+
if (leaseDenied) return leaseDenied;
|
|
80092
80167
|
try {
|
|
80093
80168
|
return {
|
|
80094
80169
|
result: ctx.collaboration.retrieve({
|
|
80095
|
-
credential,
|
|
80096
80170
|
sessionId,
|
|
80171
|
+
from: Array.isArray(p3.from) ? p3.from.filter((id) => typeof id === "string") : void 0,
|
|
80097
80172
|
max: typeof p3.max === "number" ? p3.max : void 0
|
|
80098
80173
|
})
|
|
80099
80174
|
};
|
|
@@ -87037,1187 +87112,1148 @@ var SessionRuntime2 = class extends SessionRuntime {
|
|
|
87037
87112
|
init_harness_runners();
|
|
87038
87113
|
init_codex_turn_runner();
|
|
87039
87114
|
|
|
87040
|
-
// src/
|
|
87041
|
-
|
|
87042
|
-
|
|
87043
|
-
|
|
87044
|
-
|
|
87045
|
-
|
|
87046
|
-
|
|
87047
|
-
// ../../packages/shared/src/acp-brand.ts
|
|
87048
|
-
init_acp_brand();
|
|
87049
|
-
|
|
87050
|
-
// src/session/collaboration.ts
|
|
87051
|
-
init_resolve_service();
|
|
87052
|
-
var MAX_MESSAGES_PER_RETRIEVE = 100;
|
|
87053
|
-
var EMPTY_MAILBOX_HINT = "No peer has replied yet. Do not retrieve again, do not sleep, do not wait in place \u2014 end your turn or do unrelated work. A task notification will start a new turn for you as soon as a message arrives.";
|
|
87054
|
-
function hashCredential(credential) {
|
|
87055
|
-
return createHash9("sha256").update(credential).digest("hex");
|
|
87056
|
-
}
|
|
87057
|
-
function parseConfig(raw) {
|
|
87058
|
-
try {
|
|
87059
|
-
return JSON.parse(raw);
|
|
87060
|
-
} catch {
|
|
87061
|
-
return {};
|
|
87115
|
+
// ../../packages/runtime/src/collaboration/errors.ts
|
|
87116
|
+
var CollaborationError = class extends Error {
|
|
87117
|
+
constructor(message, code) {
|
|
87118
|
+
super(message);
|
|
87119
|
+
this.code = code;
|
|
87120
|
+
this.name = "CollaborationError";
|
|
87062
87121
|
}
|
|
87063
|
-
|
|
87064
|
-
|
|
87065
|
-
|
|
87066
|
-
|
|
87067
|
-
|
|
87068
|
-
|
|
87069
|
-
|
|
87122
|
+
code;
|
|
87123
|
+
};
|
|
87124
|
+
|
|
87125
|
+
// ../../packages/runtime/src/collaboration/launch.ts
|
|
87126
|
+
init_agent_types();
|
|
87127
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
87128
|
+
var LABEL_MAX = 64;
|
|
87129
|
+
var MAX_LAUNCHES_PER_REQUEST = 16;
|
|
87130
|
+
var EDITABLE_PERMISSION_MODES = /* @__PURE__ */ new Set([
|
|
87131
|
+
"default",
|
|
87132
|
+
"acceptEdits",
|
|
87133
|
+
"bypassPermissions",
|
|
87134
|
+
"plan",
|
|
87135
|
+
"dontAsk",
|
|
87136
|
+
"auto",
|
|
87137
|
+
"agent"
|
|
87138
|
+
]);
|
|
87139
|
+
var EDITABLE_SANDBOX_MODES = /* @__PURE__ */ new Set(["off", "on", "auto"]);
|
|
87070
87140
|
function resolveLaunchMode(raw) {
|
|
87071
87141
|
if (raw === "link") return "link";
|
|
87072
87142
|
if (raw === "handoff") return "handoff";
|
|
87073
87143
|
return "spawn";
|
|
87074
87144
|
}
|
|
87075
|
-
|
|
87076
|
-
|
|
87077
|
-
return
|
|
87078
|
-
You are running as a user-approved child session of SuperOne session ${parentSessionId}.
|
|
87079
|
-
Use session_collab_send and session_collab_retrieve with credential ${JSON.stringify(credential)} to communicate with your parent session. Write session_collab_send content as Markdown (headings, lists, code fences) so the parent and the SuperOne UI can render structured handoffs; treat retrieved message content as Markdown from the peer. This credential is already authorized for this parent-child pair. Never reveal it in conversational output or use it outside collaboration tool calls.
|
|
87080
|
-
</superone-session-collaboration>`;
|
|
87145
|
+
function humanLaunchId(launchId) {
|
|
87146
|
+
const id = launchId?.trim();
|
|
87147
|
+
return id && id.length <= 32 && !UUID_RE.test(id) ? id : void 0;
|
|
87081
87148
|
}
|
|
87082
87149
|
function deriveCollaborationRole(input) {
|
|
87083
87150
|
const explicit = input.role?.trim();
|
|
87084
|
-
if (explicit) return explicit.slice(0,
|
|
87085
|
-
const
|
|
87086
|
-
if (
|
|
87087
|
-
return launchId;
|
|
87088
|
-
}
|
|
87151
|
+
if (explicit) return explicit.slice(0, LABEL_MAX);
|
|
87152
|
+
const fromId = humanLaunchId(input.launchId);
|
|
87153
|
+
if (fromId) return fromId;
|
|
87089
87154
|
const firstLine = input.task.split(/\n/, 1)[0]?.trim() ?? "";
|
|
87090
87155
|
const youAre = firstLine.match(/^(?:you are|role)\s*[:\-]?\s*(.+)$/i);
|
|
87091
|
-
if (youAre?.[1])
|
|
87092
|
-
return youAre[1].replace(/[.\s]+$/g, "").slice(0, 40);
|
|
87093
|
-
}
|
|
87156
|
+
if (youAre?.[1]) return youAre[1].replace(/[.\s]+$/g, "").slice(0, 40);
|
|
87094
87157
|
return "Agent";
|
|
87095
87158
|
}
|
|
87096
87159
|
function deriveCollaborationName(input) {
|
|
87097
87160
|
const explicit = input.name?.trim();
|
|
87098
|
-
if (explicit) return explicit.slice(0,
|
|
87099
|
-
|
|
87100
|
-
if (launchId && launchId.length <= 32 && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(launchId)) {
|
|
87101
|
-
return launchId;
|
|
87102
|
-
}
|
|
87103
|
-
return "Agent";
|
|
87161
|
+
if (explicit) return explicit.slice(0, LABEL_MAX);
|
|
87162
|
+
return humanLaunchId(input.launchId) ?? "Agent";
|
|
87104
87163
|
}
|
|
87105
87164
|
function collaborationSessionTitle(name, role) {
|
|
87106
|
-
|
|
87107
|
-
const r = role.trim() || "Agent";
|
|
87108
|
-
return `${n} - ${r}`;
|
|
87165
|
+
return `${name.trim() || "Agent"} - ${role.trim() || "Agent"}`;
|
|
87109
87166
|
}
|
|
87110
|
-
|
|
87111
|
-
|
|
87112
|
-
|
|
87167
|
+
function parseGrantConfig(raw) {
|
|
87168
|
+
try {
|
|
87169
|
+
return JSON.parse(raw);
|
|
87170
|
+
} catch {
|
|
87171
|
+
return {};
|
|
87113
87172
|
}
|
|
87114
|
-
|
|
87115
|
-
|
|
87116
|
-
|
|
87117
|
-
|
|
87118
|
-
|
|
87119
|
-
|
|
87120
|
-
|
|
87121
|
-
|
|
87122
|
-
|
|
87123
|
-
|
|
87124
|
-
|
|
87125
|
-
|
|
87126
|
-
|
|
87127
|
-
|
|
87128
|
-
|
|
87129
|
-
|
|
87130
|
-
|
|
87131
|
-
|
|
87132
|
-
|
|
87133
|
-
|
|
87134
|
-
|
|
87135
|
-
|
|
87136
|
-
for (const m3 of harnessModels) {
|
|
87137
|
-
for (const e of m3.supportedEffortLevels ?? []) efforts.add(e);
|
|
87138
|
-
}
|
|
87139
|
-
const cfg = profileConfig && typeof profileConfig === "object" && !Array.isArray(profileConfig) ? profileConfig : {};
|
|
87140
|
-
const cfgModel = typeof cfg.model === "string" && cfg.model.trim() ? cfg.model.trim() : void 0;
|
|
87141
|
-
const cfgEffort = typeof cfg.effort === "string" && cfg.effort.trim() ? cfg.effort.trim() : typeof cfg.reasoningEffort === "string" && cfg.reasoningEffort.trim() ? cfg.reasoningEffort.trim() : void 0;
|
|
87142
|
-
const acpAgentId = harnessId === "acp" && typeof cfg.agentId === "string" && cfg.agentId.trim() ? cfg.agentId.trim() : null;
|
|
87143
|
-
const modelDefault = cfgModel ?? defaultModel?.id;
|
|
87144
|
-
const effortDefault = cfgEffort ?? (efforts.has("high") ? "high" : efforts.has("medium") ? "medium" : efforts.size > 0 ? [...efforts][0] : void 0);
|
|
87145
|
-
profiles.push({
|
|
87146
|
-
id: profileId,
|
|
87147
|
-
name: harnessId === "acp" ? acpAgentDisplayName(acpAgentId) : name,
|
|
87148
|
-
harnessId,
|
|
87149
|
-
...acpAgentId ? { acpAgentId } : {},
|
|
87150
|
-
brandKey: resolveHarnessBrandKey(harnessId, acpAgentId),
|
|
87151
|
-
description,
|
|
87152
|
-
defaultConfig: {
|
|
87153
|
-
...modelDefault ? { model: modelDefault } : {},
|
|
87154
|
-
...effortDefault ? { effort: effortDefault } : {},
|
|
87155
|
-
...harnessId === "codex" ? { fastMode: typeof cfg.fastMode === "boolean" ? cfg.fastMode : false } : {}
|
|
87156
|
-
},
|
|
87157
|
-
models: models.length > 0 ? models : [{ id: "default", name: "Default" }],
|
|
87158
|
-
efforts: [...efforts],
|
|
87159
|
-
apiProviders: listHarnessApiProviders(providers, harnessId, providerOptions)
|
|
87160
|
-
});
|
|
87173
|
+
}
|
|
87174
|
+
function invalid2(message) {
|
|
87175
|
+
throw new CollaborationError(message, "invalid_argument");
|
|
87176
|
+
}
|
|
87177
|
+
function assertLabel(kind, value) {
|
|
87178
|
+
if (value.length > LABEL_MAX) invalid2(`A launch ${kind} may contain at most ${LABEL_MAX} characters`);
|
|
87179
|
+
return value;
|
|
87180
|
+
}
|
|
87181
|
+
function assertLaunchCount(count) {
|
|
87182
|
+
if (count === 0) invalid2("launches must contain at least one proposed session");
|
|
87183
|
+
if (count > MAX_LAUNCHES_PER_REQUEST) {
|
|
87184
|
+
invalid2(`A single request may contain at most ${MAX_LAUNCHES_PER_REQUEST} launches`);
|
|
87185
|
+
}
|
|
87186
|
+
}
|
|
87187
|
+
function normalizeLaunchLabels(mode, launch2, linkPeerTitle) {
|
|
87188
|
+
const summary2 = launch2.summary?.trim();
|
|
87189
|
+
if (!summary2) invalid2("Every launch must include a non-empty summary");
|
|
87190
|
+
if (mode === "link") {
|
|
87191
|
+
return {
|
|
87192
|
+
summary: summary2,
|
|
87193
|
+
name: assertLabel("name", launch2.name?.trim() || linkPeerTitle || ""),
|
|
87194
|
+
role: assertLabel("role", launch2.role?.trim() || "Peer")
|
|
87161
87195
|
};
|
|
87162
|
-
if (sessionProviders) {
|
|
87163
|
-
for (const p3 of sessionProviders.list()) {
|
|
87164
|
-
pushProfile(
|
|
87165
|
-
p3.id,
|
|
87166
|
-
p3.harnessId,
|
|
87167
|
-
p3.name,
|
|
87168
|
-
p3.isBase ? `${p3.harnessId} harness with the built-in configuration` : `Custom ${p3.harnessId} profile`,
|
|
87169
|
-
p3.config
|
|
87170
|
-
);
|
|
87171
|
-
}
|
|
87172
|
-
}
|
|
87173
|
-
if (profiles.length === 0) {
|
|
87174
|
-
const seedIds = new Set(harnesses.readySessionHarnessIds());
|
|
87175
|
-
for (const s4 of sessions.list()) {
|
|
87176
|
-
if (s4.harnessId) seedIds.add(s4.harnessId);
|
|
87177
|
-
}
|
|
87178
|
-
for (const id of seedIds) {
|
|
87179
|
-
const harnessId = normalizeSessionHarnessId(id) ?? id;
|
|
87180
|
-
pushProfile(harnessId, harnessId, harnessId, `${harnessId} harness`);
|
|
87181
|
-
}
|
|
87182
|
-
}
|
|
87183
|
-
return profiles;
|
|
87184
87196
|
}
|
|
87185
|
-
|
|
87186
|
-
|
|
87187
|
-
|
|
87188
|
-
|
|
87189
|
-
|
|
87190
|
-
|
|
87191
|
-
|
|
87192
|
-
|
|
87193
|
-
|
|
87194
|
-
|
|
87195
|
-
}
|
|
87196
|
-
|
|
87197
|
-
|
|
87198
|
-
|
|
87199
|
-
|
|
87200
|
-
|
|
87201
|
-
|
|
87202
|
-
|
|
87203
|
-
|
|
87204
|
-
|
|
87205
|
-
|
|
87197
|
+
const name = launch2.name?.trim();
|
|
87198
|
+
if (!name) invalid2(`Every ${mode} launch must include a non-empty name`);
|
|
87199
|
+
const role = launch2.role?.trim();
|
|
87200
|
+
if (!role) invalid2(`Every ${mode} launch must include a non-empty role`);
|
|
87201
|
+
return { summary: summary2, name: assertLabel("name", name), role: assertLabel("role", role) };
|
|
87202
|
+
}
|
|
87203
|
+
function normalizeStartTask(mode, raw) {
|
|
87204
|
+
const task = typeof raw === "string" ? raw.trim() : "";
|
|
87205
|
+
if (!task && mode !== "link") invalid2(`Starting a ${mode} launch requires a non-empty task`);
|
|
87206
|
+
if (task.length > SESSION_AGENT_TASK_MAX) {
|
|
87207
|
+
invalid2(`A launch task may contain at most ${SESSION_AGENT_TASK_MAX.toLocaleString()} characters`);
|
|
87208
|
+
}
|
|
87209
|
+
return task;
|
|
87210
|
+
}
|
|
87211
|
+
function patchEditableLaunchConfig(base, rawPatch) {
|
|
87212
|
+
const patch = rawPatch && typeof rawPatch === "object" ? rawPatch : {};
|
|
87213
|
+
return {
|
|
87214
|
+
...base,
|
|
87215
|
+
...typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {},
|
|
87216
|
+
...typeof patch.effort === "string" && patch.effort.trim() ? { effort: patch.effort.trim() } : {},
|
|
87217
|
+
...typeof patch.fastMode === "boolean" ? { fastMode: patch.fastMode } : {},
|
|
87218
|
+
...patch.apiProviderId === null || typeof patch.apiProviderId === "string" ? { apiProviderId: patch.apiProviderId } : {},
|
|
87219
|
+
...typeof patch.permissionMode === "string" && EDITABLE_PERMISSION_MODES.has(patch.permissionMode) ? { permissionMode: patch.permissionMode } : {},
|
|
87220
|
+
...typeof patch.sandboxMode === "string" && EDITABLE_SANDBOX_MODES.has(patch.sandboxMode) ? { sandboxMode: patch.sandboxMode } : {}
|
|
87221
|
+
};
|
|
87222
|
+
}
|
|
87223
|
+
function mergeConfirmedLaunches(proposed, content) {
|
|
87224
|
+
const packed = content?.[SESSION_AGENT_LAUNCHES_FIELD];
|
|
87225
|
+
if (typeof packed !== "string") return proposed;
|
|
87226
|
+
let edited;
|
|
87227
|
+
try {
|
|
87228
|
+
edited = JSON.parse(packed);
|
|
87229
|
+
} catch {
|
|
87230
|
+
invalid2("The confirmed launch configuration is invalid");
|
|
87231
|
+
}
|
|
87232
|
+
if (!Array.isArray(edited)) invalid2("The confirmed launch configuration is invalid");
|
|
87233
|
+
if (edited.length !== proposed.length) {
|
|
87234
|
+
invalid2("The confirmed request must contain the same launches that were proposed");
|
|
87235
|
+
}
|
|
87236
|
+
const proposedById = new Map(proposed.map((launch2) => [launch2.launchId, launch2]));
|
|
87237
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87238
|
+
return edited.map((raw) => {
|
|
87239
|
+
if (!raw || typeof raw !== "object") invalid2("The confirmed launch configuration is invalid");
|
|
87240
|
+
const item = raw;
|
|
87241
|
+
const launchId = typeof item.launchId === "string" ? item.launchId : "";
|
|
87242
|
+
const base = proposedById.get(launchId);
|
|
87243
|
+
if (!base) invalid2(`Unknown launchId in confirmed configuration: ${launchId || "(missing)"}`);
|
|
87244
|
+
if (seen.has(launchId)) invalid2("Confirmed launches must have unique launchIds");
|
|
87245
|
+
seen.add(launchId);
|
|
87246
|
+
if (base.mode === "link") return { ...base, config: { ...base.config } };
|
|
87247
|
+
return {
|
|
87248
|
+
...base,
|
|
87249
|
+
// A tampered mode could turn a supervised child into a one-way handoff.
|
|
87250
|
+
mode: base.mode ?? "spawn",
|
|
87251
|
+
config: patchEditableLaunchConfig(base.config, item.config)
|
|
87252
|
+
};
|
|
87253
|
+
});
|
|
87254
|
+
}
|
|
87255
|
+
|
|
87256
|
+
// ../../packages/runtime/src/collaboration/text.ts
|
|
87257
|
+
var HANDOFF_NO_MAILBOX = 'That session was created by a one-way handoff: it owns the task and has no mailbox, so neither side can message the other. Use mode "spawn" (nested child) or "link" (existing session) when you need to exchange messages.';
|
|
87258
|
+
var EMPTY_MAILBOX_HINT = "No peer has replied yet. Do not retrieve again, do not sleep, do not wait in place \u2014 end your turn or do unrelated work. A task notification will start a new turn for you as soon as a message arrives.";
|
|
87259
|
+
var NO_PEERS_HINT = "You have no collaboration peers. Launch or link one with session_collab_request, then session_collab_start.";
|
|
87260
|
+
var NESTED_COLLABORATION_UNSUPPORTED = "Nested collaboration is not supported. Only top-level (non-collaboration-child) sessions may request agents.";
|
|
87261
|
+
function collaborationSystemPrompt(parentSessionId) {
|
|
87262
|
+
return `<superone-session-collaboration>
|
|
87263
|
+
You are running as a user-approved child session of SuperOne session ${parentSessionId}.
|
|
87264
|
+
Your parent is your default collaboration peer: session_collab_send without \`to\` messages it, and session_collab_retrieve reads its messages. Sessions the user links to you later are peers too; reply to them with \`to\` set to their session id. Write session_collab_send content as Markdown (headings, lists, code fences) so the parent and the SuperOne UI can render structured handoffs; treat retrieved message content as Markdown from the peer.
|
|
87265
|
+
</superone-session-collaboration>`;
|
|
87266
|
+
}
|
|
87267
|
+
function sessionLabel(sessionId, title) {
|
|
87268
|
+
return `SuperOne session ${sessionId} ("${title}")`;
|
|
87269
|
+
}
|
|
87270
|
+
function mailboxWakeText(from) {
|
|
87271
|
+
return `A collaboration mailbox message is ready. It is from ${sessionLabel(from.sessionId, from.title)}. Call session_collab_retrieve to receive it, then act on it and end your turn \u2014 you will be woken again the same way for every later message, so never wait in place for one.`;
|
|
87272
|
+
}
|
|
87273
|
+
function linkActivationWakeText(input) {
|
|
87274
|
+
return `A user-approved collaboration link is active with ${sessionLabel(input.initiatorSessionId, input.initiatorTitle)}. Call session_collab_retrieve` + (input.hasOpening ? " to read the opening message" : " if a mailbox message is waiting") + `, then reply with session_collab_send({ to: ${JSON.stringify(input.initiatorSessionId)} }). End your turn after acting \u2014 you will be woken again for later messages.`;
|
|
87275
|
+
}
|
|
87276
|
+
function handoffTaskContent(input) {
|
|
87277
|
+
const title = input.parentTitle || input.parentSessionId.slice(0, 8);
|
|
87278
|
+
return `> Handed off from SuperOne session \`${input.parentSessionId}\` ("${title}"). This is a one-way handoff: you own this task now and cannot message that session back. Read its context with session_read({ sessionId }) if you need it.
|
|
87279
|
+
|
|
87280
|
+
` + input.task;
|
|
87281
|
+
}
|
|
87282
|
+
|
|
87283
|
+
// ../../packages/runtime/src/collaboration/mailbox.ts
|
|
87284
|
+
var MAX_MAILBOX_CONTENT = 1e5;
|
|
87285
|
+
var MAX_MESSAGES_PER_RETRIEVE = 100;
|
|
87286
|
+
function describeLaunchedPeer(grant) {
|
|
87287
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
87288
|
+
const name = deriveCollaborationName({ name: config2.name });
|
|
87289
|
+
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
87290
|
+
return { name, role, title: collaborationSessionTitle(name, role), agentId: grant.agent_id, config: config2 };
|
|
87291
|
+
}
|
|
87292
|
+
function describePeerForCaller(grant, callerSessionId, sessionTitle2) {
|
|
87293
|
+
const relation = grant.kind === "link" ? "link" : callerSessionId === grant.child_session_id ? "parent" : "child";
|
|
87294
|
+
if (callerSessionId === grant.child_session_id) {
|
|
87295
|
+
const title = sessionTitle2(grant.parent_session_id)?.trim() || grant.parent_session_id.slice(0, 8);
|
|
87296
|
+
return {
|
|
87297
|
+
sessionId: grant.parent_session_id,
|
|
87298
|
+
name: relation === "parent" ? "Parent" : title,
|
|
87299
|
+
role: relation === "parent" ? "" : "Peer",
|
|
87300
|
+
title,
|
|
87301
|
+
relation
|
|
87302
|
+
};
|
|
87303
|
+
}
|
|
87304
|
+
const child = describeLaunchedPeer(grant);
|
|
87305
|
+
return {
|
|
87306
|
+
sessionId: grant.child_session_id ?? "",
|
|
87307
|
+
name: child.name,
|
|
87308
|
+
role: child.role,
|
|
87309
|
+
title: child.title,
|
|
87310
|
+
relation
|
|
87311
|
+
};
|
|
87312
|
+
}
|
|
87313
|
+
function listMailboxChannels(store, callerSessionId, sessionTitle2) {
|
|
87314
|
+
return store.channelsFor(callerSessionId).map((grant) => ({
|
|
87315
|
+
grant,
|
|
87316
|
+
peer: describePeerForCaller(grant, callerSessionId, sessionTitle2)
|
|
87317
|
+
}));
|
|
87318
|
+
}
|
|
87319
|
+
function peersOf(channels) {
|
|
87320
|
+
return [...new Map(channels.map((channel) => [channel.peer.sessionId, channel.peer])).values()];
|
|
87321
|
+
}
|
|
87322
|
+
function assertNotPeeredElsewhere(store, callerSessionId, peerSessionId) {
|
|
87323
|
+
const other = store.channelsFor(callerSessionId).find((grant) => (grant.parent_session_id === peerSessionId || grant.child_session_id === peerSessionId) && !(grant.kind === "link" && grant.parent_session_id === callerSessionId));
|
|
87324
|
+
if (other) {
|
|
87325
|
+
throw new CollaborationError(
|
|
87326
|
+
`Session ${peerSessionId} is already one of your collaboration peers; message it with session_collab_send({ to: ${JSON.stringify(peerSessionId)} }).`,
|
|
87327
|
+
"failed_precondition"
|
|
87328
|
+
);
|
|
87329
|
+
}
|
|
87330
|
+
}
|
|
87331
|
+
function formatPeers(peers) {
|
|
87332
|
+
return peers.map((peer) => `${peer.sessionId} ("${peer.title}", ${peer.relation})`).join("; ");
|
|
87333
|
+
}
|
|
87334
|
+
function resolveSendChannel(store, callerSessionId, to2, sessionTitle2) {
|
|
87335
|
+
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
87336
|
+
const peers = peersOf(channels);
|
|
87337
|
+
const target = to2?.trim();
|
|
87338
|
+
if (!target) {
|
|
87339
|
+
if (peers.length === 0) throw new CollaborationError(NO_PEERS_HINT, "failed_precondition");
|
|
87340
|
+
const parent = channels.find((channel) => channel.peer.relation === "parent");
|
|
87341
|
+
if (parent) return parent;
|
|
87342
|
+
if (peers.length > 1) {
|
|
87343
|
+
throw new CollaborationError(
|
|
87344
|
+
`You have several collaboration peers; pass \`to\` with one of their session ids: ${formatPeers(peers)}`,
|
|
87345
|
+
"invalid_argument"
|
|
87206
87346
|
);
|
|
87207
87347
|
}
|
|
87208
|
-
|
|
87209
|
-
|
|
87210
|
-
|
|
87211
|
-
|
|
87212
|
-
|
|
87213
|
-
|
|
87214
|
-
|
|
87215
|
-
|
|
87216
|
-
|
|
87217
|
-
|
|
87348
|
+
return channels[channels.length - 1];
|
|
87349
|
+
}
|
|
87350
|
+
const matches = channels.filter((channel) => channel.peer.sessionId === target);
|
|
87351
|
+
if (matches.length > 0) return matches[matches.length - 1];
|
|
87352
|
+
if (store.isHandoffPair(callerSessionId, target)) {
|
|
87353
|
+
throw new CollaborationError(HANDOFF_NO_MAILBOX, "failed_precondition");
|
|
87354
|
+
}
|
|
87355
|
+
throw new CollaborationError(
|
|
87356
|
+
`Session ${target} is not one of your collaboration peers. ` + (peers.length > 0 ? `Your peers: ${formatPeers(peers)}` : NO_PEERS_HINT),
|
|
87357
|
+
"forbidden"
|
|
87358
|
+
);
|
|
87359
|
+
}
|
|
87360
|
+
function normalizeMailboxContent(raw) {
|
|
87361
|
+
const content = typeof raw === "string" ? raw.trim() : "";
|
|
87362
|
+
if (!content) throw new CollaborationError("content must not be empty", "invalid_argument");
|
|
87363
|
+
if (content.length > MAX_MAILBOX_CONTENT) {
|
|
87364
|
+
throw new CollaborationError("content may contain at most 100,000 characters", "invalid_argument");
|
|
87365
|
+
}
|
|
87366
|
+
return content;
|
|
87367
|
+
}
|
|
87368
|
+
function readCallerMailbox(store, callerSessionId, options, sessionTitle2) {
|
|
87369
|
+
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
87370
|
+
const peers = peersOf(channels);
|
|
87371
|
+
const from = options.from?.map((id) => id.trim()).filter(Boolean) ?? [];
|
|
87372
|
+
if (from.length > 0) {
|
|
87373
|
+
const known = new Set(peers.map((peer) => peer.sessionId));
|
|
87374
|
+
const unknown2 = from.filter((id) => !known.has(id));
|
|
87375
|
+
if (unknown2.length > 0) {
|
|
87376
|
+
throw new CollaborationError(
|
|
87377
|
+
`Not your collaboration peers: ${unknown2.join(", ")}. ` + (peers.length > 0 ? `Your peers: ${formatPeers(peers)}` : NO_PEERS_HINT),
|
|
87378
|
+
"forbidden"
|
|
87379
|
+
);
|
|
87218
87380
|
}
|
|
87219
|
-
|
|
87220
|
-
|
|
87221
|
-
|
|
87222
|
-
|
|
87223
|
-
|
|
87224
|
-
|
|
87225
|
-
|
|
87226
|
-
|
|
87227
|
-
|
|
87228
|
-
|
|
87229
|
-
|
|
87230
|
-
|
|
87231
|
-
|
|
87232
|
-
|
|
87233
|
-
|
|
87234
|
-
|
|
87235
|
-
|
|
87236
|
-
|
|
87237
|
-
|
|
87238
|
-
|
|
87239
|
-
|
|
87240
|
-
|
|
87241
|
-
|
|
87242
|
-
|
|
87243
|
-
|
|
87244
|
-
|
|
87245
|
-
|
|
87246
|
-
|
|
87247
|
-
|
|
87248
|
-
|
|
87249
|
-
|
|
87250
|
-
|
|
87251
|
-
|
|
87252
|
-
new Error(`A launch task may contain at most ${SESSION_AGENT_TASK_MAX.toLocaleString()} characters`),
|
|
87253
|
-
{ code: "invalid_argument" }
|
|
87254
|
-
);
|
|
87255
|
-
}
|
|
87256
|
-
const peerTitle = peer.title?.trim() || peerSessionId.slice(0, 8);
|
|
87257
|
-
const name2 = launch2.name?.trim() || peerTitle;
|
|
87258
|
-
const role2 = launch2.role?.trim() || "Peer";
|
|
87259
|
-
const projectPath = this.deps.projects.get(peer.projectId)?.path;
|
|
87260
|
-
return {
|
|
87381
|
+
}
|
|
87382
|
+
const selected = from.length > 0 ? channels.filter((channel) => from.includes(channel.peer.sessionId)) : channels;
|
|
87383
|
+
if (selected.length === 0) return { messages: [], peers };
|
|
87384
|
+
const limit = Math.min(MAX_MESSAGES_PER_RETRIEVE, Math.max(1, Math.floor(options.limit ?? MAX_MESSAGES_PER_RETRIEVE)));
|
|
87385
|
+
const peerByGrant = new Map(selected.map((channel) => [channel.grant.grant_id, channel.peer]));
|
|
87386
|
+
const perGrantLimit = Math.max(1, Math.floor(limit / selected.length));
|
|
87387
|
+
const messages = store.readMailbox(callerSessionId, [...peerByGrant.keys()], perGrantLimit).flatMap(({ grantId, rows }) => rows.map((row) => ({
|
|
87388
|
+
messageId: row.id,
|
|
87389
|
+
sequence: row.sequence,
|
|
87390
|
+
fromSessionId: row.sender_session_id,
|
|
87391
|
+
from: peerByGrant.get(grantId),
|
|
87392
|
+
content: row.content,
|
|
87393
|
+
createdAt: row.created_at
|
|
87394
|
+
}))).sort((a, b3) => a.createdAt.localeCompare(b3.createdAt)).slice(0, limit);
|
|
87395
|
+
return { messages, peers };
|
|
87396
|
+
}
|
|
87397
|
+
|
|
87398
|
+
// ../../packages/runtime/src/collaboration/start.ts
|
|
87399
|
+
var START_APPROVED_LAUNCHES_HINT = "Start each launch with session_collab_start({ launchId, task }); task is the full Markdown brief (optional for link, where it becomes the opening message).";
|
|
87400
|
+
function recordApprovedLaunch(store, parentSessionId, launch2, config2) {
|
|
87401
|
+
const mode = resolveLaunchMode(launch2.mode);
|
|
87402
|
+
const { launchId, summary: summary2, name, role } = launch2;
|
|
87403
|
+
const title = collaborationSessionTitle(name, role);
|
|
87404
|
+
if (mode === "link") {
|
|
87405
|
+
const peerSessionId = launch2.sessionId?.trim();
|
|
87406
|
+
if (!peerSessionId) throw new CollaborationError("Link launches require sessionId", "invalid_argument");
|
|
87407
|
+
const existing = store.findLinkGrant(parentSessionId, peerSessionId);
|
|
87408
|
+
if (existing) {
|
|
87409
|
+
const existingConfig = { ...parseGrantConfig(existing.config_json), launchId };
|
|
87410
|
+
store.updateConfig(existing.grant_id, existingConfig);
|
|
87411
|
+
return {
|
|
87412
|
+
grantId: existing.grant_id,
|
|
87413
|
+
approved: {
|
|
87261
87414
|
launchId,
|
|
87262
|
-
mode
|
|
87263
|
-
agentId:
|
|
87415
|
+
mode,
|
|
87416
|
+
agentId: existing.agent_id,
|
|
87264
87417
|
sessionId: peerSessionId,
|
|
87265
|
-
|
|
87266
|
-
peerProjectPath: projectPath,
|
|
87267
|
-
summary: summary3,
|
|
87268
|
-
task: task2,
|
|
87269
|
-
name: name2,
|
|
87270
|
-
role: role2,
|
|
87271
|
-
config: { name: name2, role: role2, summary: summary3 }
|
|
87272
|
-
};
|
|
87273
|
-
}
|
|
87274
|
-
const agentId = launch2.agentId?.trim();
|
|
87275
|
-
if (!agentId) {
|
|
87276
|
-
throw Object.assign(
|
|
87277
|
-
new Error(`${mode} launches require agentId from session_collab_list_agents`),
|
|
87278
|
-
{ code: "invalid_argument" }
|
|
87279
|
-
);
|
|
87280
|
-
}
|
|
87281
|
-
const profile = resolveProfile(profiles, agentId);
|
|
87282
|
-
if (!profile) {
|
|
87283
|
-
throw Object.assign(new Error(`Unknown agent profile: ${agentId}`), {
|
|
87284
|
-
code: "invalid_argument"
|
|
87285
|
-
});
|
|
87286
|
-
}
|
|
87287
|
-
const task = launch2.task?.trim();
|
|
87288
|
-
if (!task) {
|
|
87289
|
-
throw Object.assign(new Error("Every spawn launch must include a non-empty task"), {
|
|
87290
|
-
code: "invalid_argument"
|
|
87291
|
-
});
|
|
87292
|
-
}
|
|
87293
|
-
if (task.length > SESSION_AGENT_TASK_MAX) {
|
|
87294
|
-
throw Object.assign(
|
|
87295
|
-
new Error(`A launch task may contain at most ${SESSION_AGENT_TASK_MAX.toLocaleString()} characters`),
|
|
87296
|
-
{ code: "invalid_argument" }
|
|
87297
|
-
);
|
|
87298
|
-
}
|
|
87299
|
-
const summary2 = resolveLaunchSummary(task, launch2.summary);
|
|
87300
|
-
if (!summary2) {
|
|
87301
|
-
throw Object.assign(new Error("Every launch must include a non-empty summary"), {
|
|
87302
|
-
code: "invalid_argument"
|
|
87303
|
-
});
|
|
87304
|
-
}
|
|
87305
|
-
const name = launch2.name?.trim();
|
|
87306
|
-
if (!name) {
|
|
87307
|
-
throw Object.assign(new Error(`Every ${mode} launch must include a non-empty name`), {
|
|
87308
|
-
code: "invalid_argument"
|
|
87309
|
-
});
|
|
87310
|
-
}
|
|
87311
|
-
const role = launch2.role?.trim();
|
|
87312
|
-
if (!role) {
|
|
87313
|
-
throw Object.assign(new Error(`Every ${mode} launch must include a non-empty role`), {
|
|
87314
|
-
code: "invalid_argument"
|
|
87315
|
-
});
|
|
87316
|
-
}
|
|
87317
|
-
const raw = launch2.config && typeof launch2.config === "object" ? launch2.config : {};
|
|
87318
|
-
const safeFromLaunch = {};
|
|
87319
|
-
if (typeof raw.model === "string" && raw.model.trim()) safeFromLaunch.model = raw.model.trim();
|
|
87320
|
-
if (typeof raw.effort === "string" && raw.effort.trim()) safeFromLaunch.effort = raw.effort.trim();
|
|
87321
|
-
if (raw.apiProviderId === null) safeFromLaunch.apiProviderId = null;
|
|
87322
|
-
else if (typeof raw.apiProviderId === "string" && raw.apiProviderId.trim()) {
|
|
87323
|
-
safeFromLaunch.apiProviderId = raw.apiProviderId.trim();
|
|
87324
|
-
}
|
|
87325
|
-
if (raw.worktree && typeof raw.worktree === "object") safeFromLaunch.worktree = raw.worktree;
|
|
87326
|
-
if (typeof raw.worktreePath === "string" && raw.worktreePath.trim()) {
|
|
87327
|
-
safeFromLaunch.worktreePath = raw.worktreePath.trim();
|
|
87328
|
-
}
|
|
87329
|
-
if (typeof raw.cwd === "string" && raw.cwd.trim()) safeFromLaunch.cwd = raw.cwd.trim();
|
|
87330
|
-
return {
|
|
87331
|
-
launchId,
|
|
87332
|
-
mode,
|
|
87333
|
-
agentId,
|
|
87334
|
-
summary: summary2,
|
|
87335
|
-
task,
|
|
87336
|
-
name,
|
|
87337
|
-
role,
|
|
87338
|
-
config: {
|
|
87339
|
-
...profile.defaultConfig,
|
|
87340
|
-
...safeFromLaunch,
|
|
87341
|
-
permissionMode: "default",
|
|
87342
|
-
sandboxMode: "off",
|
|
87343
|
-
cwd: typeof safeFromLaunch.cwd === "string" ? safeFromLaunch.cwd : parent.cwd ?? this.deps.projects.get(parent.projectId)?.path,
|
|
87418
|
+
summary: summary2,
|
|
87344
87419
|
name,
|
|
87345
|
-
role
|
|
87420
|
+
role,
|
|
87421
|
+
title,
|
|
87422
|
+
config: existingConfig,
|
|
87423
|
+
reused: true
|
|
87346
87424
|
}
|
|
87347
87425
|
};
|
|
87348
|
-
});
|
|
87349
|
-
const launchIds = new Set(normalized.map((l) => l.launchId));
|
|
87350
|
-
if (launchIds.size !== normalized.length) {
|
|
87351
|
-
throw Object.assign(new Error("Every confirmed launch must have a unique launchId"), {
|
|
87352
|
-
code: "invalid_argument"
|
|
87353
|
-
});
|
|
87354
|
-
}
|
|
87355
|
-
let confirmed = normalized;
|
|
87356
|
-
if (input.requireUserConfirm) {
|
|
87357
|
-
const outcome = await this.deps.sessions.requestAgentsConfirm({
|
|
87358
|
-
sessionId: input.parentSessionId,
|
|
87359
|
-
launches: normalized,
|
|
87360
|
-
profiles: profileList,
|
|
87361
|
-
signal: input.signal
|
|
87362
|
-
});
|
|
87363
|
-
if (outcome.action === "cancel") {
|
|
87364
|
-
return { status: "cancelled" };
|
|
87365
|
-
}
|
|
87366
|
-
if (outcome.action === "decline") {
|
|
87367
|
-
return { status: "rejected", feedback: outcome.content?.feedback };
|
|
87368
|
-
}
|
|
87369
|
-
confirmed = this.mergeConfirmedLaunches(normalized, outcome.content);
|
|
87370
87426
|
}
|
|
87371
|
-
const
|
|
87372
|
-
|
|
87373
|
-
|
|
87374
|
-
|
|
87375
|
-
|
|
87376
|
-
|
|
87377
|
-
|
|
87378
|
-
|
|
87379
|
-
|
|
87380
|
-
|
|
87381
|
-
|
|
87382
|
-
|
|
87383
|
-
|
|
87384
|
-
|
|
87385
|
-
|
|
87386
|
-
const existing = this.deps.db.prepare(
|
|
87387
|
-
`SELECT credential_hash, credential_secret, agent_id, task, config_json
|
|
87388
|
-
FROM session_collaboration_grants
|
|
87389
|
-
WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'`
|
|
87390
|
-
).get(input.parentSessionId, peerSessionId);
|
|
87391
|
-
if (existing?.credential_secret) {
|
|
87392
|
-
const credential3 = this.deps.secrets.decrypt(existing.credential_secret);
|
|
87393
|
-
if (credential3) {
|
|
87394
|
-
return {
|
|
87395
|
-
launchId: launch2.launchId,
|
|
87396
|
-
mode: "link",
|
|
87397
|
-
agentId: existing.agent_id,
|
|
87398
|
-
sessionId: peerSessionId,
|
|
87399
|
-
peerSessionId,
|
|
87400
|
-
summary: launch2.summary,
|
|
87401
|
-
task: existing.task,
|
|
87402
|
-
name: launch2.name,
|
|
87403
|
-
role: launch2.role,
|
|
87404
|
-
title: collaborationSessionTitle(launch2.name, launch2.role),
|
|
87405
|
-
config: parseConfig(existing.config_json),
|
|
87406
|
-
credential: credential3,
|
|
87407
|
-
grantId: existing.credential_hash,
|
|
87408
|
-
reused: true
|
|
87409
|
-
};
|
|
87410
|
-
}
|
|
87411
|
-
}
|
|
87412
|
-
const credential2 = `s1sc_${randomBytes5(32).toString("base64url")}`;
|
|
87413
|
-
const credentialHash2 = hashCredential(credential2);
|
|
87414
|
-
const task = launch2.task.trim();
|
|
87415
|
-
const config3 = {
|
|
87416
|
-
name: launch2.name,
|
|
87417
|
-
role: launch2.role,
|
|
87418
|
-
summary: launch2.summary,
|
|
87419
|
-
peerSessionId,
|
|
87420
|
-
peerTitle: launch2.peerTitle,
|
|
87421
|
-
peerProjectPath: launch2.peerProjectPath
|
|
87422
|
-
};
|
|
87423
|
-
try {
|
|
87424
|
-
insertLink.run(
|
|
87425
|
-
credentialHash2,
|
|
87426
|
-
this.deps.secrets.encrypt(credential2),
|
|
87427
|
-
credential2.slice(-8),
|
|
87428
|
-
input.parentSessionId,
|
|
87429
|
-
peerSessionId,
|
|
87430
|
-
"",
|
|
87431
|
-
task,
|
|
87432
|
-
JSON.stringify(config3),
|
|
87433
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
87434
|
-
);
|
|
87435
|
-
} catch (err) {
|
|
87436
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
87437
|
-
if (/UNIQUE|unique/i.test(message)) {
|
|
87438
|
-
throw Object.assign(
|
|
87439
|
-
new Error(
|
|
87440
|
-
`Session ${peerSessionId} is already bound as a collaboration endpoint (spawn child or link peer). A session cannot be the non-initiator endpoint of two grants.`
|
|
87441
|
-
),
|
|
87442
|
-
{ code: "failed_precondition" }
|
|
87443
|
-
);
|
|
87444
|
-
}
|
|
87445
|
-
throw err;
|
|
87446
|
-
}
|
|
87447
|
-
this.deps.events.append({
|
|
87448
|
-
aggregateType: "session",
|
|
87449
|
-
aggregateId: input.parentSessionId,
|
|
87450
|
-
eventType: "collaboration.grant_created",
|
|
87451
|
-
payload: {
|
|
87452
|
-
grantId: credentialHash2,
|
|
87453
|
-
mode: "link",
|
|
87454
|
-
peerSessionId,
|
|
87455
|
-
launchId: launch2.launchId
|
|
87456
|
-
}
|
|
87457
|
-
});
|
|
87458
|
-
return {
|
|
87459
|
-
launchId: launch2.launchId,
|
|
87460
|
-
mode: "link",
|
|
87461
|
-
agentId: "",
|
|
87462
|
-
sessionId: peerSessionId,
|
|
87463
|
-
peerSessionId,
|
|
87464
|
-
summary: launch2.summary,
|
|
87465
|
-
task,
|
|
87466
|
-
name: launch2.name,
|
|
87467
|
-
role: launch2.role,
|
|
87468
|
-
title: collaborationSessionTitle(launch2.name, launch2.role),
|
|
87469
|
-
config: config3,
|
|
87470
|
-
credential: credential2,
|
|
87471
|
-
grantId: credentialHash2,
|
|
87472
|
-
reused: false
|
|
87473
|
-
};
|
|
87474
|
-
}
|
|
87475
|
-
const credential = `s1sc_${randomBytes5(32).toString("base64url")}`;
|
|
87476
|
-
const credentialHash = hashCredential(credential);
|
|
87477
|
-
const config2 = {
|
|
87478
|
-
...launch2.config,
|
|
87479
|
-
name: launch2.name,
|
|
87480
|
-
role: launch2.role,
|
|
87481
|
-
summary: launch2.summary
|
|
87482
|
-
};
|
|
87483
|
-
insertChild.run(
|
|
87484
|
-
credentialHash,
|
|
87485
|
-
this.deps.secrets.encrypt(credential),
|
|
87486
|
-
credential.slice(-8),
|
|
87487
|
-
input.parentSessionId,
|
|
87488
|
-
launch2.agentId,
|
|
87489
|
-
launch2.task,
|
|
87490
|
-
JSON.stringify(config2),
|
|
87491
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
87492
|
-
mode
|
|
87493
|
-
);
|
|
87494
|
-
this.deps.events.append({
|
|
87495
|
-
aggregateType: "session",
|
|
87496
|
-
aggregateId: input.parentSessionId,
|
|
87497
|
-
eventType: "collaboration.grant_created",
|
|
87498
|
-
payload: {
|
|
87499
|
-
grantId: credentialHash,
|
|
87500
|
-
mode,
|
|
87501
|
-
agentId: launch2.agentId,
|
|
87502
|
-
launchId: launch2.launchId
|
|
87503
|
-
}
|
|
87504
|
-
});
|
|
87505
|
-
return {
|
|
87506
|
-
launchId: launch2.launchId,
|
|
87507
|
-
mode,
|
|
87508
|
-
agentId: launch2.agentId,
|
|
87509
|
-
summary: launch2.summary,
|
|
87510
|
-
task: launch2.task,
|
|
87511
|
-
name: launch2.name,
|
|
87512
|
-
role: launch2.role,
|
|
87513
|
-
title: collaborationSessionTitle(launch2.name, launch2.role),
|
|
87514
|
-
config: config2,
|
|
87515
|
-
credential,
|
|
87516
|
-
grantId: credentialHash,
|
|
87517
|
-
reused: false
|
|
87518
|
-
};
|
|
87519
|
-
})
|
|
87520
|
-
)();
|
|
87521
|
-
return { status: "approved", launches: results };
|
|
87522
|
-
}
|
|
87523
|
-
async start(input) {
|
|
87524
|
-
let grant = this.resolveGrant(input.credential, input.grantId);
|
|
87525
|
-
if (!grant) {
|
|
87526
|
-
throw Object.assign(new Error("Invalid collaboration credential"), { code: "not_found" });
|
|
87527
|
-
}
|
|
87528
|
-
const hasBearer = typeof input.credential === "string" && input.credential.trim().length > 0;
|
|
87529
|
-
if (!hasBearer) {
|
|
87530
|
-
if (!input.callerSessionId || input.callerSessionId !== grant.parent_session_id) {
|
|
87531
|
-
throw Object.assign(
|
|
87532
|
-
new Error("grantId start requires callerSessionId matching the parent session"),
|
|
87533
|
-
{ code: "forbidden" }
|
|
87534
|
-
);
|
|
87535
|
-
}
|
|
87536
|
-
} else if (input.callerSessionId && grant.parent_session_id !== input.callerSessionId) {
|
|
87537
|
-
throw Object.assign(new Error("Only the parent session may start this credential"), {
|
|
87538
|
-
code: "forbidden"
|
|
87539
|
-
});
|
|
87540
|
-
}
|
|
87541
|
-
const credential = input.credential ?? (grant.credential_secret ? this.deps.secrets.decrypt(grant.credential_secret) : "");
|
|
87542
|
-
if (!credential) {
|
|
87543
|
-
throw Object.assign(new Error("Invalid collaboration credential"), { code: "not_found" });
|
|
87544
|
-
}
|
|
87545
|
-
if (grant.kind !== "link" && input.formAnswers && typeof input.formAnswers === "object") {
|
|
87546
|
-
grant = this.applyFormAnswers(grant, input.formAnswers);
|
|
87547
|
-
}
|
|
87548
|
-
if (grant.kind === "link") {
|
|
87549
|
-
if (!grant.child_session_id) {
|
|
87550
|
-
throw Object.assign(new Error("Link grant is missing peer session id"), {
|
|
87551
|
-
code: "failed_precondition"
|
|
87552
|
-
});
|
|
87553
|
-
}
|
|
87554
|
-
const peerSessionId = grant.child_session_id;
|
|
87555
|
-
if (!this.deps.sessions.get(peerSessionId)) {
|
|
87556
|
-
throw Object.assign(new Error(`Peer session no longer exists: ${peerSessionId}`), {
|
|
87557
|
-
code: "not_found"
|
|
87558
|
-
});
|
|
87559
|
-
}
|
|
87560
|
-
const alreadyStarted = Boolean(grant.started_at);
|
|
87561
|
-
if (!alreadyStarted) {
|
|
87562
|
-
this.deps.db.prepare(
|
|
87563
|
-
`UPDATE session_collaboration_grants SET started_at = ?
|
|
87564
|
-
WHERE credential_hash = ? AND started_at IS NULL`
|
|
87565
|
-
).run((/* @__PURE__ */ new Date()).toISOString(), grant.credential_hash);
|
|
87566
|
-
}
|
|
87567
|
-
const opening = grant.task?.trim() ?? "";
|
|
87568
|
-
const initiator = this.deps.sessions.get(grant.parent_session_id);
|
|
87569
|
-
const initiatorTitle = initiator?.title?.trim() || grant.parent_session_id.slice(0, 8);
|
|
87570
|
-
if (!alreadyStarted && opening) {
|
|
87571
|
-
await this.deliverLinkOpening(grant, credential, initiatorTitle);
|
|
87572
|
-
} else {
|
|
87573
|
-
void this.wakeLinkPeer(peerSessionId, credential, grant.parent_session_id, initiatorTitle, false);
|
|
87574
|
-
if (!alreadyStarted) {
|
|
87575
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
|
|
87576
|
-
}
|
|
87577
|
-
}
|
|
87578
|
-
const peer = this.describePeer(grant);
|
|
87579
|
-
return {
|
|
87580
|
-
status: "linked",
|
|
87581
|
-
mode: "link",
|
|
87582
|
-
sessionId: peerSessionId,
|
|
87583
|
-
peerSessionId,
|
|
87584
|
-
reused: alreadyStarted,
|
|
87585
|
-
name: peer.name,
|
|
87586
|
-
role: peer.role,
|
|
87587
|
-
title: peer.title,
|
|
87588
|
-
config: peer.config,
|
|
87589
|
-
credential,
|
|
87590
|
-
grantId: grant.credential_hash
|
|
87591
|
-
};
|
|
87592
|
-
}
|
|
87593
|
-
const isHandoff = grant.kind === "handoff";
|
|
87594
|
-
const existingHandoffSessionId = isHandoff ? parseConfig(grant.config_json).handoffSessionId : void 0;
|
|
87595
|
-
if (existingHandoffSessionId) {
|
|
87596
|
-
if (!this.deps.sessions.get(existingHandoffSessionId)) {
|
|
87597
|
-
throw Object.assign(
|
|
87598
|
-
new Error(`The handoff session no longer exists: ${existingHandoffSessionId}`),
|
|
87599
|
-
{ code: "not_found" }
|
|
87600
|
-
);
|
|
87601
|
-
}
|
|
87602
|
-
await this.deliverInitialTask(grant, existingHandoffSessionId);
|
|
87603
|
-
const peer = this.describePeer(grant);
|
|
87604
|
-
return {
|
|
87605
|
-
status: "started",
|
|
87606
|
-
mode: "handoff",
|
|
87607
|
-
sessionId: existingHandoffSessionId,
|
|
87608
|
-
reused: true,
|
|
87609
|
-
name: peer.name,
|
|
87610
|
-
role: peer.role,
|
|
87611
|
-
title: peer.title,
|
|
87612
|
-
config: peer.config,
|
|
87613
|
-
credential,
|
|
87614
|
-
grantId: grant.credential_hash
|
|
87615
|
-
};
|
|
87616
|
-
}
|
|
87617
|
-
if (grant.child_session_id) {
|
|
87618
|
-
const existing = this.deps.sessions.get(grant.child_session_id);
|
|
87619
|
-
if (existing) {
|
|
87620
|
-
this.deps.sessions.setSystemPromptAppend(
|
|
87621
|
-
existing.sessionId,
|
|
87622
|
-
collaborationSystemPrompt(credential, grant.parent_session_id)
|
|
87623
|
-
);
|
|
87624
|
-
await this.deliverInitialTask(grant, existing.sessionId);
|
|
87625
|
-
}
|
|
87626
|
-
const peer = this.describePeer(grant);
|
|
87627
|
-
return {
|
|
87628
|
-
status: "started",
|
|
87629
|
-
mode: "spawn",
|
|
87630
|
-
sessionId: grant.child_session_id,
|
|
87631
|
-
reused: true,
|
|
87632
|
-
name: peer.name,
|
|
87633
|
-
role: peer.role,
|
|
87634
|
-
title: peer.title,
|
|
87635
|
-
config: peer.config,
|
|
87636
|
-
credential,
|
|
87637
|
-
grantId: grant.credential_hash
|
|
87638
|
-
};
|
|
87639
|
-
}
|
|
87640
|
-
const parent = this.deps.sessions.get(grant.parent_session_id);
|
|
87641
|
-
if (!parent) {
|
|
87642
|
-
throw Object.assign(new Error("Parent session is not available"), { code: "not_found" });
|
|
87643
|
-
}
|
|
87644
|
-
const config2 = parseConfig(grant.config_json);
|
|
87645
|
-
let cwd = this.resolveCwd(config2, parent.projectId, parent.cwd);
|
|
87646
|
-
if (config2.worktreePath && typeof config2.worktreePath === "string" && config2.worktreePath.trim()) {
|
|
87647
|
-
cwd = pathResolve2(config2.worktreePath.trim());
|
|
87648
|
-
} else if (config2.worktree?.enabled) {
|
|
87649
|
-
const wt2 = this.deps.workspaceGit.activateWorktree(parent.projectId, {
|
|
87650
|
-
baseBranch: config2.worktree.baseBranch || "HEAD",
|
|
87651
|
-
mode: config2.worktree.mode ?? "branch",
|
|
87652
|
-
branchName: config2.worktree.branchName,
|
|
87653
|
-
carryLocalChanges: config2.worktree.carryLocalChanges
|
|
87654
|
-
});
|
|
87655
|
-
cwd = wt2.path;
|
|
87656
|
-
}
|
|
87657
|
-
const profile = resolveProfile(
|
|
87658
|
-
new Map(this.listProfiles().map((p3) => [p3.id, p3])),
|
|
87659
|
-
grant.agent_id
|
|
87660
|
-
);
|
|
87661
|
-
const harnessId = profile?.harnessId ?? normalizeSessionHarnessId(grant.agent_id) ?? "claude";
|
|
87662
|
-
const displayName = deriveCollaborationName({ name: config2.name });
|
|
87663
|
-
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
87664
|
-
const title = collaborationSessionTitle(displayName, role);
|
|
87665
|
-
let child;
|
|
87666
|
-
try {
|
|
87667
|
-
child = this.deps.sessions.create({
|
|
87668
|
-
projectId: parent.projectId,
|
|
87669
|
-
harnessId,
|
|
87670
|
-
providerId: grant.agent_id,
|
|
87671
|
-
title,
|
|
87672
|
-
cwd,
|
|
87673
|
-
model: config2.model ?? null,
|
|
87674
|
-
effort: config2.effort ?? null,
|
|
87675
|
-
permissionMode: config2.permissionMode ?? null,
|
|
87676
|
-
sandboxMode: config2.sandboxMode ?? null,
|
|
87677
|
-
apiProviderId: config2.apiProviderId ?? null,
|
|
87678
|
-
controllerClientSessionId: input.controllerClientSessionId ?? parent.controllerClientSessionId,
|
|
87679
|
-
// Handoff is one-way by construction: never hand the receiver a credential.
|
|
87680
|
-
...isHandoff ? {} : { systemPromptAppend: collaborationSystemPrompt(credential, grant.parent_session_id) }
|
|
87681
|
-
});
|
|
87682
|
-
const updated = isHandoff ? this.deps.db.prepare(
|
|
87683
|
-
`UPDATE session_collaboration_grants
|
|
87684
|
-
SET config_json = ?, started_at = ?
|
|
87685
|
-
WHERE credential_hash = ? AND started_at IS NULL`
|
|
87686
|
-
).run(
|
|
87687
|
-
JSON.stringify({ ...config2, handoffSessionId: child.sessionId }),
|
|
87688
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
87689
|
-
grant.credential_hash
|
|
87690
|
-
) : this.deps.db.prepare(
|
|
87691
|
-
`UPDATE session_collaboration_grants
|
|
87692
|
-
SET child_session_id = ?, started_at = ?
|
|
87693
|
-
WHERE credential_hash = ? AND child_session_id IS NULL`
|
|
87694
|
-
).run(child.sessionId, (/* @__PURE__ */ new Date()).toISOString(), grant.credential_hash);
|
|
87695
|
-
if (updated.changes !== 1) {
|
|
87696
|
-
throw Object.assign(new Error("Credential was already consumed"), {
|
|
87697
|
-
code: "failed_precondition"
|
|
87698
|
-
});
|
|
87699
|
-
}
|
|
87700
|
-
} catch (err) {
|
|
87701
|
-
if (child?.sessionId) {
|
|
87702
|
-
try {
|
|
87703
|
-
this.deps.sessions.remove(child.sessionId);
|
|
87704
|
-
} catch {
|
|
87705
|
-
}
|
|
87706
|
-
}
|
|
87707
|
-
throw err;
|
|
87708
|
-
}
|
|
87709
|
-
grant = isHandoff ? grant : { ...grant, child_session_id: child.sessionId };
|
|
87710
|
-
await this.deliverInitialTask(grant, child.sessionId);
|
|
87711
|
-
this.deps.events.append({
|
|
87712
|
-
aggregateType: "session",
|
|
87713
|
-
aggregateId: grant.parent_session_id,
|
|
87714
|
-
eventType: isHandoff ? "collaboration.handoff_started" : "collaboration.child_started",
|
|
87715
|
-
payload: {
|
|
87716
|
-
grantId: grant.credential_hash,
|
|
87717
|
-
childSessionId: child.sessionId
|
|
87718
|
-
}
|
|
87427
|
+
const linkConfig = {
|
|
87428
|
+
launchId,
|
|
87429
|
+
name,
|
|
87430
|
+
role,
|
|
87431
|
+
summary: summary2,
|
|
87432
|
+
peerSessionId,
|
|
87433
|
+
peerTitle: launch2.peerTitle,
|
|
87434
|
+
peerProjectPath: launch2.peerProjectPath
|
|
87435
|
+
};
|
|
87436
|
+
const grantId2 = store.createGrant({
|
|
87437
|
+
kind: "link",
|
|
87438
|
+
parentSessionId,
|
|
87439
|
+
childSessionId: peerSessionId,
|
|
87440
|
+
agentId: "",
|
|
87441
|
+
config: linkConfig
|
|
87719
87442
|
});
|
|
87720
87443
|
return {
|
|
87721
|
-
|
|
87722
|
-
|
|
87723
|
-
|
|
87724
|
-
|
|
87725
|
-
|
|
87726
|
-
|
|
87727
|
-
|
|
87728
|
-
|
|
87729
|
-
model: config2.model,
|
|
87730
|
-
effort: config2.effort,
|
|
87731
|
-
fastMode: config2.fastMode,
|
|
87732
|
-
permissionMode: config2.permissionMode,
|
|
87733
|
-
sandboxMode: config2.sandboxMode,
|
|
87734
|
-
cwd,
|
|
87735
|
-
apiProviderId: config2.apiProviderId ?? null,
|
|
87736
|
-
name: displayName,
|
|
87444
|
+
grantId: grantId2,
|
|
87445
|
+
approved: {
|
|
87446
|
+
launchId,
|
|
87447
|
+
mode,
|
|
87448
|
+
agentId: "",
|
|
87449
|
+
sessionId: peerSessionId,
|
|
87450
|
+
summary: summary2,
|
|
87451
|
+
name,
|
|
87737
87452
|
role,
|
|
87738
|
-
|
|
87739
|
-
|
|
87740
|
-
|
|
87741
|
-
credential,
|
|
87742
|
-
grantId: grant.credential_hash
|
|
87743
|
-
};
|
|
87744
|
-
}
|
|
87745
|
-
send(input) {
|
|
87746
|
-
const grant = this.grantForCredential(input.credential);
|
|
87747
|
-
if (!grant) {
|
|
87748
|
-
throw Object.assign(new Error("Invalid collaboration credential"), { code: "not_found" });
|
|
87749
|
-
}
|
|
87750
|
-
this.assertEndpoint(grant, input.sessionId);
|
|
87751
|
-
if (!grant.child_session_id) {
|
|
87752
|
-
throw Object.assign(
|
|
87753
|
-
new Error(
|
|
87754
|
-
grant.kind === "link" ? "The linked peer session is missing" : "The child session has not been started"
|
|
87755
|
-
),
|
|
87756
|
-
{ code: "failed_precondition" }
|
|
87757
|
-
);
|
|
87758
|
-
}
|
|
87759
|
-
if (grant.kind === "link" && !grant.started_at) {
|
|
87760
|
-
throw Object.assign(new Error("The collaboration link has not been started"), {
|
|
87761
|
-
code: "failed_precondition"
|
|
87762
|
-
});
|
|
87763
|
-
}
|
|
87764
|
-
const recipientSessionId = input.sessionId === grant.parent_session_id ? grant.child_session_id : grant.parent_session_id;
|
|
87765
|
-
const content = input.content?.trim();
|
|
87766
|
-
if (!content) {
|
|
87767
|
-
throw Object.assign(new Error("content must not be empty"), { code: "invalid_argument" });
|
|
87768
|
-
}
|
|
87769
|
-
if (content.length > 1e5) {
|
|
87770
|
-
throw Object.assign(new Error("content may contain at most 100,000 characters"), {
|
|
87771
|
-
code: "invalid_argument"
|
|
87772
|
-
});
|
|
87773
|
-
}
|
|
87774
|
-
const insert = this.deps.db.transaction(() => {
|
|
87775
|
-
if (input.clientMessageId) {
|
|
87776
|
-
const existing = this.deps.db.prepare(
|
|
87777
|
-
`SELECT * FROM session_collaboration_messages
|
|
87778
|
-
WHERE credential_hash = ? AND sender_session_id = ? AND client_message_id = ?`
|
|
87779
|
-
).get(grant.credential_hash, input.sessionId, input.clientMessageId);
|
|
87780
|
-
if (existing) return { row: existing, reused: true };
|
|
87453
|
+
title,
|
|
87454
|
+
config: linkConfig,
|
|
87455
|
+
reused: false
|
|
87781
87456
|
}
|
|
87782
|
-
const sequence = this.nextSequence(grant.credential_hash);
|
|
87783
|
-
const row = {
|
|
87784
|
-
id: randomUUID11(),
|
|
87785
|
-
credential_hash: grant.credential_hash,
|
|
87786
|
-
sequence,
|
|
87787
|
-
sender_session_id: input.sessionId,
|
|
87788
|
-
recipient_session_id: recipientSessionId,
|
|
87789
|
-
client_message_id: input.clientMessageId ?? null,
|
|
87790
|
-
content,
|
|
87791
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
87792
|
-
delivered_at: null
|
|
87793
|
-
};
|
|
87794
|
-
this.deps.db.prepare(
|
|
87795
|
-
`INSERT INTO session_collaboration_messages
|
|
87796
|
-
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
87797
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
87798
|
-
).run(
|
|
87799
|
-
row.id,
|
|
87800
|
-
row.credential_hash,
|
|
87801
|
-
row.sequence,
|
|
87802
|
-
row.sender_session_id,
|
|
87803
|
-
row.recipient_session_id,
|
|
87804
|
-
row.client_message_id,
|
|
87805
|
-
row.content,
|
|
87806
|
-
row.created_at
|
|
87807
|
-
);
|
|
87808
|
-
return { row, reused: false };
|
|
87809
|
-
})();
|
|
87810
|
-
if (!insert.reused) {
|
|
87811
|
-
this.deps.events.append({
|
|
87812
|
-
aggregateType: "session",
|
|
87813
|
-
aggregateId: input.sessionId,
|
|
87814
|
-
eventType: "collaboration.message",
|
|
87815
|
-
payload: {
|
|
87816
|
-
messageId: insert.row.id,
|
|
87817
|
-
grantId: grant.credential_hash,
|
|
87818
|
-
toSessionId: recipientSessionId,
|
|
87819
|
-
sequence: insert.row.sequence
|
|
87820
|
-
}
|
|
87821
|
-
});
|
|
87822
|
-
void this.wakePeer(recipientSessionId, input.credential);
|
|
87823
|
-
}
|
|
87824
|
-
return {
|
|
87825
|
-
status: "sent",
|
|
87826
|
-
messageId: insert.row.id,
|
|
87827
|
-
sequence: insert.row.sequence,
|
|
87828
|
-
reused: insert.reused,
|
|
87829
|
-
peerSessionId: recipientSessionId
|
|
87830
87457
|
};
|
|
87831
87458
|
}
|
|
87832
|
-
|
|
87833
|
-
|
|
87834
|
-
|
|
87835
|
-
|
|
87836
|
-
|
|
87837
|
-
|
|
87838
|
-
|
|
87459
|
+
const grantConfig = { ...config2, launchId, name, role, summary: summary2 };
|
|
87460
|
+
const grantId = store.createGrant({ kind: mode, parentSessionId, agentId: launch2.agentId, config: grantConfig });
|
|
87461
|
+
return {
|
|
87462
|
+
grantId,
|
|
87463
|
+
approved: {
|
|
87464
|
+
launchId,
|
|
87465
|
+
mode,
|
|
87466
|
+
agentId: launch2.agentId,
|
|
87467
|
+
summary: summary2,
|
|
87468
|
+
name,
|
|
87469
|
+
role,
|
|
87470
|
+
title,
|
|
87471
|
+
config: grantConfig,
|
|
87472
|
+
reused: false
|
|
87839
87473
|
}
|
|
87840
|
-
|
|
87841
|
-
|
|
87842
|
-
|
|
87474
|
+
};
|
|
87475
|
+
}
|
|
87476
|
+
function prepareLaunchStart(store, callerSessionId, input) {
|
|
87477
|
+
const launchId = typeof input.launchId === "string" ? input.launchId.trim() : "";
|
|
87478
|
+
if (!launchId) throw new CollaborationError("launchId is required", "invalid_argument");
|
|
87479
|
+
const grant = store.grantForLaunch(callerSessionId, launchId);
|
|
87480
|
+
if (!grant) {
|
|
87481
|
+
throw new CollaborationError(
|
|
87482
|
+
`No approved launch "${launchId}" in this session. Pass a launchId returned by session_collab_request.`,
|
|
87483
|
+
"not_found"
|
|
87843
87484
|
);
|
|
87844
|
-
const all = [];
|
|
87845
|
-
for (const credential of credentials) {
|
|
87846
|
-
const grant = this.grantForCredential(credential);
|
|
87847
|
-
if (!grant) {
|
|
87848
|
-
throw Object.assign(new Error("Invalid collaboration credential"), { code: "not_found" });
|
|
87849
|
-
}
|
|
87850
|
-
this.assertEndpoint(grant, input.sessionId);
|
|
87851
|
-
const messages = this.deps.db.transaction(() => {
|
|
87852
|
-
const cursor = this.deps.db.prepare(
|
|
87853
|
-
`SELECT last_sequence FROM session_collaboration_cursors
|
|
87854
|
-
WHERE credential_hash = ? AND session_id = ?`
|
|
87855
|
-
).get(grant.credential_hash, input.sessionId);
|
|
87856
|
-
const rows = this.deps.db.prepare(
|
|
87857
|
-
`SELECT * FROM session_collaboration_messages
|
|
87858
|
-
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence > ?
|
|
87859
|
-
ORDER BY sequence LIMIT ?`
|
|
87860
|
-
).all(
|
|
87861
|
-
grant.credential_hash,
|
|
87862
|
-
input.sessionId,
|
|
87863
|
-
cursor?.last_sequence ?? 0,
|
|
87864
|
-
max
|
|
87865
|
-
);
|
|
87866
|
-
if (rows.length === 0) return [];
|
|
87867
|
-
const lastSequence = rows[rows.length - 1].sequence;
|
|
87868
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
87869
|
-
this.deps.db.prepare(
|
|
87870
|
-
`INSERT INTO session_collaboration_cursors (credential_hash, session_id, last_sequence)
|
|
87871
|
-
VALUES (?, ?, ?)
|
|
87872
|
-
ON CONFLICT(credential_hash, session_id) DO UPDATE SET last_sequence = excluded.last_sequence`
|
|
87873
|
-
).run(grant.credential_hash, input.sessionId, lastSequence);
|
|
87874
|
-
this.deps.db.prepare(
|
|
87875
|
-
`UPDATE session_collaboration_messages SET delivered_at = COALESCE(delivered_at, ?)
|
|
87876
|
-
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence <= ?`
|
|
87877
|
-
).run(now, grant.credential_hash, input.sessionId, lastSequence);
|
|
87878
|
-
return rows;
|
|
87879
|
-
})();
|
|
87880
|
-
for (const row of messages) {
|
|
87881
|
-
all.push({
|
|
87882
|
-
messageId: row.id,
|
|
87883
|
-
sequence: row.sequence,
|
|
87884
|
-
fromSessionId: row.sender_session_id,
|
|
87885
|
-
content: row.content,
|
|
87886
|
-
createdAt: row.created_at,
|
|
87887
|
-
...credentials.length > 1 ? { credential } : {}
|
|
87888
|
-
});
|
|
87889
|
-
}
|
|
87890
|
-
}
|
|
87891
|
-
if (all.length === 0) {
|
|
87892
|
-
return { status: "empty", messages: [], hint: EMPTY_MAILBOX_HINT };
|
|
87893
|
-
}
|
|
87894
|
-
return { status: "messages", messages: all };
|
|
87895
87485
|
}
|
|
87896
|
-
|
|
87897
|
-
|
|
87898
|
-
|
|
87899
|
-
|
|
87900
|
-
|
|
87901
|
-
|
|
87902
|
-
|
|
87903
|
-
|
|
87904
|
-
|
|
87905
|
-
|
|
87906
|
-
|
|
87907
|
-
|
|
87908
|
-
|
|
87909
|
-
|
|
87910
|
-
|
|
87911
|
-
|
|
87912
|
-
|
|
87913
|
-
|
|
87486
|
+
if (grant.task_sent === 1 || grant.task) return grant;
|
|
87487
|
+
const task = normalizeStartTask(grant.kind, input.task);
|
|
87488
|
+
if (!task) return grant;
|
|
87489
|
+
store.setTask(grant.grant_id, task);
|
|
87490
|
+
return { ...grant, task };
|
|
87491
|
+
}
|
|
87492
|
+
|
|
87493
|
+
// ../../packages/runtime/src/collaboration/store.ts
|
|
87494
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
87495
|
+
var GRANT_COLUMNS = `credential_hash AS grant_id, parent_session_id, child_session_id,
|
|
87496
|
+
agent_id, task, config_json, task_sent, COALESCE(kind, 'spawn') AS kind, started_at`;
|
|
87497
|
+
var MESSAGE_COLUMNS = `id, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at`;
|
|
87498
|
+
function nowIso() {
|
|
87499
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
87500
|
+
}
|
|
87501
|
+
var CollaborationStore = class {
|
|
87502
|
+
constructor(db) {
|
|
87503
|
+
this.db = db;
|
|
87914
87504
|
}
|
|
87915
|
-
|
|
87916
|
-
|
|
87917
|
-
|
|
87918
|
-
if (grantId && grantId.trim()) {
|
|
87919
|
-
return this.deps.db.prepare(
|
|
87920
|
-
`SELECT credential_hash, credential_secret, parent_session_id, child_session_id,
|
|
87921
|
-
agent_id, task, config_json, task_sent,
|
|
87922
|
-
COALESCE(kind, 'spawn') AS kind, started_at
|
|
87923
|
-
FROM session_collaboration_grants WHERE credential_hash = ?`
|
|
87924
|
-
).get(grantId.trim()) ?? null;
|
|
87925
|
-
}
|
|
87926
|
-
return null;
|
|
87505
|
+
db;
|
|
87506
|
+
transaction(fn) {
|
|
87507
|
+
return this.db.transaction(fn)();
|
|
87927
87508
|
}
|
|
87928
|
-
|
|
87929
|
-
|
|
87930
|
-
|
|
87931
|
-
|
|
87932
|
-
|
|
87933
|
-
|
|
87934
|
-
|
|
87935
|
-
|
|
87936
|
-
|
|
87937
|
-
|
|
87938
|
-
|
|
87939
|
-
|
|
87940
|
-
|
|
87941
|
-
|
|
87942
|
-
|
|
87943
|
-
|
|
87944
|
-
|
|
87945
|
-
|
|
87946
|
-
|
|
87947
|
-
|
|
87948
|
-
|
|
87949
|
-
|
|
87950
|
-
|
|
87951
|
-
|
|
87952
|
-
|
|
87953
|
-
|
|
87954
|
-
|
|
87955
|
-
|
|
87956
|
-
|
|
87957
|
-
|
|
87958
|
-
|
|
87959
|
-
|
|
87960
|
-
|
|
87961
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
|
|
87962
|
-
void this.wakeLinkPeer(
|
|
87963
|
-
recipientSessionId,
|
|
87964
|
-
credential,
|
|
87965
|
-
grant.parent_session_id,
|
|
87966
|
-
initiatorTitle,
|
|
87967
|
-
true
|
|
87968
|
-
);
|
|
87509
|
+
// --- grants --------------------------------------------------------------
|
|
87510
|
+
grantById(grantId) {
|
|
87511
|
+
return this.db.prepare(`SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants WHERE credential_hash = ?`).get(grantId) ?? null;
|
|
87512
|
+
}
|
|
87513
|
+
/**
|
|
87514
|
+
* The grant an approved launch created. launchIds are agent-chosen and may
|
|
87515
|
+
* repeat across requests, so the latest approval wins.
|
|
87516
|
+
*/
|
|
87517
|
+
grantForLaunch(parentSessionId, launchId) {
|
|
87518
|
+
return this.db.prepare(`
|
|
87519
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87520
|
+
WHERE parent_session_id = ? AND json_extract(config_json, '$.launchId') = ?
|
|
87521
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
87522
|
+
`).get(parentSessionId, launchId) ?? null;
|
|
87523
|
+
}
|
|
87524
|
+
/** True when `sessionId` is a spawn child (nested collaboration is unsupported). */
|
|
87525
|
+
isSpawnChild(sessionId) {
|
|
87526
|
+
return Boolean(this.db.prepare(`
|
|
87527
|
+
SELECT 1 FROM session_collaboration_grants
|
|
87528
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn' LIMIT 1
|
|
87529
|
+
`).get(sessionId));
|
|
87530
|
+
}
|
|
87531
|
+
spawnGrantForChild(childSessionId) {
|
|
87532
|
+
return this.db.prepare(`
|
|
87533
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87534
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn'
|
|
87535
|
+
`).get(childSessionId) ?? null;
|
|
87536
|
+
}
|
|
87537
|
+
startedSpawnGrants() {
|
|
87538
|
+
return this.db.prepare(`
|
|
87539
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87540
|
+
WHERE child_session_id IS NOT NULL AND COALESCE(kind, 'spawn') = 'spawn'
|
|
87541
|
+
`).all();
|
|
87969
87542
|
}
|
|
87970
|
-
|
|
87971
|
-
|
|
87972
|
-
|
|
87543
|
+
/**
|
|
87544
|
+
* Open mailbox channels of `sessionId`: spawn grants with a bound child and
|
|
87545
|
+
* started link grants, oldest first. Handoff grants are never channels.
|
|
87546
|
+
*/
|
|
87547
|
+
channelsFor(sessionId) {
|
|
87548
|
+
return this.db.prepare(`
|
|
87549
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87550
|
+
WHERE (parent_session_id = ? OR child_session_id = ?)
|
|
87551
|
+
AND child_session_id IS NOT NULL
|
|
87552
|
+
AND (COALESCE(kind, 'spawn') = 'spawn' OR (kind = 'link' AND started_at IS NOT NULL))
|
|
87553
|
+
ORDER BY created_at, rowid
|
|
87554
|
+
`).all(sessionId, sessionId);
|
|
87555
|
+
}
|
|
87556
|
+
/** True when one of the two sessions was created by a handoff from the other. */
|
|
87557
|
+
isHandoffPair(a, b3) {
|
|
87558
|
+
return Boolean(this.db.prepare(`
|
|
87559
|
+
SELECT 1 FROM session_collaboration_grants
|
|
87560
|
+
WHERE kind = 'handoff' AND (
|
|
87561
|
+
(parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
87562
|
+
OR (parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
87563
|
+
) LIMIT 1
|
|
87564
|
+
`).get(a, b3, b3, a));
|
|
87565
|
+
}
|
|
87566
|
+
findLinkGrant(initiatorSessionId, peerSessionId) {
|
|
87567
|
+
return this.db.prepare(`
|
|
87568
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87569
|
+
WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'
|
|
87570
|
+
`).get(initiatorSessionId, peerSessionId) ?? null;
|
|
87571
|
+
}
|
|
87572
|
+
/**
|
|
87573
|
+
* Persist an approved launch. Link grants bind the peer at once; spawn and
|
|
87574
|
+
* handoff grants bind their session on start. The brief arrives on start.
|
|
87575
|
+
*/
|
|
87576
|
+
createGrant(input) {
|
|
87577
|
+
const grantId = randomUUID11();
|
|
87973
87578
|
try {
|
|
87974
|
-
|
|
87975
|
-
|
|
87976
|
-
|
|
87977
|
-
|
|
87978
|
-
|
|
87979
|
-
|
|
87980
|
-
|
|
87579
|
+
this.db.prepare(`
|
|
87580
|
+
INSERT INTO session_collaboration_grants
|
|
87581
|
+
(credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id,
|
|
87582
|
+
agent_id, task, config_json, created_at, kind)
|
|
87583
|
+
VALUES (?, NULL, '', ?, ?, ?, '', ?, ?, ?)
|
|
87584
|
+
`).run(
|
|
87585
|
+
grantId,
|
|
87586
|
+
input.parentSessionId,
|
|
87587
|
+
input.childSessionId ?? null,
|
|
87588
|
+
input.agentId,
|
|
87589
|
+
JSON.stringify(input.config),
|
|
87590
|
+
nowIso(),
|
|
87591
|
+
input.kind
|
|
87592
|
+
);
|
|
87593
|
+
} catch (error51) {
|
|
87594
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
87595
|
+
if (input.childSessionId && /UNIQUE|unique/i.test(message)) {
|
|
87596
|
+
throw new CollaborationError(
|
|
87597
|
+
`Session ${input.parentSessionId} already has a link to ${input.childSessionId}; request it again to reuse it.`,
|
|
87598
|
+
"failed_precondition"
|
|
87599
|
+
);
|
|
87600
|
+
}
|
|
87601
|
+
throw error51;
|
|
87981
87602
|
}
|
|
87603
|
+
return grantId;
|
|
87982
87604
|
}
|
|
87983
|
-
|
|
87984
|
-
|
|
87985
|
-
throw Object.assign(new Error(HANDOFF_NO_MAILBOX), { code: "failed_precondition" });
|
|
87986
|
-
}
|
|
87987
|
-
if (callerSessionId !== grant.parent_session_id && callerSessionId !== grant.child_session_id) {
|
|
87988
|
-
throw Object.assign(new Error("This credential does not authorize the current session"), {
|
|
87989
|
-
code: "forbidden"
|
|
87990
|
-
});
|
|
87991
|
-
}
|
|
87605
|
+
updateConfig(grantId, config2) {
|
|
87606
|
+
this.db.prepare("UPDATE session_collaboration_grants SET config_json = ? WHERE credential_hash = ?").run(JSON.stringify(config2), grantId);
|
|
87992
87607
|
}
|
|
87993
|
-
|
|
87994
|
-
|
|
87995
|
-
|
|
87996
|
-
FROM session_collaboration_messages WHERE credential_hash = ?`
|
|
87997
|
-
).get(credentialHash);
|
|
87998
|
-
return row.next_sequence;
|
|
87608
|
+
/** Record the brief of a grant whose task has not been delivered yet. */
|
|
87609
|
+
setTask(grantId, task) {
|
|
87610
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task = ? WHERE credential_hash = ? AND task_sent = 0").run(task, grantId);
|
|
87999
87611
|
}
|
|
88000
|
-
|
|
88001
|
-
|
|
88002
|
-
const name = deriveCollaborationName({ name: config2.name });
|
|
88003
|
-
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
88004
|
-
return {
|
|
88005
|
-
name,
|
|
88006
|
-
role,
|
|
88007
|
-
title: collaborationSessionTitle(name, role),
|
|
88008
|
-
config: config2
|
|
88009
|
-
};
|
|
87612
|
+
markTaskSent(grantId) {
|
|
87613
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?").run(grantId);
|
|
88010
87614
|
}
|
|
88011
|
-
|
|
88012
|
-
|
|
88013
|
-
|
|
88014
|
-
|
|
88015
|
-
|
|
88016
|
-
throw Object.assign(new Error(`Working directory does not exist: ${cwd}`), {
|
|
88017
|
-
code: "invalid_argument"
|
|
88018
|
-
});
|
|
88019
|
-
}
|
|
88020
|
-
return cwd;
|
|
88021
|
-
}
|
|
88022
|
-
applyFormAnswers(grant, formAnswers) {
|
|
88023
|
-
const packed = formAnswers[SESSION_AGENT_LAUNCHES_FIELD] ?? formAnswers.sessionAgentLaunchesJson;
|
|
88024
|
-
if (typeof packed !== "string") return grant;
|
|
88025
|
-
let edited;
|
|
88026
|
-
try {
|
|
88027
|
-
edited = JSON.parse(packed);
|
|
88028
|
-
} catch {
|
|
88029
|
-
return grant;
|
|
88030
|
-
}
|
|
88031
|
-
if (!Array.isArray(edited) || edited.length === 0) return grant;
|
|
88032
|
-
const item = edited[0];
|
|
88033
|
-
if (!item?.config || typeof item.config !== "object") return grant;
|
|
88034
|
-
const base = parseConfig(grant.config_json);
|
|
88035
|
-
const patch = item.config;
|
|
88036
|
-
const next = {
|
|
88037
|
-
...base,
|
|
88038
|
-
...typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {},
|
|
88039
|
-
...typeof patch.effort === "string" && patch.effort.trim() ? { effort: patch.effort.trim() } : {},
|
|
88040
|
-
...typeof patch.fastMode === "boolean" ? { fastMode: patch.fastMode } : {},
|
|
88041
|
-
...typeof patch.permissionMode === "string" ? { permissionMode: patch.permissionMode } : {},
|
|
88042
|
-
...typeof patch.sandboxMode === "string" ? { sandboxMode: patch.sandboxMode } : {},
|
|
88043
|
-
...patch.apiProviderId === null || typeof patch.apiProviderId === "string" ? { apiProviderId: patch.apiProviderId } : {}
|
|
88044
|
-
};
|
|
88045
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET config_json = ? WHERE credential_hash = ?`).run(JSON.stringify(next), grant.credential_hash);
|
|
88046
|
-
return { ...grant, config_json: JSON.stringify(next) };
|
|
87615
|
+
/** Link start. Returns false when the grant had already been started. */
|
|
87616
|
+
markStarted(grantId) {
|
|
87617
|
+
return this.db.prepare(`
|
|
87618
|
+
UPDATE session_collaboration_grants SET started_at = ? WHERE credential_hash = ? AND started_at IS NULL
|
|
87619
|
+
`).run(nowIso(), grantId).changes === 1;
|
|
88047
87620
|
}
|
|
88048
87621
|
/**
|
|
88049
|
-
*
|
|
88050
|
-
*
|
|
87622
|
+
* Record the session a spawn/handoff start created. Throws when a concurrent
|
|
87623
|
+
* start already consumed the grant.
|
|
87624
|
+
*
|
|
87625
|
+
* A handoff session is deliberately *not* written to child_session_id: that
|
|
87626
|
+
* column holds channel endpoints (spawn children, link peers), and a handoff
|
|
87627
|
+
* sibling is neither. The created id lives in config_json instead.
|
|
88051
87628
|
*/
|
|
88052
|
-
|
|
88053
|
-
const
|
|
88054
|
-
|
|
88055
|
-
|
|
88056
|
-
|
|
88057
|
-
|
|
88058
|
-
|
|
88059
|
-
|
|
88060
|
-
|
|
88061
|
-
|
|
88062
|
-
|
|
88063
|
-
|
|
88064
|
-
|
|
88065
|
-
|
|
88066
|
-
|
|
88067
|
-
|
|
88068
|
-
|
|
88069
|
-
|
|
88070
|
-
|
|
88071
|
-
|
|
88072
|
-
)
|
|
88073
|
-
|
|
88074
|
-
|
|
88075
|
-
|
|
88076
|
-
|
|
88077
|
-
|
|
88078
|
-
|
|
88079
|
-
|
|
88080
|
-
|
|
88081
|
-
|
|
88082
|
-
|
|
88083
|
-
|
|
88084
|
-
|
|
88085
|
-
|
|
88086
|
-
|
|
88087
|
-
|
|
88088
|
-
|
|
88089
|
-
|
|
87629
|
+
bindStartedSession(grant, sessionId, config2) {
|
|
87630
|
+
const changes = grant.kind === "handoff" ? this.db.prepare(`
|
|
87631
|
+
UPDATE session_collaboration_grants SET config_json = ?, started_at = ?
|
|
87632
|
+
WHERE credential_hash = ? AND started_at IS NULL
|
|
87633
|
+
`).run(JSON.stringify({ ...config2, handoffSessionId: sessionId }), nowIso(), grant.grant_id).changes : this.db.prepare(`
|
|
87634
|
+
UPDATE session_collaboration_grants SET child_session_id = ?, started_at = ?
|
|
87635
|
+
WHERE credential_hash = ? AND child_session_id IS NULL
|
|
87636
|
+
`).run(sessionId, nowIso(), grant.grant_id).changes;
|
|
87637
|
+
if (changes !== 1) throw new CollaborationError("This launch was already started", "failed_precondition");
|
|
87638
|
+
}
|
|
87639
|
+
// --- mailbox -------------------------------------------------------------
|
|
87640
|
+
nextSequence(grantId) {
|
|
87641
|
+
const row = this.db.prepare(`
|
|
87642
|
+
SELECT COALESCE(MAX(sequence), 0) + 1 AS next_sequence
|
|
87643
|
+
FROM session_collaboration_messages WHERE credential_hash = ?
|
|
87644
|
+
`).get(grantId);
|
|
87645
|
+
return row.next_sequence;
|
|
87646
|
+
}
|
|
87647
|
+
insertMessage(grantId, input) {
|
|
87648
|
+
const row = {
|
|
87649
|
+
id: randomUUID11(),
|
|
87650
|
+
sequence: this.nextSequence(grantId),
|
|
87651
|
+
sender_session_id: input.senderSessionId,
|
|
87652
|
+
recipient_session_id: input.recipientSessionId,
|
|
87653
|
+
client_message_id: input.clientMessageId,
|
|
87654
|
+
content: input.content,
|
|
87655
|
+
created_at: nowIso()
|
|
87656
|
+
};
|
|
87657
|
+
this.db.prepare(`
|
|
87658
|
+
INSERT INTO session_collaboration_messages
|
|
87659
|
+
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
87660
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
87661
|
+
`).run(
|
|
87662
|
+
row.id,
|
|
87663
|
+
grantId,
|
|
87664
|
+
row.sequence,
|
|
87665
|
+
row.sender_session_id,
|
|
87666
|
+
row.recipient_session_id,
|
|
87667
|
+
row.client_message_id,
|
|
87668
|
+
row.content,
|
|
87669
|
+
row.created_at
|
|
87670
|
+
);
|
|
87671
|
+
return row;
|
|
87672
|
+
}
|
|
87673
|
+
/** Append one message; a repeated `clientMessageId` returns the stored row. */
|
|
87674
|
+
appendMessage(input) {
|
|
87675
|
+
return this.transaction(() => {
|
|
87676
|
+
if (input.clientMessageId) {
|
|
87677
|
+
const existing = this.db.prepare(`
|
|
87678
|
+
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
87679
|
+
WHERE credential_hash = ? AND sender_session_id = ? AND client_message_id = ?
|
|
87680
|
+
`).get(input.grantId, input.senderSessionId, input.clientMessageId);
|
|
87681
|
+
if (existing) return { row: existing, reused: true };
|
|
88090
87682
|
}
|
|
88091
|
-
|
|
88092
|
-
|
|
88093
|
-
|
|
87683
|
+
const row = this.insertMessage(input.grantId, { ...input, clientMessageId: input.clientMessageId ?? null });
|
|
87684
|
+
return { row, reused: false };
|
|
87685
|
+
});
|
|
87686
|
+
}
|
|
87687
|
+
/** Queue a link grant's opening body for the peer exactly once, then mark the task sent. */
|
|
87688
|
+
appendLinkOpening(grant, recipientSessionId, content) {
|
|
87689
|
+
this.transaction(() => {
|
|
87690
|
+
try {
|
|
87691
|
+
this.insertMessage(grant.grant_id, {
|
|
87692
|
+
senderSessionId: grant.parent_session_id,
|
|
87693
|
+
recipientSessionId,
|
|
87694
|
+
clientMessageId: `link-opening:${grant.grant_id}`,
|
|
87695
|
+
content
|
|
88094
87696
|
});
|
|
87697
|
+
} catch {
|
|
88095
87698
|
}
|
|
88096
|
-
|
|
88097
|
-
if (base.mode === "link") {
|
|
88098
|
-
return {
|
|
88099
|
-
...base,
|
|
88100
|
-
mode: "link",
|
|
88101
|
-
sessionId: base.sessionId,
|
|
88102
|
-
peerTitle: base.peerTitle,
|
|
88103
|
-
peerProjectPath: base.peerProjectPath,
|
|
88104
|
-
config: { ...base.config }
|
|
88105
|
-
};
|
|
88106
|
-
}
|
|
88107
|
-
const patch = item.config && typeof item.config === "object" ? item.config : {};
|
|
88108
|
-
return {
|
|
88109
|
-
...base,
|
|
88110
|
-
// Mode comes from the server-side proposal, never from renderer formAnswers.
|
|
88111
|
-
mode: base.mode ?? "spawn",
|
|
88112
|
-
config: {
|
|
88113
|
-
...base.config,
|
|
88114
|
-
...typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {},
|
|
88115
|
-
...typeof patch.effort === "string" && patch.effort.trim() ? { effort: patch.effort.trim() } : {},
|
|
88116
|
-
...typeof patch.fastMode === "boolean" ? { fastMode: patch.fastMode } : {},
|
|
88117
|
-
...typeof patch.permissionMode === "string" ? { permissionMode: patch.permissionMode } : {},
|
|
88118
|
-
...typeof patch.sandboxMode === "string" ? { sandboxMode: patch.sandboxMode } : {},
|
|
88119
|
-
...patch.apiProviderId === null || typeof patch.apiProviderId === "string" ? { apiProviderId: patch.apiProviderId } : {}
|
|
88120
|
-
}
|
|
88121
|
-
};
|
|
87699
|
+
this.markTaskSent(grant.grant_id);
|
|
88122
87700
|
});
|
|
88123
87701
|
}
|
|
88124
87702
|
/**
|
|
88125
|
-
*
|
|
88126
|
-
*
|
|
87703
|
+
* Drain unread messages addressed to `sessionId` across grants, advancing this
|
|
87704
|
+
* endpoint's cursor. Batches keep the order of `grantIds`.
|
|
88127
87705
|
*/
|
|
88128
|
-
|
|
88129
|
-
|
|
88130
|
-
|
|
88131
|
-
|
|
88132
|
-
|
|
87706
|
+
readMailbox(sessionId, grantIds, limitPerGrant) {
|
|
87707
|
+
return this.transaction(() => grantIds.flatMap((grantId) => {
|
|
87708
|
+
const cursor = this.db.prepare(`
|
|
87709
|
+
SELECT last_sequence FROM session_collaboration_cursors WHERE credential_hash = ? AND session_id = ?
|
|
87710
|
+
`).get(grantId, sessionId);
|
|
87711
|
+
const rows = this.db.prepare(`
|
|
87712
|
+
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
87713
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence > ?
|
|
87714
|
+
ORDER BY sequence LIMIT ?
|
|
87715
|
+
`).all(grantId, sessionId, cursor?.last_sequence ?? 0, limitPerGrant);
|
|
87716
|
+
if (rows.length === 0) return [];
|
|
87717
|
+
const lastSequence = rows[rows.length - 1].sequence;
|
|
87718
|
+
this.db.prepare(`
|
|
87719
|
+
INSERT INTO session_collaboration_cursors (credential_hash, session_id, last_sequence)
|
|
87720
|
+
VALUES (?, ?, ?)
|
|
87721
|
+
ON CONFLICT(credential_hash, session_id) DO UPDATE SET last_sequence = excluded.last_sequence
|
|
87722
|
+
`).run(grantId, sessionId, lastSequence);
|
|
87723
|
+
this.db.prepare(`
|
|
87724
|
+
UPDATE session_collaboration_messages SET delivered_at = COALESCE(delivered_at, ?)
|
|
87725
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence <= ?
|
|
87726
|
+
`).run(nowIso(), grantId, sessionId, lastSequence);
|
|
87727
|
+
return [{ grantId, rows }];
|
|
87728
|
+
}));
|
|
87729
|
+
}
|
|
87730
|
+
};
|
|
88133
87731
|
|
|
88134
|
-
|
|
87732
|
+
// src/session/collaboration-messaging.ts
|
|
87733
|
+
function sessionTitle(ctx) {
|
|
87734
|
+
return (sessionId) => ctx.deps.sessions.get(sessionId)?.title ?? null;
|
|
87735
|
+
}
|
|
87736
|
+
async function wakePeer(ctx, sessionId, fromSessionId) {
|
|
87737
|
+
if (!ctx.deps.sessions.get(sessionId)) return;
|
|
87738
|
+
const fromTitle = ctx.deps.sessions.get(fromSessionId)?.title?.trim() || fromSessionId.slice(0, 8);
|
|
87739
|
+
try {
|
|
87740
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
87741
|
+
sessionId,
|
|
87742
|
+
text: mailboxWakeText({ sessionId: fromSessionId, title: fromTitle }),
|
|
87743
|
+
source: "task-notification",
|
|
87744
|
+
requestId: `collab-wake-${fromSessionId.slice(0, 8)}-${Date.now()}`
|
|
87745
|
+
});
|
|
87746
|
+
} catch {
|
|
88135
87747
|
}
|
|
88136
|
-
|
|
88137
|
-
|
|
88138
|
-
|
|
88139
|
-
|
|
88140
|
-
|
|
88141
|
-
|
|
88142
|
-
|
|
88143
|
-
|
|
88144
|
-
|
|
88145
|
-
|
|
88146
|
-
|
|
88147
|
-
|
|
88148
|
-
|
|
88149
|
-
|
|
88150
|
-
|
|
87748
|
+
}
|
|
87749
|
+
function sendCollaborationMessage(ctx, input) {
|
|
87750
|
+
const channel = resolveSendChannel(ctx.store, input.sessionId, input.to, sessionTitle(ctx));
|
|
87751
|
+
const content = normalizeMailboxContent(input.content);
|
|
87752
|
+
const recipientSessionId = channel.peer.sessionId;
|
|
87753
|
+
const insert = ctx.store.appendMessage({
|
|
87754
|
+
grantId: channel.grant.grant_id,
|
|
87755
|
+
senderSessionId: input.sessionId,
|
|
87756
|
+
recipientSessionId,
|
|
87757
|
+
clientMessageId: input.clientMessageId,
|
|
87758
|
+
content
|
|
87759
|
+
});
|
|
87760
|
+
if (!insert.reused) {
|
|
87761
|
+
ctx.deps.events.append({
|
|
87762
|
+
aggregateType: "session",
|
|
87763
|
+
aggregateId: input.sessionId,
|
|
87764
|
+
eventType: "collaboration.message",
|
|
87765
|
+
payload: {
|
|
87766
|
+
messageId: insert.row.id,
|
|
87767
|
+
grantId: channel.grant.grant_id,
|
|
87768
|
+
toSessionId: recipientSessionId,
|
|
87769
|
+
sequence: insert.row.sequence
|
|
87770
|
+
}
|
|
87771
|
+
});
|
|
87772
|
+
void wakePeer(ctx, recipientSessionId, input.sessionId);
|
|
87773
|
+
}
|
|
87774
|
+
return {
|
|
87775
|
+
status: "sent",
|
|
87776
|
+
messageId: insert.row.id,
|
|
87777
|
+
sequence: insert.row.sequence,
|
|
87778
|
+
reused: insert.reused,
|
|
87779
|
+
to: channel.peer,
|
|
87780
|
+
peerSessionId: recipientSessionId
|
|
87781
|
+
};
|
|
87782
|
+
}
|
|
87783
|
+
function retrieveCollaborationMessages(ctx, input) {
|
|
87784
|
+
const { messages, peers } = readCallerMailbox(
|
|
87785
|
+
ctx.store,
|
|
87786
|
+
input.sessionId,
|
|
87787
|
+
{ from: input.from, limit: input.max },
|
|
87788
|
+
sessionTitle(ctx)
|
|
87789
|
+
);
|
|
87790
|
+
if (messages.length > 0) return { status: "messages", messages, peers };
|
|
87791
|
+
return { status: "empty", messages: [], peers, hint: peers.length > 0 ? EMPTY_MAILBOX_HINT : NO_PEERS_HINT };
|
|
87792
|
+
}
|
|
87793
|
+
|
|
87794
|
+
// src/session/collaboration-profiles.ts
|
|
87795
|
+
init_environment();
|
|
87796
|
+
|
|
87797
|
+
// ../../packages/shared/src/acp-brand.ts
|
|
87798
|
+
init_acp_brand();
|
|
87799
|
+
|
|
87800
|
+
// src/session/collaboration-profiles.ts
|
|
87801
|
+
init_resolve_service();
|
|
87802
|
+
function listCollaborationProfiles(deps) {
|
|
87803
|
+
const { harnesses, providers, sessions, sessionProviders } = deps;
|
|
87804
|
+
const profiles = [];
|
|
87805
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87806
|
+
const providerOptions = {
|
|
87807
|
+
experimentalClaudeOpenAiChatEnabled: deps.experimentalClaudeOpenAiChatEnabled?.() ?? false
|
|
87808
|
+
};
|
|
87809
|
+
const pushProfile = (profileId, harnessId, name, description, profileConfig) => {
|
|
87810
|
+
if (seen.has(profileId)) return;
|
|
87811
|
+
if (!harnesses.isSessionHarnessRunnable(harnessId)) return;
|
|
87812
|
+
seen.add(profileId);
|
|
87813
|
+
const harnessModels = listHarnessModels(providers, harnessId, null, providerOptions);
|
|
87814
|
+
const models = harnessModels.map((m3) => ({
|
|
87815
|
+
id: m3.id,
|
|
87816
|
+
name: m3.name || m3.id,
|
|
87817
|
+
...m3.description ? { description: m3.description } : {},
|
|
87818
|
+
...m3.serviceTiers?.length ? { serviceTiers: m3.serviceTiers } : {}
|
|
87819
|
+
}));
|
|
87820
|
+
const defaultModel = harnessModels.find((m3) => m3.isDefault) ?? harnessModels[0];
|
|
87821
|
+
const efforts = /* @__PURE__ */ new Set();
|
|
87822
|
+
for (const m3 of harnessModels) {
|
|
87823
|
+
for (const e of m3.supportedEffortLevels ?? []) efforts.add(e);
|
|
87824
|
+
}
|
|
87825
|
+
const cfg = profileConfig && typeof profileConfig === "object" && !Array.isArray(profileConfig) ? profileConfig : {};
|
|
87826
|
+
const cfgModel = typeof cfg.model === "string" && cfg.model.trim() ? cfg.model.trim() : void 0;
|
|
87827
|
+
const cfgEffort = typeof cfg.effort === "string" && cfg.effort.trim() ? cfg.effort.trim() : typeof cfg.reasoningEffort === "string" && cfg.reasoningEffort.trim() ? cfg.reasoningEffort.trim() : void 0;
|
|
87828
|
+
const acpAgentId = harnessId === "acp" && typeof cfg.agentId === "string" && cfg.agentId.trim() ? cfg.agentId.trim() : null;
|
|
87829
|
+
const modelDefault = cfgModel ?? defaultModel?.id;
|
|
87830
|
+
const effortDefault = cfgEffort ?? (efforts.has("high") ? "high" : efforts.has("medium") ? "medium" : efforts.size > 0 ? [...efforts][0] : void 0);
|
|
87831
|
+
profiles.push({
|
|
87832
|
+
id: profileId,
|
|
87833
|
+
name: harnessId === "acp" ? acpAgentDisplayName(acpAgentId) : name,
|
|
87834
|
+
harnessId,
|
|
87835
|
+
...acpAgentId ? { acpAgentId } : {},
|
|
87836
|
+
brandKey: resolveHarnessBrandKey(harnessId, acpAgentId),
|
|
87837
|
+
description,
|
|
87838
|
+
defaultConfig: {
|
|
87839
|
+
...modelDefault ? { model: modelDefault } : {},
|
|
87840
|
+
...effortDefault ? { effort: effortDefault } : {},
|
|
87841
|
+
...harnessId === "codex" ? { fastMode: typeof cfg.fastMode === "boolean" ? cfg.fastMode : false } : {}
|
|
87842
|
+
},
|
|
87843
|
+
models: models.length > 0 ? models : [{ id: "default", name: "Default" }],
|
|
87844
|
+
efforts: [...efforts],
|
|
87845
|
+
apiProviders: listHarnessApiProviders(providers, harnessId, providerOptions)
|
|
87846
|
+
});
|
|
87847
|
+
};
|
|
87848
|
+
if (sessionProviders) {
|
|
87849
|
+
for (const p3 of sessionProviders.list()) {
|
|
87850
|
+
pushProfile(
|
|
87851
|
+
p3.id,
|
|
87852
|
+
p3.harnessId,
|
|
87853
|
+
p3.name,
|
|
87854
|
+
p3.isBase ? `${p3.harnessId} harness with the built-in configuration` : `Custom ${p3.harnessId} profile`,
|
|
87855
|
+
p3.config
|
|
87856
|
+
);
|
|
88151
87857
|
}
|
|
88152
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
|
|
88153
87858
|
}
|
|
88154
|
-
|
|
88155
|
-
|
|
88156
|
-
|
|
88157
|
-
|
|
88158
|
-
|
|
88159
|
-
|
|
88160
|
-
|
|
88161
|
-
|
|
88162
|
-
});
|
|
88163
|
-
} catch {
|
|
87859
|
+
if (profiles.length === 0) {
|
|
87860
|
+
const seedIds = new Set(harnesses.readySessionHarnessIds());
|
|
87861
|
+
for (const s4 of sessions.list()) {
|
|
87862
|
+
if (s4.harnessId) seedIds.add(s4.harnessId);
|
|
87863
|
+
}
|
|
87864
|
+
for (const id of seedIds) {
|
|
87865
|
+
const harnessId = normalizeSessionHarnessId(id) ?? id;
|
|
87866
|
+
pushProfile(harnessId, harnessId, harnessId, `${harnessId} harness`);
|
|
88164
87867
|
}
|
|
88165
87868
|
}
|
|
88166
|
-
|
|
87869
|
+
return profiles;
|
|
87870
|
+
}
|
|
88167
87871
|
|
|
88168
|
-
// src/
|
|
88169
|
-
import {
|
|
88170
|
-
|
|
88171
|
-
|
|
88172
|
-
|
|
88173
|
-
|
|
88174
|
-
|
|
88175
|
-
|
|
87872
|
+
// src/session/collaboration-request.ts
|
|
87873
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
87874
|
+
|
|
87875
|
+
// src/session/collaboration-context.ts
|
|
87876
|
+
init_environment();
|
|
87877
|
+
function resolveProfile(profiles, agentId) {
|
|
87878
|
+
const direct = profiles.get(agentId);
|
|
87879
|
+
if (direct) return direct;
|
|
87880
|
+
const wire = normalizeSessionHarnessId(agentId);
|
|
87881
|
+
return wire ? profiles.get(`${wire}-base`) : void 0;
|
|
88176
87882
|
}
|
|
88177
|
-
function
|
|
88178
|
-
|
|
88179
|
-
|
|
88180
|
-
|
|
87883
|
+
function initiatorTitleOf(ctx, grant) {
|
|
87884
|
+
return ctx.deps.sessions.get(grant.parent_session_id)?.title?.trim() || grant.parent_session_id.slice(0, 8);
|
|
87885
|
+
}
|
|
87886
|
+
|
|
87887
|
+
// src/session/collaboration-request.ts
|
|
87888
|
+
function safeLaunchConfig(raw) {
|
|
87889
|
+
const config2 = raw && typeof raw === "object" ? raw : {};
|
|
87890
|
+
const safe = {};
|
|
87891
|
+
if (typeof config2.model === "string" && config2.model.trim()) safe.model = config2.model.trim();
|
|
87892
|
+
if (typeof config2.effort === "string" && config2.effort.trim()) safe.effort = config2.effort.trim();
|
|
87893
|
+
if (config2.apiProviderId === null) safe.apiProviderId = null;
|
|
87894
|
+
else if (typeof config2.apiProviderId === "string" && config2.apiProviderId.trim()) {
|
|
87895
|
+
safe.apiProviderId = config2.apiProviderId.trim();
|
|
87896
|
+
}
|
|
87897
|
+
if (config2.worktree && typeof config2.worktree === "object") safe.worktree = config2.worktree;
|
|
87898
|
+
if (typeof config2.worktreePath === "string" && config2.worktreePath.trim()) {
|
|
87899
|
+
safe.worktreePath = config2.worktreePath.trim();
|
|
87900
|
+
}
|
|
87901
|
+
if (typeof config2.cwd === "string" && config2.cwd.trim()) safe.cwd = config2.cwd.trim();
|
|
87902
|
+
return safe;
|
|
87903
|
+
}
|
|
87904
|
+
function normalizeLaunches(ctx, input, profileList) {
|
|
87905
|
+
const parent = ctx.deps.sessions.get(input.parentSessionId);
|
|
87906
|
+
const profiles = new Map(profileList.map((p3) => [p3.id, p3]));
|
|
87907
|
+
return input.launches.map((launch2) => {
|
|
87908
|
+
const mode = resolveLaunchMode(launch2.mode);
|
|
87909
|
+
const launchId = launch2.launchId?.trim() || randomUUID12();
|
|
87910
|
+
if (mode === "link") {
|
|
87911
|
+
const peerSessionId = launch2.sessionId?.trim();
|
|
87912
|
+
if (!peerSessionId) {
|
|
87913
|
+
throw new CollaborationError("Link launches require sessionId of an existing SuperOne session", "invalid_argument");
|
|
87914
|
+
}
|
|
87915
|
+
if (peerSessionId === input.parentSessionId) {
|
|
87916
|
+
throw new CollaborationError("Cannot link a session to itself", "invalid_argument");
|
|
87917
|
+
}
|
|
87918
|
+
const peer = ctx.deps.sessions.get(peerSessionId);
|
|
87919
|
+
if (!peer) throw new CollaborationError(`Unknown sessionId for link: ${peerSessionId}`, "not_found");
|
|
87920
|
+
assertNotPeeredElsewhere(ctx.store, input.parentSessionId, peerSessionId);
|
|
87921
|
+
const peerTitle = peer.title?.trim() || peerSessionId.slice(0, 8);
|
|
87922
|
+
const { summary: summary3, name: name2, role: role2 } = normalizeLaunchLabels("link", launch2, peerTitle);
|
|
87923
|
+
return {
|
|
87924
|
+
launchId,
|
|
87925
|
+
mode: "link",
|
|
87926
|
+
agentId: "",
|
|
87927
|
+
sessionId: peerSessionId,
|
|
87928
|
+
peerTitle,
|
|
87929
|
+
peerProjectPath: ctx.deps.projects.get(peer.projectId)?.path,
|
|
87930
|
+
summary: summary3,
|
|
87931
|
+
name: name2,
|
|
87932
|
+
role: role2,
|
|
87933
|
+
config: { name: name2, role: role2, summary: summary3 }
|
|
87934
|
+
};
|
|
87935
|
+
}
|
|
87936
|
+
const agentId = launch2.agentId?.trim();
|
|
87937
|
+
if (!agentId) {
|
|
87938
|
+
throw new CollaborationError(`${mode} launches require agentId from session_collab_list_agents`, "invalid_argument");
|
|
87939
|
+
}
|
|
87940
|
+
const profile = resolveProfile(profiles, agentId);
|
|
87941
|
+
if (!profile) throw new CollaborationError(`Unknown agent profile: ${agentId}`, "invalid_argument");
|
|
87942
|
+
const { summary: summary2, name, role } = normalizeLaunchLabels(mode, launch2);
|
|
87943
|
+
const safe = safeLaunchConfig(launch2.config);
|
|
87944
|
+
return {
|
|
87945
|
+
launchId,
|
|
87946
|
+
mode,
|
|
87947
|
+
agentId,
|
|
87948
|
+
summary: summary2,
|
|
87949
|
+
name,
|
|
87950
|
+
role,
|
|
87951
|
+
config: {
|
|
87952
|
+
...profile.defaultConfig,
|
|
87953
|
+
...safe,
|
|
87954
|
+
permissionMode: "default",
|
|
87955
|
+
sandboxMode: "off",
|
|
87956
|
+
cwd: safe.cwd ?? (parent.cwd ?? ctx.deps.projects.get(parent.projectId)?.path),
|
|
87957
|
+
name,
|
|
87958
|
+
role
|
|
87959
|
+
}
|
|
87960
|
+
};
|
|
87961
|
+
});
|
|
87962
|
+
}
|
|
87963
|
+
function createGrant(ctx, parentSessionId, launch2) {
|
|
87964
|
+
const { grantId, approved } = recordApprovedLaunch(ctx.store, parentSessionId, launch2, launch2.config);
|
|
87965
|
+
if (approved.reused) return approved;
|
|
87966
|
+
ctx.deps.events.append({
|
|
87967
|
+
aggregateType: "session",
|
|
87968
|
+
aggregateId: parentSessionId,
|
|
87969
|
+
eventType: "collaboration.grant_created",
|
|
87970
|
+
payload: approved.mode === "link" ? { grantId, mode: "link", peerSessionId: approved.sessionId, launchId: approved.launchId } : { grantId, mode: approved.mode, agentId: approved.agentId, launchId: approved.launchId }
|
|
87971
|
+
});
|
|
87972
|
+
return approved;
|
|
87973
|
+
}
|
|
87974
|
+
async function requestCollaboration(ctx, input) {
|
|
87975
|
+
if (!ctx.deps.sessions.get(input.parentSessionId)) {
|
|
87976
|
+
throw new CollaborationError("Parent session is not available", "not_found");
|
|
87977
|
+
}
|
|
87978
|
+
if (ctx.store.isSpawnChild(input.parentSessionId)) {
|
|
87979
|
+
throw new CollaborationError(NESTED_COLLABORATION_UNSUPPORTED, "failed_precondition");
|
|
87980
|
+
}
|
|
87981
|
+
if (!Array.isArray(input.launches)) {
|
|
87982
|
+
throw new CollaborationError("launches must contain at least one proposed session", "invalid_argument");
|
|
87983
|
+
}
|
|
87984
|
+
assertLaunchCount(input.launches.length);
|
|
87985
|
+
const profileList = ctx.listProfiles();
|
|
87986
|
+
const normalized = normalizeLaunches(ctx, input, profileList);
|
|
87987
|
+
const launchIds = new Set(normalized.map((l) => l.launchId));
|
|
87988
|
+
if (launchIds.size !== normalized.length) {
|
|
87989
|
+
throw new CollaborationError("Every confirmed launch must have a unique launchId", "invalid_argument");
|
|
87990
|
+
}
|
|
87991
|
+
let confirmed = normalized;
|
|
87992
|
+
if (input.requireUserConfirm) {
|
|
87993
|
+
const outcome = await ctx.deps.sessions.requestAgentsConfirm({
|
|
87994
|
+
sessionId: input.parentSessionId,
|
|
87995
|
+
launches: normalized,
|
|
87996
|
+
profiles: profileList,
|
|
87997
|
+
signal: input.signal
|
|
87998
|
+
});
|
|
87999
|
+
if (outcome.action === "cancel") return { status: "cancelled" };
|
|
88000
|
+
if (outcome.action === "decline") return { status: "rejected", feedback: outcome.content?.feedback };
|
|
88001
|
+
confirmed = mergeConfirmedLaunches(normalized, outcome.content);
|
|
88181
88002
|
}
|
|
88182
|
-
|
|
88183
|
-
|
|
88184
|
-
|
|
88003
|
+
const launches = ctx.store.transaction(() => confirmed.map((launch2) => createGrant(ctx, input.parentSessionId, launch2)));
|
|
88004
|
+
return { status: "approved", launches, next: START_APPROVED_LAUNCHES_HINT };
|
|
88005
|
+
}
|
|
88006
|
+
|
|
88007
|
+
// src/session/collaboration-start.ts
|
|
88008
|
+
init_agent_types();
|
|
88009
|
+
init_environment();
|
|
88010
|
+
import { existsSync as existsSync39, statSync as statSync15 } from "node:fs";
|
|
88011
|
+
import { resolve as pathResolve2 } from "node:path";
|
|
88012
|
+
function resolveCwd(ctx, config2, projectId, parentCwd) {
|
|
88013
|
+
const project = ctx.deps.projects.get(projectId);
|
|
88014
|
+
const fallback = parentCwd || project?.path || process.cwd();
|
|
88015
|
+
const cwd = pathResolve2(config2.cwd || fallback);
|
|
88016
|
+
if (!existsSync39(cwd) || !statSync15(cwd).isDirectory()) {
|
|
88017
|
+
throw new CollaborationError(`Working directory does not exist: ${cwd}`, "invalid_argument");
|
|
88018
|
+
}
|
|
88019
|
+
return cwd;
|
|
88020
|
+
}
|
|
88021
|
+
function applyFormAnswers(ctx, grant, formAnswers) {
|
|
88022
|
+
const packed = formAnswers[SESSION_AGENT_LAUNCHES_FIELD];
|
|
88023
|
+
if (typeof packed !== "string") return grant;
|
|
88024
|
+
let edited;
|
|
88185
88025
|
try {
|
|
88186
|
-
|
|
88026
|
+
edited = JSON.parse(packed);
|
|
88027
|
+
} catch {
|
|
88028
|
+
return grant;
|
|
88029
|
+
}
|
|
88030
|
+
if (!Array.isArray(edited) || edited.length === 0) return grant;
|
|
88031
|
+
const patch = edited[0]?.config;
|
|
88032
|
+
if (!patch || typeof patch !== "object") return grant;
|
|
88033
|
+
const next = patchEditableLaunchConfig(parseGrantConfig(grant.config_json), patch);
|
|
88034
|
+
ctx.store.updateConfig(grant.grant_id, next);
|
|
88035
|
+
return { ...grant, config_json: JSON.stringify(next) };
|
|
88036
|
+
}
|
|
88037
|
+
async function deliverInitialTask(ctx, grant, childSessionId) {
|
|
88038
|
+
if (grant.task_sent === 1) return;
|
|
88039
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
88040
|
+
const text2 = grant.kind === "handoff" ? handoffTaskContent({
|
|
88041
|
+
parentSessionId: grant.parent_session_id,
|
|
88042
|
+
parentTitle: ctx.deps.sessions.get(grant.parent_session_id)?.title?.trim() || null,
|
|
88043
|
+
task: grant.task
|
|
88044
|
+
}) : grant.task;
|
|
88045
|
+
try {
|
|
88046
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
88047
|
+
sessionId: childSessionId,
|
|
88048
|
+
text: text2,
|
|
88049
|
+
model: config2.model,
|
|
88050
|
+
effort: config2.effort,
|
|
88051
|
+
permissionMode: config2.permissionMode,
|
|
88052
|
+
sandboxMode: config2.sandboxMode,
|
|
88053
|
+
apiProviderId: config2.apiProviderId,
|
|
88054
|
+
requestId: `collaboration-task-${grant.grant_id.slice(0, 16)}`
|
|
88055
|
+
});
|
|
88187
88056
|
} catch {
|
|
88188
88057
|
}
|
|
88189
|
-
|
|
88058
|
+
ctx.store.markTaskSent(grant.grant_id);
|
|
88190
88059
|
}
|
|
88191
|
-
function
|
|
88192
|
-
|
|
88060
|
+
async function wakeLinkPeer(ctx, sessionId, grant, hasOpening) {
|
|
88061
|
+
if (!ctx.deps.sessions.get(sessionId)) return;
|
|
88062
|
+
try {
|
|
88063
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
88064
|
+
sessionId,
|
|
88065
|
+
text: linkActivationWakeText({
|
|
88066
|
+
initiatorSessionId: grant.parent_session_id,
|
|
88067
|
+
initiatorTitle: initiatorTitleOf(ctx, grant),
|
|
88068
|
+
hasOpening
|
|
88069
|
+
}),
|
|
88070
|
+
source: "task-notification",
|
|
88071
|
+
requestId: `collab-link-wake-${grant.grant_id.slice(0, 12)}-${Date.now()}`
|
|
88072
|
+
});
|
|
88073
|
+
} catch {
|
|
88074
|
+
}
|
|
88075
|
+
}
|
|
88076
|
+
async function startLink(ctx, grant) {
|
|
88077
|
+
if (!grant.child_session_id) {
|
|
88078
|
+
throw new CollaborationError("Link grant is missing peer session id", "failed_precondition");
|
|
88079
|
+
}
|
|
88080
|
+
const peerSessionId = grant.child_session_id;
|
|
88081
|
+
if (!ctx.deps.sessions.get(peerSessionId)) {
|
|
88082
|
+
throw new CollaborationError(`Peer session no longer exists: ${peerSessionId}`, "not_found");
|
|
88083
|
+
}
|
|
88084
|
+
const alreadyStarted = Boolean(grant.started_at);
|
|
88085
|
+
if (!alreadyStarted) ctx.store.markStarted(grant.grant_id);
|
|
88086
|
+
const opening = grant.task?.trim() ?? "";
|
|
88087
|
+
if (!alreadyStarted && opening) {
|
|
88088
|
+
if (grant.task_sent !== 1) {
|
|
88089
|
+
ctx.store.appendLinkOpening(grant, peerSessionId, opening);
|
|
88090
|
+
void wakeLinkPeer(ctx, peerSessionId, grant, true);
|
|
88091
|
+
}
|
|
88092
|
+
} else {
|
|
88093
|
+
void wakeLinkPeer(ctx, peerSessionId, grant, false);
|
|
88094
|
+
if (!alreadyStarted) ctx.store.markTaskSent(grant.grant_id);
|
|
88095
|
+
}
|
|
88096
|
+
const peer = describeLaunchedPeer(grant);
|
|
88193
88097
|
return {
|
|
88194
|
-
|
|
88195
|
-
|
|
88196
|
-
|
|
88197
|
-
|
|
88198
|
-
|
|
88199
|
-
|
|
88200
|
-
|
|
88201
|
-
|
|
88202
|
-
|
|
88203
|
-
|
|
88204
|
-
|
|
88205
|
-
|
|
88098
|
+
status: "linked",
|
|
88099
|
+
mode: "link",
|
|
88100
|
+
sessionId: peerSessionId,
|
|
88101
|
+
peerSessionId,
|
|
88102
|
+
reused: alreadyStarted,
|
|
88103
|
+
name: peer.name,
|
|
88104
|
+
role: peer.role,
|
|
88105
|
+
title: peer.title,
|
|
88106
|
+
config: peer.config
|
|
88107
|
+
};
|
|
88108
|
+
}
|
|
88109
|
+
async function startCollaboration(ctx, input) {
|
|
88110
|
+
let grant = prepareLaunchStart(ctx.store, input.callerSessionId, input);
|
|
88111
|
+
if (grant.kind !== "link" && input.formAnswers && typeof input.formAnswers === "object") {
|
|
88112
|
+
grant = applyFormAnswers(ctx, grant, input.formAnswers);
|
|
88113
|
+
}
|
|
88114
|
+
if (grant.kind === "link") return startLink(ctx, grant);
|
|
88115
|
+
const isHandoff = grant.kind === "handoff";
|
|
88116
|
+
const existingHandoffSessionId = isHandoff ? parseGrantConfig(grant.config_json).handoffSessionId : void 0;
|
|
88117
|
+
const existingSessionId = existingHandoffSessionId ?? grant.child_session_id;
|
|
88118
|
+
if (existingSessionId) {
|
|
88119
|
+
const existing = ctx.deps.sessions.get(existingSessionId);
|
|
88120
|
+
if (isHandoff && !existing) {
|
|
88121
|
+
throw new CollaborationError(`The handoff session no longer exists: ${existingSessionId}`, "not_found");
|
|
88122
|
+
}
|
|
88123
|
+
if (existing && !isHandoff) {
|
|
88124
|
+
ctx.deps.sessions.setSystemPromptAppend(
|
|
88125
|
+
existing.sessionId,
|
|
88126
|
+
collaborationSystemPrompt(grant.parent_session_id)
|
|
88127
|
+
);
|
|
88128
|
+
}
|
|
88129
|
+
if (existing) await deliverInitialTask(ctx, grant, existingSessionId);
|
|
88130
|
+
const peer = describeLaunchedPeer(grant);
|
|
88131
|
+
return {
|
|
88132
|
+
status: "started",
|
|
88133
|
+
mode: grant.kind,
|
|
88134
|
+
sessionId: existingSessionId,
|
|
88135
|
+
reused: true,
|
|
88136
|
+
name: peer.name,
|
|
88137
|
+
role: peer.role,
|
|
88138
|
+
title: peer.title,
|
|
88139
|
+
config: peer.config
|
|
88140
|
+
};
|
|
88141
|
+
}
|
|
88142
|
+
const parent = ctx.deps.sessions.get(grant.parent_session_id);
|
|
88143
|
+
if (!parent) throw new CollaborationError("Parent session is not available", "not_found");
|
|
88144
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
88145
|
+
let cwd = resolveCwd(ctx, config2, parent.projectId, parent.cwd);
|
|
88146
|
+
if (config2.worktreePath && typeof config2.worktreePath === "string" && config2.worktreePath.trim()) {
|
|
88147
|
+
cwd = pathResolve2(config2.worktreePath.trim());
|
|
88148
|
+
} else if (config2.worktree?.enabled) {
|
|
88149
|
+
cwd = ctx.deps.workspaceGit.activateWorktree(parent.projectId, {
|
|
88150
|
+
baseBranch: config2.worktree.baseBranch || "HEAD",
|
|
88151
|
+
mode: config2.worktree.mode ?? "branch",
|
|
88152
|
+
branchName: config2.worktree.branchName,
|
|
88153
|
+
carryLocalChanges: config2.worktree.carryLocalChanges
|
|
88154
|
+
}).path;
|
|
88155
|
+
}
|
|
88156
|
+
const profile = resolveProfile(new Map(ctx.listProfiles().map((p3) => [p3.id, p3])), grant.agent_id);
|
|
88157
|
+
const harnessId = profile?.harnessId ?? normalizeSessionHarnessId(grant.agent_id) ?? "claude";
|
|
88158
|
+
const displayName = deriveCollaborationName({ name: config2.name });
|
|
88159
|
+
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
88160
|
+
const title = collaborationSessionTitle(displayName, role);
|
|
88161
|
+
let child;
|
|
88162
|
+
try {
|
|
88163
|
+
child = ctx.deps.sessions.create({
|
|
88164
|
+
projectId: parent.projectId,
|
|
88165
|
+
harnessId,
|
|
88166
|
+
providerId: grant.agent_id,
|
|
88167
|
+
title,
|
|
88168
|
+
cwd,
|
|
88169
|
+
model: config2.model ?? null,
|
|
88170
|
+
effort: config2.effort ?? null,
|
|
88171
|
+
permissionMode: config2.permissionMode ?? null,
|
|
88172
|
+
sandboxMode: config2.sandboxMode ?? null,
|
|
88173
|
+
apiProviderId: config2.apiProviderId ?? null,
|
|
88174
|
+
controllerClientSessionId: input.controllerClientSessionId ?? parent.controllerClientSessionId,
|
|
88175
|
+
// Handoff is one-way by construction: the receiver gets no collaboration prompt.
|
|
88176
|
+
...isHandoff ? {} : { systemPromptAppend: collaborationSystemPrompt(grant.parent_session_id) }
|
|
88177
|
+
});
|
|
88178
|
+
ctx.store.bindStartedSession(grant, child.sessionId, config2);
|
|
88179
|
+
} catch (err) {
|
|
88180
|
+
if (child?.sessionId) {
|
|
88206
88181
|
try {
|
|
88207
|
-
|
|
88208
|
-
const iv = buf.subarray(0, 12);
|
|
88209
|
-
const tag = buf.subarray(12, 28);
|
|
88210
|
-
const data = buf.subarray(28);
|
|
88211
|
-
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
88212
|
-
decipher.setAuthTag(tag);
|
|
88213
|
-
return Buffer.concat([decipher.update(data), decipher.final()]).toString("utf8");
|
|
88182
|
+
ctx.deps.sessions.remove(child.sessionId);
|
|
88214
88183
|
} catch {
|
|
88215
|
-
return "";
|
|
88216
88184
|
}
|
|
88217
88185
|
}
|
|
88186
|
+
throw err;
|
|
88187
|
+
}
|
|
88188
|
+
grant = isHandoff ? grant : { ...grant, child_session_id: child.sessionId };
|
|
88189
|
+
await deliverInitialTask(ctx, grant, child.sessionId);
|
|
88190
|
+
ctx.deps.events.append({
|
|
88191
|
+
aggregateType: "session",
|
|
88192
|
+
aggregateId: grant.parent_session_id,
|
|
88193
|
+
eventType: isHandoff ? "collaboration.handoff_started" : "collaboration.child_started",
|
|
88194
|
+
payload: { grantId: grant.grant_id, childSessionId: child.sessionId }
|
|
88195
|
+
});
|
|
88196
|
+
return {
|
|
88197
|
+
status: "started",
|
|
88198
|
+
mode: grant.kind,
|
|
88199
|
+
sessionId: child.sessionId,
|
|
88200
|
+
reused: false,
|
|
88201
|
+
name: displayName,
|
|
88202
|
+
role,
|
|
88203
|
+
title,
|
|
88204
|
+
config: {
|
|
88205
|
+
model: config2.model,
|
|
88206
|
+
effort: config2.effort,
|
|
88207
|
+
fastMode: config2.fastMode,
|
|
88208
|
+
permissionMode: config2.permissionMode,
|
|
88209
|
+
sandboxMode: config2.sandboxMode,
|
|
88210
|
+
cwd,
|
|
88211
|
+
apiProviderId: config2.apiProviderId ?? null,
|
|
88212
|
+
name: displayName,
|
|
88213
|
+
role,
|
|
88214
|
+
...config2.worktree ? { worktree: config2.worktree } : {},
|
|
88215
|
+
...config2.worktreePath ? { worktreePath: config2.worktreePath } : {}
|
|
88216
|
+
}
|
|
88218
88217
|
};
|
|
88219
88218
|
}
|
|
88220
88219
|
|
|
88220
|
+
// src/session/collaboration.ts
|
|
88221
|
+
var CollaborationService = class {
|
|
88222
|
+
constructor(deps) {
|
|
88223
|
+
this.deps = deps;
|
|
88224
|
+
this.ctx = {
|
|
88225
|
+
deps,
|
|
88226
|
+
store: new CollaborationStore(deps.db),
|
|
88227
|
+
listProfiles: () => this.listProfiles()
|
|
88228
|
+
};
|
|
88229
|
+
}
|
|
88230
|
+
deps;
|
|
88231
|
+
ctx;
|
|
88232
|
+
/** Agent profiles from session_providers (+ ready-harness fallback). */
|
|
88233
|
+
listProfiles() {
|
|
88234
|
+
return listCollaborationProfiles(this.deps);
|
|
88235
|
+
}
|
|
88236
|
+
request(input) {
|
|
88237
|
+
return requestCollaboration(this.ctx, input);
|
|
88238
|
+
}
|
|
88239
|
+
start(input) {
|
|
88240
|
+
return startCollaboration(this.ctx, input);
|
|
88241
|
+
}
|
|
88242
|
+
send(input) {
|
|
88243
|
+
return sendCollaborationMessage(this.ctx, input);
|
|
88244
|
+
}
|
|
88245
|
+
retrieve(input) {
|
|
88246
|
+
return retrieveCollaborationMessages(this.ctx, input);
|
|
88247
|
+
}
|
|
88248
|
+
/** Reconstruct system-prompt append for spawn children after restart (never link). */
|
|
88249
|
+
rehydrateSystemPrompts() {
|
|
88250
|
+
for (const grant of this.ctx.store.startedSpawnGrants()) {
|
|
88251
|
+
if (!grant.child_session_id || !this.deps.sessions.get(grant.child_session_id)) continue;
|
|
88252
|
+
this.deps.sessions.setSystemPromptAppend(grant.child_session_id, collaborationSystemPrompt(grant.parent_session_id));
|
|
88253
|
+
}
|
|
88254
|
+
}
|
|
88255
|
+
};
|
|
88256
|
+
|
|
88221
88257
|
// src/workspace/watch-service.ts
|
|
88222
88258
|
import { watch } from "node:fs";
|
|
88223
88259
|
|
|
@@ -88324,7 +88360,7 @@ var WorkspaceWatchService = class {
|
|
|
88324
88360
|
|
|
88325
88361
|
// src/workspace/tail-watch-service.ts
|
|
88326
88362
|
init_fs();
|
|
88327
|
-
import { existsSync as
|
|
88363
|
+
import { existsSync as existsSync40, fstatSync as fstatSync2, openSync as openSync2, closeSync as closeSync2, statSync as statSync16, readSync as readSync2, realpathSync as realpathSync6 } from "node:fs";
|
|
88328
88364
|
var MAX_POLL_BYTES = 10 * 1024 * 1024;
|
|
88329
88365
|
var WorkspaceTailWatchService = class {
|
|
88330
88366
|
constructor(projects, fs2) {
|
|
@@ -88348,7 +88384,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88348
88384
|
);
|
|
88349
88385
|
}
|
|
88350
88386
|
try {
|
|
88351
|
-
resolvedAbs =
|
|
88387
|
+
resolvedAbs = existsSync40(absolutePath) ? realpathSync6(absolutePath) : absolutePath;
|
|
88352
88388
|
} catch {
|
|
88353
88389
|
resolvedAbs = absolutePath;
|
|
88354
88390
|
}
|
|
@@ -88379,7 +88415,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88379
88415
|
code: "invalid_argument"
|
|
88380
88416
|
});
|
|
88381
88417
|
}
|
|
88382
|
-
if (
|
|
88418
|
+
if (existsSync40(resolvedAbs)) {
|
|
88383
88419
|
try {
|
|
88384
88420
|
const st2 = statSync16(resolvedAbs);
|
|
88385
88421
|
if (!st2.isFile()) {
|
|
@@ -88430,7 +88466,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88430
88466
|
{ code: "invalid_argument" }
|
|
88431
88467
|
);
|
|
88432
88468
|
}
|
|
88433
|
-
if (!
|
|
88469
|
+
if (!existsSync40(absolutePath)) {
|
|
88434
88470
|
return {
|
|
88435
88471
|
content: "",
|
|
88436
88472
|
encoding: "base64",
|
|
@@ -88521,7 +88557,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88521
88557
|
};
|
|
88522
88558
|
|
|
88523
88559
|
// src/auth/idempotency.ts
|
|
88524
|
-
import { createHash as
|
|
88560
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
88525
88561
|
var IdempotencyService = class {
|
|
88526
88562
|
constructor(db) {
|
|
88527
88563
|
this.db = db;
|
|
@@ -88529,7 +88565,7 @@ var IdempotencyService = class {
|
|
|
88529
88565
|
db;
|
|
88530
88566
|
inflight = /* @__PURE__ */ new Map();
|
|
88531
88567
|
payloadHash(payload) {
|
|
88532
|
-
return
|
|
88568
|
+
return createHash9("sha256").update(JSON.stringify(payload ?? null)).digest("hex");
|
|
88533
88569
|
}
|
|
88534
88570
|
key(clientIdentity, operation, idempotencyKey) {
|
|
88535
88571
|
return `${clientIdentity}\0${operation}\0${idempotencyKey}`;
|
|
@@ -88628,7 +88664,62 @@ var IdempotencyService = class {
|
|
|
88628
88664
|
};
|
|
88629
88665
|
|
|
88630
88666
|
// src/provider/provider-store.ts
|
|
88631
|
-
import { randomUUID as
|
|
88667
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
88668
|
+
|
|
88669
|
+
// src/provider/secret-crypto.ts
|
|
88670
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync18, chmodSync as chmodSync2 } from "node:fs";
|
|
88671
|
+
import { dirname as dirname18 } from "node:path";
|
|
88672
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
|
|
88673
|
+
var ENC_PREFIX2 = "enc:v1:";
|
|
88674
|
+
var KEY_BYTES = 32;
|
|
88675
|
+
function isEncryptedSecret(value) {
|
|
88676
|
+
return typeof value === "string" && value.startsWith(ENC_PREFIX2);
|
|
88677
|
+
}
|
|
88678
|
+
function ensureKeyFile(keyPath) {
|
|
88679
|
+
if (existsSync41(keyPath)) {
|
|
88680
|
+
const raw = readFileSync20(keyPath);
|
|
88681
|
+
if (raw.length === KEY_BYTES) return raw;
|
|
88682
|
+
}
|
|
88683
|
+
mkdirSync23(dirname18(keyPath), { recursive: true, mode: 448 });
|
|
88684
|
+
const key = randomBytes5(KEY_BYTES);
|
|
88685
|
+
writeFileSync18(keyPath, key, { mode: 384 });
|
|
88686
|
+
try {
|
|
88687
|
+
chmodSync2(keyPath, 384);
|
|
88688
|
+
} catch {
|
|
88689
|
+
}
|
|
88690
|
+
return key;
|
|
88691
|
+
}
|
|
88692
|
+
function createNodeSecretCrypto(keyPath) {
|
|
88693
|
+
const key = ensureKeyFile(keyPath);
|
|
88694
|
+
return {
|
|
88695
|
+
encrypt(plain) {
|
|
88696
|
+
if (!plain) return "";
|
|
88697
|
+
if (isEncryptedSecret(plain)) return plain;
|
|
88698
|
+
const iv = randomBytes5(12);
|
|
88699
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
88700
|
+
const ciphertext = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
|
|
88701
|
+
const tag = cipher.getAuthTag();
|
|
88702
|
+
return ENC_PREFIX2 + Buffer.concat([iv, tag, ciphertext]).toString("base64");
|
|
88703
|
+
},
|
|
88704
|
+
decrypt(stored) {
|
|
88705
|
+
if (!stored) return "";
|
|
88706
|
+
if (!isEncryptedSecret(stored)) return stored;
|
|
88707
|
+
try {
|
|
88708
|
+
const buf = Buffer.from(stored.slice(ENC_PREFIX2.length), "base64");
|
|
88709
|
+
const iv = buf.subarray(0, 12);
|
|
88710
|
+
const tag = buf.subarray(12, 28);
|
|
88711
|
+
const data = buf.subarray(28);
|
|
88712
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
88713
|
+
decipher.setAuthTag(tag);
|
|
88714
|
+
return Buffer.concat([decipher.update(data), decipher.final()]).toString("utf8");
|
|
88715
|
+
} catch {
|
|
88716
|
+
return "";
|
|
88717
|
+
}
|
|
88718
|
+
}
|
|
88719
|
+
};
|
|
88720
|
+
}
|
|
88721
|
+
|
|
88722
|
+
// src/provider/provider-store.ts
|
|
88632
88723
|
function safeParse3(text2, fallback) {
|
|
88633
88724
|
if (!text2) return fallback;
|
|
88634
88725
|
try {
|
|
@@ -88660,7 +88751,7 @@ var ProviderStore = class {
|
|
|
88660
88751
|
}
|
|
88661
88752
|
createCredential(input) {
|
|
88662
88753
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
88663
|
-
const id = input.id?.trim() ||
|
|
88754
|
+
const id = input.id?.trim() || randomUUID13();
|
|
88664
88755
|
const maxOrder = this.db.prepare("SELECT MAX(sort_order) as m FROM provider_credentials").get()?.m ?? -1;
|
|
88665
88756
|
this.db.prepare(
|
|
88666
88757
|
`INSERT INTO provider_credentials
|
|
@@ -88856,7 +88947,7 @@ var ProviderStore = class {
|
|
|
88856
88947
|
};
|
|
88857
88948
|
|
|
88858
88949
|
// src/session/host-action-mcp-server.ts
|
|
88859
|
-
import { randomUUID as
|
|
88950
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
88860
88951
|
import {
|
|
88861
88952
|
createServer as createHttpServer
|
|
88862
88953
|
} from "node:http";
|
|
@@ -92451,7 +92542,7 @@ init_interaction_memory();
|
|
|
92451
92542
|
// ../../packages/runtime/src/fs/interaction-memory.ts
|
|
92452
92543
|
init_superone_home();
|
|
92453
92544
|
init_interaction_memory();
|
|
92454
|
-
import { createHash as
|
|
92545
|
+
import { createHash as createHash10, randomUUID as randomUUID14 } from "node:crypto";
|
|
92455
92546
|
import { lstat, mkdir, open, readdir, rename, rm } from "node:fs/promises";
|
|
92456
92547
|
import { constants as constants4 } from "node:fs";
|
|
92457
92548
|
import { join as join34 } from "node:path";
|
|
@@ -92647,7 +92738,7 @@ function indexEntry(note) {
|
|
|
92647
92738
|
};
|
|
92648
92739
|
}
|
|
92649
92740
|
async function writeAtomic(dir, name, raw) {
|
|
92650
|
-
const temp = join34(dir, `.${name}.${
|
|
92741
|
+
const temp = join34(dir, `.${name}.${randomUUID14()}.tmp`);
|
|
92651
92742
|
try {
|
|
92652
92743
|
const file2 = await open(temp, "wx", 384);
|
|
92653
92744
|
try {
|
|
@@ -92705,7 +92796,7 @@ var InteractionMemoryStore = class _InteractionMemoryStore {
|
|
|
92705
92796
|
await file2.close();
|
|
92706
92797
|
}
|
|
92707
92798
|
const { meta: meta3, content } = parseNote(raw, { title: topic2, resource: target.resource });
|
|
92708
|
-
return { ...meta3, topic: topic2, content, revision:
|
|
92799
|
+
return { ...meta3, topic: topic2, content, revision: createHash10("sha256").update(raw).digest("hex") };
|
|
92709
92800
|
} catch (err) {
|
|
92710
92801
|
if (missing(err)) return null;
|
|
92711
92802
|
throw new Error(`Cannot read personal memory ${target.label}/${topic2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -92798,7 +92889,7 @@ var InteractionMemoryStore = class _InteractionMemoryStore {
|
|
|
92798
92889
|
saved: true,
|
|
92799
92890
|
created: !existing,
|
|
92800
92891
|
...target.identity,
|
|
92801
|
-
...summary({ ...meta3, topic: args.topic, content, revision:
|
|
92892
|
+
...summary({ ...meta3, topic: args.topic, content, revision: createHash10("sha256").update(raw).digest("hex") })
|
|
92802
92893
|
};
|
|
92803
92894
|
} finally {
|
|
92804
92895
|
await rm(lock, { recursive: true, force: true });
|
|
@@ -100998,9 +101089,7 @@ function registerNodeCollabTools(server, superoneSessionId, collab) {
|
|
|
100998
101089
|
"session_collab_start",
|
|
100999
101090
|
{
|
|
101000
101091
|
description: startDesc.description,
|
|
101001
|
-
inputSchema:
|
|
101002
|
-
credential: external_exports.string().min(1)
|
|
101003
|
-
}
|
|
101092
|
+
inputSchema: jsonSchemaToZodShape(startDesc.inputSchema)
|
|
101004
101093
|
},
|
|
101005
101094
|
async (args) => {
|
|
101006
101095
|
try {
|
|
@@ -101135,7 +101224,7 @@ function headerValue(req, name) {
|
|
|
101135
101224
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
101136
101225
|
}
|
|
101137
101226
|
async function startHostActionMcpServer(opts) {
|
|
101138
|
-
const masterToken = opts.masterToken ??
|
|
101227
|
+
const masterToken = opts.masterToken ?? randomUUID15();
|
|
101139
101228
|
const httpSessions = /* @__PURE__ */ new Map();
|
|
101140
101229
|
const httpSessionsBySuperone = /* @__PURE__ */ new Map();
|
|
101141
101230
|
let httpUrl2 = "";
|
|
@@ -101203,7 +101292,7 @@ async function startHostActionMcpServer(opts) {
|
|
|
101203
101292
|
});
|
|
101204
101293
|
let sessionRef;
|
|
101205
101294
|
const transport = new StreamableHTTPServerTransport({
|
|
101206
|
-
sessionIdGenerator:
|
|
101295
|
+
sessionIdGenerator: randomUUID15,
|
|
101207
101296
|
onsessioninitialized: (id) => {
|
|
101208
101297
|
if (sessionRef) httpSessions.set(id, sessionRef);
|
|
101209
101298
|
},
|
|
@@ -101987,7 +102076,7 @@ function computeNextRunAt(schedule) {
|
|
|
101987
102076
|
}
|
|
101988
102077
|
|
|
101989
102078
|
// ../../packages/runtime/src/automations/store.ts
|
|
101990
|
-
import { randomUUID as
|
|
102079
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
101991
102080
|
function toRecord(row, projectPath) {
|
|
101992
102081
|
return {
|
|
101993
102082
|
id: row.id,
|
|
@@ -102019,7 +102108,7 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102019
102108
|
return { ...toRecord(row, path), projectId: row.project_id };
|
|
102020
102109
|
},
|
|
102021
102110
|
create(projectId, projectPath, data) {
|
|
102022
|
-
const id =
|
|
102111
|
+
const id = randomUUID16();
|
|
102023
102112
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
102024
102113
|
const nextRunAt = computeNextRunAt(data.schedule);
|
|
102025
102114
|
db.prepare(
|
|
@@ -102069,8 +102158,8 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102069
102158
|
delete(id) {
|
|
102070
102159
|
return db.prepare("DELETE FROM automations WHERE id = ?").run(id).changes > 0;
|
|
102071
102160
|
},
|
|
102072
|
-
listDue(
|
|
102073
|
-
const reclaimBefore = new Date(Date.parse(
|
|
102161
|
+
listDue(nowIso2) {
|
|
102162
|
+
const reclaimBefore = new Date(Date.parse(nowIso2) - 30 * 60 * 1e3).toISOString();
|
|
102074
102163
|
const rows = db.prepare(
|
|
102075
102164
|
`SELECT * FROM automations
|
|
102076
102165
|
WHERE enabled = 1
|
|
@@ -102081,7 +102170,7 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102081
102170
|
OR last_run_status != 'running'
|
|
102082
102171
|
OR COALESCE(last_run_at, updated_at) < ?
|
|
102083
102172
|
)`
|
|
102084
|
-
).all(
|
|
102173
|
+
).all(nowIso2, reclaimBefore);
|
|
102085
102174
|
return rows.map((r) => {
|
|
102086
102175
|
const path = resolveProjectPath2(r.project_id) ?? "";
|
|
102087
102176
|
return toRecord(r, path);
|
|
@@ -102540,7 +102629,7 @@ ON CONFLICT(id) DO UPDATE SET
|
|
|
102540
102629
|
|
|
102541
102630
|
// src/workspace/artifact-zone.ts
|
|
102542
102631
|
init_environment();
|
|
102543
|
-
import { createHash as
|
|
102632
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
102544
102633
|
import {
|
|
102545
102634
|
closeSync as closeSync3,
|
|
102546
102635
|
fstatSync as fstatSync3,
|
|
@@ -102862,7 +102951,7 @@ var ArtifactZoneService = class {
|
|
|
102862
102951
|
written: 0,
|
|
102863
102952
|
total: req.total,
|
|
102864
102953
|
sha256: req.sha256,
|
|
102865
|
-
hash:
|
|
102954
|
+
hash: createHash11("sha256"),
|
|
102866
102955
|
lastActivityAt: Date.now()
|
|
102867
102956
|
};
|
|
102868
102957
|
this.transfers.set(transferId, transfer);
|
|
@@ -102968,7 +103057,6 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
102968
103057
|
}
|
|
102969
103058
|
const allowSimulatedTurnFallback = partial2.allowSimulatedTurnFallback ?? simulatedHarness;
|
|
102970
103059
|
const providers = new ProviderStore(db, paths.providerSecretsKey);
|
|
102971
|
-
const collabSecrets = createNodeSecretCrypto(paths.providerSecretsKey);
|
|
102972
103060
|
let sessionsRef = null;
|
|
102973
103061
|
let collaborationRef = null;
|
|
102974
103062
|
const hostActionMcp = await startHostActionMcpServer({
|
|
@@ -103003,8 +103091,9 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103003
103091
|
if (!collaborationRef) throw Object.assign(new Error("collab not ready"), { code: "failed_precondition" });
|
|
103004
103092
|
const a = args && typeof args === "object" ? args : {};
|
|
103005
103093
|
return collaborationRef.start({
|
|
103006
|
-
|
|
103007
|
-
|
|
103094
|
+
callerSessionId: sessionId,
|
|
103095
|
+
launchId: typeof a.launchId === "string" ? a.launchId : "",
|
|
103096
|
+
task: typeof a.task === "string" ? a.task : void 0
|
|
103008
103097
|
});
|
|
103009
103098
|
},
|
|
103010
103099
|
send: async (sessionId, args) => {
|
|
@@ -103012,7 +103101,7 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103012
103101
|
const a = args && typeof args === "object" ? args : {};
|
|
103013
103102
|
return collaborationRef.send({
|
|
103014
103103
|
sessionId,
|
|
103015
|
-
|
|
103104
|
+
to: typeof a.to === "string" ? a.to : void 0,
|
|
103016
103105
|
content: String(a.content ?? ""),
|
|
103017
103106
|
clientMessageId: typeof a.clientMessageId === "string" ? a.clientMessageId : void 0
|
|
103018
103107
|
});
|
|
@@ -103022,8 +103111,7 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103022
103111
|
const a = args && typeof args === "object" ? args : {};
|
|
103023
103112
|
return collaborationRef.retrieve({
|
|
103024
103113
|
sessionId,
|
|
103025
|
-
|
|
103026
|
-
credential: typeof a.credential === "string" ? a.credential : void 0
|
|
103114
|
+
from: Array.isArray(a.from) ? a.from.filter((id) => typeof id === "string") : void 0
|
|
103027
103115
|
});
|
|
103028
103116
|
}
|
|
103029
103117
|
}
|
|
@@ -103070,7 +103158,6 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103070
103158
|
providers,
|
|
103071
103159
|
projects,
|
|
103072
103160
|
workspaceGit,
|
|
103073
|
-
secrets: collabSecrets,
|
|
103074
103161
|
sessionProviders,
|
|
103075
103162
|
experimentalClaudeOpenAiChatEnabled: () => loadNodeAgentSettings(paths.configJson).experimentalClaudeOpenAiChatEnabled
|
|
103076
103163
|
});
|