@super-one/cli 0.68.0 → 0.69.0-alpha.1
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 +1446 -1388
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
process.env.SUPERONE_VARIANT = "
|
|
1
|
+
process.env.SUPERONE_VARIANT = "alpha";
|
|
2
2
|
import { createRequire as __superoneCreateRequire } from 'node:module';
|
|
3
3
|
import { fileURLToPath as __superoneFileURLToPath } from 'node:url';
|
|
4
4
|
import { dirname as __superoneDirname } from 'node:path';
|
|
@@ -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",
|
|
@@ -10230,7 +10239,7 @@ async function openCodexAppServer(opts) {
|
|
|
10230
10239
|
stderrBuf = (stderrBuf + chunk).slice(-MAX_STDERR_CHARS);
|
|
10231
10240
|
});
|
|
10232
10241
|
const rl = createInterface({ input: child.stdout });
|
|
10233
|
-
const
|
|
10242
|
+
const pending2 = /* @__PURE__ */ new Map();
|
|
10234
10243
|
const notificationWaiters = [];
|
|
10235
10244
|
const notificationQueue = [];
|
|
10236
10245
|
let nextId = 1;
|
|
@@ -10250,11 +10259,11 @@ async function openCodexAppServer(opts) {
|
|
|
10250
10259
|
}
|
|
10251
10260
|
};
|
|
10252
10261
|
const failAll = (err) => {
|
|
10253
|
-
for (const [, p3] of
|
|
10262
|
+
for (const [, p3] of pending2) {
|
|
10254
10263
|
clearTimeout(p3.timer);
|
|
10255
10264
|
p3.reject(err);
|
|
10256
10265
|
}
|
|
10257
|
-
|
|
10266
|
+
pending2.clear();
|
|
10258
10267
|
while (notificationWaiters.length) {
|
|
10259
10268
|
const w3 = notificationWaiters.shift();
|
|
10260
10269
|
clearTimeout(w3.timer);
|
|
@@ -10354,9 +10363,9 @@ async function openCodexAppServer(opts) {
|
|
|
10354
10363
|
}
|
|
10355
10364
|
if (rec.id != null && (typeof rec.id === "number" || typeof rec.id === "string")) {
|
|
10356
10365
|
const id = typeof rec.id === "number" ? rec.id : Number(rec.id);
|
|
10357
|
-
const waiter =
|
|
10366
|
+
const waiter = pending2.get(id);
|
|
10358
10367
|
if (!waiter) continue;
|
|
10359
|
-
|
|
10368
|
+
pending2.delete(id);
|
|
10360
10369
|
clearTimeout(waiter.timer);
|
|
10361
10370
|
if (rec.error) {
|
|
10362
10371
|
const errObj = rec.error;
|
|
@@ -10394,14 +10403,14 @@ async function openCodexAppServer(opts) {
|
|
|
10394
10403
|
}
|
|
10395
10404
|
const id = nextId++;
|
|
10396
10405
|
const timer = setTimeout(() => {
|
|
10397
|
-
|
|
10406
|
+
pending2.delete(id);
|
|
10398
10407
|
reject(new Error(`Codex app-server ${method} timed out after ${timeoutMs}ms`));
|
|
10399
10408
|
}, timeoutMs);
|
|
10400
|
-
|
|
10409
|
+
pending2.set(id, { resolve: resolve17, reject, timer });
|
|
10401
10410
|
try {
|
|
10402
10411
|
writeLine({ jsonrpc: "2.0", id, method, params: params ?? {} });
|
|
10403
10412
|
} catch (err) {
|
|
10404
|
-
|
|
10413
|
+
pending2.delete(id);
|
|
10405
10414
|
clearTimeout(timer);
|
|
10406
10415
|
reject(safePublicError("Codex app-server write failed", err));
|
|
10407
10416
|
}
|
|
@@ -11208,9 +11217,9 @@ var init_account_manager = __esm({
|
|
|
11208
11217
|
type,
|
|
11209
11218
|
...type === "chatgpt" ? { useHostedLoginSuccessPage: true, appBrand: "chatgpt" } : {}
|
|
11210
11219
|
}));
|
|
11211
|
-
const
|
|
11212
|
-
this.pending.set(result.loginId,
|
|
11213
|
-
void this.wait(result.loginId,
|
|
11220
|
+
const pending2 = { accountId: id, connection };
|
|
11221
|
+
this.pending.set(result.loginId, pending2);
|
|
11222
|
+
void this.wait(result.loginId, pending2);
|
|
11214
11223
|
return { ...result, accountId: id };
|
|
11215
11224
|
} catch (error51) {
|
|
11216
11225
|
this.failed.add(id);
|
|
@@ -11221,53 +11230,53 @@ var init_account_manager = __esm({
|
|
|
11221
11230
|
this.starting = false;
|
|
11222
11231
|
}
|
|
11223
11232
|
}
|
|
11224
|
-
async wait(loginId,
|
|
11233
|
+
async wait(loginId, pending2) {
|
|
11225
11234
|
const deadline = Date.now() + 15 * 6e4;
|
|
11226
11235
|
const timeout = setTimeout(() => {
|
|
11227
11236
|
void this.cancel(loginId).catch(() => {
|
|
11228
11237
|
});
|
|
11229
|
-
this.failed.add(
|
|
11238
|
+
this.failed.add(pending2.accountId);
|
|
11230
11239
|
}, 15 * 6e4);
|
|
11231
11240
|
timeout.unref?.();
|
|
11232
11241
|
try {
|
|
11233
|
-
while (this.pending.get(loginId) ===
|
|
11234
|
-
const notification = await
|
|
11242
|
+
while (this.pending.get(loginId) === pending2 && Date.now() < deadline) {
|
|
11243
|
+
const notification = await pending2.connection.nextNotification();
|
|
11235
11244
|
if (!notification) throw new Error("Login connection closed");
|
|
11236
11245
|
if (notification?.method !== "account/login/completed" || notification.params.loginId !== loginId) continue;
|
|
11237
11246
|
if (notification.params.success !== true) throw new Error("Sign-in failed");
|
|
11238
|
-
const status = parseAccountStatus(await
|
|
11247
|
+
const status = parseAccountStatus(await pending2.connection.request("account/read", { refreshToken: false }));
|
|
11239
11248
|
if (!status.signedIn) throw new Error("Sign-in did not complete");
|
|
11240
|
-
if (this.pending.get(loginId) !==
|
|
11241
|
-
this.store.update(
|
|
11242
|
-
this.store.invalidateCredentials(
|
|
11243
|
-
this.changed(codexAccountProviderId(
|
|
11249
|
+
if (this.pending.get(loginId) !== pending2) return;
|
|
11250
|
+
this.store.update(pending2.accountId, status);
|
|
11251
|
+
this.store.invalidateCredentials(pending2.accountId);
|
|
11252
|
+
this.changed(codexAccountProviderId(pending2.accountId));
|
|
11244
11253
|
return;
|
|
11245
11254
|
}
|
|
11246
|
-
if (this.pending.get(loginId) ===
|
|
11255
|
+
if (this.pending.get(loginId) === pending2) this.failed.add(pending2.accountId);
|
|
11247
11256
|
} catch {
|
|
11248
|
-
this.failed.add(
|
|
11257
|
+
this.failed.add(pending2.accountId);
|
|
11249
11258
|
} finally {
|
|
11250
11259
|
clearTimeout(timeout);
|
|
11251
|
-
if (this.pending.get(loginId) ===
|
|
11252
|
-
await
|
|
11260
|
+
if (this.pending.get(loginId) === pending2) this.pending.delete(loginId);
|
|
11261
|
+
await pending2.connection.close().catch(() => {
|
|
11253
11262
|
});
|
|
11254
11263
|
}
|
|
11255
11264
|
}
|
|
11256
11265
|
async cancel(loginId) {
|
|
11257
|
-
const
|
|
11258
|
-
if (!
|
|
11266
|
+
const pending2 = this.pending.get(loginId);
|
|
11267
|
+
if (!pending2) return;
|
|
11259
11268
|
this.pending.delete(loginId);
|
|
11260
11269
|
try {
|
|
11261
|
-
await
|
|
11270
|
+
await pending2.connection.request("account/login/cancel", { loginId });
|
|
11262
11271
|
} finally {
|
|
11263
|
-
await
|
|
11272
|
+
await pending2.connection.close();
|
|
11264
11273
|
}
|
|
11265
11274
|
}
|
|
11266
11275
|
async logout(providerId) {
|
|
11267
11276
|
const id = codexAccountId(providerId) ?? codexAccountId(this.store.defaultProviderId()) ?? CODEX_CLI_ACCOUNT_ID;
|
|
11268
11277
|
this.revisions.set(id, (this.revisions.get(id) ?? 0) + 1);
|
|
11269
|
-
for (const [loginId,
|
|
11270
|
-
if (
|
|
11278
|
+
for (const [loginId, pending2] of this.pending) {
|
|
11279
|
+
if (pending2.accountId === id) await this.cancel(loginId);
|
|
11271
11280
|
}
|
|
11272
11281
|
const selected = codexAccountProviderId(id);
|
|
11273
11282
|
const connection = await this.open(selected);
|
|
@@ -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
|
}
|
|
@@ -13587,7 +13596,7 @@ async function fetchClaudeModels(opts) {
|
|
|
13587
13596
|
};
|
|
13588
13597
|
let q2;
|
|
13589
13598
|
try {
|
|
13590
|
-
q2 = (opts.queryFn ?? sdkQuery3)({ prompt:
|
|
13599
|
+
q2 = (opts.queryFn ?? sdkQuery3)({ prompt: CLAUDE_METADATA_PROBE_PROMPT, options });
|
|
13591
13600
|
const models = await withTimeout(
|
|
13592
13601
|
(async () => {
|
|
13593
13602
|
await q2.initializationResult();
|
|
@@ -13620,12 +13629,13 @@ function withTimeout(promise2, ms2) {
|
|
|
13620
13629
|
);
|
|
13621
13630
|
});
|
|
13622
13631
|
}
|
|
13623
|
-
var MODEL_NAME_RE, DEFAULT_PROBE_TIMEOUT_MS;
|
|
13632
|
+
var CLAUDE_METADATA_PROBE_PROMPT, MODEL_NAME_RE, DEFAULT_PROBE_TIMEOUT_MS;
|
|
13624
13633
|
var init_fetch_models = __esm({
|
|
13625
13634
|
"../../packages/claude/src/fetch-models.ts"() {
|
|
13626
13635
|
"use strict";
|
|
13627
13636
|
init_resolve_sdk_binary();
|
|
13628
13637
|
init_root_permission_guard();
|
|
13638
|
+
CLAUDE_METADATA_PROBE_PROMPT = "/help";
|
|
13629
13639
|
MODEL_NAME_RE = /^(\w+ [\d.]+)(?:\s+with\s+(\w+)\s+context)?/;
|
|
13630
13640
|
DEFAULT_PROBE_TIMEOUT_MS = 2e4;
|
|
13631
13641
|
}
|
|
@@ -13950,23 +13960,23 @@ function mapPlanRequest(payload, fallbackId) {
|
|
|
13950
13960
|
allowedPrompts: Array.isArray(payload.allowedPrompts) ? payload.allowedPrompts : Array.isArray(input.allowedPrompts) ? input.allowedPrompts : []
|
|
13951
13961
|
};
|
|
13952
13962
|
}
|
|
13953
|
-
function userMessage(ctx, blockId, text2,
|
|
13963
|
+
function userMessage(ctx, blockId, text2, nowIso2) {
|
|
13954
13964
|
return {
|
|
13955
13965
|
id: blockId,
|
|
13956
13966
|
role: "user",
|
|
13957
13967
|
status: "complete",
|
|
13958
13968
|
content: text2 ? [{ type: "text", text: text2 }] : [],
|
|
13959
|
-
createdAt:
|
|
13969
|
+
createdAt: nowIso2,
|
|
13960
13970
|
providerId: ctx.providerId ?? "codex"
|
|
13961
13971
|
};
|
|
13962
13972
|
}
|
|
13963
|
-
function assistantMessage(ctx, blockId,
|
|
13973
|
+
function assistantMessage(ctx, blockId, nowIso2) {
|
|
13964
13974
|
return {
|
|
13965
13975
|
id: blockId,
|
|
13966
13976
|
role: "assistant",
|
|
13967
13977
|
status: "streaming",
|
|
13968
13978
|
content: [],
|
|
13969
|
-
createdAt:
|
|
13979
|
+
createdAt: nowIso2,
|
|
13970
13980
|
providerId: ctx.providerId ?? "codex"
|
|
13971
13981
|
};
|
|
13972
13982
|
}
|
|
@@ -13982,7 +13992,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
13982
13992
|
const completedAssistantIds = /* @__PURE__ */ new Set();
|
|
13983
13993
|
let lastAssistantId = null;
|
|
13984
13994
|
let rawTerminalThisTurn = false;
|
|
13985
|
-
const
|
|
13995
|
+
const nowIso2 = ctx.nowIso ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
13986
13996
|
const ensureAssistant = (push, preferredId) => {
|
|
13987
13997
|
if (lastAssistantId) return lastAssistantId;
|
|
13988
13998
|
const id = preferredId && preferredId.length > 0 ? preferredId : `assistant-${ctx.sessionId}`;
|
|
@@ -13990,7 +14000,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
13990
14000
|
startedAssistantIds.add(id);
|
|
13991
14001
|
push({
|
|
13992
14002
|
type: "message_start",
|
|
13993
|
-
message: assistantMessage(ctx, id,
|
|
14003
|
+
message: assistantMessage(ctx, id, nowIso2())
|
|
13994
14004
|
});
|
|
13995
14005
|
}
|
|
13996
14006
|
lastAssistantId = id;
|
|
@@ -14012,7 +14022,7 @@ function createNodeSessionEventMapper(ctx) {
|
|
|
14012
14022
|
const text2 = asString(payload.text) ?? "";
|
|
14013
14023
|
push({
|
|
14014
14024
|
type: "user_message_appended",
|
|
14015
|
-
message: userMessage(ctx, blockId, text2,
|
|
14025
|
+
message: userMessage(ctx, blockId, text2, nowIso2())
|
|
14016
14026
|
});
|
|
14017
14027
|
break;
|
|
14018
14028
|
}
|
|
@@ -14906,6 +14916,20 @@ var init_session_title = __esm({
|
|
|
14906
14916
|
}
|
|
14907
14917
|
});
|
|
14908
14918
|
|
|
14919
|
+
// ../../packages/shared/src/host-wake.ts
|
|
14920
|
+
function isModelOnlyHostWake(text2) {
|
|
14921
|
+
return MODEL_ONLY_WAKE.test(text2.trimStart());
|
|
14922
|
+
}
|
|
14923
|
+
var MODEL_ONLY_WAKE;
|
|
14924
|
+
var init_host_wake = __esm({
|
|
14925
|
+
"../../packages/shared/src/host-wake.ts"() {
|
|
14926
|
+
"use strict";
|
|
14927
|
+
MODEL_ONLY_WAKE = new RegExp(
|
|
14928
|
+
'^(?:<task_notification source="(?:browser_download|artifact_sync)"|A collaboration mailbox message is ready\\.|A user-approved collaboration link is active with SuperOne session )'
|
|
14929
|
+
);
|
|
14930
|
+
}
|
|
14931
|
+
});
|
|
14932
|
+
|
|
14909
14933
|
// ../../packages/runtime/src/session/host-action-store.ts
|
|
14910
14934
|
import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID4, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
14911
14935
|
function hashToken(token) {
|
|
@@ -15624,15 +15648,6 @@ var init_host_action_store = __esm({
|
|
|
15624
15648
|
}
|
|
15625
15649
|
});
|
|
15626
15650
|
|
|
15627
|
-
// ../../packages/runtime/src/session/types.ts
|
|
15628
|
-
var DEFAULT_PERMISSION_TIMEOUT_MS;
|
|
15629
|
-
var init_types = __esm({
|
|
15630
|
-
"../../packages/runtime/src/session/types.ts"() {
|
|
15631
|
-
"use strict";
|
|
15632
|
-
DEFAULT_PERMISSION_TIMEOUT_MS = 6e4;
|
|
15633
|
-
}
|
|
15634
|
-
});
|
|
15635
|
-
|
|
15636
15651
|
// ../../packages/runtime/src/session/runtime-policy.ts
|
|
15637
15652
|
function getRuntimeIdleTimeoutMs(activeRuntimeCount) {
|
|
15638
15653
|
if (activeRuntimeCount <= 4) return TWENTY_MINUTES_MS;
|
|
@@ -15652,9 +15667,6 @@ var init_runtime_policy = __esm({
|
|
|
15652
15667
|
|
|
15653
15668
|
// ../../packages/runtime/src/session/session-runtime.ts
|
|
15654
15669
|
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
15670
|
function deriveSessionTitleFromUserText(text2) {
|
|
15659
15671
|
const cleaned = stripMiniAppMarkup(text2).trim().replace(/\s+/g, " ");
|
|
15660
15672
|
if (!cleaned) return null;
|
|
@@ -15758,10 +15770,9 @@ var init_session_runtime = __esm({
|
|
|
15758
15770
|
init_message_catalog();
|
|
15759
15771
|
init_miniapp_prompt_tags();
|
|
15760
15772
|
init_session_title();
|
|
15773
|
+
init_host_wake();
|
|
15761
15774
|
init_host_action_store();
|
|
15762
|
-
init_types();
|
|
15763
15775
|
init_runtime_policy();
|
|
15764
|
-
init_types();
|
|
15765
15776
|
DEFAULT_AGENTS_CONFIRM_TIMEOUT_MS = 10 * 6e4;
|
|
15766
15777
|
SessionRuntime = class {
|
|
15767
15778
|
constructor(store, events, leases, environmentId, turnRunner, opts) {
|
|
@@ -15771,7 +15782,6 @@ var init_session_runtime = __esm({
|
|
|
15771
15782
|
this.environmentId = environmentId;
|
|
15772
15783
|
this.turnRunner = turnRunner;
|
|
15773
15784
|
this.defaultApiProviderId = opts?.defaultApiProviderId;
|
|
15774
|
-
this.permissionTimeoutMs = opts?.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
|
|
15775
15785
|
this.agentsConfirmTimeoutMs = opts?.agentsConfirmTimeoutMs ?? DEFAULT_AGENTS_CONFIRM_TIMEOUT_MS;
|
|
15776
15786
|
this.hostActions = opts?.hostActions ?? null;
|
|
15777
15787
|
this.hydrateFromStore();
|
|
@@ -15806,14 +15816,13 @@ var init_session_runtime = __esm({
|
|
|
15806
15816
|
disposing = false;
|
|
15807
15817
|
/**
|
|
15808
15818
|
* Active permission waiters keyed by interactionId.
|
|
15809
|
-
* respondPermission resolves these;
|
|
15819
|
+
* respondPermission resolves these; abort/close deny them.
|
|
15810
15820
|
*/
|
|
15811
15821
|
permissionWaiters = /* @__PURE__ */ new Map();
|
|
15812
15822
|
questionWaiters = /* @__PURE__ */ new Map();
|
|
15813
15823
|
planWaiters = /* @__PURE__ */ new Map();
|
|
15814
15824
|
agentsConfirmWaiters = /* @__PURE__ */ new Map();
|
|
15815
15825
|
defaultApiProviderId;
|
|
15816
|
-
permissionTimeoutMs;
|
|
15817
15826
|
agentsConfirmTimeoutMs;
|
|
15818
15827
|
hostActions;
|
|
15819
15828
|
/** Live waiters for requestHostAction terminal settlement. */
|
|
@@ -16422,18 +16431,18 @@ var init_session_runtime = __esm({
|
|
|
16422
16431
|
return { delivered: true };
|
|
16423
16432
|
}
|
|
16424
16433
|
appendUserMessage(session, opts) {
|
|
16425
|
-
|
|
16434
|
+
if (opts.source === "task-notification" && isModelOnlyHostWake(opts.text)) return;
|
|
16426
16435
|
const userBlock = {
|
|
16427
16436
|
id: randomUUID5(),
|
|
16428
16437
|
role: "user",
|
|
16429
|
-
text:
|
|
16438
|
+
text: opts.text,
|
|
16430
16439
|
createdAt: Date.now()
|
|
16431
16440
|
};
|
|
16432
16441
|
session.transcript.push(userBlock);
|
|
16433
16442
|
session.updatedAt = Date.now();
|
|
16434
16443
|
let autoTitle = null;
|
|
16435
16444
|
if (!session.title || !session.title.trim()) {
|
|
16436
|
-
autoTitle = deriveSessionTitleFromUserText(
|
|
16445
|
+
autoTitle = deriveSessionTitleFromUserText(opts.text);
|
|
16437
16446
|
if (autoTitle) session.title = autoTitle;
|
|
16438
16447
|
}
|
|
16439
16448
|
this.persist(session);
|
|
@@ -16442,7 +16451,7 @@ var init_session_runtime = __esm({
|
|
|
16442
16451
|
eventType: SESSION_DURABLE_EVENT.userMessage,
|
|
16443
16452
|
payload: {
|
|
16444
16453
|
blockId: userBlock.id,
|
|
16445
|
-
text:
|
|
16454
|
+
text: opts.text,
|
|
16446
16455
|
...opts.source ? { source: opts.source } : {}
|
|
16447
16456
|
},
|
|
16448
16457
|
causationRequestId: opts.requestId
|
|
@@ -16651,7 +16660,7 @@ var init_session_runtime = __esm({
|
|
|
16651
16660
|
this.aborts.delete(sessionId);
|
|
16652
16661
|
session.closed = true;
|
|
16653
16662
|
session.status = "ended";
|
|
16654
|
-
this.rejectPendingPermission(session
|
|
16663
|
+
this.rejectPendingPermission(session);
|
|
16655
16664
|
this.cancelHostActionsForSession(sessionId, "session_closed");
|
|
16656
16665
|
session.updatedAt = Date.now();
|
|
16657
16666
|
this.persist(session);
|
|
@@ -17079,11 +17088,11 @@ var init_session_runtime = __esm({
|
|
|
17079
17088
|
generation: input.generation,
|
|
17080
17089
|
holderClientId: input.client.clientSessionId
|
|
17081
17090
|
});
|
|
17082
|
-
const
|
|
17083
|
-
if (!
|
|
17091
|
+
const pending2 = session.pendingInteraction;
|
|
17092
|
+
if (!pending2 || pending2.interactionId !== input.interactionId) {
|
|
17084
17093
|
throw Object.assign(new Error("no matching pending permission"), { code: "failed_precondition" });
|
|
17085
17094
|
}
|
|
17086
|
-
if (
|
|
17095
|
+
if (pending2.kind === "session_agents_confirm") {
|
|
17087
17096
|
const waiter2 = this.agentsConfirmWaiters.get(input.interactionId);
|
|
17088
17097
|
if (!waiter2 || waiter2.sessionId !== input.sessionId) {
|
|
17089
17098
|
throw Object.assign(new Error("no matching pending agents confirm"), {
|
|
@@ -17101,7 +17110,7 @@ var init_session_runtime = __esm({
|
|
|
17101
17110
|
});
|
|
17102
17111
|
return;
|
|
17103
17112
|
}
|
|
17104
|
-
if (
|
|
17113
|
+
if (pending2.kind && pending2.kind !== "permission") {
|
|
17105
17114
|
throw Object.assign(new Error("no matching pending permission"), { code: "failed_precondition" });
|
|
17106
17115
|
}
|
|
17107
17116
|
const waiter = this.permissionWaiters.get(input.interactionId);
|
|
@@ -17138,7 +17147,7 @@ var init_session_runtime = __esm({
|
|
|
17138
17147
|
);
|
|
17139
17148
|
}
|
|
17140
17149
|
if (session.pendingInteraction) {
|
|
17141
|
-
this.rejectPendingPermission(session
|
|
17150
|
+
this.rejectPendingPermission(session);
|
|
17142
17151
|
}
|
|
17143
17152
|
const interaction = {
|
|
17144
17153
|
interactionId: `sessionagents_${Date.now()}_${randomUUID5().slice(0, 8)}`,
|
|
@@ -17264,11 +17273,11 @@ var init_session_runtime = __esm({
|
|
|
17264
17273
|
}
|
|
17265
17274
|
/**
|
|
17266
17275
|
* Block the turn until the control-lease holder responds, or until
|
|
17267
|
-
*
|
|
17276
|
+
* abort / session close. Always emits a durable permission event.
|
|
17268
17277
|
*/
|
|
17269
17278
|
waitForPermissionDecision(session, interaction, signal, requestId) {
|
|
17270
17279
|
if (session.pendingInteraction) {
|
|
17271
|
-
this.rejectPendingPermission(session
|
|
17280
|
+
this.rejectPendingPermission(session);
|
|
17272
17281
|
}
|
|
17273
17282
|
return new Promise((resolve17) => {
|
|
17274
17283
|
let settled = false;
|
|
@@ -17284,11 +17293,7 @@ var init_session_runtime = __esm({
|
|
|
17284
17293
|
const settle = (result) => {
|
|
17285
17294
|
if (settled) return;
|
|
17286
17295
|
settled = true;
|
|
17287
|
-
|
|
17288
|
-
if (waiter) {
|
|
17289
|
-
clearTimeout(waiter.timer);
|
|
17290
|
-
this.permissionWaiters.delete(interaction.interactionId);
|
|
17291
|
-
}
|
|
17296
|
+
this.permissionWaiters.delete(interaction.interactionId);
|
|
17292
17297
|
if (session.pendingInteraction?.interactionId === interaction.interactionId) {
|
|
17293
17298
|
session.pendingInteraction = null;
|
|
17294
17299
|
session.updatedAt = Date.now();
|
|
@@ -17303,12 +17308,6 @@ var init_session_runtime = __esm({
|
|
|
17303
17308
|
decision: result.clientDecision ?? result.decision
|
|
17304
17309
|
}
|
|
17305
17310
|
});
|
|
17306
|
-
} else if (result.reason === "timeout") {
|
|
17307
|
-
this.events.appendSession({
|
|
17308
|
-
sessionId: session.sessionId,
|
|
17309
|
-
eventType: SESSION_DURABLE_EVENT.permissionTimeout,
|
|
17310
|
-
payload: { interactionId: interaction.interactionId, decision: "deny" }
|
|
17311
|
-
});
|
|
17312
17311
|
} else {
|
|
17313
17312
|
this.events.appendSession({
|
|
17314
17313
|
sessionId: session.sessionId,
|
|
@@ -17318,13 +17317,9 @@ var init_session_runtime = __esm({
|
|
|
17318
17317
|
}
|
|
17319
17318
|
resolve17(result.decision);
|
|
17320
17319
|
};
|
|
17321
|
-
const timer = setTimeout(() => {
|
|
17322
|
-
settle({ decision: "deny", reason: "timeout" });
|
|
17323
|
-
}, this.permissionTimeoutMs);
|
|
17324
17320
|
this.permissionWaiters.set(interaction.interactionId, {
|
|
17325
17321
|
sessionId: session.sessionId,
|
|
17326
|
-
settle
|
|
17327
|
-
timer
|
|
17322
|
+
settle
|
|
17328
17323
|
});
|
|
17329
17324
|
if (signal.aborted) {
|
|
17330
17325
|
settle({ decision: "deny", reason: "aborted" });
|
|
@@ -17340,59 +17335,55 @@ var init_session_runtime = __esm({
|
|
|
17340
17335
|
});
|
|
17341
17336
|
}
|
|
17342
17337
|
/** Deny and clear any active permission waiter for this session. */
|
|
17343
|
-
rejectPendingPermission(session
|
|
17344
|
-
const
|
|
17345
|
-
if (!
|
|
17346
|
-
if (
|
|
17347
|
-
const qw = this.questionWaiters.get(
|
|
17348
|
-
if (qw) qw.settle({ answers: {}, reason });
|
|
17338
|
+
rejectPendingPermission(session) {
|
|
17339
|
+
const pending2 = session.pendingInteraction;
|
|
17340
|
+
if (!pending2) return;
|
|
17341
|
+
if (pending2.kind === "question") {
|
|
17342
|
+
const qw = this.questionWaiters.get(pending2.interactionId);
|
|
17343
|
+
if (qw) qw.settle({ answers: {}, reason: "aborted" });
|
|
17349
17344
|
else session.pendingInteraction = null;
|
|
17350
17345
|
return;
|
|
17351
17346
|
}
|
|
17352
|
-
if (
|
|
17353
|
-
const pw = this.planWaiters.get(
|
|
17354
|
-
if (pw) pw.settle({ decision: "reject", reason });
|
|
17347
|
+
if (pending2.kind === "plan") {
|
|
17348
|
+
const pw = this.planWaiters.get(pending2.interactionId);
|
|
17349
|
+
if (pw) pw.settle({ decision: "reject", reason: "aborted" });
|
|
17355
17350
|
else session.pendingInteraction = null;
|
|
17356
17351
|
return;
|
|
17357
17352
|
}
|
|
17358
|
-
if (
|
|
17359
|
-
const aw = this.agentsConfirmWaiters.get(
|
|
17360
|
-
if (aw) aw.settle({ action: "cancel", reason });
|
|
17353
|
+
if (pending2.kind === "session_agents_confirm") {
|
|
17354
|
+
const aw = this.agentsConfirmWaiters.get(pending2.interactionId);
|
|
17355
|
+
if (aw) aw.settle({ action: "cancel", reason: "aborted" });
|
|
17361
17356
|
else session.pendingInteraction = null;
|
|
17362
17357
|
return;
|
|
17363
17358
|
}
|
|
17364
|
-
const waiter = this.permissionWaiters.get(
|
|
17359
|
+
const waiter = this.permissionWaiters.get(pending2.interactionId);
|
|
17365
17360
|
if (waiter) {
|
|
17366
|
-
waiter.settle({ decision: "deny", reason });
|
|
17361
|
+
waiter.settle({ decision: "deny", reason: "aborted" });
|
|
17367
17362
|
} else {
|
|
17368
17363
|
session.pendingInteraction = null;
|
|
17369
17364
|
}
|
|
17370
17365
|
}
|
|
17371
17366
|
waitForQuestionDecision(session, interaction, signal, requestId) {
|
|
17372
17367
|
if (session.pendingInteraction) {
|
|
17373
|
-
this.rejectPendingPermission(session
|
|
17368
|
+
this.rejectPendingPermission(session);
|
|
17374
17369
|
}
|
|
17375
|
-
const
|
|
17370
|
+
const pending2 = { ...interaction, kind: "question" };
|
|
17376
17371
|
return new Promise((resolve17) => {
|
|
17377
17372
|
let settled = false;
|
|
17378
|
-
session.pendingInteraction =
|
|
17373
|
+
session.pendingInteraction = pending2;
|
|
17379
17374
|
session.updatedAt = Date.now();
|
|
17380
17375
|
this.persist(session);
|
|
17381
17376
|
this.events.appendSession({
|
|
17382
17377
|
sessionId: session.sessionId,
|
|
17383
17378
|
eventType: SESSION_DURABLE_EVENT.questionRequested,
|
|
17384
|
-
payload:
|
|
17379
|
+
payload: pending2,
|
|
17385
17380
|
causationRequestId: requestId
|
|
17386
17381
|
});
|
|
17387
17382
|
const settle = (result) => {
|
|
17388
17383
|
if (settled) return;
|
|
17389
17384
|
settled = true;
|
|
17390
|
-
|
|
17391
|
-
if (
|
|
17392
|
-
clearTimeout(waiter.timer);
|
|
17393
|
-
this.questionWaiters.delete(pending.interactionId);
|
|
17394
|
-
}
|
|
17395
|
-
if (session.pendingInteraction?.interactionId === pending.interactionId) {
|
|
17385
|
+
this.questionWaiters.delete(pending2.interactionId);
|
|
17386
|
+
if (session.pendingInteraction?.interactionId === pending2.interactionId) {
|
|
17396
17387
|
session.pendingInteraction = null;
|
|
17397
17388
|
session.updatedAt = Date.now();
|
|
17398
17389
|
this.persist(session);
|
|
@@ -17401,30 +17392,20 @@ var init_session_runtime = __esm({
|
|
|
17401
17392
|
this.events.appendSession({
|
|
17402
17393
|
sessionId: session.sessionId,
|
|
17403
17394
|
eventType: SESSION_DURABLE_EVENT.questionResponded,
|
|
17404
|
-
payload: { interactionId:
|
|
17405
|
-
});
|
|
17406
|
-
} else if (result.reason === "timeout") {
|
|
17407
|
-
this.events.appendSession({
|
|
17408
|
-
sessionId: session.sessionId,
|
|
17409
|
-
eventType: SESSION_DURABLE_EVENT.questionTimeout,
|
|
17410
|
-
payload: { interactionId: pending.interactionId }
|
|
17395
|
+
payload: { interactionId: pending2.interactionId, answers: result.answers }
|
|
17411
17396
|
});
|
|
17412
17397
|
} else {
|
|
17413
17398
|
this.events.appendSession({
|
|
17414
17399
|
sessionId: session.sessionId,
|
|
17415
17400
|
eventType: SESSION_DURABLE_EVENT.questionAborted,
|
|
17416
|
-
payload: { interactionId:
|
|
17401
|
+
payload: { interactionId: pending2.interactionId }
|
|
17417
17402
|
});
|
|
17418
17403
|
}
|
|
17419
17404
|
resolve17(result.answers);
|
|
17420
17405
|
};
|
|
17421
|
-
|
|
17422
|
-
settle({ answers: {}, reason: "timeout" });
|
|
17423
|
-
}, this.permissionTimeoutMs);
|
|
17424
|
-
this.questionWaiters.set(pending.interactionId, {
|
|
17406
|
+
this.questionWaiters.set(pending2.interactionId, {
|
|
17425
17407
|
sessionId: session.sessionId,
|
|
17426
|
-
settle
|
|
17427
|
-
timer
|
|
17408
|
+
settle
|
|
17428
17409
|
});
|
|
17429
17410
|
if (signal.aborted) {
|
|
17430
17411
|
settle({ answers: {}, reason: "aborted" });
|
|
@@ -17441,29 +17422,25 @@ var init_session_runtime = __esm({
|
|
|
17441
17422
|
}
|
|
17442
17423
|
waitForPlanDecision(session, interaction, signal, requestId) {
|
|
17443
17424
|
if (session.pendingInteraction) {
|
|
17444
|
-
this.rejectPendingPermission(session
|
|
17425
|
+
this.rejectPendingPermission(session);
|
|
17445
17426
|
}
|
|
17446
|
-
const
|
|
17427
|
+
const pending2 = { ...interaction, kind: "plan" };
|
|
17447
17428
|
return new Promise((resolve17) => {
|
|
17448
17429
|
let settled = false;
|
|
17449
|
-
session.pendingInteraction =
|
|
17430
|
+
session.pendingInteraction = pending2;
|
|
17450
17431
|
session.updatedAt = Date.now();
|
|
17451
17432
|
this.persist(session);
|
|
17452
17433
|
this.events.appendSession({
|
|
17453
17434
|
sessionId: session.sessionId,
|
|
17454
17435
|
eventType: SESSION_DURABLE_EVENT.planRequested,
|
|
17455
|
-
payload:
|
|
17436
|
+
payload: pending2,
|
|
17456
17437
|
causationRequestId: requestId
|
|
17457
17438
|
});
|
|
17458
17439
|
const settle = (result) => {
|
|
17459
17440
|
if (settled) return;
|
|
17460
17441
|
settled = true;
|
|
17461
|
-
|
|
17462
|
-
if (
|
|
17463
|
-
clearTimeout(waiter.timer);
|
|
17464
|
-
this.planWaiters.delete(pending.interactionId);
|
|
17465
|
-
}
|
|
17466
|
-
if (session.pendingInteraction?.interactionId === pending.interactionId) {
|
|
17442
|
+
this.planWaiters.delete(pending2.interactionId);
|
|
17443
|
+
if (session.pendingInteraction?.interactionId === pending2.interactionId) {
|
|
17467
17444
|
session.pendingInteraction = null;
|
|
17468
17445
|
session.updatedAt = Date.now();
|
|
17469
17446
|
this.persist(session);
|
|
@@ -17473,33 +17450,23 @@ var init_session_runtime = __esm({
|
|
|
17473
17450
|
sessionId: session.sessionId,
|
|
17474
17451
|
eventType: SESSION_DURABLE_EVENT.planResponded,
|
|
17475
17452
|
payload: {
|
|
17476
|
-
interactionId:
|
|
17453
|
+
interactionId: pending2.interactionId,
|
|
17477
17454
|
decision: result.decision,
|
|
17478
17455
|
options: result.options
|
|
17479
17456
|
}
|
|
17480
17457
|
});
|
|
17481
|
-
} else if (result.reason === "timeout") {
|
|
17482
|
-
this.events.appendSession({
|
|
17483
|
-
sessionId: session.sessionId,
|
|
17484
|
-
eventType: SESSION_DURABLE_EVENT.planTimeout,
|
|
17485
|
-
payload: { interactionId: pending.interactionId, decision: "reject" }
|
|
17486
|
-
});
|
|
17487
17458
|
} else {
|
|
17488
17459
|
this.events.appendSession({
|
|
17489
17460
|
sessionId: session.sessionId,
|
|
17490
17461
|
eventType: SESSION_DURABLE_EVENT.planAborted,
|
|
17491
|
-
payload: { interactionId:
|
|
17462
|
+
payload: { interactionId: pending2.interactionId, decision: "reject" }
|
|
17492
17463
|
});
|
|
17493
17464
|
}
|
|
17494
17465
|
resolve17({ decision: result.decision, options: result.options });
|
|
17495
17466
|
};
|
|
17496
|
-
|
|
17497
|
-
settle({ decision: "reject", reason: "timeout" });
|
|
17498
|
-
}, this.permissionTimeoutMs);
|
|
17499
|
-
this.planWaiters.set(pending.interactionId, {
|
|
17467
|
+
this.planWaiters.set(pending2.interactionId, {
|
|
17500
17468
|
sessionId: session.sessionId,
|
|
17501
|
-
settle
|
|
17502
|
-
timer
|
|
17469
|
+
settle
|
|
17503
17470
|
});
|
|
17504
17471
|
if (signal.aborted) {
|
|
17505
17472
|
settle({ decision: "reject", reason: "aborted" });
|
|
@@ -18696,7 +18663,7 @@ var init_relay_identify = __esm({
|
|
|
18696
18663
|
|
|
18697
18664
|
// ../../packages/shared/src/platform-registry/types.ts
|
|
18698
18665
|
var CONSUMER_TASK;
|
|
18699
|
-
var
|
|
18666
|
+
var init_types = __esm({
|
|
18700
18667
|
"../../packages/shared/src/platform-registry/types.ts"() {
|
|
18701
18668
|
"use strict";
|
|
18702
18669
|
CONSUMER_TASK = {
|
|
@@ -19027,10 +18994,10 @@ var init_builtin = __esm({
|
|
|
19027
18994
|
init_effective_endpoints();
|
|
19028
18995
|
init_protocols();
|
|
19029
18996
|
XIAOMI_MODELS = {
|
|
19030
|
-
default: { id: "mimo-v2.
|
|
19031
|
-
opus: { id: "mimo-v2.
|
|
19032
|
-
sonnet: { id: "mimo-v2.
|
|
19033
|
-
haiku: { id: "mimo-v2.
|
|
18997
|
+
default: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
18998
|
+
opus: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
18999
|
+
sonnet: { id: "mimo-v2.6-pro[1m]", name: "MiMo V2.6 Pro" },
|
|
19000
|
+
haiku: { id: "mimo-v2.6-pro", name: "MiMo V2.6 Pro" }
|
|
19034
19001
|
};
|
|
19035
19002
|
BAILIAN_CODING_PLAN_MODELS = {
|
|
19036
19003
|
default: { id: "qwen3.7-plus", name: "Qwen 3.7 Plus" },
|
|
@@ -19535,6 +19502,7 @@ var init_builtin = __esm({
|
|
|
19535
19502
|
auth: "api-key",
|
|
19536
19503
|
baseUrl: "https://api.minimax.cn",
|
|
19537
19504
|
apiKeyUrl: "https://platform.minimax.cn/user-center/basic-information/interface-key",
|
|
19505
|
+
catalogProviderId: "minimax-cn",
|
|
19538
19506
|
endpoints: [
|
|
19539
19507
|
anthropic("/anthropic", {
|
|
19540
19508
|
extraEnv: MINIMAX_EXTRA_ENV,
|
|
@@ -19552,6 +19520,7 @@ var init_builtin = __esm({
|
|
|
19552
19520
|
auth: "api-key",
|
|
19553
19521
|
baseUrl: "https://api.minimax.io",
|
|
19554
19522
|
apiKeyUrl: "https://platform.minimax.io/user-center/basic-information/interface-key",
|
|
19523
|
+
catalogProviderId: "minimax",
|
|
19555
19524
|
endpoints: [
|
|
19556
19525
|
anthropic("/anthropic", {
|
|
19557
19526
|
extraEnv: MINIMAX_EXTRA_ENV,
|
|
@@ -19648,6 +19617,7 @@ var init_builtin = __esm({
|
|
|
19648
19617
|
auth: "api-key",
|
|
19649
19618
|
baseUrl: "https://token-plan-cn.xiaomimimo.com",
|
|
19650
19619
|
apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys",
|
|
19620
|
+
catalogProviderId: "xiaomi-token-plan-cn",
|
|
19651
19621
|
endpoints: [
|
|
19652
19622
|
anthropic("/anthropic", {
|
|
19653
19623
|
extraEnv: { ...EMPTY_AUTH_TOKEN },
|
|
@@ -19661,6 +19631,7 @@ var init_builtin = __esm({
|
|
|
19661
19631
|
auth: "api-key",
|
|
19662
19632
|
baseUrl: "https://api.xiaomimimo.com",
|
|
19663
19633
|
apiKeyUrl: "https://platform.xiaomimimo.com/#/console/api-keys",
|
|
19634
|
+
catalogProviderId: "xiaomi",
|
|
19664
19635
|
endpoints: [
|
|
19665
19636
|
anthropic("/anthropic", {
|
|
19666
19637
|
extraEnv: { ...EMPTY_AUTH_TOKEN },
|
|
@@ -19779,6 +19750,7 @@ var init_builtin = __esm({
|
|
|
19779
19750
|
brand: "longcat",
|
|
19780
19751
|
name: "Longcat",
|
|
19781
19752
|
description: "Longcat \u2014 \u957F\u732B AI \u7F16\u7A0B\u52A9\u624B",
|
|
19753
|
+
catalogProviderId: "longcat",
|
|
19782
19754
|
plans: [
|
|
19783
19755
|
{
|
|
19784
19756
|
id: "coding",
|
|
@@ -19883,6 +19855,7 @@ var init_builtin = __esm({
|
|
|
19883
19855
|
brand: "modelscope",
|
|
19884
19856
|
name: "ModelScope",
|
|
19885
19857
|
description: "ModelScope \u9B54\u642D \u2014 \u963F\u91CC\u5DF4\u5DF4\u6A21\u578B\u805A\u5408\u5E73\u53F0",
|
|
19858
|
+
catalogProviderId: "modelscope",
|
|
19886
19859
|
plans: [
|
|
19887
19860
|
{
|
|
19888
19861
|
id: "api",
|
|
@@ -19909,6 +19882,7 @@ var init_builtin = __esm({
|
|
|
19909
19882
|
brand: "siliconcloud",
|
|
19910
19883
|
name: "SiliconFlow",
|
|
19911
19884
|
description: "SiliconFlow \u7845\u57FA\u6D41\u52A8 \u2014 AI \u6A21\u578B\u805A\u5408\u63A8\u7406\u5E73\u53F0",
|
|
19885
|
+
catalogProviderId: "siliconflow-cn",
|
|
19912
19886
|
plans: [
|
|
19913
19887
|
{
|
|
19914
19888
|
id: "api",
|
|
@@ -19958,6 +19932,7 @@ var init_builtin = __esm({
|
|
|
19958
19932
|
brand: "bedrock",
|
|
19959
19933
|
name: "AWS Bedrock",
|
|
19960
19934
|
description: "Amazon Bedrock \u2014 run Claude on AWS infrastructure with IAM authentication",
|
|
19935
|
+
catalogProviderId: "amazon-bedrock",
|
|
19961
19936
|
plans: [
|
|
19962
19937
|
{
|
|
19963
19938
|
id: "aws",
|
|
@@ -19979,6 +19954,7 @@ var init_builtin = __esm({
|
|
|
19979
19954
|
brand: "vertexai",
|
|
19980
19955
|
name: "Google Vertex",
|
|
19981
19956
|
description: "Google Vertex AI \u2014 run Claude on GCP infrastructure with service account authentication",
|
|
19957
|
+
catalogProviderId: "google-vertex-anthropic",
|
|
19982
19958
|
plans: [
|
|
19983
19959
|
{
|
|
19984
19960
|
id: "gcp",
|
|
@@ -20118,9 +20094,9 @@ var init_platform_registry = __esm({
|
|
|
20118
20094
|
"use strict";
|
|
20119
20095
|
init_relay_identify();
|
|
20120
20096
|
init_protocols();
|
|
20121
|
-
|
|
20097
|
+
init_types();
|
|
20122
20098
|
init_protocols();
|
|
20123
|
-
|
|
20099
|
+
init_types();
|
|
20124
20100
|
init_capabilities2();
|
|
20125
20101
|
init_claude_env();
|
|
20126
20102
|
init_merge();
|
|
@@ -64627,6 +64603,23 @@ var init_cursor_store = __esm({
|
|
|
64627
64603
|
}
|
|
64628
64604
|
});
|
|
64629
64605
|
|
|
64606
|
+
// ../../packages/cursor/src/cursor-sdk.ts
|
|
64607
|
+
function loadCursorSdk() {
|
|
64608
|
+
pending ??= import("@cursor/sdk").then((sdk) => loaded = sdk);
|
|
64609
|
+
return pending;
|
|
64610
|
+
}
|
|
64611
|
+
function isCursorSdkError(error51, name) {
|
|
64612
|
+
return loaded !== null && error51 instanceof loaded[name];
|
|
64613
|
+
}
|
|
64614
|
+
var pending, loaded;
|
|
64615
|
+
var init_cursor_sdk = __esm({
|
|
64616
|
+
"../../packages/cursor/src/cursor-sdk.ts"() {
|
|
64617
|
+
"use strict";
|
|
64618
|
+
pending = null;
|
|
64619
|
+
loaded = null;
|
|
64620
|
+
}
|
|
64621
|
+
});
|
|
64622
|
+
|
|
64630
64623
|
// ../../packages/shared/src/model-option-params.ts
|
|
64631
64624
|
function parseCatalogContextWindow(raw) {
|
|
64632
64625
|
if (!raw) return null;
|
|
@@ -64677,33 +64670,30 @@ var init_cursor_model_selection = __esm({
|
|
|
64677
64670
|
});
|
|
64678
64671
|
|
|
64679
64672
|
// ../../packages/cursor/src/cursor-client.ts
|
|
64680
|
-
import { Agent as Agent2, Cursor } from "@cursor/sdk";
|
|
64681
64673
|
var init_cursor_client = __esm({
|
|
64682
64674
|
"../../packages/cursor/src/cursor-client.ts"() {
|
|
64683
64675
|
"use strict";
|
|
64676
|
+
init_cursor_sdk();
|
|
64684
64677
|
init_cursor_config();
|
|
64685
64678
|
init_cursor_model_selection();
|
|
64686
64679
|
}
|
|
64687
64680
|
});
|
|
64688
64681
|
|
|
64689
64682
|
// ../../packages/cursor/src/cursor-cloud.ts
|
|
64690
|
-
import {
|
|
64691
|
-
Agent as Agent3,
|
|
64692
|
-
Cursor as Cursor2
|
|
64693
|
-
} from "@cursor/sdk";
|
|
64694
64683
|
var init_cursor_cloud = __esm({
|
|
64695
64684
|
"../../packages/cursor/src/cursor-cloud.ts"() {
|
|
64696
64685
|
"use strict";
|
|
64686
|
+
init_cursor_sdk();
|
|
64697
64687
|
init_cursor_config();
|
|
64698
64688
|
init_cursor_store();
|
|
64699
64689
|
}
|
|
64700
64690
|
});
|
|
64701
64691
|
|
|
64702
64692
|
// ../../packages/cursor/src/cursor-sdk-auth.ts
|
|
64703
|
-
import { Cursor as Cursor3 } from "@cursor/sdk";
|
|
64704
64693
|
var init_cursor_sdk_auth = __esm({
|
|
64705
64694
|
"../../packages/cursor/src/cursor-sdk-auth.ts"() {
|
|
64706
64695
|
"use strict";
|
|
64696
|
+
init_cursor_sdk();
|
|
64707
64697
|
}
|
|
64708
64698
|
});
|
|
64709
64699
|
|
|
@@ -64742,9 +64732,7 @@ var init_cursor_local_options = __esm({
|
|
|
64742
64732
|
});
|
|
64743
64733
|
|
|
64744
64734
|
// ../../packages/cursor/src/cursor-network-retry.ts
|
|
64745
|
-
import { NetworkError } from "@cursor/sdk";
|
|
64746
64735
|
function isCursorRetryableNetworkError(error51) {
|
|
64747
|
-
if (error51 instanceof NetworkError) return error51.isRetryable;
|
|
64748
64736
|
if (!error51 || typeof error51 !== "object") return false;
|
|
64749
64737
|
const name = "name" in error51 ? String(error51.name) : "";
|
|
64750
64738
|
if (name !== "NetworkError") return false;
|
|
@@ -64828,14 +64816,8 @@ var init_cursor_sdk_trace = __esm({
|
|
|
64828
64816
|
});
|
|
64829
64817
|
|
|
64830
64818
|
// ../../packages/cursor/src/cursor-runtime.ts
|
|
64831
|
-
import {
|
|
64832
|
-
Agent as Agent4,
|
|
64833
|
-
AgentBusyError,
|
|
64834
|
-
Cursor as Cursor4,
|
|
64835
|
-
IntegrationNotConnectedError
|
|
64836
|
-
} from "@cursor/sdk";
|
|
64837
64819
|
function formatCursorError(error51) {
|
|
64838
|
-
if (error51
|
|
64820
|
+
if (isCursorSdkError(error51, "IntegrationNotConnectedError")) {
|
|
64839
64821
|
return new CursorIntegrationError(
|
|
64840
64822
|
`${error51.message} Connect ${error51.provider} at ${error51.helpUrl} then retry.`,
|
|
64841
64823
|
{ provider: error51.provider, helpUrl: error51.helpUrl }
|
|
@@ -64863,9 +64845,10 @@ async function createCursorRuntime(opts) {
|
|
|
64863
64845
|
"Cursor User API Key missing. Create one at https://cursor.com/dashboard/api, set it on the Cursor provider, or export CURSOR_API_KEY."
|
|
64864
64846
|
);
|
|
64865
64847
|
}
|
|
64848
|
+
const { Agent: Agent2, Cursor } = await loadCursorSdk();
|
|
64866
64849
|
if (plan.config.useHttp1ForAgent != null) {
|
|
64867
64850
|
try {
|
|
64868
|
-
|
|
64851
|
+
Cursor.configure({ local: { useHttp1ForAgent: plan.config.useHttp1ForAgent } });
|
|
64869
64852
|
} catch (error51) {
|
|
64870
64853
|
log2.debug("[CursorRuntime] Cursor.configure useHttp1ForAgent failed:", error51);
|
|
64871
64854
|
}
|
|
@@ -64917,7 +64900,7 @@ async function createCursorRuntime(opts) {
|
|
|
64917
64900
|
}, opts.sessionId);
|
|
64918
64901
|
const openAgent = (sandbox) => withCursorPlatformLookup(async () => {
|
|
64919
64902
|
if (opts.providerSessionId) {
|
|
64920
|
-
return
|
|
64903
|
+
return Agent2.resume(opts.providerSessionId, {
|
|
64921
64904
|
apiKey,
|
|
64922
64905
|
...model ? { model } : {},
|
|
64923
64906
|
...agentName ? { name: agentName } : {},
|
|
@@ -64940,7 +64923,7 @@ async function createCursorRuntime(opts) {
|
|
|
64940
64923
|
});
|
|
64941
64924
|
}
|
|
64942
64925
|
if (isCloud) {
|
|
64943
|
-
return
|
|
64926
|
+
return Agent2.create({
|
|
64944
64927
|
apiKey,
|
|
64945
64928
|
...model ? { model } : {},
|
|
64946
64929
|
...agentName ? { name: agentName } : {},
|
|
@@ -64949,7 +64932,7 @@ async function createCursorRuntime(opts) {
|
|
|
64949
64932
|
cloud: buildCloudOptions(config2)
|
|
64950
64933
|
});
|
|
64951
64934
|
}
|
|
64952
|
-
return
|
|
64935
|
+
return Agent2.create({
|
|
64953
64936
|
apiKey,
|
|
64954
64937
|
model,
|
|
64955
64938
|
...agentName ? { name: agentName } : {},
|
|
@@ -65132,7 +65115,7 @@ ${text2}` : text2;
|
|
|
65132
65115
|
try {
|
|
65133
65116
|
run = await agent.send(userMessage2, sendOptions);
|
|
65134
65117
|
} catch (error51) {
|
|
65135
|
-
if (error51
|
|
65118
|
+
if (isCursorSdkError(error51, "AgentBusyError") && !sendOpts?.force && !isCloud) {
|
|
65136
65119
|
log2.warn("[CursorRuntime] AgentBusyError \u2014 retrying with local.force");
|
|
65137
65120
|
tracer.runtime("agent_busy_retry", {
|
|
65138
65121
|
message: error51 instanceof Error ? error51.message : String(error51)
|
|
@@ -65303,6 +65286,7 @@ var init_cursor_runtime = __esm({
|
|
|
65303
65286
|
"../../packages/cursor/src/cursor-runtime.ts"() {
|
|
65304
65287
|
"use strict";
|
|
65305
65288
|
init_superone_system_prompt();
|
|
65289
|
+
init_cursor_sdk();
|
|
65306
65290
|
init_cursor_config();
|
|
65307
65291
|
init_cursor_local_options();
|
|
65308
65292
|
init_cursor_network_retry();
|
|
@@ -65335,10 +65319,10 @@ var init_cursor_runtime = __esm({
|
|
|
65335
65319
|
});
|
|
65336
65320
|
|
|
65337
65321
|
// ../../packages/cursor/src/cursor-workspace-prewarm.ts
|
|
65338
|
-
import { createAgentPlatform } from "@cursor/sdk";
|
|
65339
65322
|
var init_cursor_workspace_prewarm = __esm({
|
|
65340
65323
|
"../../packages/cursor/src/cursor-workspace-prewarm.ts"() {
|
|
65341
65324
|
"use strict";
|
|
65325
|
+
init_cursor_sdk();
|
|
65342
65326
|
init_cursor_sdk_trace();
|
|
65343
65327
|
init_cursor_local_options();
|
|
65344
65328
|
init_cursor_platform_binaries();
|
|
@@ -65486,6 +65470,7 @@ var init_src5 = __esm({
|
|
|
65486
65470
|
init_cursor_event_map();
|
|
65487
65471
|
init_cursor_store();
|
|
65488
65472
|
init_cursor_client();
|
|
65473
|
+
init_cursor_sdk();
|
|
65489
65474
|
init_cursor_model_selection();
|
|
65490
65475
|
init_cursor_cloud();
|
|
65491
65476
|
init_cursor_sdk_auth();
|
|
@@ -71246,8 +71231,8 @@ var require_multipleOf = __commonJS({
|
|
|
71246
71231
|
const { gen, data, schemaCode, it: it2 } = cxt;
|
|
71247
71232
|
const prec = it2.opts.multipleOfPrecision;
|
|
71248
71233
|
const res = gen.let("res");
|
|
71249
|
-
const
|
|
71250
|
-
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${
|
|
71234
|
+
const invalid3 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
|
|
71235
|
+
cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid3}))`);
|
|
71251
71236
|
}
|
|
71252
71237
|
};
|
|
71253
71238
|
exports.default = def;
|
|
@@ -73403,8 +73388,8 @@ import { fileURLToPath } from "node:url";
|
|
|
73403
73388
|
function resolveCliReleaseVersion() {
|
|
73404
73389
|
const fromEnv = process.env.SUPERONE_CLI_VERSION?.trim();
|
|
73405
73390
|
if (fromEnv) return fromEnv;
|
|
73406
|
-
if ("0.
|
|
73407
|
-
return "0.
|
|
73391
|
+
if ("0.69.0-alpha.1".trim()) {
|
|
73392
|
+
return "0.69.0-alpha.1".trim();
|
|
73408
73393
|
}
|
|
73409
73394
|
const fromDist = readDistManifestVersion();
|
|
73410
73395
|
if (fromDist) return fromDist;
|
|
@@ -74599,7 +74584,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
|
|
|
74599
74584
|
credential_secret TEXT,
|
|
74600
74585
|
credential_hint TEXT NOT NULL,
|
|
74601
74586
|
parent_session_id TEXT NOT NULL,
|
|
74602
|
-
child_session_id TEXT
|
|
74587
|
+
child_session_id TEXT,
|
|
74603
74588
|
agent_id TEXT NOT NULL,
|
|
74604
74589
|
task TEXT NOT NULL,
|
|
74605
74590
|
config_json TEXT NOT NULL,
|
|
@@ -74684,6 +74669,66 @@ CREATE INDEX IF NOT EXISTS idx_session_providers_harness ON session_providers(ha
|
|
|
74684
74669
|
`;
|
|
74685
74670
|
var SCHEMA_GENERATION = 1;
|
|
74686
74671
|
|
|
74672
|
+
// ../../packages/runtime/src/collaboration/schema.ts
|
|
74673
|
+
var GRANTS_TABLE = "session_collaboration_grants";
|
|
74674
|
+
function ensureCollaborationGrantUniqueness(db, { warn = console.warn } = {}) {
|
|
74675
|
+
const rebuiltDdl = hasColumnUniqueChild(db) ? ddlWithoutColumnUnique(db) : null;
|
|
74676
|
+
if (rebuiltDdl === void 0) {
|
|
74677
|
+
warn(`[collaboration] ${GRANTS_TABLE}: unrecognized child_session_id UNIQUE DDL; keeping one link per peer`);
|
|
74678
|
+
}
|
|
74679
|
+
const apply = () => {
|
|
74680
|
+
if (rebuiltDdl) rebuildTable(db, rebuiltDdl);
|
|
74681
|
+
db.exec(`
|
|
74682
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_collaboration_spawn_child
|
|
74683
|
+
ON ${GRANTS_TABLE}(child_session_id) WHERE kind = 'spawn';
|
|
74684
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_collaboration_link_pair
|
|
74685
|
+
ON ${GRANTS_TABLE}(parent_session_id, child_session_id) WHERE kind = 'link';
|
|
74686
|
+
`);
|
|
74687
|
+
};
|
|
74688
|
+
const foreignKeysOn = Boolean(rebuiltDdl) && db.pragma("foreign_keys", { simple: true }) === 1;
|
|
74689
|
+
if (db.inTransaction) {
|
|
74690
|
+
if (foreignKeysOn) {
|
|
74691
|
+
throw new Error(`Rebuilding ${GRANTS_TABLE} inside a transaction requires foreign keys off`);
|
|
74692
|
+
}
|
|
74693
|
+
apply();
|
|
74694
|
+
return;
|
|
74695
|
+
}
|
|
74696
|
+
if (foreignKeysOn) db.pragma("foreign_keys = OFF");
|
|
74697
|
+
try {
|
|
74698
|
+
db.transaction(apply)();
|
|
74699
|
+
} finally {
|
|
74700
|
+
if (foreignKeysOn) db.pragma("foreign_keys = ON");
|
|
74701
|
+
}
|
|
74702
|
+
}
|
|
74703
|
+
function hasColumnUniqueChild(db) {
|
|
74704
|
+
const indexes = db.prepare(`PRAGMA index_list(${GRANTS_TABLE})`).all();
|
|
74705
|
+
return indexes.some((index) => {
|
|
74706
|
+
if (index.origin !== "u") return false;
|
|
74707
|
+
const columns = db.prepare(`PRAGMA index_info(${JSON.stringify(index.name)})`).all();
|
|
74708
|
+
return columns.length === 1 && columns[0].name === "child_session_id";
|
|
74709
|
+
});
|
|
74710
|
+
}
|
|
74711
|
+
var REBUILT_TABLE = `${GRANTS_TABLE}_rebuild`;
|
|
74712
|
+
function ddlWithoutColumnUnique(db) {
|
|
74713
|
+
const { sql } = db.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`).get(GRANTS_TABLE);
|
|
74714
|
+
const withoutUnique = sql.replace(/(\bchild_session_id\s+TEXT)\s+UNIQUE\b/i, "$1");
|
|
74715
|
+
const renamed = withoutUnique.replace(
|
|
74716
|
+
/^CREATE TABLE\s+(?:IF NOT EXISTS\s+)?["`]?session_collaboration_grants["`]?/i,
|
|
74717
|
+
`CREATE TABLE ${REBUILT_TABLE}`
|
|
74718
|
+
);
|
|
74719
|
+
return withoutUnique !== sql && renamed !== withoutUnique ? renamed : void 0;
|
|
74720
|
+
}
|
|
74721
|
+
function rebuildTable(db, ddl) {
|
|
74722
|
+
const indexes = db.prepare(`
|
|
74723
|
+
SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL
|
|
74724
|
+
`).all(GRANTS_TABLE);
|
|
74725
|
+
db.exec(ddl);
|
|
74726
|
+
db.exec(`INSERT INTO ${REBUILT_TABLE} SELECT * FROM ${GRANTS_TABLE}`);
|
|
74727
|
+
db.exec("DROP TABLE session_collaboration_grants");
|
|
74728
|
+
db.exec(`ALTER TABLE ${REBUILT_TABLE} RENAME TO session_collaboration_grants`);
|
|
74729
|
+
for (const index of indexes) db.exec(index.sql);
|
|
74730
|
+
}
|
|
74731
|
+
|
|
74687
74732
|
// ../../packages/runtime/src/db/database.ts
|
|
74688
74733
|
init_session_provider_definitions();
|
|
74689
74734
|
init_platform_registry();
|
|
@@ -74920,7 +74965,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_grants (
|
|
|
74920
74965
|
credential_secret TEXT,
|
|
74921
74966
|
credential_hint TEXT NOT NULL,
|
|
74922
74967
|
parent_session_id TEXT NOT NULL,
|
|
74923
|
-
child_session_id TEXT
|
|
74968
|
+
child_session_id TEXT,
|
|
74924
74969
|
agent_id TEXT NOT NULL,
|
|
74925
74970
|
task TEXT NOT NULL,
|
|
74926
74971
|
config_json TEXT NOT NULL,
|
|
@@ -74962,6 +75007,7 @@ CREATE TABLE IF NOT EXISTS session_collaboration_cursors (
|
|
|
74962
75007
|
if (!grantCols.some((c) => c.name === "kind")) {
|
|
74963
75008
|
db.exec(`ALTER TABLE session_collaboration_grants ADD COLUMN kind TEXT NOT NULL DEFAULT 'spawn'`);
|
|
74964
75009
|
}
|
|
75010
|
+
ensureCollaborationGrantUniqueness(db);
|
|
74965
75011
|
}
|
|
74966
75012
|
function ensureSessionTagsColumn(db) {
|
|
74967
75013
|
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
@@ -79999,10 +80045,9 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
79999
80045
|
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80000
80046
|
if (denied) return denied;
|
|
80001
80047
|
const p3 = asRecord24(payload);
|
|
80002
|
-
const
|
|
80003
|
-
|
|
80004
|
-
|
|
80005
|
-
return { error: { code: "invalid_argument", message: "credential or grantId required" } };
|
|
80048
|
+
const launchId = typeof p3.launchId === "string" ? p3.launchId.trim() : "";
|
|
80049
|
+
if (!launchId) {
|
|
80050
|
+
return { error: { code: "invalid_argument", message: "launchId required" } };
|
|
80006
80051
|
}
|
|
80007
80052
|
const callerSessionId = typeof p3.callerSessionId === "string" && p3.callerSessionId.trim() ? p3.callerSessionId.trim() : typeof p3.parentSessionId === "string" && p3.parentSessionId.trim() ? p3.parentSessionId.trim() : "";
|
|
80008
80053
|
if (!callerSessionId) {
|
|
@@ -80030,8 +80075,8 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
80030
80075
|
try {
|
|
80031
80076
|
return {
|
|
80032
80077
|
result: await ctx.collaboration.start({
|
|
80033
|
-
|
|
80034
|
-
|
|
80078
|
+
launchId,
|
|
80079
|
+
task: typeof p3.task === "string" ? p3.task : void 0,
|
|
80035
80080
|
formAnswers: p3.formAnswers && typeof p3.formAnswers === "object" ? p3.formAnswers : void 0,
|
|
80036
80081
|
callerSessionId,
|
|
80037
80082
|
controllerClientSessionId: ctx.client.clientSessionId
|
|
@@ -80041,36 +80086,39 @@ async function handleCollaborationStart(payload, ctx) {
|
|
|
80041
80086
|
return mapThrown8(err);
|
|
80042
80087
|
}
|
|
80043
80088
|
}
|
|
80089
|
+
function assertMailboxLease(p3, sessionId, ctx) {
|
|
80090
|
+
const leaseId = String(p3.leaseId ?? "").trim();
|
|
80091
|
+
if (!leaseId) return { error: { code: "invalid_argument", message: "leaseId required" } };
|
|
80092
|
+
try {
|
|
80093
|
+
ctx.leases.assertValid({
|
|
80094
|
+
resource: { environmentId: ctx.identity.environmentId, sessionId },
|
|
80095
|
+
leaseId,
|
|
80096
|
+
generation: String(p3.generation ?? ""),
|
|
80097
|
+
holderClientId: ctx.client.clientSessionId
|
|
80098
|
+
});
|
|
80099
|
+
return null;
|
|
80100
|
+
} catch (err) {
|
|
80101
|
+
return mapThrown8(err);
|
|
80102
|
+
}
|
|
80103
|
+
}
|
|
80044
80104
|
function handleCollaborationSend(payload, ctx) {
|
|
80045
80105
|
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80046
80106
|
if (denied) return denied;
|
|
80047
80107
|
const p3 = asRecord24(payload);
|
|
80048
|
-
const credential = String(p3.credential ?? "");
|
|
80049
80108
|
const sessionId = String(p3.sessionId ?? p3.fromSessionId ?? "");
|
|
80050
80109
|
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
80110
|
if (!sessionId) {
|
|
80055
80111
|
return { error: { code: "invalid_argument", message: "sessionId required" } };
|
|
80056
80112
|
}
|
|
80057
|
-
const
|
|
80058
|
-
if (
|
|
80059
|
-
return { error: { code: "invalid_argument", message: "leaseId required" } };
|
|
80060
|
-
}
|
|
80113
|
+
const leaseDenied = assertMailboxLease(p3, sessionId, ctx);
|
|
80114
|
+
if (leaseDenied) return leaseDenied;
|
|
80061
80115
|
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
80116
|
return {
|
|
80069
80117
|
result: ctx.collaboration.send({
|
|
80070
|
-
|
|
80118
|
+
sessionId,
|
|
80119
|
+
to: typeof p3.to === "string" ? p3.to : void 0,
|
|
80071
80120
|
content,
|
|
80072
|
-
clientMessageId: typeof p3.clientMessageId === "string" ? p3.clientMessageId : void 0
|
|
80073
|
-
sessionId
|
|
80121
|
+
clientMessageId: typeof p3.clientMessageId === "string" ? p3.clientMessageId : void 0
|
|
80074
80122
|
})
|
|
80075
80123
|
};
|
|
80076
80124
|
} catch (err) {
|
|
@@ -80078,22 +80126,20 @@ function handleCollaborationSend(payload, ctx) {
|
|
|
80078
80126
|
}
|
|
80079
80127
|
}
|
|
80080
80128
|
function handleCollaborationRetrieve(payload, ctx) {
|
|
80081
|
-
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.
|
|
80129
|
+
const denied = requireScopes8(ctx.client, OPERATION_SCOPES.operateSession);
|
|
80082
80130
|
if (denied) return denied;
|
|
80083
80131
|
const p3 = asRecord24(payload);
|
|
80084
|
-
const credential = String(p3.credential ?? "");
|
|
80085
80132
|
const sessionId = String(p3.sessionId ?? "");
|
|
80086
|
-
if (!credential) {
|
|
80087
|
-
return { error: { code: "invalid_argument", message: "credential required" } };
|
|
80088
|
-
}
|
|
80089
80133
|
if (!sessionId) {
|
|
80090
80134
|
return { error: { code: "invalid_argument", message: "sessionId required" } };
|
|
80091
80135
|
}
|
|
80136
|
+
const leaseDenied = assertMailboxLease(p3, sessionId, ctx);
|
|
80137
|
+
if (leaseDenied) return leaseDenied;
|
|
80092
80138
|
try {
|
|
80093
80139
|
return {
|
|
80094
80140
|
result: ctx.collaboration.retrieve({
|
|
80095
|
-
credential,
|
|
80096
80141
|
sessionId,
|
|
80142
|
+
from: Array.isArray(p3.from) ? p3.from.filter((id) => typeof id === "string") : void 0,
|
|
80097
80143
|
max: typeof p3.max === "number" ? p3.max : void 0
|
|
80098
80144
|
})
|
|
80099
80145
|
};
|
|
@@ -87037,1187 +87083,1148 @@ var SessionRuntime2 = class extends SessionRuntime {
|
|
|
87037
87083
|
init_harness_runners();
|
|
87038
87084
|
init_codex_turn_runner();
|
|
87039
87085
|
|
|
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 {};
|
|
87086
|
+
// ../../packages/runtime/src/collaboration/errors.ts
|
|
87087
|
+
var CollaborationError = class extends Error {
|
|
87088
|
+
constructor(message, code) {
|
|
87089
|
+
super(message);
|
|
87090
|
+
this.code = code;
|
|
87091
|
+
this.name = "CollaborationError";
|
|
87062
87092
|
}
|
|
87063
|
-
|
|
87064
|
-
|
|
87065
|
-
|
|
87066
|
-
|
|
87067
|
-
|
|
87068
|
-
|
|
87069
|
-
|
|
87093
|
+
code;
|
|
87094
|
+
};
|
|
87095
|
+
|
|
87096
|
+
// ../../packages/runtime/src/collaboration/launch.ts
|
|
87097
|
+
init_agent_types();
|
|
87098
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
87099
|
+
var LABEL_MAX = 64;
|
|
87100
|
+
var MAX_LAUNCHES_PER_REQUEST = 16;
|
|
87101
|
+
var EDITABLE_PERMISSION_MODES = /* @__PURE__ */ new Set([
|
|
87102
|
+
"default",
|
|
87103
|
+
"acceptEdits",
|
|
87104
|
+
"bypassPermissions",
|
|
87105
|
+
"plan",
|
|
87106
|
+
"dontAsk",
|
|
87107
|
+
"auto",
|
|
87108
|
+
"agent"
|
|
87109
|
+
]);
|
|
87110
|
+
var EDITABLE_SANDBOX_MODES = /* @__PURE__ */ new Set(["off", "on", "auto"]);
|
|
87070
87111
|
function resolveLaunchMode(raw) {
|
|
87071
87112
|
if (raw === "link") return "link";
|
|
87072
87113
|
if (raw === "handoff") return "handoff";
|
|
87073
87114
|
return "spawn";
|
|
87074
87115
|
}
|
|
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>`;
|
|
87116
|
+
function humanLaunchId(launchId) {
|
|
87117
|
+
const id = launchId?.trim();
|
|
87118
|
+
return id && id.length <= 32 && !UUID_RE.test(id) ? id : void 0;
|
|
87081
87119
|
}
|
|
87082
87120
|
function deriveCollaborationRole(input) {
|
|
87083
87121
|
const explicit = input.role?.trim();
|
|
87084
|
-
if (explicit) return explicit.slice(0,
|
|
87085
|
-
const
|
|
87086
|
-
if (
|
|
87087
|
-
return launchId;
|
|
87088
|
-
}
|
|
87122
|
+
if (explicit) return explicit.slice(0, LABEL_MAX);
|
|
87123
|
+
const fromId = humanLaunchId(input.launchId);
|
|
87124
|
+
if (fromId) return fromId;
|
|
87089
87125
|
const firstLine = input.task.split(/\n/, 1)[0]?.trim() ?? "";
|
|
87090
87126
|
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
|
-
}
|
|
87127
|
+
if (youAre?.[1]) return youAre[1].replace(/[.\s]+$/g, "").slice(0, 40);
|
|
87094
87128
|
return "Agent";
|
|
87095
87129
|
}
|
|
87096
87130
|
function deriveCollaborationName(input) {
|
|
87097
87131
|
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";
|
|
87132
|
+
if (explicit) return explicit.slice(0, LABEL_MAX);
|
|
87133
|
+
return humanLaunchId(input.launchId) ?? "Agent";
|
|
87104
87134
|
}
|
|
87105
87135
|
function collaborationSessionTitle(name, role) {
|
|
87106
|
-
|
|
87107
|
-
const r = role.trim() || "Agent";
|
|
87108
|
-
return `${n} - ${r}`;
|
|
87136
|
+
return `${name.trim() || "Agent"} - ${role.trim() || "Agent"}`;
|
|
87109
87137
|
}
|
|
87110
|
-
|
|
87111
|
-
|
|
87112
|
-
|
|
87138
|
+
function parseGrantConfig(raw) {
|
|
87139
|
+
try {
|
|
87140
|
+
return JSON.parse(raw);
|
|
87141
|
+
} catch {
|
|
87142
|
+
return {};
|
|
87113
87143
|
}
|
|
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
|
-
});
|
|
87144
|
+
}
|
|
87145
|
+
function invalid2(message) {
|
|
87146
|
+
throw new CollaborationError(message, "invalid_argument");
|
|
87147
|
+
}
|
|
87148
|
+
function assertLabel(kind, value) {
|
|
87149
|
+
if (value.length > LABEL_MAX) invalid2(`A launch ${kind} may contain at most ${LABEL_MAX} characters`);
|
|
87150
|
+
return value;
|
|
87151
|
+
}
|
|
87152
|
+
function assertLaunchCount(count) {
|
|
87153
|
+
if (count === 0) invalid2("launches must contain at least one proposed session");
|
|
87154
|
+
if (count > MAX_LAUNCHES_PER_REQUEST) {
|
|
87155
|
+
invalid2(`A single request may contain at most ${MAX_LAUNCHES_PER_REQUEST} launches`);
|
|
87156
|
+
}
|
|
87157
|
+
}
|
|
87158
|
+
function normalizeLaunchLabels(mode, launch2, linkPeerTitle) {
|
|
87159
|
+
const summary2 = launch2.summary?.trim();
|
|
87160
|
+
if (!summary2) invalid2("Every launch must include a non-empty summary");
|
|
87161
|
+
if (mode === "link") {
|
|
87162
|
+
return {
|
|
87163
|
+
summary: summary2,
|
|
87164
|
+
name: assertLabel("name", launch2.name?.trim() || linkPeerTitle || ""),
|
|
87165
|
+
role: assertLabel("role", launch2.role?.trim() || "Peer")
|
|
87161
87166
|
};
|
|
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
87167
|
}
|
|
87185
|
-
|
|
87186
|
-
|
|
87187
|
-
|
|
87188
|
-
|
|
87189
|
-
|
|
87190
|
-
|
|
87191
|
-
|
|
87192
|
-
|
|
87193
|
-
|
|
87194
|
-
|
|
87195
|
-
}
|
|
87196
|
-
|
|
87197
|
-
|
|
87198
|
-
|
|
87199
|
-
|
|
87200
|
-
|
|
87201
|
-
|
|
87202
|
-
|
|
87203
|
-
|
|
87204
|
-
|
|
87205
|
-
|
|
87168
|
+
const name = launch2.name?.trim();
|
|
87169
|
+
if (!name) invalid2(`Every ${mode} launch must include a non-empty name`);
|
|
87170
|
+
const role = launch2.role?.trim();
|
|
87171
|
+
if (!role) invalid2(`Every ${mode} launch must include a non-empty role`);
|
|
87172
|
+
return { summary: summary2, name: assertLabel("name", name), role: assertLabel("role", role) };
|
|
87173
|
+
}
|
|
87174
|
+
function normalizeStartTask(mode, raw) {
|
|
87175
|
+
const task = typeof raw === "string" ? raw.trim() : "";
|
|
87176
|
+
if (!task && mode !== "link") invalid2(`Starting a ${mode} launch requires a non-empty task`);
|
|
87177
|
+
if (task.length > SESSION_AGENT_TASK_MAX) {
|
|
87178
|
+
invalid2(`A launch task may contain at most ${SESSION_AGENT_TASK_MAX.toLocaleString()} characters`);
|
|
87179
|
+
}
|
|
87180
|
+
return task;
|
|
87181
|
+
}
|
|
87182
|
+
function patchEditableLaunchConfig(base, rawPatch) {
|
|
87183
|
+
const patch = rawPatch && typeof rawPatch === "object" ? rawPatch : {};
|
|
87184
|
+
return {
|
|
87185
|
+
...base,
|
|
87186
|
+
...typeof patch.model === "string" && patch.model.trim() ? { model: patch.model.trim() } : {},
|
|
87187
|
+
...typeof patch.effort === "string" && patch.effort.trim() ? { effort: patch.effort.trim() } : {},
|
|
87188
|
+
...typeof patch.fastMode === "boolean" ? { fastMode: patch.fastMode } : {},
|
|
87189
|
+
...patch.apiProviderId === null || typeof patch.apiProviderId === "string" ? { apiProviderId: patch.apiProviderId } : {},
|
|
87190
|
+
...typeof patch.permissionMode === "string" && EDITABLE_PERMISSION_MODES.has(patch.permissionMode) ? { permissionMode: patch.permissionMode } : {},
|
|
87191
|
+
...typeof patch.sandboxMode === "string" && EDITABLE_SANDBOX_MODES.has(patch.sandboxMode) ? { sandboxMode: patch.sandboxMode } : {}
|
|
87192
|
+
};
|
|
87193
|
+
}
|
|
87194
|
+
function mergeConfirmedLaunches(proposed, content) {
|
|
87195
|
+
const packed = content?.[SESSION_AGENT_LAUNCHES_FIELD];
|
|
87196
|
+
if (typeof packed !== "string") return proposed;
|
|
87197
|
+
let edited;
|
|
87198
|
+
try {
|
|
87199
|
+
edited = JSON.parse(packed);
|
|
87200
|
+
} catch {
|
|
87201
|
+
invalid2("The confirmed launch configuration is invalid");
|
|
87202
|
+
}
|
|
87203
|
+
if (!Array.isArray(edited)) invalid2("The confirmed launch configuration is invalid");
|
|
87204
|
+
if (edited.length !== proposed.length) {
|
|
87205
|
+
invalid2("The confirmed request must contain the same launches that were proposed");
|
|
87206
|
+
}
|
|
87207
|
+
const proposedById = new Map(proposed.map((launch2) => [launch2.launchId, launch2]));
|
|
87208
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87209
|
+
return edited.map((raw) => {
|
|
87210
|
+
if (!raw || typeof raw !== "object") invalid2("The confirmed launch configuration is invalid");
|
|
87211
|
+
const item = raw;
|
|
87212
|
+
const launchId = typeof item.launchId === "string" ? item.launchId : "";
|
|
87213
|
+
const base = proposedById.get(launchId);
|
|
87214
|
+
if (!base) invalid2(`Unknown launchId in confirmed configuration: ${launchId || "(missing)"}`);
|
|
87215
|
+
if (seen.has(launchId)) invalid2("Confirmed launches must have unique launchIds");
|
|
87216
|
+
seen.add(launchId);
|
|
87217
|
+
if (base.mode === "link") return { ...base, config: { ...base.config } };
|
|
87218
|
+
return {
|
|
87219
|
+
...base,
|
|
87220
|
+
// A tampered mode could turn a supervised child into a one-way handoff.
|
|
87221
|
+
mode: base.mode ?? "spawn",
|
|
87222
|
+
config: patchEditableLaunchConfig(base.config, item.config)
|
|
87223
|
+
};
|
|
87224
|
+
});
|
|
87225
|
+
}
|
|
87226
|
+
|
|
87227
|
+
// ../../packages/runtime/src/collaboration/text.ts
|
|
87228
|
+
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.';
|
|
87229
|
+
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.";
|
|
87230
|
+
var NO_PEERS_HINT = "You have no collaboration peers. Launch or link one with session_collab_request, then session_collab_start.";
|
|
87231
|
+
var NESTED_COLLABORATION_UNSUPPORTED = "Nested collaboration is not supported. Only top-level (non-collaboration-child) sessions may request agents.";
|
|
87232
|
+
function collaborationSystemPrompt(parentSessionId) {
|
|
87233
|
+
return `<superone-session-collaboration>
|
|
87234
|
+
You are running as a user-approved child session of SuperOne session ${parentSessionId}.
|
|
87235
|
+
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.
|
|
87236
|
+
</superone-session-collaboration>`;
|
|
87237
|
+
}
|
|
87238
|
+
function sessionLabel(sessionId, title) {
|
|
87239
|
+
return `SuperOne session ${sessionId} ("${title}")`;
|
|
87240
|
+
}
|
|
87241
|
+
function mailboxWakeText(from) {
|
|
87242
|
+
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.`;
|
|
87243
|
+
}
|
|
87244
|
+
function linkActivationWakeText(input) {
|
|
87245
|
+
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.`;
|
|
87246
|
+
}
|
|
87247
|
+
function handoffTaskContent(input) {
|
|
87248
|
+
const title = input.parentTitle || input.parentSessionId.slice(0, 8);
|
|
87249
|
+
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.
|
|
87250
|
+
|
|
87251
|
+
` + input.task;
|
|
87252
|
+
}
|
|
87253
|
+
|
|
87254
|
+
// ../../packages/runtime/src/collaboration/mailbox.ts
|
|
87255
|
+
var MAX_MAILBOX_CONTENT = 1e5;
|
|
87256
|
+
var MAX_MESSAGES_PER_RETRIEVE = 100;
|
|
87257
|
+
function describeLaunchedPeer(grant) {
|
|
87258
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
87259
|
+
const name = deriveCollaborationName({ name: config2.name });
|
|
87260
|
+
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
87261
|
+
return { name, role, title: collaborationSessionTitle(name, role), agentId: grant.agent_id, config: config2 };
|
|
87262
|
+
}
|
|
87263
|
+
function describePeerForCaller(grant, callerSessionId, sessionTitle2) {
|
|
87264
|
+
const relation = grant.kind === "link" ? "link" : callerSessionId === grant.child_session_id ? "parent" : "child";
|
|
87265
|
+
if (callerSessionId === grant.child_session_id) {
|
|
87266
|
+
const title = sessionTitle2(grant.parent_session_id)?.trim() || grant.parent_session_id.slice(0, 8);
|
|
87267
|
+
return {
|
|
87268
|
+
sessionId: grant.parent_session_id,
|
|
87269
|
+
name: relation === "parent" ? "Parent" : title,
|
|
87270
|
+
role: relation === "parent" ? "" : "Peer",
|
|
87271
|
+
title,
|
|
87272
|
+
relation
|
|
87273
|
+
};
|
|
87274
|
+
}
|
|
87275
|
+
const child = describeLaunchedPeer(grant);
|
|
87276
|
+
return {
|
|
87277
|
+
sessionId: grant.child_session_id ?? "",
|
|
87278
|
+
name: child.name,
|
|
87279
|
+
role: child.role,
|
|
87280
|
+
title: child.title,
|
|
87281
|
+
relation
|
|
87282
|
+
};
|
|
87283
|
+
}
|
|
87284
|
+
function listMailboxChannels(store, callerSessionId, sessionTitle2) {
|
|
87285
|
+
return store.channelsFor(callerSessionId).map((grant) => ({
|
|
87286
|
+
grant,
|
|
87287
|
+
peer: describePeerForCaller(grant, callerSessionId, sessionTitle2)
|
|
87288
|
+
}));
|
|
87289
|
+
}
|
|
87290
|
+
function peersOf(channels) {
|
|
87291
|
+
return [...new Map(channels.map((channel) => [channel.peer.sessionId, channel.peer])).values()];
|
|
87292
|
+
}
|
|
87293
|
+
function assertNotPeeredElsewhere(store, callerSessionId, peerSessionId) {
|
|
87294
|
+
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));
|
|
87295
|
+
if (other) {
|
|
87296
|
+
throw new CollaborationError(
|
|
87297
|
+
`Session ${peerSessionId} is already one of your collaboration peers; message it with session_collab_send({ to: ${JSON.stringify(peerSessionId)} }).`,
|
|
87298
|
+
"failed_precondition"
|
|
87299
|
+
);
|
|
87300
|
+
}
|
|
87301
|
+
}
|
|
87302
|
+
function formatPeers(peers) {
|
|
87303
|
+
return peers.map((peer) => `${peer.sessionId} ("${peer.title}", ${peer.relation})`).join("; ");
|
|
87304
|
+
}
|
|
87305
|
+
function resolveSendChannel(store, callerSessionId, to2, sessionTitle2) {
|
|
87306
|
+
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
87307
|
+
const peers = peersOf(channels);
|
|
87308
|
+
const target = to2?.trim();
|
|
87309
|
+
if (!target) {
|
|
87310
|
+
if (peers.length === 0) throw new CollaborationError(NO_PEERS_HINT, "failed_precondition");
|
|
87311
|
+
const parent = channels.find((channel) => channel.peer.relation === "parent");
|
|
87312
|
+
if (parent) return parent;
|
|
87313
|
+
if (peers.length > 1) {
|
|
87314
|
+
throw new CollaborationError(
|
|
87315
|
+
`You have several collaboration peers; pass \`to\` with one of their session ids: ${formatPeers(peers)}`,
|
|
87316
|
+
"invalid_argument"
|
|
87206
87317
|
);
|
|
87207
87318
|
}
|
|
87208
|
-
|
|
87209
|
-
|
|
87210
|
-
|
|
87211
|
-
|
|
87212
|
-
|
|
87213
|
-
|
|
87214
|
-
|
|
87215
|
-
|
|
87216
|
-
|
|
87217
|
-
|
|
87319
|
+
return channels[channels.length - 1];
|
|
87320
|
+
}
|
|
87321
|
+
const matches = channels.filter((channel) => channel.peer.sessionId === target);
|
|
87322
|
+
if (matches.length > 0) return matches[matches.length - 1];
|
|
87323
|
+
if (store.isHandoffPair(callerSessionId, target)) {
|
|
87324
|
+
throw new CollaborationError(HANDOFF_NO_MAILBOX, "failed_precondition");
|
|
87325
|
+
}
|
|
87326
|
+
throw new CollaborationError(
|
|
87327
|
+
`Session ${target} is not one of your collaboration peers. ` + (peers.length > 0 ? `Your peers: ${formatPeers(peers)}` : NO_PEERS_HINT),
|
|
87328
|
+
"forbidden"
|
|
87329
|
+
);
|
|
87330
|
+
}
|
|
87331
|
+
function normalizeMailboxContent(raw) {
|
|
87332
|
+
const content = typeof raw === "string" ? raw.trim() : "";
|
|
87333
|
+
if (!content) throw new CollaborationError("content must not be empty", "invalid_argument");
|
|
87334
|
+
if (content.length > MAX_MAILBOX_CONTENT) {
|
|
87335
|
+
throw new CollaborationError("content may contain at most 100,000 characters", "invalid_argument");
|
|
87336
|
+
}
|
|
87337
|
+
return content;
|
|
87338
|
+
}
|
|
87339
|
+
function readCallerMailbox(store, callerSessionId, options, sessionTitle2) {
|
|
87340
|
+
const channels = listMailboxChannels(store, callerSessionId, sessionTitle2);
|
|
87341
|
+
const peers = peersOf(channels);
|
|
87342
|
+
const from = options.from?.map((id) => id.trim()).filter(Boolean) ?? [];
|
|
87343
|
+
if (from.length > 0) {
|
|
87344
|
+
const known = new Set(peers.map((peer) => peer.sessionId));
|
|
87345
|
+
const unknown2 = from.filter((id) => !known.has(id));
|
|
87346
|
+
if (unknown2.length > 0) {
|
|
87347
|
+
throw new CollaborationError(
|
|
87348
|
+
`Not your collaboration peers: ${unknown2.join(", ")}. ` + (peers.length > 0 ? `Your peers: ${formatPeers(peers)}` : NO_PEERS_HINT),
|
|
87349
|
+
"forbidden"
|
|
87350
|
+
);
|
|
87218
87351
|
}
|
|
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 {
|
|
87352
|
+
}
|
|
87353
|
+
const selected = from.length > 0 ? channels.filter((channel) => from.includes(channel.peer.sessionId)) : channels;
|
|
87354
|
+
if (selected.length === 0) return { messages: [], peers };
|
|
87355
|
+
const limit = Math.min(MAX_MESSAGES_PER_RETRIEVE, Math.max(1, Math.floor(options.limit ?? MAX_MESSAGES_PER_RETRIEVE)));
|
|
87356
|
+
const peerByGrant = new Map(selected.map((channel) => [channel.grant.grant_id, channel.peer]));
|
|
87357
|
+
const perGrantLimit = Math.max(1, Math.floor(limit / selected.length));
|
|
87358
|
+
const messages = store.readMailbox(callerSessionId, [...peerByGrant.keys()], perGrantLimit).flatMap(({ grantId, rows }) => rows.map((row) => ({
|
|
87359
|
+
messageId: row.id,
|
|
87360
|
+
sequence: row.sequence,
|
|
87361
|
+
fromSessionId: row.sender_session_id,
|
|
87362
|
+
from: peerByGrant.get(grantId),
|
|
87363
|
+
content: row.content,
|
|
87364
|
+
createdAt: row.created_at
|
|
87365
|
+
}))).sort((a, b3) => a.createdAt.localeCompare(b3.createdAt)).slice(0, limit);
|
|
87366
|
+
return { messages, peers };
|
|
87367
|
+
}
|
|
87368
|
+
|
|
87369
|
+
// ../../packages/runtime/src/collaboration/start.ts
|
|
87370
|
+
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).";
|
|
87371
|
+
function recordApprovedLaunch(store, parentSessionId, launch2, config2) {
|
|
87372
|
+
const mode = resolveLaunchMode(launch2.mode);
|
|
87373
|
+
const { launchId, summary: summary2, name, role } = launch2;
|
|
87374
|
+
const title = collaborationSessionTitle(name, role);
|
|
87375
|
+
if (mode === "link") {
|
|
87376
|
+
const peerSessionId = launch2.sessionId?.trim();
|
|
87377
|
+
if (!peerSessionId) throw new CollaborationError("Link launches require sessionId", "invalid_argument");
|
|
87378
|
+
const existing = store.findLinkGrant(parentSessionId, peerSessionId);
|
|
87379
|
+
if (existing) {
|
|
87380
|
+
const existingConfig = { ...parseGrantConfig(existing.config_json), launchId };
|
|
87381
|
+
store.updateConfig(existing.grant_id, existingConfig);
|
|
87382
|
+
return {
|
|
87383
|
+
grantId: existing.grant_id,
|
|
87384
|
+
approved: {
|
|
87261
87385
|
launchId,
|
|
87262
|
-
mode
|
|
87263
|
-
agentId:
|
|
87386
|
+
mode,
|
|
87387
|
+
agentId: existing.agent_id,
|
|
87264
87388
|
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,
|
|
87389
|
+
summary: summary2,
|
|
87344
87390
|
name,
|
|
87345
|
-
role
|
|
87346
|
-
|
|
87347
|
-
|
|
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
|
-
}
|
|
87371
|
-
const insertChild = this.deps.db.prepare(`
|
|
87372
|
-
INSERT INTO session_collaboration_grants
|
|
87373
|
-
(credential_hash, credential_secret, credential_hint, parent_session_id, agent_id, task, config_json, created_at, kind)
|
|
87374
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
87375
|
-
`);
|
|
87376
|
-
const insertLink = this.deps.db.prepare(`
|
|
87377
|
-
INSERT INTO session_collaboration_grants
|
|
87378
|
-
(credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id, agent_id, task, config_json, created_at, kind)
|
|
87379
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'link')
|
|
87380
|
-
`);
|
|
87381
|
-
const results = this.deps.db.transaction(
|
|
87382
|
-
() => confirmed.map((launch2) => {
|
|
87383
|
-
const mode = resolveLaunchMode(launch2.mode);
|
|
87384
|
-
if (mode === "link") {
|
|
87385
|
-
const peerSessionId = launch2.sessionId;
|
|
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);
|
|
87391
|
+
role,
|
|
87392
|
+
title,
|
|
87393
|
+
config: existingConfig,
|
|
87394
|
+
reused: true
|
|
87576
87395
|
}
|
|
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
87396
|
};
|
|
87616
87397
|
}
|
|
87617
|
-
|
|
87618
|
-
|
|
87619
|
-
|
|
87620
|
-
|
|
87621
|
-
|
|
87622
|
-
|
|
87623
|
-
|
|
87624
|
-
|
|
87625
|
-
|
|
87626
|
-
|
|
87627
|
-
|
|
87628
|
-
|
|
87629
|
-
|
|
87630
|
-
|
|
87631
|
-
|
|
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
|
-
}
|
|
87398
|
+
const linkConfig = {
|
|
87399
|
+
launchId,
|
|
87400
|
+
name,
|
|
87401
|
+
role,
|
|
87402
|
+
summary: summary2,
|
|
87403
|
+
peerSessionId,
|
|
87404
|
+
peerTitle: launch2.peerTitle,
|
|
87405
|
+
peerProjectPath: launch2.peerProjectPath
|
|
87406
|
+
};
|
|
87407
|
+
const grantId2 = store.createGrant({
|
|
87408
|
+
kind: "link",
|
|
87409
|
+
parentSessionId,
|
|
87410
|
+
childSessionId: peerSessionId,
|
|
87411
|
+
agentId: "",
|
|
87412
|
+
config: linkConfig
|
|
87719
87413
|
});
|
|
87720
87414
|
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,
|
|
87415
|
+
grantId: grantId2,
|
|
87416
|
+
approved: {
|
|
87417
|
+
launchId,
|
|
87418
|
+
mode,
|
|
87419
|
+
agentId: "",
|
|
87420
|
+
sessionId: peerSessionId,
|
|
87421
|
+
summary: summary2,
|
|
87422
|
+
name,
|
|
87737
87423
|
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 };
|
|
87424
|
+
title,
|
|
87425
|
+
config: linkConfig,
|
|
87426
|
+
reused: false
|
|
87781
87427
|
}
|
|
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
87428
|
};
|
|
87831
87429
|
}
|
|
87832
|
-
|
|
87833
|
-
|
|
87834
|
-
|
|
87835
|
-
|
|
87836
|
-
|
|
87837
|
-
|
|
87838
|
-
|
|
87430
|
+
const grantConfig = { ...config2, launchId, name, role, summary: summary2 };
|
|
87431
|
+
const grantId = store.createGrant({ kind: mode, parentSessionId, agentId: launch2.agentId, config: grantConfig });
|
|
87432
|
+
return {
|
|
87433
|
+
grantId,
|
|
87434
|
+
approved: {
|
|
87435
|
+
launchId,
|
|
87436
|
+
mode,
|
|
87437
|
+
agentId: launch2.agentId,
|
|
87438
|
+
summary: summary2,
|
|
87439
|
+
name,
|
|
87440
|
+
role,
|
|
87441
|
+
title,
|
|
87442
|
+
config: grantConfig,
|
|
87443
|
+
reused: false
|
|
87839
87444
|
}
|
|
87840
|
-
|
|
87841
|
-
|
|
87842
|
-
|
|
87445
|
+
};
|
|
87446
|
+
}
|
|
87447
|
+
function prepareLaunchStart(store, callerSessionId, input) {
|
|
87448
|
+
const launchId = typeof input.launchId === "string" ? input.launchId.trim() : "";
|
|
87449
|
+
if (!launchId) throw new CollaborationError("launchId is required", "invalid_argument");
|
|
87450
|
+
const grant = store.grantForLaunch(callerSessionId, launchId);
|
|
87451
|
+
if (!grant) {
|
|
87452
|
+
throw new CollaborationError(
|
|
87453
|
+
`No approved launch "${launchId}" in this session. Pass a launchId returned by session_collab_request.`,
|
|
87454
|
+
"not_found"
|
|
87843
87455
|
);
|
|
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
87456
|
}
|
|
87896
|
-
|
|
87897
|
-
|
|
87898
|
-
|
|
87899
|
-
|
|
87900
|
-
|
|
87901
|
-
|
|
87902
|
-
|
|
87903
|
-
|
|
87904
|
-
|
|
87905
|
-
|
|
87906
|
-
|
|
87907
|
-
|
|
87908
|
-
|
|
87909
|
-
|
|
87910
|
-
|
|
87911
|
-
|
|
87912
|
-
|
|
87913
|
-
|
|
87457
|
+
if (grant.task_sent === 1 || grant.task) return grant;
|
|
87458
|
+
const task = normalizeStartTask(grant.kind, input.task);
|
|
87459
|
+
if (!task) return grant;
|
|
87460
|
+
store.setTask(grant.grant_id, task);
|
|
87461
|
+
return { ...grant, task };
|
|
87462
|
+
}
|
|
87463
|
+
|
|
87464
|
+
// ../../packages/runtime/src/collaboration/store.ts
|
|
87465
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
87466
|
+
var GRANT_COLUMNS = `credential_hash AS grant_id, parent_session_id, child_session_id,
|
|
87467
|
+
agent_id, task, config_json, task_sent, COALESCE(kind, 'spawn') AS kind, started_at`;
|
|
87468
|
+
var MESSAGE_COLUMNS = `id, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at`;
|
|
87469
|
+
function nowIso() {
|
|
87470
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
87471
|
+
}
|
|
87472
|
+
var CollaborationStore = class {
|
|
87473
|
+
constructor(db) {
|
|
87474
|
+
this.db = db;
|
|
87914
87475
|
}
|
|
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;
|
|
87476
|
+
db;
|
|
87477
|
+
transaction(fn) {
|
|
87478
|
+
return this.db.transaction(fn)();
|
|
87927
87479
|
}
|
|
87928
|
-
|
|
87929
|
-
|
|
87930
|
-
|
|
87931
|
-
agent_id, task, config_json, task_sent,
|
|
87932
|
-
COALESCE(kind, 'spawn') AS kind, started_at
|
|
87933
|
-
FROM session_collaboration_grants WHERE credential_hash = ?`
|
|
87934
|
-
).get(hashCredential(credential)) ?? null;
|
|
87935
|
-
}
|
|
87936
|
-
async deliverLinkOpening(grant, credential, initiatorTitle) {
|
|
87937
|
-
if (grant.task_sent === 1 || !grant.child_session_id) return;
|
|
87938
|
-
const content = grant.task.trim();
|
|
87939
|
-
if (!content) {
|
|
87940
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
|
|
87941
|
-
return;
|
|
87942
|
-
}
|
|
87943
|
-
const recipientSessionId = grant.child_session_id;
|
|
87944
|
-
try {
|
|
87945
|
-
this.deps.db.prepare(
|
|
87946
|
-
`INSERT INTO session_collaboration_messages
|
|
87947
|
-
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
87948
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
87949
|
-
).run(
|
|
87950
|
-
randomUUID11(),
|
|
87951
|
-
grant.credential_hash,
|
|
87952
|
-
this.nextSequence(grant.credential_hash),
|
|
87953
|
-
grant.parent_session_id,
|
|
87954
|
-
recipientSessionId,
|
|
87955
|
-
`link-opening:${grant.credential_hash}`,
|
|
87956
|
-
content,
|
|
87957
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
87958
|
-
);
|
|
87959
|
-
} catch {
|
|
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
|
-
);
|
|
87480
|
+
// --- grants --------------------------------------------------------------
|
|
87481
|
+
grantById(grantId) {
|
|
87482
|
+
return this.db.prepare(`SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants WHERE credential_hash = ?`).get(grantId) ?? null;
|
|
87969
87483
|
}
|
|
87970
|
-
|
|
87971
|
-
|
|
87972
|
-
|
|
87973
|
-
|
|
87974
|
-
|
|
87975
|
-
|
|
87976
|
-
|
|
87977
|
-
|
|
87978
|
-
|
|
87979
|
-
|
|
87980
|
-
|
|
87981
|
-
|
|
87484
|
+
/**
|
|
87485
|
+
* The grant an approved launch created. launchIds are agent-chosen and may
|
|
87486
|
+
* repeat across requests, so the latest approval wins.
|
|
87487
|
+
*/
|
|
87488
|
+
grantForLaunch(parentSessionId, launchId) {
|
|
87489
|
+
return this.db.prepare(`
|
|
87490
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87491
|
+
WHERE parent_session_id = ? AND json_extract(config_json, '$.launchId') = ?
|
|
87492
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
87493
|
+
`).get(parentSessionId, launchId) ?? null;
|
|
87494
|
+
}
|
|
87495
|
+
/** True when `sessionId` is a spawn child (nested collaboration is unsupported). */
|
|
87496
|
+
isSpawnChild(sessionId) {
|
|
87497
|
+
return Boolean(this.db.prepare(`
|
|
87498
|
+
SELECT 1 FROM session_collaboration_grants
|
|
87499
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn' LIMIT 1
|
|
87500
|
+
`).get(sessionId));
|
|
87501
|
+
}
|
|
87502
|
+
spawnGrantForChild(childSessionId) {
|
|
87503
|
+
return this.db.prepare(`
|
|
87504
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87505
|
+
WHERE child_session_id = ? AND COALESCE(kind, 'spawn') = 'spawn'
|
|
87506
|
+
`).get(childSessionId) ?? null;
|
|
87507
|
+
}
|
|
87508
|
+
startedSpawnGrants() {
|
|
87509
|
+
return this.db.prepare(`
|
|
87510
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87511
|
+
WHERE child_session_id IS NOT NULL AND COALESCE(kind, 'spawn') = 'spawn'
|
|
87512
|
+
`).all();
|
|
87982
87513
|
}
|
|
87983
|
-
|
|
87984
|
-
|
|
87985
|
-
|
|
87986
|
-
|
|
87987
|
-
|
|
87988
|
-
|
|
87989
|
-
|
|
87990
|
-
|
|
87514
|
+
/**
|
|
87515
|
+
* Open mailbox channels of `sessionId`: spawn grants with a bound child and
|
|
87516
|
+
* started link grants, oldest first. Handoff grants are never channels.
|
|
87517
|
+
*/
|
|
87518
|
+
channelsFor(sessionId) {
|
|
87519
|
+
return this.db.prepare(`
|
|
87520
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87521
|
+
WHERE (parent_session_id = ? OR child_session_id = ?)
|
|
87522
|
+
AND child_session_id IS NOT NULL
|
|
87523
|
+
AND (COALESCE(kind, 'spawn') = 'spawn' OR (kind = 'link' AND started_at IS NOT NULL))
|
|
87524
|
+
ORDER BY created_at, rowid
|
|
87525
|
+
`).all(sessionId, sessionId);
|
|
87526
|
+
}
|
|
87527
|
+
/** True when one of the two sessions was created by a handoff from the other. */
|
|
87528
|
+
isHandoffPair(a, b3) {
|
|
87529
|
+
return Boolean(this.db.prepare(`
|
|
87530
|
+
SELECT 1 FROM session_collaboration_grants
|
|
87531
|
+
WHERE kind = 'handoff' AND (
|
|
87532
|
+
(parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
87533
|
+
OR (parent_session_id = ? AND json_extract(config_json, '$.handoffSessionId') = ?)
|
|
87534
|
+
) LIMIT 1
|
|
87535
|
+
`).get(a, b3, b3, a));
|
|
87536
|
+
}
|
|
87537
|
+
findLinkGrant(initiatorSessionId, peerSessionId) {
|
|
87538
|
+
return this.db.prepare(`
|
|
87539
|
+
SELECT ${GRANT_COLUMNS} FROM session_collaboration_grants
|
|
87540
|
+
WHERE parent_session_id = ? AND child_session_id = ? AND kind = 'link'
|
|
87541
|
+
`).get(initiatorSessionId, peerSessionId) ?? null;
|
|
87542
|
+
}
|
|
87543
|
+
/**
|
|
87544
|
+
* Persist an approved launch. Link grants bind the peer at once; spawn and
|
|
87545
|
+
* handoff grants bind their session on start. The brief arrives on start.
|
|
87546
|
+
*/
|
|
87547
|
+
createGrant(input) {
|
|
87548
|
+
const grantId = randomUUID11();
|
|
87549
|
+
try {
|
|
87550
|
+
this.db.prepare(`
|
|
87551
|
+
INSERT INTO session_collaboration_grants
|
|
87552
|
+
(credential_hash, credential_secret, credential_hint, parent_session_id, child_session_id,
|
|
87553
|
+
agent_id, task, config_json, created_at, kind)
|
|
87554
|
+
VALUES (?, NULL, '', ?, ?, ?, '', ?, ?, ?)
|
|
87555
|
+
`).run(
|
|
87556
|
+
grantId,
|
|
87557
|
+
input.parentSessionId,
|
|
87558
|
+
input.childSessionId ?? null,
|
|
87559
|
+
input.agentId,
|
|
87560
|
+
JSON.stringify(input.config),
|
|
87561
|
+
nowIso(),
|
|
87562
|
+
input.kind
|
|
87563
|
+
);
|
|
87564
|
+
} catch (error51) {
|
|
87565
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
87566
|
+
if (input.childSessionId && /UNIQUE|unique/i.test(message)) {
|
|
87567
|
+
throw new CollaborationError(
|
|
87568
|
+
`Session ${input.parentSessionId} already has a link to ${input.childSessionId}; request it again to reuse it.`,
|
|
87569
|
+
"failed_precondition"
|
|
87570
|
+
);
|
|
87571
|
+
}
|
|
87572
|
+
throw error51;
|
|
87991
87573
|
}
|
|
87574
|
+
return grantId;
|
|
87992
87575
|
}
|
|
87993
|
-
|
|
87994
|
-
|
|
87995
|
-
`SELECT COALESCE(MAX(sequence), 0) + 1 AS next_sequence
|
|
87996
|
-
FROM session_collaboration_messages WHERE credential_hash = ?`
|
|
87997
|
-
).get(credentialHash);
|
|
87998
|
-
return row.next_sequence;
|
|
87576
|
+
updateConfig(grantId, config2) {
|
|
87577
|
+
this.db.prepare("UPDATE session_collaboration_grants SET config_json = ? WHERE credential_hash = ?").run(JSON.stringify(config2), grantId);
|
|
87999
87578
|
}
|
|
88000
|
-
|
|
88001
|
-
|
|
88002
|
-
|
|
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
|
-
};
|
|
87579
|
+
/** Record the brief of a grant whose task has not been delivered yet. */
|
|
87580
|
+
setTask(grantId, task) {
|
|
87581
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task = ? WHERE credential_hash = ? AND task_sent = 0").run(task, grantId);
|
|
88010
87582
|
}
|
|
88011
|
-
|
|
88012
|
-
|
|
88013
|
-
const fallback = parentCwd || project?.path || process.cwd();
|
|
88014
|
-
const cwd = pathResolve2(config2.cwd || fallback);
|
|
88015
|
-
if (!existsSync39(cwd) || !statSync15(cwd).isDirectory()) {
|
|
88016
|
-
throw Object.assign(new Error(`Working directory does not exist: ${cwd}`), {
|
|
88017
|
-
code: "invalid_argument"
|
|
88018
|
-
});
|
|
88019
|
-
}
|
|
88020
|
-
return cwd;
|
|
87583
|
+
markTaskSent(grantId) {
|
|
87584
|
+
this.db.prepare("UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?").run(grantId);
|
|
88021
87585
|
}
|
|
88022
|
-
|
|
88023
|
-
|
|
88024
|
-
|
|
88025
|
-
|
|
88026
|
-
|
|
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) };
|
|
87586
|
+
/** Link start. Returns false when the grant had already been started. */
|
|
87587
|
+
markStarted(grantId) {
|
|
87588
|
+
return this.db.prepare(`
|
|
87589
|
+
UPDATE session_collaboration_grants SET started_at = ? WHERE credential_hash = ? AND started_at IS NULL
|
|
87590
|
+
`).run(nowIso(), grantId).changes === 1;
|
|
88047
87591
|
}
|
|
88048
87592
|
/**
|
|
88049
|
-
*
|
|
88050
|
-
*
|
|
87593
|
+
* Record the session a spawn/handoff start created. Throws when a concurrent
|
|
87594
|
+
* start already consumed the grant.
|
|
87595
|
+
*
|
|
87596
|
+
* A handoff session is deliberately *not* written to child_session_id: that
|
|
87597
|
+
* column holds channel endpoints (spawn children, link peers), and a handoff
|
|
87598
|
+
* sibling is neither. The created id lives in config_json instead.
|
|
88051
87599
|
*/
|
|
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
|
-
|
|
87600
|
+
bindStartedSession(grant, sessionId, config2) {
|
|
87601
|
+
const changes = grant.kind === "handoff" ? this.db.prepare(`
|
|
87602
|
+
UPDATE session_collaboration_grants SET config_json = ?, started_at = ?
|
|
87603
|
+
WHERE credential_hash = ? AND started_at IS NULL
|
|
87604
|
+
`).run(JSON.stringify({ ...config2, handoffSessionId: sessionId }), nowIso(), grant.grant_id).changes : this.db.prepare(`
|
|
87605
|
+
UPDATE session_collaboration_grants SET child_session_id = ?, started_at = ?
|
|
87606
|
+
WHERE credential_hash = ? AND child_session_id IS NULL
|
|
87607
|
+
`).run(sessionId, nowIso(), grant.grant_id).changes;
|
|
87608
|
+
if (changes !== 1) throw new CollaborationError("This launch was already started", "failed_precondition");
|
|
87609
|
+
}
|
|
87610
|
+
// --- mailbox -------------------------------------------------------------
|
|
87611
|
+
nextSequence(grantId) {
|
|
87612
|
+
const row = this.db.prepare(`
|
|
87613
|
+
SELECT COALESCE(MAX(sequence), 0) + 1 AS next_sequence
|
|
87614
|
+
FROM session_collaboration_messages WHERE credential_hash = ?
|
|
87615
|
+
`).get(grantId);
|
|
87616
|
+
return row.next_sequence;
|
|
87617
|
+
}
|
|
87618
|
+
insertMessage(grantId, input) {
|
|
87619
|
+
const row = {
|
|
87620
|
+
id: randomUUID11(),
|
|
87621
|
+
sequence: this.nextSequence(grantId),
|
|
87622
|
+
sender_session_id: input.senderSessionId,
|
|
87623
|
+
recipient_session_id: input.recipientSessionId,
|
|
87624
|
+
client_message_id: input.clientMessageId,
|
|
87625
|
+
content: input.content,
|
|
87626
|
+
created_at: nowIso()
|
|
87627
|
+
};
|
|
87628
|
+
this.db.prepare(`
|
|
87629
|
+
INSERT INTO session_collaboration_messages
|
|
87630
|
+
(id, credential_hash, sequence, sender_session_id, recipient_session_id, client_message_id, content, created_at)
|
|
87631
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
87632
|
+
`).run(
|
|
87633
|
+
row.id,
|
|
87634
|
+
grantId,
|
|
87635
|
+
row.sequence,
|
|
87636
|
+
row.sender_session_id,
|
|
87637
|
+
row.recipient_session_id,
|
|
87638
|
+
row.client_message_id,
|
|
87639
|
+
row.content,
|
|
87640
|
+
row.created_at
|
|
87641
|
+
);
|
|
87642
|
+
return row;
|
|
87643
|
+
}
|
|
87644
|
+
/** Append one message; a repeated `clientMessageId` returns the stored row. */
|
|
87645
|
+
appendMessage(input) {
|
|
87646
|
+
return this.transaction(() => {
|
|
87647
|
+
if (input.clientMessageId) {
|
|
87648
|
+
const existing = this.db.prepare(`
|
|
87649
|
+
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
87650
|
+
WHERE credential_hash = ? AND sender_session_id = ? AND client_message_id = ?
|
|
87651
|
+
`).get(input.grantId, input.senderSessionId, input.clientMessageId);
|
|
87652
|
+
if (existing) return { row: existing, reused: true };
|
|
88090
87653
|
}
|
|
88091
|
-
|
|
88092
|
-
|
|
88093
|
-
|
|
87654
|
+
const row = this.insertMessage(input.grantId, { ...input, clientMessageId: input.clientMessageId ?? null });
|
|
87655
|
+
return { row, reused: false };
|
|
87656
|
+
});
|
|
87657
|
+
}
|
|
87658
|
+
/** Queue a link grant's opening body for the peer exactly once, then mark the task sent. */
|
|
87659
|
+
appendLinkOpening(grant, recipientSessionId, content) {
|
|
87660
|
+
this.transaction(() => {
|
|
87661
|
+
try {
|
|
87662
|
+
this.insertMessage(grant.grant_id, {
|
|
87663
|
+
senderSessionId: grant.parent_session_id,
|
|
87664
|
+
recipientSessionId,
|
|
87665
|
+
clientMessageId: `link-opening:${grant.grant_id}`,
|
|
87666
|
+
content
|
|
88094
87667
|
});
|
|
87668
|
+
} catch {
|
|
88095
87669
|
}
|
|
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
|
-
};
|
|
87670
|
+
this.markTaskSent(grant.grant_id);
|
|
88122
87671
|
});
|
|
88123
87672
|
}
|
|
88124
87673
|
/**
|
|
88125
|
-
*
|
|
88126
|
-
*
|
|
87674
|
+
* Drain unread messages addressed to `sessionId` across grants, advancing this
|
|
87675
|
+
* endpoint's cursor. Batches keep the order of `grantIds`.
|
|
88127
87676
|
*/
|
|
88128
|
-
|
|
88129
|
-
|
|
88130
|
-
|
|
88131
|
-
|
|
88132
|
-
|
|
87677
|
+
readMailbox(sessionId, grantIds, limitPerGrant) {
|
|
87678
|
+
return this.transaction(() => grantIds.flatMap((grantId) => {
|
|
87679
|
+
const cursor = this.db.prepare(`
|
|
87680
|
+
SELECT last_sequence FROM session_collaboration_cursors WHERE credential_hash = ? AND session_id = ?
|
|
87681
|
+
`).get(grantId, sessionId);
|
|
87682
|
+
const rows = this.db.prepare(`
|
|
87683
|
+
SELECT ${MESSAGE_COLUMNS} FROM session_collaboration_messages
|
|
87684
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence > ?
|
|
87685
|
+
ORDER BY sequence LIMIT ?
|
|
87686
|
+
`).all(grantId, sessionId, cursor?.last_sequence ?? 0, limitPerGrant);
|
|
87687
|
+
if (rows.length === 0) return [];
|
|
87688
|
+
const lastSequence = rows[rows.length - 1].sequence;
|
|
87689
|
+
this.db.prepare(`
|
|
87690
|
+
INSERT INTO session_collaboration_cursors (credential_hash, session_id, last_sequence)
|
|
87691
|
+
VALUES (?, ?, ?)
|
|
87692
|
+
ON CONFLICT(credential_hash, session_id) DO UPDATE SET last_sequence = excluded.last_sequence
|
|
87693
|
+
`).run(grantId, sessionId, lastSequence);
|
|
87694
|
+
this.db.prepare(`
|
|
87695
|
+
UPDATE session_collaboration_messages SET delivered_at = COALESCE(delivered_at, ?)
|
|
87696
|
+
WHERE credential_hash = ? AND recipient_session_id = ? AND sequence <= ?
|
|
87697
|
+
`).run(nowIso(), grantId, sessionId, lastSequence);
|
|
87698
|
+
return [{ grantId, rows }];
|
|
87699
|
+
}));
|
|
87700
|
+
}
|
|
87701
|
+
};
|
|
88133
87702
|
|
|
88134
|
-
|
|
87703
|
+
// src/session/collaboration-messaging.ts
|
|
87704
|
+
function sessionTitle(ctx) {
|
|
87705
|
+
return (sessionId) => ctx.deps.sessions.get(sessionId)?.title ?? null;
|
|
87706
|
+
}
|
|
87707
|
+
async function wakePeer(ctx, sessionId, fromSessionId) {
|
|
87708
|
+
if (!ctx.deps.sessions.get(sessionId)) return;
|
|
87709
|
+
const fromTitle = ctx.deps.sessions.get(fromSessionId)?.title?.trim() || fromSessionId.slice(0, 8);
|
|
87710
|
+
try {
|
|
87711
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
87712
|
+
sessionId,
|
|
87713
|
+
text: mailboxWakeText({ sessionId: fromSessionId, title: fromTitle }),
|
|
87714
|
+
source: "task-notification",
|
|
87715
|
+
requestId: `collab-wake-${fromSessionId.slice(0, 8)}-${Date.now()}`
|
|
87716
|
+
});
|
|
87717
|
+
} catch {
|
|
88135
87718
|
}
|
|
88136
|
-
|
|
88137
|
-
|
|
88138
|
-
|
|
88139
|
-
|
|
88140
|
-
|
|
88141
|
-
|
|
88142
|
-
|
|
88143
|
-
|
|
88144
|
-
|
|
88145
|
-
|
|
88146
|
-
|
|
88147
|
-
|
|
88148
|
-
|
|
88149
|
-
|
|
88150
|
-
|
|
87719
|
+
}
|
|
87720
|
+
function sendCollaborationMessage(ctx, input) {
|
|
87721
|
+
const channel = resolveSendChannel(ctx.store, input.sessionId, input.to, sessionTitle(ctx));
|
|
87722
|
+
const content = normalizeMailboxContent(input.content);
|
|
87723
|
+
const recipientSessionId = channel.peer.sessionId;
|
|
87724
|
+
const insert = ctx.store.appendMessage({
|
|
87725
|
+
grantId: channel.grant.grant_id,
|
|
87726
|
+
senderSessionId: input.sessionId,
|
|
87727
|
+
recipientSessionId,
|
|
87728
|
+
clientMessageId: input.clientMessageId,
|
|
87729
|
+
content
|
|
87730
|
+
});
|
|
87731
|
+
if (!insert.reused) {
|
|
87732
|
+
ctx.deps.events.append({
|
|
87733
|
+
aggregateType: "session",
|
|
87734
|
+
aggregateId: input.sessionId,
|
|
87735
|
+
eventType: "collaboration.message",
|
|
87736
|
+
payload: {
|
|
87737
|
+
messageId: insert.row.id,
|
|
87738
|
+
grantId: channel.grant.grant_id,
|
|
87739
|
+
toSessionId: recipientSessionId,
|
|
87740
|
+
sequence: insert.row.sequence
|
|
87741
|
+
}
|
|
87742
|
+
});
|
|
87743
|
+
void wakePeer(ctx, recipientSessionId, input.sessionId);
|
|
87744
|
+
}
|
|
87745
|
+
return {
|
|
87746
|
+
status: "sent",
|
|
87747
|
+
messageId: insert.row.id,
|
|
87748
|
+
sequence: insert.row.sequence,
|
|
87749
|
+
reused: insert.reused,
|
|
87750
|
+
to: channel.peer,
|
|
87751
|
+
peerSessionId: recipientSessionId
|
|
87752
|
+
};
|
|
87753
|
+
}
|
|
87754
|
+
function retrieveCollaborationMessages(ctx, input) {
|
|
87755
|
+
const { messages, peers } = readCallerMailbox(
|
|
87756
|
+
ctx.store,
|
|
87757
|
+
input.sessionId,
|
|
87758
|
+
{ from: input.from, limit: input.max },
|
|
87759
|
+
sessionTitle(ctx)
|
|
87760
|
+
);
|
|
87761
|
+
if (messages.length > 0) return { status: "messages", messages, peers };
|
|
87762
|
+
return { status: "empty", messages: [], peers, hint: peers.length > 0 ? EMPTY_MAILBOX_HINT : NO_PEERS_HINT };
|
|
87763
|
+
}
|
|
87764
|
+
|
|
87765
|
+
// src/session/collaboration-profiles.ts
|
|
87766
|
+
init_environment();
|
|
87767
|
+
|
|
87768
|
+
// ../../packages/shared/src/acp-brand.ts
|
|
87769
|
+
init_acp_brand();
|
|
87770
|
+
|
|
87771
|
+
// src/session/collaboration-profiles.ts
|
|
87772
|
+
init_resolve_service();
|
|
87773
|
+
function listCollaborationProfiles(deps) {
|
|
87774
|
+
const { harnesses, providers, sessions, sessionProviders } = deps;
|
|
87775
|
+
const profiles = [];
|
|
87776
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87777
|
+
const providerOptions = {
|
|
87778
|
+
experimentalClaudeOpenAiChatEnabled: deps.experimentalClaudeOpenAiChatEnabled?.() ?? false
|
|
87779
|
+
};
|
|
87780
|
+
const pushProfile = (profileId, harnessId, name, description, profileConfig) => {
|
|
87781
|
+
if (seen.has(profileId)) return;
|
|
87782
|
+
if (!harnesses.isSessionHarnessRunnable(harnessId)) return;
|
|
87783
|
+
seen.add(profileId);
|
|
87784
|
+
const harnessModels = listHarnessModels(providers, harnessId, null, providerOptions);
|
|
87785
|
+
const models = harnessModels.map((m3) => ({
|
|
87786
|
+
id: m3.id,
|
|
87787
|
+
name: m3.name || m3.id,
|
|
87788
|
+
...m3.description ? { description: m3.description } : {},
|
|
87789
|
+
...m3.serviceTiers?.length ? { serviceTiers: m3.serviceTiers } : {}
|
|
87790
|
+
}));
|
|
87791
|
+
const defaultModel = harnessModels.find((m3) => m3.isDefault) ?? harnessModels[0];
|
|
87792
|
+
const efforts = /* @__PURE__ */ new Set();
|
|
87793
|
+
for (const m3 of harnessModels) {
|
|
87794
|
+
for (const e of m3.supportedEffortLevels ?? []) efforts.add(e);
|
|
87795
|
+
}
|
|
87796
|
+
const cfg = profileConfig && typeof profileConfig === "object" && !Array.isArray(profileConfig) ? profileConfig : {};
|
|
87797
|
+
const cfgModel = typeof cfg.model === "string" && cfg.model.trim() ? cfg.model.trim() : void 0;
|
|
87798
|
+
const cfgEffort = typeof cfg.effort === "string" && cfg.effort.trim() ? cfg.effort.trim() : typeof cfg.reasoningEffort === "string" && cfg.reasoningEffort.trim() ? cfg.reasoningEffort.trim() : void 0;
|
|
87799
|
+
const acpAgentId = harnessId === "acp" && typeof cfg.agentId === "string" && cfg.agentId.trim() ? cfg.agentId.trim() : null;
|
|
87800
|
+
const modelDefault = cfgModel ?? defaultModel?.id;
|
|
87801
|
+
const effortDefault = cfgEffort ?? (efforts.has("high") ? "high" : efforts.has("medium") ? "medium" : efforts.size > 0 ? [...efforts][0] : void 0);
|
|
87802
|
+
profiles.push({
|
|
87803
|
+
id: profileId,
|
|
87804
|
+
name: harnessId === "acp" ? acpAgentDisplayName(acpAgentId) : name,
|
|
87805
|
+
harnessId,
|
|
87806
|
+
...acpAgentId ? { acpAgentId } : {},
|
|
87807
|
+
brandKey: resolveHarnessBrandKey(harnessId, acpAgentId),
|
|
87808
|
+
description,
|
|
87809
|
+
defaultConfig: {
|
|
87810
|
+
...modelDefault ? { model: modelDefault } : {},
|
|
87811
|
+
...effortDefault ? { effort: effortDefault } : {},
|
|
87812
|
+
...harnessId === "codex" ? { fastMode: typeof cfg.fastMode === "boolean" ? cfg.fastMode : false } : {}
|
|
87813
|
+
},
|
|
87814
|
+
models: models.length > 0 ? models : [{ id: "default", name: "Default" }],
|
|
87815
|
+
efforts: [...efforts],
|
|
87816
|
+
apiProviders: listHarnessApiProviders(providers, harnessId, providerOptions)
|
|
87817
|
+
});
|
|
87818
|
+
};
|
|
87819
|
+
if (sessionProviders) {
|
|
87820
|
+
for (const p3 of sessionProviders.list()) {
|
|
87821
|
+
pushProfile(
|
|
87822
|
+
p3.id,
|
|
87823
|
+
p3.harnessId,
|
|
87824
|
+
p3.name,
|
|
87825
|
+
p3.isBase ? `${p3.harnessId} harness with the built-in configuration` : `Custom ${p3.harnessId} profile`,
|
|
87826
|
+
p3.config
|
|
87827
|
+
);
|
|
88151
87828
|
}
|
|
88152
|
-
this.deps.db.prepare(`UPDATE session_collaboration_grants SET task_sent = 1 WHERE credential_hash = ?`).run(grant.credential_hash);
|
|
88153
87829
|
}
|
|
88154
|
-
|
|
88155
|
-
|
|
88156
|
-
|
|
88157
|
-
|
|
88158
|
-
|
|
88159
|
-
|
|
88160
|
-
|
|
88161
|
-
|
|
88162
|
-
});
|
|
88163
|
-
} catch {
|
|
87830
|
+
if (profiles.length === 0) {
|
|
87831
|
+
const seedIds = new Set(harnesses.readySessionHarnessIds());
|
|
87832
|
+
for (const s4 of sessions.list()) {
|
|
87833
|
+
if (s4.harnessId) seedIds.add(s4.harnessId);
|
|
87834
|
+
}
|
|
87835
|
+
for (const id of seedIds) {
|
|
87836
|
+
const harnessId = normalizeSessionHarnessId(id) ?? id;
|
|
87837
|
+
pushProfile(harnessId, harnessId, harnessId, `${harnessId} harness`);
|
|
88164
87838
|
}
|
|
88165
87839
|
}
|
|
88166
|
-
|
|
87840
|
+
return profiles;
|
|
87841
|
+
}
|
|
88167
87842
|
|
|
88168
|
-
// src/
|
|
88169
|
-
import {
|
|
88170
|
-
|
|
88171
|
-
|
|
88172
|
-
|
|
88173
|
-
|
|
88174
|
-
|
|
88175
|
-
|
|
87843
|
+
// src/session/collaboration-request.ts
|
|
87844
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
87845
|
+
|
|
87846
|
+
// src/session/collaboration-context.ts
|
|
87847
|
+
init_environment();
|
|
87848
|
+
function resolveProfile(profiles, agentId) {
|
|
87849
|
+
const direct = profiles.get(agentId);
|
|
87850
|
+
if (direct) return direct;
|
|
87851
|
+
const wire = normalizeSessionHarnessId(agentId);
|
|
87852
|
+
return wire ? profiles.get(`${wire}-base`) : void 0;
|
|
88176
87853
|
}
|
|
88177
|
-
function
|
|
88178
|
-
|
|
88179
|
-
|
|
88180
|
-
|
|
87854
|
+
function initiatorTitleOf(ctx, grant) {
|
|
87855
|
+
return ctx.deps.sessions.get(grant.parent_session_id)?.title?.trim() || grant.parent_session_id.slice(0, 8);
|
|
87856
|
+
}
|
|
87857
|
+
|
|
87858
|
+
// src/session/collaboration-request.ts
|
|
87859
|
+
function safeLaunchConfig(raw) {
|
|
87860
|
+
const config2 = raw && typeof raw === "object" ? raw : {};
|
|
87861
|
+
const safe = {};
|
|
87862
|
+
if (typeof config2.model === "string" && config2.model.trim()) safe.model = config2.model.trim();
|
|
87863
|
+
if (typeof config2.effort === "string" && config2.effort.trim()) safe.effort = config2.effort.trim();
|
|
87864
|
+
if (config2.apiProviderId === null) safe.apiProviderId = null;
|
|
87865
|
+
else if (typeof config2.apiProviderId === "string" && config2.apiProviderId.trim()) {
|
|
87866
|
+
safe.apiProviderId = config2.apiProviderId.trim();
|
|
87867
|
+
}
|
|
87868
|
+
if (config2.worktree && typeof config2.worktree === "object") safe.worktree = config2.worktree;
|
|
87869
|
+
if (typeof config2.worktreePath === "string" && config2.worktreePath.trim()) {
|
|
87870
|
+
safe.worktreePath = config2.worktreePath.trim();
|
|
87871
|
+
}
|
|
87872
|
+
if (typeof config2.cwd === "string" && config2.cwd.trim()) safe.cwd = config2.cwd.trim();
|
|
87873
|
+
return safe;
|
|
87874
|
+
}
|
|
87875
|
+
function normalizeLaunches(ctx, input, profileList) {
|
|
87876
|
+
const parent = ctx.deps.sessions.get(input.parentSessionId);
|
|
87877
|
+
const profiles = new Map(profileList.map((p3) => [p3.id, p3]));
|
|
87878
|
+
return input.launches.map((launch2) => {
|
|
87879
|
+
const mode = resolveLaunchMode(launch2.mode);
|
|
87880
|
+
const launchId = launch2.launchId?.trim() || randomUUID12();
|
|
87881
|
+
if (mode === "link") {
|
|
87882
|
+
const peerSessionId = launch2.sessionId?.trim();
|
|
87883
|
+
if (!peerSessionId) {
|
|
87884
|
+
throw new CollaborationError("Link launches require sessionId of an existing SuperOne session", "invalid_argument");
|
|
87885
|
+
}
|
|
87886
|
+
if (peerSessionId === input.parentSessionId) {
|
|
87887
|
+
throw new CollaborationError("Cannot link a session to itself", "invalid_argument");
|
|
87888
|
+
}
|
|
87889
|
+
const peer = ctx.deps.sessions.get(peerSessionId);
|
|
87890
|
+
if (!peer) throw new CollaborationError(`Unknown sessionId for link: ${peerSessionId}`, "not_found");
|
|
87891
|
+
assertNotPeeredElsewhere(ctx.store, input.parentSessionId, peerSessionId);
|
|
87892
|
+
const peerTitle = peer.title?.trim() || peerSessionId.slice(0, 8);
|
|
87893
|
+
const { summary: summary3, name: name2, role: role2 } = normalizeLaunchLabels("link", launch2, peerTitle);
|
|
87894
|
+
return {
|
|
87895
|
+
launchId,
|
|
87896
|
+
mode: "link",
|
|
87897
|
+
agentId: "",
|
|
87898
|
+
sessionId: peerSessionId,
|
|
87899
|
+
peerTitle,
|
|
87900
|
+
peerProjectPath: ctx.deps.projects.get(peer.projectId)?.path,
|
|
87901
|
+
summary: summary3,
|
|
87902
|
+
name: name2,
|
|
87903
|
+
role: role2,
|
|
87904
|
+
config: { name: name2, role: role2, summary: summary3 }
|
|
87905
|
+
};
|
|
87906
|
+
}
|
|
87907
|
+
const agentId = launch2.agentId?.trim();
|
|
87908
|
+
if (!agentId) {
|
|
87909
|
+
throw new CollaborationError(`${mode} launches require agentId from session_collab_list_agents`, "invalid_argument");
|
|
87910
|
+
}
|
|
87911
|
+
const profile = resolveProfile(profiles, agentId);
|
|
87912
|
+
if (!profile) throw new CollaborationError(`Unknown agent profile: ${agentId}`, "invalid_argument");
|
|
87913
|
+
const { summary: summary2, name, role } = normalizeLaunchLabels(mode, launch2);
|
|
87914
|
+
const safe = safeLaunchConfig(launch2.config);
|
|
87915
|
+
return {
|
|
87916
|
+
launchId,
|
|
87917
|
+
mode,
|
|
87918
|
+
agentId,
|
|
87919
|
+
summary: summary2,
|
|
87920
|
+
name,
|
|
87921
|
+
role,
|
|
87922
|
+
config: {
|
|
87923
|
+
...profile.defaultConfig,
|
|
87924
|
+
...safe,
|
|
87925
|
+
permissionMode: "default",
|
|
87926
|
+
sandboxMode: "off",
|
|
87927
|
+
cwd: safe.cwd ?? (parent.cwd ?? ctx.deps.projects.get(parent.projectId)?.path),
|
|
87928
|
+
name,
|
|
87929
|
+
role
|
|
87930
|
+
}
|
|
87931
|
+
};
|
|
87932
|
+
});
|
|
87933
|
+
}
|
|
87934
|
+
function createGrant(ctx, parentSessionId, launch2) {
|
|
87935
|
+
const { grantId, approved } = recordApprovedLaunch(ctx.store, parentSessionId, launch2, launch2.config);
|
|
87936
|
+
if (approved.reused) return approved;
|
|
87937
|
+
ctx.deps.events.append({
|
|
87938
|
+
aggregateType: "session",
|
|
87939
|
+
aggregateId: parentSessionId,
|
|
87940
|
+
eventType: "collaboration.grant_created",
|
|
87941
|
+
payload: approved.mode === "link" ? { grantId, mode: "link", peerSessionId: approved.sessionId, launchId: approved.launchId } : { grantId, mode: approved.mode, agentId: approved.agentId, launchId: approved.launchId }
|
|
87942
|
+
});
|
|
87943
|
+
return approved;
|
|
87944
|
+
}
|
|
87945
|
+
async function requestCollaboration(ctx, input) {
|
|
87946
|
+
if (!ctx.deps.sessions.get(input.parentSessionId)) {
|
|
87947
|
+
throw new CollaborationError("Parent session is not available", "not_found");
|
|
87948
|
+
}
|
|
87949
|
+
if (ctx.store.isSpawnChild(input.parentSessionId)) {
|
|
87950
|
+
throw new CollaborationError(NESTED_COLLABORATION_UNSUPPORTED, "failed_precondition");
|
|
87951
|
+
}
|
|
87952
|
+
if (!Array.isArray(input.launches)) {
|
|
87953
|
+
throw new CollaborationError("launches must contain at least one proposed session", "invalid_argument");
|
|
87954
|
+
}
|
|
87955
|
+
assertLaunchCount(input.launches.length);
|
|
87956
|
+
const profileList = ctx.listProfiles();
|
|
87957
|
+
const normalized = normalizeLaunches(ctx, input, profileList);
|
|
87958
|
+
const launchIds = new Set(normalized.map((l) => l.launchId));
|
|
87959
|
+
if (launchIds.size !== normalized.length) {
|
|
87960
|
+
throw new CollaborationError("Every confirmed launch must have a unique launchId", "invalid_argument");
|
|
87961
|
+
}
|
|
87962
|
+
let confirmed = normalized;
|
|
87963
|
+
if (input.requireUserConfirm) {
|
|
87964
|
+
const outcome = await ctx.deps.sessions.requestAgentsConfirm({
|
|
87965
|
+
sessionId: input.parentSessionId,
|
|
87966
|
+
launches: normalized,
|
|
87967
|
+
profiles: profileList,
|
|
87968
|
+
signal: input.signal
|
|
87969
|
+
});
|
|
87970
|
+
if (outcome.action === "cancel") return { status: "cancelled" };
|
|
87971
|
+
if (outcome.action === "decline") return { status: "rejected", feedback: outcome.content?.feedback };
|
|
87972
|
+
confirmed = mergeConfirmedLaunches(normalized, outcome.content);
|
|
88181
87973
|
}
|
|
88182
|
-
|
|
88183
|
-
|
|
88184
|
-
|
|
87974
|
+
const launches = ctx.store.transaction(() => confirmed.map((launch2) => createGrant(ctx, input.parentSessionId, launch2)));
|
|
87975
|
+
return { status: "approved", launches, next: START_APPROVED_LAUNCHES_HINT };
|
|
87976
|
+
}
|
|
87977
|
+
|
|
87978
|
+
// src/session/collaboration-start.ts
|
|
87979
|
+
init_agent_types();
|
|
87980
|
+
init_environment();
|
|
87981
|
+
import { existsSync as existsSync39, statSync as statSync15 } from "node:fs";
|
|
87982
|
+
import { resolve as pathResolve2 } from "node:path";
|
|
87983
|
+
function resolveCwd(ctx, config2, projectId, parentCwd) {
|
|
87984
|
+
const project = ctx.deps.projects.get(projectId);
|
|
87985
|
+
const fallback = parentCwd || project?.path || process.cwd();
|
|
87986
|
+
const cwd = pathResolve2(config2.cwd || fallback);
|
|
87987
|
+
if (!existsSync39(cwd) || !statSync15(cwd).isDirectory()) {
|
|
87988
|
+
throw new CollaborationError(`Working directory does not exist: ${cwd}`, "invalid_argument");
|
|
87989
|
+
}
|
|
87990
|
+
return cwd;
|
|
87991
|
+
}
|
|
87992
|
+
function applyFormAnswers(ctx, grant, formAnswers) {
|
|
87993
|
+
const packed = formAnswers[SESSION_AGENT_LAUNCHES_FIELD];
|
|
87994
|
+
if (typeof packed !== "string") return grant;
|
|
87995
|
+
let edited;
|
|
88185
87996
|
try {
|
|
88186
|
-
|
|
87997
|
+
edited = JSON.parse(packed);
|
|
87998
|
+
} catch {
|
|
87999
|
+
return grant;
|
|
88000
|
+
}
|
|
88001
|
+
if (!Array.isArray(edited) || edited.length === 0) return grant;
|
|
88002
|
+
const patch = edited[0]?.config;
|
|
88003
|
+
if (!patch || typeof patch !== "object") return grant;
|
|
88004
|
+
const next = patchEditableLaunchConfig(parseGrantConfig(grant.config_json), patch);
|
|
88005
|
+
ctx.store.updateConfig(grant.grant_id, next);
|
|
88006
|
+
return { ...grant, config_json: JSON.stringify(next) };
|
|
88007
|
+
}
|
|
88008
|
+
async function deliverInitialTask(ctx, grant, childSessionId) {
|
|
88009
|
+
if (grant.task_sent === 1) return;
|
|
88010
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
88011
|
+
const text2 = grant.kind === "handoff" ? handoffTaskContent({
|
|
88012
|
+
parentSessionId: grant.parent_session_id,
|
|
88013
|
+
parentTitle: ctx.deps.sessions.get(grant.parent_session_id)?.title?.trim() || null,
|
|
88014
|
+
task: grant.task
|
|
88015
|
+
}) : grant.task;
|
|
88016
|
+
try {
|
|
88017
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
88018
|
+
sessionId: childSessionId,
|
|
88019
|
+
text: text2,
|
|
88020
|
+
model: config2.model,
|
|
88021
|
+
effort: config2.effort,
|
|
88022
|
+
permissionMode: config2.permissionMode,
|
|
88023
|
+
sandboxMode: config2.sandboxMode,
|
|
88024
|
+
apiProviderId: config2.apiProviderId,
|
|
88025
|
+
requestId: `collaboration-task-${grant.grant_id.slice(0, 16)}`
|
|
88026
|
+
});
|
|
88187
88027
|
} catch {
|
|
88188
88028
|
}
|
|
88189
|
-
|
|
88029
|
+
ctx.store.markTaskSent(grant.grant_id);
|
|
88190
88030
|
}
|
|
88191
|
-
function
|
|
88192
|
-
|
|
88031
|
+
async function wakeLinkPeer(ctx, sessionId, grant, hasOpening) {
|
|
88032
|
+
if (!ctx.deps.sessions.get(sessionId)) return;
|
|
88033
|
+
try {
|
|
88034
|
+
await ctx.deps.sessions.sendWithoutLease({
|
|
88035
|
+
sessionId,
|
|
88036
|
+
text: linkActivationWakeText({
|
|
88037
|
+
initiatorSessionId: grant.parent_session_id,
|
|
88038
|
+
initiatorTitle: initiatorTitleOf(ctx, grant),
|
|
88039
|
+
hasOpening
|
|
88040
|
+
}),
|
|
88041
|
+
source: "task-notification",
|
|
88042
|
+
requestId: `collab-link-wake-${grant.grant_id.slice(0, 12)}-${Date.now()}`
|
|
88043
|
+
});
|
|
88044
|
+
} catch {
|
|
88045
|
+
}
|
|
88046
|
+
}
|
|
88047
|
+
async function startLink(ctx, grant) {
|
|
88048
|
+
if (!grant.child_session_id) {
|
|
88049
|
+
throw new CollaborationError("Link grant is missing peer session id", "failed_precondition");
|
|
88050
|
+
}
|
|
88051
|
+
const peerSessionId = grant.child_session_id;
|
|
88052
|
+
if (!ctx.deps.sessions.get(peerSessionId)) {
|
|
88053
|
+
throw new CollaborationError(`Peer session no longer exists: ${peerSessionId}`, "not_found");
|
|
88054
|
+
}
|
|
88055
|
+
const alreadyStarted = Boolean(grant.started_at);
|
|
88056
|
+
if (!alreadyStarted) ctx.store.markStarted(grant.grant_id);
|
|
88057
|
+
const opening = grant.task?.trim() ?? "";
|
|
88058
|
+
if (!alreadyStarted && opening) {
|
|
88059
|
+
if (grant.task_sent !== 1) {
|
|
88060
|
+
ctx.store.appendLinkOpening(grant, peerSessionId, opening);
|
|
88061
|
+
void wakeLinkPeer(ctx, peerSessionId, grant, true);
|
|
88062
|
+
}
|
|
88063
|
+
} else {
|
|
88064
|
+
void wakeLinkPeer(ctx, peerSessionId, grant, false);
|
|
88065
|
+
if (!alreadyStarted) ctx.store.markTaskSent(grant.grant_id);
|
|
88066
|
+
}
|
|
88067
|
+
const peer = describeLaunchedPeer(grant);
|
|
88193
88068
|
return {
|
|
88194
|
-
|
|
88195
|
-
|
|
88196
|
-
|
|
88197
|
-
|
|
88198
|
-
|
|
88199
|
-
|
|
88200
|
-
|
|
88201
|
-
|
|
88202
|
-
|
|
88203
|
-
|
|
88204
|
-
|
|
88205
|
-
|
|
88069
|
+
status: "linked",
|
|
88070
|
+
mode: "link",
|
|
88071
|
+
sessionId: peerSessionId,
|
|
88072
|
+
peerSessionId,
|
|
88073
|
+
reused: alreadyStarted,
|
|
88074
|
+
name: peer.name,
|
|
88075
|
+
role: peer.role,
|
|
88076
|
+
title: peer.title,
|
|
88077
|
+
config: peer.config
|
|
88078
|
+
};
|
|
88079
|
+
}
|
|
88080
|
+
async function startCollaboration(ctx, input) {
|
|
88081
|
+
let grant = prepareLaunchStart(ctx.store, input.callerSessionId, input);
|
|
88082
|
+
if (grant.kind !== "link" && input.formAnswers && typeof input.formAnswers === "object") {
|
|
88083
|
+
grant = applyFormAnswers(ctx, grant, input.formAnswers);
|
|
88084
|
+
}
|
|
88085
|
+
if (grant.kind === "link") return startLink(ctx, grant);
|
|
88086
|
+
const isHandoff = grant.kind === "handoff";
|
|
88087
|
+
const existingHandoffSessionId = isHandoff ? parseGrantConfig(grant.config_json).handoffSessionId : void 0;
|
|
88088
|
+
const existingSessionId = existingHandoffSessionId ?? grant.child_session_id;
|
|
88089
|
+
if (existingSessionId) {
|
|
88090
|
+
const existing = ctx.deps.sessions.get(existingSessionId);
|
|
88091
|
+
if (isHandoff && !existing) {
|
|
88092
|
+
throw new CollaborationError(`The handoff session no longer exists: ${existingSessionId}`, "not_found");
|
|
88093
|
+
}
|
|
88094
|
+
if (existing && !isHandoff) {
|
|
88095
|
+
ctx.deps.sessions.setSystemPromptAppend(
|
|
88096
|
+
existing.sessionId,
|
|
88097
|
+
collaborationSystemPrompt(grant.parent_session_id)
|
|
88098
|
+
);
|
|
88099
|
+
}
|
|
88100
|
+
if (existing) await deliverInitialTask(ctx, grant, existingSessionId);
|
|
88101
|
+
const peer = describeLaunchedPeer(grant);
|
|
88102
|
+
return {
|
|
88103
|
+
status: "started",
|
|
88104
|
+
mode: grant.kind,
|
|
88105
|
+
sessionId: existingSessionId,
|
|
88106
|
+
reused: true,
|
|
88107
|
+
name: peer.name,
|
|
88108
|
+
role: peer.role,
|
|
88109
|
+
title: peer.title,
|
|
88110
|
+
config: peer.config
|
|
88111
|
+
};
|
|
88112
|
+
}
|
|
88113
|
+
const parent = ctx.deps.sessions.get(grant.parent_session_id);
|
|
88114
|
+
if (!parent) throw new CollaborationError("Parent session is not available", "not_found");
|
|
88115
|
+
const config2 = parseGrantConfig(grant.config_json);
|
|
88116
|
+
let cwd = resolveCwd(ctx, config2, parent.projectId, parent.cwd);
|
|
88117
|
+
if (config2.worktreePath && typeof config2.worktreePath === "string" && config2.worktreePath.trim()) {
|
|
88118
|
+
cwd = pathResolve2(config2.worktreePath.trim());
|
|
88119
|
+
} else if (config2.worktree?.enabled) {
|
|
88120
|
+
cwd = ctx.deps.workspaceGit.activateWorktree(parent.projectId, {
|
|
88121
|
+
baseBranch: config2.worktree.baseBranch || "HEAD",
|
|
88122
|
+
mode: config2.worktree.mode ?? "branch",
|
|
88123
|
+
branchName: config2.worktree.branchName,
|
|
88124
|
+
carryLocalChanges: config2.worktree.carryLocalChanges
|
|
88125
|
+
}).path;
|
|
88126
|
+
}
|
|
88127
|
+
const profile = resolveProfile(new Map(ctx.listProfiles().map((p3) => [p3.id, p3])), grant.agent_id);
|
|
88128
|
+
const harnessId = profile?.harnessId ?? normalizeSessionHarnessId(grant.agent_id) ?? "claude";
|
|
88129
|
+
const displayName = deriveCollaborationName({ name: config2.name });
|
|
88130
|
+
const role = deriveCollaborationRole({ role: config2.role, task: grant.task });
|
|
88131
|
+
const title = collaborationSessionTitle(displayName, role);
|
|
88132
|
+
let child;
|
|
88133
|
+
try {
|
|
88134
|
+
child = ctx.deps.sessions.create({
|
|
88135
|
+
projectId: parent.projectId,
|
|
88136
|
+
harnessId,
|
|
88137
|
+
providerId: grant.agent_id,
|
|
88138
|
+
title,
|
|
88139
|
+
cwd,
|
|
88140
|
+
model: config2.model ?? null,
|
|
88141
|
+
effort: config2.effort ?? null,
|
|
88142
|
+
permissionMode: config2.permissionMode ?? null,
|
|
88143
|
+
sandboxMode: config2.sandboxMode ?? null,
|
|
88144
|
+
apiProviderId: config2.apiProviderId ?? null,
|
|
88145
|
+
controllerClientSessionId: input.controllerClientSessionId ?? parent.controllerClientSessionId,
|
|
88146
|
+
// Handoff is one-way by construction: the receiver gets no collaboration prompt.
|
|
88147
|
+
...isHandoff ? {} : { systemPromptAppend: collaborationSystemPrompt(grant.parent_session_id) }
|
|
88148
|
+
});
|
|
88149
|
+
ctx.store.bindStartedSession(grant, child.sessionId, config2);
|
|
88150
|
+
} catch (err) {
|
|
88151
|
+
if (child?.sessionId) {
|
|
88206
88152
|
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");
|
|
88153
|
+
ctx.deps.sessions.remove(child.sessionId);
|
|
88214
88154
|
} catch {
|
|
88215
|
-
return "";
|
|
88216
88155
|
}
|
|
88217
88156
|
}
|
|
88157
|
+
throw err;
|
|
88158
|
+
}
|
|
88159
|
+
grant = isHandoff ? grant : { ...grant, child_session_id: child.sessionId };
|
|
88160
|
+
await deliverInitialTask(ctx, grant, child.sessionId);
|
|
88161
|
+
ctx.deps.events.append({
|
|
88162
|
+
aggregateType: "session",
|
|
88163
|
+
aggregateId: grant.parent_session_id,
|
|
88164
|
+
eventType: isHandoff ? "collaboration.handoff_started" : "collaboration.child_started",
|
|
88165
|
+
payload: { grantId: grant.grant_id, childSessionId: child.sessionId }
|
|
88166
|
+
});
|
|
88167
|
+
return {
|
|
88168
|
+
status: "started",
|
|
88169
|
+
mode: grant.kind,
|
|
88170
|
+
sessionId: child.sessionId,
|
|
88171
|
+
reused: false,
|
|
88172
|
+
name: displayName,
|
|
88173
|
+
role,
|
|
88174
|
+
title,
|
|
88175
|
+
config: {
|
|
88176
|
+
model: config2.model,
|
|
88177
|
+
effort: config2.effort,
|
|
88178
|
+
fastMode: config2.fastMode,
|
|
88179
|
+
permissionMode: config2.permissionMode,
|
|
88180
|
+
sandboxMode: config2.sandboxMode,
|
|
88181
|
+
cwd,
|
|
88182
|
+
apiProviderId: config2.apiProviderId ?? null,
|
|
88183
|
+
name: displayName,
|
|
88184
|
+
role,
|
|
88185
|
+
...config2.worktree ? { worktree: config2.worktree } : {},
|
|
88186
|
+
...config2.worktreePath ? { worktreePath: config2.worktreePath } : {}
|
|
88187
|
+
}
|
|
88218
88188
|
};
|
|
88219
88189
|
}
|
|
88220
88190
|
|
|
88191
|
+
// src/session/collaboration.ts
|
|
88192
|
+
var CollaborationService = class {
|
|
88193
|
+
constructor(deps) {
|
|
88194
|
+
this.deps = deps;
|
|
88195
|
+
this.ctx = {
|
|
88196
|
+
deps,
|
|
88197
|
+
store: new CollaborationStore(deps.db),
|
|
88198
|
+
listProfiles: () => this.listProfiles()
|
|
88199
|
+
};
|
|
88200
|
+
}
|
|
88201
|
+
deps;
|
|
88202
|
+
ctx;
|
|
88203
|
+
/** Agent profiles from session_providers (+ ready-harness fallback). */
|
|
88204
|
+
listProfiles() {
|
|
88205
|
+
return listCollaborationProfiles(this.deps);
|
|
88206
|
+
}
|
|
88207
|
+
request(input) {
|
|
88208
|
+
return requestCollaboration(this.ctx, input);
|
|
88209
|
+
}
|
|
88210
|
+
start(input) {
|
|
88211
|
+
return startCollaboration(this.ctx, input);
|
|
88212
|
+
}
|
|
88213
|
+
send(input) {
|
|
88214
|
+
return sendCollaborationMessage(this.ctx, input);
|
|
88215
|
+
}
|
|
88216
|
+
retrieve(input) {
|
|
88217
|
+
return retrieveCollaborationMessages(this.ctx, input);
|
|
88218
|
+
}
|
|
88219
|
+
/** Reconstruct system-prompt append for spawn children after restart (never link). */
|
|
88220
|
+
rehydrateSystemPrompts() {
|
|
88221
|
+
for (const grant of this.ctx.store.startedSpawnGrants()) {
|
|
88222
|
+
if (!grant.child_session_id || !this.deps.sessions.get(grant.child_session_id)) continue;
|
|
88223
|
+
this.deps.sessions.setSystemPromptAppend(grant.child_session_id, collaborationSystemPrompt(grant.parent_session_id));
|
|
88224
|
+
}
|
|
88225
|
+
}
|
|
88226
|
+
};
|
|
88227
|
+
|
|
88221
88228
|
// src/workspace/watch-service.ts
|
|
88222
88229
|
import { watch } from "node:fs";
|
|
88223
88230
|
|
|
@@ -88324,7 +88331,7 @@ var WorkspaceWatchService = class {
|
|
|
88324
88331
|
|
|
88325
88332
|
// src/workspace/tail-watch-service.ts
|
|
88326
88333
|
init_fs();
|
|
88327
|
-
import { existsSync as
|
|
88334
|
+
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
88335
|
var MAX_POLL_BYTES = 10 * 1024 * 1024;
|
|
88329
88336
|
var WorkspaceTailWatchService = class {
|
|
88330
88337
|
constructor(projects, fs2) {
|
|
@@ -88348,7 +88355,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88348
88355
|
);
|
|
88349
88356
|
}
|
|
88350
88357
|
try {
|
|
88351
|
-
resolvedAbs =
|
|
88358
|
+
resolvedAbs = existsSync40(absolutePath) ? realpathSync6(absolutePath) : absolutePath;
|
|
88352
88359
|
} catch {
|
|
88353
88360
|
resolvedAbs = absolutePath;
|
|
88354
88361
|
}
|
|
@@ -88379,7 +88386,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88379
88386
|
code: "invalid_argument"
|
|
88380
88387
|
});
|
|
88381
88388
|
}
|
|
88382
|
-
if (
|
|
88389
|
+
if (existsSync40(resolvedAbs)) {
|
|
88383
88390
|
try {
|
|
88384
88391
|
const st2 = statSync16(resolvedAbs);
|
|
88385
88392
|
if (!st2.isFile()) {
|
|
@@ -88430,7 +88437,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88430
88437
|
{ code: "invalid_argument" }
|
|
88431
88438
|
);
|
|
88432
88439
|
}
|
|
88433
|
-
if (!
|
|
88440
|
+
if (!existsSync40(absolutePath)) {
|
|
88434
88441
|
return {
|
|
88435
88442
|
content: "",
|
|
88436
88443
|
encoding: "base64",
|
|
@@ -88521,7 +88528,7 @@ var WorkspaceTailWatchService = class {
|
|
|
88521
88528
|
};
|
|
88522
88529
|
|
|
88523
88530
|
// src/auth/idempotency.ts
|
|
88524
|
-
import { createHash as
|
|
88531
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
88525
88532
|
var IdempotencyService = class {
|
|
88526
88533
|
constructor(db) {
|
|
88527
88534
|
this.db = db;
|
|
@@ -88529,7 +88536,7 @@ var IdempotencyService = class {
|
|
|
88529
88536
|
db;
|
|
88530
88537
|
inflight = /* @__PURE__ */ new Map();
|
|
88531
88538
|
payloadHash(payload) {
|
|
88532
|
-
return
|
|
88539
|
+
return createHash9("sha256").update(JSON.stringify(payload ?? null)).digest("hex");
|
|
88533
88540
|
}
|
|
88534
88541
|
key(clientIdentity, operation, idempotencyKey) {
|
|
88535
88542
|
return `${clientIdentity}\0${operation}\0${idempotencyKey}`;
|
|
@@ -88628,7 +88635,62 @@ var IdempotencyService = class {
|
|
|
88628
88635
|
};
|
|
88629
88636
|
|
|
88630
88637
|
// src/provider/provider-store.ts
|
|
88631
|
-
import { randomUUID as
|
|
88638
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
88639
|
+
|
|
88640
|
+
// src/provider/secret-crypto.ts
|
|
88641
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync18, chmodSync as chmodSync2 } from "node:fs";
|
|
88642
|
+
import { dirname as dirname18 } from "node:path";
|
|
88643
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
|
|
88644
|
+
var ENC_PREFIX2 = "enc:v1:";
|
|
88645
|
+
var KEY_BYTES = 32;
|
|
88646
|
+
function isEncryptedSecret(value) {
|
|
88647
|
+
return typeof value === "string" && value.startsWith(ENC_PREFIX2);
|
|
88648
|
+
}
|
|
88649
|
+
function ensureKeyFile(keyPath) {
|
|
88650
|
+
if (existsSync41(keyPath)) {
|
|
88651
|
+
const raw = readFileSync20(keyPath);
|
|
88652
|
+
if (raw.length === KEY_BYTES) return raw;
|
|
88653
|
+
}
|
|
88654
|
+
mkdirSync23(dirname18(keyPath), { recursive: true, mode: 448 });
|
|
88655
|
+
const key = randomBytes5(KEY_BYTES);
|
|
88656
|
+
writeFileSync18(keyPath, key, { mode: 384 });
|
|
88657
|
+
try {
|
|
88658
|
+
chmodSync2(keyPath, 384);
|
|
88659
|
+
} catch {
|
|
88660
|
+
}
|
|
88661
|
+
return key;
|
|
88662
|
+
}
|
|
88663
|
+
function createNodeSecretCrypto(keyPath) {
|
|
88664
|
+
const key = ensureKeyFile(keyPath);
|
|
88665
|
+
return {
|
|
88666
|
+
encrypt(plain) {
|
|
88667
|
+
if (!plain) return "";
|
|
88668
|
+
if (isEncryptedSecret(plain)) return plain;
|
|
88669
|
+
const iv = randomBytes5(12);
|
|
88670
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
88671
|
+
const ciphertext = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
|
|
88672
|
+
const tag = cipher.getAuthTag();
|
|
88673
|
+
return ENC_PREFIX2 + Buffer.concat([iv, tag, ciphertext]).toString("base64");
|
|
88674
|
+
},
|
|
88675
|
+
decrypt(stored) {
|
|
88676
|
+
if (!stored) return "";
|
|
88677
|
+
if (!isEncryptedSecret(stored)) return stored;
|
|
88678
|
+
try {
|
|
88679
|
+
const buf = Buffer.from(stored.slice(ENC_PREFIX2.length), "base64");
|
|
88680
|
+
const iv = buf.subarray(0, 12);
|
|
88681
|
+
const tag = buf.subarray(12, 28);
|
|
88682
|
+
const data = buf.subarray(28);
|
|
88683
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
88684
|
+
decipher.setAuthTag(tag);
|
|
88685
|
+
return Buffer.concat([decipher.update(data), decipher.final()]).toString("utf8");
|
|
88686
|
+
} catch {
|
|
88687
|
+
return "";
|
|
88688
|
+
}
|
|
88689
|
+
}
|
|
88690
|
+
};
|
|
88691
|
+
}
|
|
88692
|
+
|
|
88693
|
+
// src/provider/provider-store.ts
|
|
88632
88694
|
function safeParse3(text2, fallback) {
|
|
88633
88695
|
if (!text2) return fallback;
|
|
88634
88696
|
try {
|
|
@@ -88660,7 +88722,7 @@ var ProviderStore = class {
|
|
|
88660
88722
|
}
|
|
88661
88723
|
createCredential(input) {
|
|
88662
88724
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
88663
|
-
const id = input.id?.trim() ||
|
|
88725
|
+
const id = input.id?.trim() || randomUUID13();
|
|
88664
88726
|
const maxOrder = this.db.prepare("SELECT MAX(sort_order) as m FROM provider_credentials").get()?.m ?? -1;
|
|
88665
88727
|
this.db.prepare(
|
|
88666
88728
|
`INSERT INTO provider_credentials
|
|
@@ -88856,7 +88918,7 @@ var ProviderStore = class {
|
|
|
88856
88918
|
};
|
|
88857
88919
|
|
|
88858
88920
|
// src/session/host-action-mcp-server.ts
|
|
88859
|
-
import { randomUUID as
|
|
88921
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
88860
88922
|
import {
|
|
88861
88923
|
createServer as createHttpServer
|
|
88862
88924
|
} from "node:http";
|
|
@@ -92451,7 +92513,7 @@ init_interaction_memory();
|
|
|
92451
92513
|
// ../../packages/runtime/src/fs/interaction-memory.ts
|
|
92452
92514
|
init_superone_home();
|
|
92453
92515
|
init_interaction_memory();
|
|
92454
|
-
import { createHash as
|
|
92516
|
+
import { createHash as createHash10, randomUUID as randomUUID14 } from "node:crypto";
|
|
92455
92517
|
import { lstat, mkdir, open, readdir, rename, rm } from "node:fs/promises";
|
|
92456
92518
|
import { constants as constants4 } from "node:fs";
|
|
92457
92519
|
import { join as join34 } from "node:path";
|
|
@@ -92647,7 +92709,7 @@ function indexEntry(note) {
|
|
|
92647
92709
|
};
|
|
92648
92710
|
}
|
|
92649
92711
|
async function writeAtomic(dir, name, raw) {
|
|
92650
|
-
const temp = join34(dir, `.${name}.${
|
|
92712
|
+
const temp = join34(dir, `.${name}.${randomUUID14()}.tmp`);
|
|
92651
92713
|
try {
|
|
92652
92714
|
const file2 = await open(temp, "wx", 384);
|
|
92653
92715
|
try {
|
|
@@ -92705,7 +92767,7 @@ var InteractionMemoryStore = class _InteractionMemoryStore {
|
|
|
92705
92767
|
await file2.close();
|
|
92706
92768
|
}
|
|
92707
92769
|
const { meta: meta3, content } = parseNote(raw, { title: topic2, resource: target.resource });
|
|
92708
|
-
return { ...meta3, topic: topic2, content, revision:
|
|
92770
|
+
return { ...meta3, topic: topic2, content, revision: createHash10("sha256").update(raw).digest("hex") };
|
|
92709
92771
|
} catch (err) {
|
|
92710
92772
|
if (missing(err)) return null;
|
|
92711
92773
|
throw new Error(`Cannot read personal memory ${target.label}/${topic2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -92798,7 +92860,7 @@ var InteractionMemoryStore = class _InteractionMemoryStore {
|
|
|
92798
92860
|
saved: true,
|
|
92799
92861
|
created: !existing,
|
|
92800
92862
|
...target.identity,
|
|
92801
|
-
...summary({ ...meta3, topic: args.topic, content, revision:
|
|
92863
|
+
...summary({ ...meta3, topic: args.topic, content, revision: createHash10("sha256").update(raw).digest("hex") })
|
|
92802
92864
|
};
|
|
92803
92865
|
} finally {
|
|
92804
92866
|
await rm(lock, { recursive: true, force: true });
|
|
@@ -100998,9 +101060,7 @@ function registerNodeCollabTools(server, superoneSessionId, collab) {
|
|
|
100998
101060
|
"session_collab_start",
|
|
100999
101061
|
{
|
|
101000
101062
|
description: startDesc.description,
|
|
101001
|
-
inputSchema:
|
|
101002
|
-
credential: external_exports.string().min(1)
|
|
101003
|
-
}
|
|
101063
|
+
inputSchema: jsonSchemaToZodShape(startDesc.inputSchema)
|
|
101004
101064
|
},
|
|
101005
101065
|
async (args) => {
|
|
101006
101066
|
try {
|
|
@@ -101135,7 +101195,7 @@ function headerValue(req, name) {
|
|
|
101135
101195
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
101136
101196
|
}
|
|
101137
101197
|
async function startHostActionMcpServer(opts) {
|
|
101138
|
-
const masterToken = opts.masterToken ??
|
|
101198
|
+
const masterToken = opts.masterToken ?? randomUUID15();
|
|
101139
101199
|
const httpSessions = /* @__PURE__ */ new Map();
|
|
101140
101200
|
const httpSessionsBySuperone = /* @__PURE__ */ new Map();
|
|
101141
101201
|
let httpUrl2 = "";
|
|
@@ -101203,7 +101263,7 @@ async function startHostActionMcpServer(opts) {
|
|
|
101203
101263
|
});
|
|
101204
101264
|
let sessionRef;
|
|
101205
101265
|
const transport = new StreamableHTTPServerTransport({
|
|
101206
|
-
sessionIdGenerator:
|
|
101266
|
+
sessionIdGenerator: randomUUID15,
|
|
101207
101267
|
onsessioninitialized: (id) => {
|
|
101208
101268
|
if (sessionRef) httpSessions.set(id, sessionRef);
|
|
101209
101269
|
},
|
|
@@ -101987,7 +102047,7 @@ function computeNextRunAt(schedule) {
|
|
|
101987
102047
|
}
|
|
101988
102048
|
|
|
101989
102049
|
// ../../packages/runtime/src/automations/store.ts
|
|
101990
|
-
import { randomUUID as
|
|
102050
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
101991
102051
|
function toRecord(row, projectPath) {
|
|
101992
102052
|
return {
|
|
101993
102053
|
id: row.id,
|
|
@@ -102019,7 +102079,7 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102019
102079
|
return { ...toRecord(row, path), projectId: row.project_id };
|
|
102020
102080
|
},
|
|
102021
102081
|
create(projectId, projectPath, data) {
|
|
102022
|
-
const id =
|
|
102082
|
+
const id = randomUUID16();
|
|
102023
102083
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
102024
102084
|
const nextRunAt = computeNextRunAt(data.schedule);
|
|
102025
102085
|
db.prepare(
|
|
@@ -102069,8 +102129,8 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102069
102129
|
delete(id) {
|
|
102070
102130
|
return db.prepare("DELETE FROM automations WHERE id = ?").run(id).changes > 0;
|
|
102071
102131
|
},
|
|
102072
|
-
listDue(
|
|
102073
|
-
const reclaimBefore = new Date(Date.parse(
|
|
102132
|
+
listDue(nowIso2) {
|
|
102133
|
+
const reclaimBefore = new Date(Date.parse(nowIso2) - 30 * 60 * 1e3).toISOString();
|
|
102074
102134
|
const rows = db.prepare(
|
|
102075
102135
|
`SELECT * FROM automations
|
|
102076
102136
|
WHERE enabled = 1
|
|
@@ -102081,7 +102141,7 @@ function createAutomationStore(db, resolveProjectPath2) {
|
|
|
102081
102141
|
OR last_run_status != 'running'
|
|
102082
102142
|
OR COALESCE(last_run_at, updated_at) < ?
|
|
102083
102143
|
)`
|
|
102084
|
-
).all(
|
|
102144
|
+
).all(nowIso2, reclaimBefore);
|
|
102085
102145
|
return rows.map((r) => {
|
|
102086
102146
|
const path = resolveProjectPath2(r.project_id) ?? "";
|
|
102087
102147
|
return toRecord(r, path);
|
|
@@ -102324,10 +102384,10 @@ init_environment();
|
|
|
102324
102384
|
// ../../packages/shared/src/environment/draft-content.ts
|
|
102325
102385
|
function hasPersistableDraftContent(draft) {
|
|
102326
102386
|
if (draft.text.trim() || draft.attachments?.length) return true;
|
|
102327
|
-
const
|
|
102387
|
+
const pending2 = [draft.docJson];
|
|
102328
102388
|
const seen = /* @__PURE__ */ new Set();
|
|
102329
|
-
while (
|
|
102330
|
-
const node =
|
|
102389
|
+
while (pending2.length) {
|
|
102390
|
+
const node = pending2.pop();
|
|
102331
102391
|
if (!node || typeof node !== "object") continue;
|
|
102332
102392
|
if (seen.has(node)) continue;
|
|
102333
102393
|
seen.add(node);
|
|
@@ -102335,7 +102395,7 @@ function hasPersistableDraftContent(draft) {
|
|
|
102335
102395
|
if (type === "text" && typeof text2 === "string" && text2.trim()) return true;
|
|
102336
102396
|
const value = type === "pasteChip" ? attrs?.text : type === "mention" ? attrs?.value : null;
|
|
102337
102397
|
if (typeof value === "string" && value.trim()) return true;
|
|
102338
|
-
if (Array.isArray(content))
|
|
102398
|
+
if (Array.isArray(content)) pending2.push(...content);
|
|
102339
102399
|
}
|
|
102340
102400
|
return false;
|
|
102341
102401
|
}
|
|
@@ -102540,7 +102600,7 @@ ON CONFLICT(id) DO UPDATE SET
|
|
|
102540
102600
|
|
|
102541
102601
|
// src/workspace/artifact-zone.ts
|
|
102542
102602
|
init_environment();
|
|
102543
|
-
import { createHash as
|
|
102603
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
102544
102604
|
import {
|
|
102545
102605
|
closeSync as closeSync3,
|
|
102546
102606
|
fstatSync as fstatSync3,
|
|
@@ -102862,7 +102922,7 @@ var ArtifactZoneService = class {
|
|
|
102862
102922
|
written: 0,
|
|
102863
102923
|
total: req.total,
|
|
102864
102924
|
sha256: req.sha256,
|
|
102865
|
-
hash:
|
|
102925
|
+
hash: createHash11("sha256"),
|
|
102866
102926
|
lastActivityAt: Date.now()
|
|
102867
102927
|
};
|
|
102868
102928
|
this.transfers.set(transferId, transfer);
|
|
@@ -102968,7 +103028,6 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
102968
103028
|
}
|
|
102969
103029
|
const allowSimulatedTurnFallback = partial2.allowSimulatedTurnFallback ?? simulatedHarness;
|
|
102970
103030
|
const providers = new ProviderStore(db, paths.providerSecretsKey);
|
|
102971
|
-
const collabSecrets = createNodeSecretCrypto(paths.providerSecretsKey);
|
|
102972
103031
|
let sessionsRef = null;
|
|
102973
103032
|
let collaborationRef = null;
|
|
102974
103033
|
const hostActionMcp = await startHostActionMcpServer({
|
|
@@ -103003,8 +103062,9 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103003
103062
|
if (!collaborationRef) throw Object.assign(new Error("collab not ready"), { code: "failed_precondition" });
|
|
103004
103063
|
const a = args && typeof args === "object" ? args : {};
|
|
103005
103064
|
return collaborationRef.start({
|
|
103006
|
-
|
|
103007
|
-
|
|
103065
|
+
callerSessionId: sessionId,
|
|
103066
|
+
launchId: typeof a.launchId === "string" ? a.launchId : "",
|
|
103067
|
+
task: typeof a.task === "string" ? a.task : void 0
|
|
103008
103068
|
});
|
|
103009
103069
|
},
|
|
103010
103070
|
send: async (sessionId, args) => {
|
|
@@ -103012,7 +103072,7 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103012
103072
|
const a = args && typeof args === "object" ? args : {};
|
|
103013
103073
|
return collaborationRef.send({
|
|
103014
103074
|
sessionId,
|
|
103015
|
-
|
|
103075
|
+
to: typeof a.to === "string" ? a.to : void 0,
|
|
103016
103076
|
content: String(a.content ?? ""),
|
|
103017
103077
|
clientMessageId: typeof a.clientMessageId === "string" ? a.clientMessageId : void 0
|
|
103018
103078
|
});
|
|
@@ -103022,8 +103082,7 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103022
103082
|
const a = args && typeof args === "object" ? args : {};
|
|
103023
103083
|
return collaborationRef.retrieve({
|
|
103024
103084
|
sessionId,
|
|
103025
|
-
|
|
103026
|
-
credential: typeof a.credential === "string" ? a.credential : void 0
|
|
103085
|
+
from: Array.isArray(a.from) ? a.from.filter((id) => typeof id === "string") : void 0
|
|
103027
103086
|
});
|
|
103028
103087
|
}
|
|
103029
103088
|
}
|
|
@@ -103070,7 +103129,6 @@ async function startNodeRuntime(partial2 = {}) {
|
|
|
103070
103129
|
providers,
|
|
103071
103130
|
projects,
|
|
103072
103131
|
workspaceGit,
|
|
103073
|
-
secrets: collabSecrets,
|
|
103074
103132
|
sessionProviders,
|
|
103075
103133
|
experimentalClaudeOpenAiChatEnabled: () => loadNodeAgentSettings(paths.configJson).experimentalClaudeOpenAiChatEnabled
|
|
103076
103134
|
});
|