chatroom-cli 1.57.3 → 1.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1697 -309
- package/dist/index.js.map +53 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28903,6 +28903,12 @@ function normalizeCursorSdkListedModels(listedModelIds) {
|
|
|
28903
28903
|
var CURSOR_PROVIDER2 = "cursor", DEFAULT_AUTO_MODEL_ID = "default", UI_AUTO_MODEL_ID = "auto";
|
|
28904
28904
|
|
|
28905
28905
|
// src/infrastructure/services/remote-agents/cursor-sdk/cursor-sdk-package.ts
|
|
28906
|
+
var exports_cursor_sdk_package = {};
|
|
28907
|
+
__export(exports_cursor_sdk_package, {
|
|
28908
|
+
importBundledCursorSdk: () => importBundledCursorSdk,
|
|
28909
|
+
getBundledCursorSdkVersion: () => getBundledCursorSdkVersion,
|
|
28910
|
+
formatCursorSdkLoadError: () => formatCursorSdkLoadError
|
|
28911
|
+
});
|
|
28906
28912
|
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
28907
28913
|
import { createRequire as createRequire3 } from "node:module";
|
|
28908
28914
|
import { dirname as dirname2, join as join7 } from "node:path";
|
|
@@ -32520,6 +32526,12 @@ var init_opencode_sdk = __esm(() => {
|
|
|
32520
32526
|
});
|
|
32521
32527
|
|
|
32522
32528
|
// src/infrastructure/services/remote-agents/pi-sdk/pi-sdk-package.ts
|
|
32529
|
+
var exports_pi_sdk_package = {};
|
|
32530
|
+
__export(exports_pi_sdk_package, {
|
|
32531
|
+
importBundledPiSdk: () => importBundledPiSdk,
|
|
32532
|
+
getBundledPiSdkVersion: () => getBundledPiSdkVersion,
|
|
32533
|
+
formatPiSdkLoadError: () => formatPiSdkLoadError
|
|
32534
|
+
});
|
|
32523
32535
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
32524
32536
|
import { dirname as dirname4, join as join9 } from "node:path";
|
|
32525
32537
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -75554,14 +75566,25 @@ Put your **complete** deliverable in the handoff message — not in session text
|
|
|
75554
75566
|
function getBuilderToPlannerHandoffTemplate() {
|
|
75555
75567
|
return `${getHandoffRecipientVisibilityCallout("planner")}
|
|
75556
75568
|
|
|
75557
|
-
**Handoff Template (Builder → Planner)** — paste into the handoff message. Fill in EVERY section
|
|
75569
|
+
**Handoff Template (Builder → Planner)** — paste into the handoff message. Fill in EVERY section below. If a section does not apply, write \`Not Applicable\` (do not delete the section):
|
|
75558
75570
|
|
|
75559
75571
|
\`\`\`markdown
|
|
75560
75572
|
## Summary
|
|
75561
75573
|
<what was implemented or attempted, in plain terms>
|
|
75562
75574
|
|
|
75563
|
-
##
|
|
75575
|
+
## Template Disclosure Confirmation
|
|
75576
|
+
- [ ] I confirm that I have seen this template at the start of this task, before implementing or modifying any code
|
|
75577
|
+
|
|
75578
|
+
## Proof of Principle
|
|
75579
|
+
<!-- Demonstrate adherence to:
|
|
75580
|
+
- Organization & Maintainability: a small change in requirements should result in a small change in code in a small number of files and folders.
|
|
75581
|
+
- Static Evaluability and Provability: the system's behavior should be provably correct by looking at the source code, then automated tests, then manual tests, in this order.
|
|
75582
|
+
-->
|
|
75583
|
+
<how this work follows the principles above — localized changes, readable structure, correctness provable from source then tests>
|
|
75584
|
+
|
|
75585
|
+
## Proof of Completion
|
|
75564
75586
|
- \`path/to/file.ts\` — <what changed and why>
|
|
75587
|
+
<evidence the goal was met — list every file you modified>
|
|
75565
75588
|
|
|
75566
75589
|
## Verification
|
|
75567
75590
|
- \`pnpm typecheck && pnpm test\` — <pass/fail + notes>
|
|
@@ -75643,7 +75666,7 @@ Cross-file types, interfaces, or patterns that apply beyond a single file. Write
|
|
|
75643
75666
|
- <e.g. "Do not add new abstractions", "Do not refactor unrelated files", "Do not change existing public APIs", or "Not Applicable">
|
|
75644
75667
|
|
|
75645
75668
|
## Skills to activate
|
|
75646
|
-
- <e.g. chatroom skill activate
|
|
75669
|
+
- <e.g. chatroom skill activate code-review --chatroom-id=<id> --role=builder, or "Not Applicable">
|
|
75647
75670
|
|
|
75648
75671
|
## Out of scope
|
|
75649
75672
|
- <files or areas the builder must NOT touch in this slice, or "Not Applicable">
|
|
@@ -75684,6 +75707,15 @@ function getPlannerToUserReportTemplate() {
|
|
|
75684
75707
|
- \`path/to/file.ts\` — <what changed and why>
|
|
75685
75708
|
<evidence the goal was met — list every file you (or the builder) modified>
|
|
75686
75709
|
|
|
75710
|
+
## Backlog Tasks Implemented
|
|
75711
|
+
- \`backlog-item-id\` — <backlog item title/summary and how this work addresses it>
|
|
75712
|
+
<List every backlog item this work implemented. Write \`Not Applicable\` if no backlog items were in scope.>
|
|
75713
|
+
|
|
75714
|
+
## Backlog Pending User Review Confirmation
|
|
75715
|
+
- [ ] I confirm that every backlog item implemented in this work has been moved to \`pending_user_review\` via \`chatroom backlog mark-for-review\` because a PR has been raised for user review
|
|
75716
|
+
- PR URL(s): <link to PR(s), or \`Not Applicable\` if no PR was raised>
|
|
75717
|
+
- If no backlog items apply, write \`Not Applicable\` for the checkbox and explain in one line
|
|
75718
|
+
|
|
75687
75719
|
## Key Technical Decisions
|
|
75688
75720
|
- <schema design, modules, interfaces, domain entities — what you chose and why, or "Not Applicable">
|
|
75689
75721
|
|
|
@@ -76013,16 +76045,11 @@ var init_fromContext = __esm(() => {
|
|
|
76013
76045
|
init_planner();
|
|
76014
76046
|
init_solo();
|
|
76015
76047
|
});
|
|
76016
|
-
// ../../services/backend/prompts/cli/sections/handoff-templates-preview.ts
|
|
76017
|
-
var init_handoff_templates_preview = __esm(() => {
|
|
76018
|
-
init_handoff_templates3();
|
|
76019
|
-
});
|
|
76020
76048
|
|
|
76021
76049
|
// ../../services/backend/prompts/native/task-started-content.ts
|
|
76022
76050
|
var init_task_started_content = __esm(() => {
|
|
76023
76051
|
init_new();
|
|
76024
76052
|
});
|
|
76025
|
-
|
|
76026
76053
|
// ../../services/backend/prompts/sections/classification-guide.ts
|
|
76027
76054
|
var init_classification_guide = __esm(() => {
|
|
76028
76055
|
init_cli();
|
|
@@ -76050,6 +76077,8 @@ var init_current_classification = () => {};
|
|
|
76050
76077
|
var init_getting_started = __esm(() => {
|
|
76051
76078
|
init_getting_started_content();
|
|
76052
76079
|
});
|
|
76080
|
+
// ../../services/backend/src/domain/usecase/skills/modules/attachments/index.ts
|
|
76081
|
+
var init_attachments = () => {};
|
|
76053
76082
|
|
|
76054
76083
|
// ../../services/backend/prompts/cli/backlog/command.ts
|
|
76055
76084
|
var init_command3 = __esm(() => {
|
|
@@ -76064,18 +76093,11 @@ var init_backlog = __esm(() => {
|
|
|
76064
76093
|
// ../../services/backend/src/domain/usecase/skills/modules/code-review/index.ts
|
|
76065
76094
|
var init_code_review = () => {};
|
|
76066
76095
|
|
|
76067
|
-
// ../../services/backend/src/domain/usecase/skills/modules/development-workflow/index.ts
|
|
76068
|
-
var init_development_workflow = () => {};
|
|
76069
|
-
|
|
76070
|
-
// ../../services/backend/src/domain/usecase/skills/modules/software-engineering/index.ts
|
|
76071
|
-
var init_software_engineering = () => {};
|
|
76072
|
-
|
|
76073
76096
|
// ../../services/backend/src/domain/usecase/skills/registry.ts
|
|
76074
76097
|
var init_registry3 = __esm(() => {
|
|
76098
|
+
init_attachments();
|
|
76075
76099
|
init_backlog();
|
|
76076
76100
|
init_code_review();
|
|
76077
|
-
init_development_workflow();
|
|
76078
|
-
init_software_engineering();
|
|
76079
76101
|
});
|
|
76080
76102
|
|
|
76081
76103
|
// ../../services/backend/prompts/sections/glossary.ts
|
|
@@ -76102,20 +76124,15 @@ var init_glossary = __esm(() => {
|
|
|
76102
76124
|
linkedSkillId: "backlog"
|
|
76103
76125
|
},
|
|
76104
76126
|
{
|
|
76105
|
-
term: "
|
|
76106
|
-
definition: "
|
|
76107
|
-
linkedSkillId: "
|
|
76127
|
+
term: "attachments",
|
|
76128
|
+
definition: "Message attachment types (task, backlog, message, snippet) and their compose, delivery, and task-read paths. " + "Use when adding or changing attachment UI, delivery XML, or agent-facing attachment formats.",
|
|
76129
|
+
linkedSkillId: "attachments"
|
|
76108
76130
|
},
|
|
76109
76131
|
{
|
|
76110
76132
|
term: "code-review",
|
|
76111
76133
|
definition: "Eight-pillar code review framework: simplification, type drift, duplication, design patterns, security, test quality, ownership/observability, and dead code elimination. " + "Covers AI-generated code review with focus on maintainability and tech debt prevention.",
|
|
76112
76134
|
linkedSkillId: "code-review"
|
|
76113
76135
|
},
|
|
76114
|
-
{
|
|
76115
|
-
term: "development-workflow",
|
|
76116
|
-
definition: "Manages the development and release flow: creating release branches, updating versions, raising PRs, and managing feature branches. " + "Use this skill for coordinating complex release and development processes.",
|
|
76117
|
-
linkedSkillId: "development-workflow"
|
|
76118
|
-
},
|
|
76119
76136
|
{
|
|
76120
76137
|
term: "structural-decisions",
|
|
76121
76138
|
definition: "Meta-level architectural choices that persist in the codebase and influence consistency: " + "folder structure, file naming, interface definitions, and key abstraction names/locations " + "(e.g., Repository/Service layers)."
|
|
@@ -76394,7 +76411,6 @@ var init_generator = __esm(() => {
|
|
|
76394
76411
|
init_command();
|
|
76395
76412
|
init_reminder();
|
|
76396
76413
|
init_fromContext();
|
|
76397
|
-
init_handoff_templates_preview();
|
|
76398
76414
|
init_classification_guide();
|
|
76399
76415
|
init_commands_reference();
|
|
76400
76416
|
init_current_classification();
|
|
@@ -77401,6 +77417,52 @@ function readFileContent(filePath, optionName) {
|
|
|
77401
77417
|
}
|
|
77402
77418
|
var init_file_content = () => {};
|
|
77403
77419
|
|
|
77420
|
+
// ../../services/backend/prompts/attachments/render-delivery-attachments.ts
|
|
77421
|
+
function renderSnippetAttachment(snippet) {
|
|
77422
|
+
return [
|
|
77423
|
+
` <attachment reference="${snippet.reference}">`,
|
|
77424
|
+
` <snippet file-source="${snippet.fileSource}">`,
|
|
77425
|
+
` <user-selected-content>`,
|
|
77426
|
+
snippet.selectedContent,
|
|
77427
|
+
` </user-selected-content>`,
|
|
77428
|
+
` </snippet>`,
|
|
77429
|
+
` </attachment>`
|
|
77430
|
+
];
|
|
77431
|
+
}
|
|
77432
|
+
function renderBacklogAttachments(items, ctx) {
|
|
77433
|
+
const lines = [];
|
|
77434
|
+
for (const item of items) {
|
|
77435
|
+
lines.push(` <attachment type="backlog-item">`);
|
|
77436
|
+
lines.push(` - [${item.status.toUpperCase()}] ${item.content}`);
|
|
77437
|
+
lines.push(` ID: ${item._id}`);
|
|
77438
|
+
lines.push(` <hint>Work on this item. When done: chatroom backlog mark-for-review --chatroom-id="${ctx.chatroomId}" --role="${ctx.role}" --backlog-item-id=${item._id}</hint>`);
|
|
77439
|
+
lines.push(` </attachment>`);
|
|
77440
|
+
}
|
|
77441
|
+
return lines;
|
|
77442
|
+
}
|
|
77443
|
+
function renderSnippetAttachments(snippets) {
|
|
77444
|
+
const lines = [];
|
|
77445
|
+
for (const snippet of snippets) {
|
|
77446
|
+
lines.push(...renderSnippetAttachment(snippet));
|
|
77447
|
+
}
|
|
77448
|
+
return lines;
|
|
77449
|
+
}
|
|
77450
|
+
function renderDeliveryAttachmentsBlock(input, ctx) {
|
|
77451
|
+
const backlogLines = input.attachedBacklogItems?.length ? DELIVERY_ATTACHMENT_RENDERERS.backlog(input.attachedBacklogItems, ctx) : [];
|
|
77452
|
+
const snippetLines = input.attachedSnippets?.length ? DELIVERY_ATTACHMENT_RENDERERS.snippet(input.attachedSnippets, ctx) : [];
|
|
77453
|
+
if (backlogLines.length === 0 && snippetLines.length === 0)
|
|
77454
|
+
return [];
|
|
77455
|
+
return ["", "<attachments>", ...backlogLines, ...snippetLines, "</attachments>"];
|
|
77456
|
+
}
|
|
77457
|
+
var DELIVERY_ATTACHMENT_RENDERERS;
|
|
77458
|
+
var init_render_delivery_attachments = __esm(() => {
|
|
77459
|
+
DELIVERY_ATTACHMENT_RENDERERS = {
|
|
77460
|
+
backlog: renderBacklogAttachments,
|
|
77461
|
+
snippet: renderSnippetAttachments,
|
|
77462
|
+
message: () => []
|
|
77463
|
+
};
|
|
77464
|
+
});
|
|
77465
|
+
|
|
77404
77466
|
// src/commands/task/read/render.ts
|
|
77405
77467
|
function detectBacklogDivergence(contextContent, attachedIds) {
|
|
77406
77468
|
if (!contextContent || attachedIds.length === 0) {
|
|
@@ -77409,19 +77471,10 @@ function detectBacklogDivergence(contextContent, attachedIds) {
|
|
|
77409
77471
|
return attachedIds.filter((id3) => !contextContent.includes(id3));
|
|
77410
77472
|
}
|
|
77411
77473
|
function renderAttachments(input, chatroomId, role) {
|
|
77412
|
-
|
|
77413
|
-
|
|
77414
|
-
|
|
77415
|
-
|
|
77416
|
-
for (const item of input.attachedBacklogItems) {
|
|
77417
|
-
lines.push(` <attachment type="backlog-item">`);
|
|
77418
|
-
lines.push(` - [${item.status.toUpperCase()}] ${item.content}`);
|
|
77419
|
-
lines.push(` ID: ${item._id}`);
|
|
77420
|
-
lines.push(` <hint>Work on this item. When done: chatroom backlog mark-for-review --chatroom-id="${chatroomId}" --role="${role}" --backlog-item-id=${item._id}</hint>`);
|
|
77421
|
-
lines.push(` </attachment>`);
|
|
77422
|
-
}
|
|
77423
|
-
lines.push("</attachments>");
|
|
77424
|
-
return lines;
|
|
77474
|
+
return renderDeliveryAttachmentsBlock({
|
|
77475
|
+
attachedBacklogItems: input.attachedBacklogItems,
|
|
77476
|
+
attachedSnippets: input.attachedSnippets
|
|
77477
|
+
}, { chatroomId, role, mode: "task-read" });
|
|
77425
77478
|
}
|
|
77426
77479
|
function renderDivergenceWarnings(input) {
|
|
77427
77480
|
const ctx = input.context;
|
|
@@ -77476,6 +77529,9 @@ ${content}`);
|
|
|
77476
77529
|
return lines.join(`
|
|
77477
77530
|
`);
|
|
77478
77531
|
}
|
|
77532
|
+
var init_render = __esm(() => {
|
|
77533
|
+
init_render_delivery_attachments();
|
|
77534
|
+
});
|
|
77479
77535
|
|
|
77480
77536
|
// src/commands/task/read/index.ts
|
|
77481
77537
|
var exports_read = {};
|
|
@@ -77503,6 +77559,18 @@ function validateTaskId(taskId) {
|
|
|
77503
77559
|
}
|
|
77504
77560
|
return exports_Effect.void;
|
|
77505
77561
|
}
|
|
77562
|
+
function buildTaskPromptFromReadResult(result, chatroomId, role) {
|
|
77563
|
+
return renderTaskPrompt({
|
|
77564
|
+
taskId: result.taskId,
|
|
77565
|
+
status: result.status,
|
|
77566
|
+
content: result.content,
|
|
77567
|
+
chatroomId,
|
|
77568
|
+
role,
|
|
77569
|
+
context: result.context && typeof result.context === "object" ? result.context : undefined,
|
|
77570
|
+
attachedBacklogItems: Array.isArray(result.attachedBacklogItems) ? result.attachedBacklogItems : undefined,
|
|
77571
|
+
attachedSnippets: Array.isArray(result.attachedSnippets) ? result.attachedSnippets : undefined
|
|
77572
|
+
});
|
|
77573
|
+
}
|
|
77506
77574
|
function handleTaskReadError(err) {
|
|
77507
77575
|
return exports_Effect.sync(() => {
|
|
77508
77576
|
const handler = taskReadErrorHandlers[err._tag];
|
|
@@ -77535,19 +77603,12 @@ var taskReadEffect = (chatroomId, options) => exports_Effect.gen(function* () {
|
|
|
77535
77603
|
taskId
|
|
77536
77604
|
}).pipe(exports_Effect.mapError((cause3) => ({ _tag: "MutationFailed", cause: cause3 })));
|
|
77537
77605
|
yield* exports_Effect.sync(() => {
|
|
77538
|
-
console.log(
|
|
77539
|
-
taskId: result.taskId,
|
|
77540
|
-
status: result.status,
|
|
77541
|
-
content: result.content,
|
|
77542
|
-
chatroomId,
|
|
77543
|
-
role,
|
|
77544
|
-
context: result.context && typeof result.context === "object" ? result.context : undefined,
|
|
77545
|
-
attachedBacklogItems: Array.isArray(result.attachedBacklogItems) ? result.attachedBacklogItems : undefined
|
|
77546
|
-
}));
|
|
77606
|
+
console.log(buildTaskPromptFromReadResult(result, chatroomId, role));
|
|
77547
77607
|
});
|
|
77548
77608
|
}), taskReadErrorHandlers;
|
|
77549
77609
|
var init_read = __esm(() => {
|
|
77550
77610
|
init_esm();
|
|
77611
|
+
init_render();
|
|
77551
77612
|
init_api3();
|
|
77552
77613
|
init_storage();
|
|
77553
77614
|
init_client2();
|
|
@@ -82679,161 +82740,311 @@ var init_commit_detail_sync = __esm(() => {
|
|
|
82679
82740
|
seenShas = new Map;
|
|
82680
82741
|
});
|
|
82681
82742
|
|
|
82682
|
-
// src/
|
|
82683
|
-
|
|
82684
|
-
const { publisher, machineId } = deps;
|
|
82685
|
-
const { harness, workspace } = input;
|
|
82686
|
-
const [agents, providers] = await Promise.all([
|
|
82687
|
-
harness.listAgents(),
|
|
82688
|
-
harness.listProviders()
|
|
82689
|
-
]);
|
|
82690
|
-
await publisher.publish({
|
|
82691
|
-
machineId,
|
|
82692
|
-
lastSeenAt: Date.now(),
|
|
82693
|
-
workspaces: [
|
|
82694
|
-
{
|
|
82695
|
-
workspaceId: workspace.workspaceId,
|
|
82696
|
-
cwd: workspace.cwd,
|
|
82697
|
-
name: workspace.name,
|
|
82698
|
-
harnesses: [
|
|
82699
|
-
{
|
|
82700
|
-
name: harness.type,
|
|
82701
|
-
displayName: harness.displayName,
|
|
82702
|
-
agents: [...agents],
|
|
82703
|
-
providers: [...providers]
|
|
82704
|
-
}
|
|
82705
|
-
]
|
|
82706
|
-
}
|
|
82707
|
-
]
|
|
82708
|
-
});
|
|
82709
|
-
}
|
|
82743
|
+
// src/infrastructure/harnesses/cursor-sdk/cursor-session.ts
|
|
82744
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
82710
82745
|
|
|
82711
|
-
|
|
82712
|
-
|
|
82713
|
-
|
|
82714
|
-
|
|
82715
|
-
|
|
82716
|
-
|
|
82717
|
-
|
|
82718
|
-
|
|
82746
|
+
class CursorSdkSession {
|
|
82747
|
+
opencodeSessionId;
|
|
82748
|
+
_sessionTitle;
|
|
82749
|
+
get sessionTitle() {
|
|
82750
|
+
return this._sessionTitle;
|
|
82751
|
+
}
|
|
82752
|
+
agent;
|
|
82753
|
+
onClose;
|
|
82754
|
+
listeners = new Set;
|
|
82755
|
+
closed = false;
|
|
82756
|
+
turnCount = 0;
|
|
82757
|
+
constructor(options) {
|
|
82758
|
+
this.agent = options.agent;
|
|
82759
|
+
this.opencodeSessionId = options.opencodeSessionId;
|
|
82760
|
+
this._sessionTitle = options.sessionTitle;
|
|
82761
|
+
this.onClose = options.onClose;
|
|
82762
|
+
}
|
|
82763
|
+
setTitle(title) {
|
|
82764
|
+
this._sessionTitle = title;
|
|
82765
|
+
}
|
|
82766
|
+
async prompt(input) {
|
|
82767
|
+
if (this.closed)
|
|
82768
|
+
throw new Error("Session is closed");
|
|
82769
|
+
const text = input.parts.map((p) => p.text).join(`
|
|
82770
|
+
`);
|
|
82771
|
+
const messageId = randomUUID3();
|
|
82772
|
+
const isFirstTurn = this.turnCount === 0;
|
|
82773
|
+
this.turnCount += 1;
|
|
82774
|
+
const modelId = input.model ? resolveModelFromPrompt(input.model.providerID, input.model.modelID) : undefined;
|
|
82775
|
+
const run3 = await withTimeout(this.agent.send(text, {
|
|
82776
|
+
local: { force: isFirstTurn },
|
|
82777
|
+
idempotencyKey: randomUUID3(),
|
|
82778
|
+
...modelId ? { model: { id: modelId } } : {}
|
|
82779
|
+
}), SEND_TIMEOUT_MS2, "agent.send");
|
|
82780
|
+
for await (const message of run3.stream()) {
|
|
82781
|
+
if (this.closed)
|
|
82782
|
+
break;
|
|
82783
|
+
this.emitFromSdkMessage(message, messageId);
|
|
82784
|
+
}
|
|
82785
|
+
const result = await withTimeout(run3.wait(), RUN_WAIT_TIMEOUT_MS2, "run.wait");
|
|
82786
|
+
if (result.status === "error") {
|
|
82787
|
+
const detail = typeof result.result === "string" && result.result.trim().length > 0 ? result.result.trim() : `run ${result.id} failed`;
|
|
82788
|
+
throw new Error(detail);
|
|
82789
|
+
}
|
|
82790
|
+
this.emit({ type: "session.idle", payload: {}, timestamp: Date.now() });
|
|
82791
|
+
}
|
|
82792
|
+
onEvent(listener) {
|
|
82793
|
+
this.listeners.add(listener);
|
|
82794
|
+
return () => {
|
|
82795
|
+
this.listeners.delete(listener);
|
|
82796
|
+
};
|
|
82797
|
+
}
|
|
82798
|
+
async close() {
|
|
82799
|
+
if (this.closed)
|
|
82719
82800
|
return;
|
|
82801
|
+
this.closed = true;
|
|
82802
|
+
try {
|
|
82803
|
+
this.agent.close();
|
|
82804
|
+
} catch {}
|
|
82805
|
+
this.onClose?.(this.opencodeSessionId);
|
|
82806
|
+
this.listeners.clear();
|
|
82807
|
+
}
|
|
82808
|
+
emit(event) {
|
|
82809
|
+
for (const listener of this.listeners) {
|
|
82810
|
+
listener(event);
|
|
82720
82811
|
}
|
|
82721
|
-
|
|
82722
|
-
|
|
82723
|
-
|
|
82724
|
-
|
|
82725
|
-
|
|
82726
|
-
|
|
82812
|
+
}
|
|
82813
|
+
emitDelta(messageId, delta, partType) {
|
|
82814
|
+
if (!delta)
|
|
82815
|
+
return;
|
|
82816
|
+
this.emit({
|
|
82817
|
+
type: "message.part.delta",
|
|
82818
|
+
payload: { messageID: messageId, delta, partType },
|
|
82819
|
+
timestamp: Date.now()
|
|
82727
82820
|
});
|
|
82728
|
-
};
|
|
82729
|
-
const unsub = wsClient2.onUpdate(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId }, () => runDrain(), (err) => {
|
|
82730
|
-
console.warn("[direct-harness] Command subscription error:", err instanceof Error ? err.message : String(err));
|
|
82731
|
-
});
|
|
82732
|
-
return { stop: unsub };
|
|
82733
|
-
}
|
|
82734
|
-
async function dispatchPendingCommand(session2, deps, cmd) {
|
|
82735
|
-
switch (cmd.type) {
|
|
82736
|
-
case "refreshCapabilities":
|
|
82737
|
-
await handleRefreshCapabilities(session2, deps, cmd);
|
|
82738
|
-
break;
|
|
82739
|
-
case "refreshSessionTitle":
|
|
82740
|
-
await handleRefreshSessionTitle(session2, deps, cmd);
|
|
82741
|
-
break;
|
|
82742
|
-
default:
|
|
82743
|
-
await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
|
|
82744
82821
|
}
|
|
82745
|
-
|
|
82746
|
-
|
|
82747
|
-
|
|
82748
|
-
|
|
82749
|
-
|
|
82750
|
-
|
|
82822
|
+
emitFromSdkMessage(message, messageId) {
|
|
82823
|
+
switch (message.type) {
|
|
82824
|
+
case "assistant":
|
|
82825
|
+
for (const block of message.message.content) {
|
|
82826
|
+
if (block.type === "text" && block.text) {
|
|
82827
|
+
this.emitDelta(messageId, block.text, "text");
|
|
82828
|
+
}
|
|
82829
|
+
}
|
|
82830
|
+
break;
|
|
82831
|
+
case "thinking":
|
|
82832
|
+
if (message.text) {
|
|
82833
|
+
this.emitDelta(messageId, message.text, "reasoning");
|
|
82834
|
+
}
|
|
82835
|
+
break;
|
|
82836
|
+
default:
|
|
82837
|
+
break;
|
|
82838
|
+
}
|
|
82751
82839
|
}
|
|
82752
|
-
await dispatchPendingCommand(session2, deps, cmd);
|
|
82753
82840
|
}
|
|
82754
|
-
|
|
82755
|
-
|
|
82756
|
-
|
|
82757
|
-
|
|
82758
|
-
|
|
82759
|
-
|
|
82760
|
-
|
|
82761
|
-
|
|
82762
|
-
|
|
82763
|
-
|
|
82764
|
-
|
|
82765
|
-
|
|
82766
|
-
|
|
82767
|
-
|
|
82768
|
-
|
|
82769
|
-
|
|
82841
|
+
function resolveModelFromPrompt(providerID, modelID) {
|
|
82842
|
+
if (providerID === "cursor")
|
|
82843
|
+
return resolveCursorSdkModel(modelID);
|
|
82844
|
+
return resolveCursorSdkModel(`${providerID}/${modelID}`);
|
|
82845
|
+
}
|
|
82846
|
+
var SEND_TIMEOUT_MS2 = 60000, RUN_WAIT_TIMEOUT_MS2 = 3600000;
|
|
82847
|
+
var init_cursor_session = () => {};
|
|
82848
|
+
|
|
82849
|
+
// src/infrastructure/harnesses/cursor-sdk/cursor-harness.ts
|
|
82850
|
+
async function loadSdk3() {
|
|
82851
|
+
if (_sdkCache3)
|
|
82852
|
+
return _sdkCache3;
|
|
82853
|
+
if (_sdkLoadError3)
|
|
82854
|
+
throw _sdkLoadError3;
|
|
82855
|
+
try {
|
|
82856
|
+
_sdkCache3 = await importBundledCursorSdk();
|
|
82857
|
+
return _sdkCache3;
|
|
82858
|
+
} catch (err) {
|
|
82859
|
+
_sdkLoadError3 = err;
|
|
82860
|
+
throw err;
|
|
82770
82861
|
}
|
|
82771
82862
|
}
|
|
82772
|
-
|
|
82773
|
-
|
|
82774
|
-
|
|
82775
|
-
|
|
82776
|
-
|
|
82777
|
-
|
|
82778
|
-
|
|
82779
|
-
|
|
82780
|
-
|
|
82781
|
-
|
|
82863
|
+
|
|
82864
|
+
class CursorSdkHarness {
|
|
82865
|
+
type = "cursor-sdk";
|
|
82866
|
+
displayName = "Cursor (SDK)";
|
|
82867
|
+
cwd;
|
|
82868
|
+
closed = false;
|
|
82869
|
+
sessions = new Map;
|
|
82870
|
+
constructor(cwd) {
|
|
82871
|
+
this.cwd = cwd;
|
|
82872
|
+
}
|
|
82873
|
+
async models() {
|
|
82874
|
+
const providers = await this.listProviders();
|
|
82875
|
+
const models = [];
|
|
82876
|
+
for (const provider of providers) {
|
|
82877
|
+
for (const model of provider.models) {
|
|
82878
|
+
models.push({
|
|
82879
|
+
id: `${provider.providerID}/${model.modelID}`,
|
|
82880
|
+
name: model.name,
|
|
82881
|
+
provider: provider.name
|
|
82882
|
+
});
|
|
82883
|
+
}
|
|
82782
82884
|
}
|
|
82783
|
-
|
|
82784
|
-
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
82785
|
-
sessionId: session2.sessionId,
|
|
82786
|
-
commandId: cmd._id,
|
|
82787
|
-
status: "done"
|
|
82788
|
-
});
|
|
82789
|
-
console.log(`[direct-harness] Capabilities refreshed for workspace=${cmd.workspaceId}`);
|
|
82790
|
-
}
|
|
82791
|
-
async function handleRefreshSessionTitle(session2, deps, cmd) {
|
|
82792
|
-
const { harnessSessionId } = cmd.refreshSessionTitle ?? {};
|
|
82793
|
-
if (!harnessSessionId) {
|
|
82794
|
-
await markFailed(session2, cmd._id, "refreshSessionTitle: missing harnessSessionId");
|
|
82795
|
-
return;
|
|
82885
|
+
return models;
|
|
82796
82886
|
}
|
|
82797
|
-
|
|
82798
|
-
|
|
82799
|
-
}
|
|
82800
|
-
|
|
82801
|
-
|
|
82802
|
-
|
|
82803
|
-
|
|
82804
|
-
|
|
82887
|
+
async listAgents() {
|
|
82888
|
+
return [{ name: "builder", mode: "primary" }];
|
|
82889
|
+
}
|
|
82890
|
+
async listProviders() {
|
|
82891
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82892
|
+
if (!apiKey)
|
|
82893
|
+
return [];
|
|
82894
|
+
const { Cursor } = await loadSdk3();
|
|
82895
|
+
const listed = await withTimeout(Cursor.models.list({ apiKey }), MODELS_LIST_TIMEOUT_MS2, "Cursor.models.list");
|
|
82896
|
+
const modelIds = normalizeCursorSdkListedModels(listed.map((m) => m.id).filter((id3) => id3.length > 0));
|
|
82897
|
+
return [
|
|
82898
|
+
{
|
|
82899
|
+
providerID: "cursor",
|
|
82900
|
+
name: "Cursor",
|
|
82901
|
+
models: modelIds.map((modelID) => ({ modelID, name: modelID }))
|
|
82902
|
+
}
|
|
82903
|
+
];
|
|
82904
|
+
}
|
|
82905
|
+
async newSession(config3) {
|
|
82906
|
+
if (this.closed)
|
|
82907
|
+
throw new Error("Harness is closed");
|
|
82908
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82909
|
+
if (!apiKey)
|
|
82910
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82911
|
+
const modelId = resolveCursorSdkModel(config3.model ?? DEFAULT_MODEL2);
|
|
82912
|
+
const { Agent } = await loadSdk3();
|
|
82913
|
+
const agent = await withTimeout(Agent.create({
|
|
82914
|
+
apiKey,
|
|
82915
|
+
model: { id: modelId },
|
|
82916
|
+
local: { cwd: this.cwd, settingSources: [] }
|
|
82917
|
+
}), AGENT_CREATE_TIMEOUT_MS2, "Agent.create");
|
|
82918
|
+
const session2 = new CursorSdkSession({
|
|
82919
|
+
agent,
|
|
82920
|
+
opencodeSessionId: agent.agentId,
|
|
82921
|
+
sessionTitle: config3.title ?? "",
|
|
82922
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
82805
82923
|
});
|
|
82806
|
-
|
|
82924
|
+
this.sessions.set(agent.agentId, session2);
|
|
82925
|
+
return session2;
|
|
82807
82926
|
}
|
|
82808
|
-
|
|
82809
|
-
|
|
82810
|
-
|
|
82811
|
-
|
|
82812
|
-
|
|
82813
|
-
|
|
82814
|
-
|
|
82927
|
+
async resumeSession(sessionId, _options) {
|
|
82928
|
+
if (this.closed)
|
|
82929
|
+
throw new Error("Harness is closed");
|
|
82930
|
+
const existing = this.sessions.get(sessionId);
|
|
82931
|
+
if (existing)
|
|
82932
|
+
return existing;
|
|
82933
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82934
|
+
if (!apiKey)
|
|
82935
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82936
|
+
const { Agent } = await loadSdk3();
|
|
82937
|
+
const agent = await withTimeout(Agent.resume(sessionId, {
|
|
82938
|
+
apiKey,
|
|
82939
|
+
model: { id: DEFAULT_MODEL2 },
|
|
82940
|
+
local: { cwd: this.cwd, settingSources: [] }
|
|
82941
|
+
}), AGENT_CREATE_TIMEOUT_MS2, "Agent.resume");
|
|
82942
|
+
const session2 = new CursorSdkSession({
|
|
82943
|
+
agent,
|
|
82944
|
+
opencodeSessionId: agent.agentId,
|
|
82945
|
+
sessionTitle: "",
|
|
82946
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
82815
82947
|
});
|
|
82816
|
-
|
|
82948
|
+
this.sessions.set(agent.agentId, session2);
|
|
82949
|
+
return session2;
|
|
82950
|
+
}
|
|
82951
|
+
async fetchSessionTitle(_opencodeSessionId) {
|
|
82952
|
+
return;
|
|
82953
|
+
}
|
|
82954
|
+
isAlive() {
|
|
82955
|
+
return !this.closed;
|
|
82956
|
+
}
|
|
82957
|
+
async close() {
|
|
82958
|
+
if (this.closed)
|
|
82959
|
+
return;
|
|
82960
|
+
this.closed = true;
|
|
82961
|
+
const closing = [...this.sessions.values()].map((s) => s.close().catch(() => {}));
|
|
82962
|
+
await Promise.all(closing);
|
|
82963
|
+
this.sessions.clear();
|
|
82817
82964
|
}
|
|
82818
|
-
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
82819
|
-
sessionId: session2.sessionId,
|
|
82820
|
-
commandId: cmd._id,
|
|
82821
|
-
status: "done"
|
|
82822
|
-
});
|
|
82823
82965
|
}
|
|
82824
|
-
|
|
82825
|
-
|
|
82826
|
-
|
|
82827
|
-
|
|
82828
|
-
|
|
82829
|
-
|
|
82830
|
-
|
|
82966
|
+
var DEFAULT_MODEL2 = "composer-2.5", MODELS_LIST_TIMEOUT_MS2 = 60000, AGENT_CREATE_TIMEOUT_MS2 = 60000, _sdkCache3, _sdkLoadError3, startCursorSdkHarness = async (config3) => {
|
|
82967
|
+
try {
|
|
82968
|
+
await loadSdk3();
|
|
82969
|
+
} catch (err) {
|
|
82970
|
+
throw new Error(`cursor-sdk unavailable: ${formatCursorSdkLoadError(err)}`);
|
|
82971
|
+
}
|
|
82972
|
+
if (!process.env.CURSOR_API_KEY?.trim()) {
|
|
82973
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82974
|
+
}
|
|
82975
|
+
return new CursorSdkHarness(config3.workingDir);
|
|
82976
|
+
};
|
|
82977
|
+
var init_cursor_harness = __esm(() => {
|
|
82978
|
+
init_cursor_session();
|
|
82979
|
+
init_cursor_sdk_package();
|
|
82980
|
+
});
|
|
82981
|
+
|
|
82982
|
+
// src/infrastructure/harnesses/shared-chunk-extractor.ts
|
|
82983
|
+
function createStandardSdkChunkExtractor() {
|
|
82984
|
+
return function extract(event) {
|
|
82985
|
+
if (event.type !== "message.part.delta")
|
|
82986
|
+
return null;
|
|
82987
|
+
const payload = event.payload;
|
|
82988
|
+
const delta = payload?.delta;
|
|
82989
|
+
if (!delta || delta.length === 0)
|
|
82990
|
+
return null;
|
|
82991
|
+
const messageId = payload?.messageID;
|
|
82992
|
+
if (!messageId)
|
|
82993
|
+
return null;
|
|
82994
|
+
return {
|
|
82995
|
+
content: delta,
|
|
82996
|
+
messageId,
|
|
82997
|
+
partType: payload?.partType ?? "text"
|
|
82998
|
+
};
|
|
82999
|
+
};
|
|
82831
83000
|
}
|
|
82832
|
-
|
|
82833
|
-
|
|
82834
|
-
|
|
83001
|
+
|
|
83002
|
+
// src/infrastructure/harnesses/cursor-sdk/event-extractor.ts
|
|
83003
|
+
function createCursorSdkChunkExtractor() {
|
|
83004
|
+
return createStandardSdkChunkExtractor();
|
|
83005
|
+
}
|
|
83006
|
+
var init_event_extractor = () => {};
|
|
83007
|
+
|
|
83008
|
+
// src/infrastructure/harnesses/cursor-sdk/index.ts
|
|
83009
|
+
var init_cursor_sdk2 = __esm(() => {
|
|
83010
|
+
init_cursor_harness();
|
|
83011
|
+
init_cursor_session();
|
|
83012
|
+
init_event_extractor();
|
|
82835
83013
|
});
|
|
82836
83014
|
|
|
83015
|
+
// src/infrastructure/harnesses/opencode-sdk/event-extractor.ts
|
|
83016
|
+
function createOpencodeSdkChunkExtractor() {
|
|
83017
|
+
const partMap = new Map;
|
|
83018
|
+
return function extract(event) {
|
|
83019
|
+
if (event.type === "message.part.updated") {
|
|
83020
|
+
const payload = event.payload;
|
|
83021
|
+
const part = payload?.part;
|
|
83022
|
+
if (part?.id && part?.messageID) {
|
|
83023
|
+
const partType = part.type === "reasoning" ? "reasoning" : "text";
|
|
83024
|
+
partMap.set(part.id, { messageId: part.messageID, partType });
|
|
83025
|
+
const delta = payload?.delta;
|
|
83026
|
+
if (delta && delta.length > 0) {
|
|
83027
|
+
return { content: delta, messageId: part.messageID, partType };
|
|
83028
|
+
}
|
|
83029
|
+
}
|
|
83030
|
+
return null;
|
|
83031
|
+
}
|
|
83032
|
+
if (event.type === "message.part.delta") {
|
|
83033
|
+
const payload = event.payload;
|
|
83034
|
+
const delta = payload?.delta;
|
|
83035
|
+
if (!delta || delta.length === 0)
|
|
83036
|
+
return null;
|
|
83037
|
+
const messageId = payload?.messageID;
|
|
83038
|
+
if (!messageId)
|
|
83039
|
+
return null;
|
|
83040
|
+
const partID = payload?.partID;
|
|
83041
|
+
const partType = (partID ? partMap.get(partID)?.partType : undefined) ?? "text";
|
|
83042
|
+
return { content: delta, messageId, partType };
|
|
83043
|
+
}
|
|
83044
|
+
return null;
|
|
83045
|
+
};
|
|
83046
|
+
}
|
|
83047
|
+
|
|
82837
83048
|
// src/infrastructure/harnesses/opencode-sdk/sse-event-buffer.ts
|
|
82838
83049
|
var SseEventBuffer;
|
|
82839
83050
|
var init_sse_event_buffer = __esm(() => {
|
|
@@ -83331,44 +83542,560 @@ var init_opencode_harness = __esm(() => {
|
|
|
83331
83542
|
init_spawn_env();
|
|
83332
83543
|
});
|
|
83333
83544
|
|
|
83334
|
-
// src/infrastructure/harnesses/
|
|
83335
|
-
|
|
83336
|
-
|
|
83337
|
-
|
|
83338
|
-
|
|
83339
|
-
|
|
83340
|
-
|
|
83341
|
-
|
|
83342
|
-
|
|
83343
|
-
|
|
83344
|
-
|
|
83345
|
-
|
|
83346
|
-
|
|
83347
|
-
|
|
83348
|
-
|
|
83349
|
-
|
|
83545
|
+
// src/infrastructure/harnesses/pi-sdk/pi-session.ts
|
|
83546
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
83547
|
+
|
|
83548
|
+
class PiSdkSession {
|
|
83549
|
+
opencodeSessionId;
|
|
83550
|
+
_sessionTitle;
|
|
83551
|
+
get sessionTitle() {
|
|
83552
|
+
return this._sessionTitle;
|
|
83553
|
+
}
|
|
83554
|
+
session;
|
|
83555
|
+
onClose;
|
|
83556
|
+
listeners = new Set;
|
|
83557
|
+
closed = false;
|
|
83558
|
+
unsubscribe;
|
|
83559
|
+
constructor(options) {
|
|
83560
|
+
this.session = options.session;
|
|
83561
|
+
this.opencodeSessionId = options.opencodeSessionId;
|
|
83562
|
+
this._sessionTitle = options.sessionTitle;
|
|
83563
|
+
this.onClose = options.onClose;
|
|
83564
|
+
}
|
|
83565
|
+
setTitle(title) {
|
|
83566
|
+
this._sessionTitle = title;
|
|
83567
|
+
}
|
|
83568
|
+
async prompt(input) {
|
|
83569
|
+
if (this.closed)
|
|
83570
|
+
throw new Error("Session is closed");
|
|
83571
|
+
const text = input.parts.map((p) => p.text).join(`
|
|
83572
|
+
`);
|
|
83573
|
+
const messageId = randomUUID4();
|
|
83574
|
+
const onSessionEvent = (event) => {
|
|
83575
|
+
if (this.closed)
|
|
83576
|
+
return;
|
|
83577
|
+
this.emitFromPiEvent(event, messageId);
|
|
83578
|
+
};
|
|
83579
|
+
this.unsubscribe?.();
|
|
83580
|
+
this.unsubscribe = this.session.subscribe(onSessionEvent);
|
|
83581
|
+
await withTimeout(this.session.prompt(text), PROMPT_TIMEOUT_MS2, "session.prompt");
|
|
83582
|
+
this.emit({ type: "session.idle", payload: {}, timestamp: Date.now() });
|
|
83583
|
+
}
|
|
83584
|
+
onEvent(listener) {
|
|
83585
|
+
this.listeners.add(listener);
|
|
83586
|
+
return () => {
|
|
83587
|
+
this.listeners.delete(listener);
|
|
83588
|
+
};
|
|
83589
|
+
}
|
|
83590
|
+
async close() {
|
|
83591
|
+
if (this.closed)
|
|
83592
|
+
return;
|
|
83593
|
+
this.closed = true;
|
|
83594
|
+
this.unsubscribe?.();
|
|
83595
|
+
try {
|
|
83596
|
+
await this.session.abort();
|
|
83597
|
+
} catch {}
|
|
83598
|
+
try {
|
|
83599
|
+
this.session.dispose();
|
|
83600
|
+
} catch {}
|
|
83601
|
+
this.onClose?.(this.opencodeSessionId);
|
|
83602
|
+
this.listeners.clear();
|
|
83603
|
+
}
|
|
83604
|
+
emit(event) {
|
|
83605
|
+
for (const listener of this.listeners) {
|
|
83606
|
+
listener(event);
|
|
83350
83607
|
}
|
|
83351
|
-
|
|
83352
|
-
|
|
83353
|
-
|
|
83354
|
-
|
|
83355
|
-
|
|
83356
|
-
|
|
83357
|
-
|
|
83358
|
-
|
|
83359
|
-
|
|
83360
|
-
|
|
83361
|
-
|
|
83608
|
+
}
|
|
83609
|
+
emitDelta(messageId, delta, partType) {
|
|
83610
|
+
if (!delta)
|
|
83611
|
+
return;
|
|
83612
|
+
this.emit({
|
|
83613
|
+
type: "message.part.delta",
|
|
83614
|
+
payload: { messageID: messageId, delta, partType },
|
|
83615
|
+
timestamp: Date.now()
|
|
83616
|
+
});
|
|
83617
|
+
}
|
|
83618
|
+
emitFromPiEvent(event, messageId) {
|
|
83619
|
+
if (event.type !== "message_update")
|
|
83620
|
+
return;
|
|
83621
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
83622
|
+
if (assistantEvent.type === "text_delta") {
|
|
83623
|
+
this.emitDelta(messageId, assistantEvent.delta, "text");
|
|
83624
|
+
} else if (assistantEvent.type === "thinking_delta") {
|
|
83625
|
+
this.emitDelta(messageId, assistantEvent.delta, "reasoning");
|
|
83362
83626
|
}
|
|
83363
|
-
|
|
83364
|
-
|
|
83627
|
+
}
|
|
83628
|
+
}
|
|
83629
|
+
var PROMPT_TIMEOUT_MS2 = 3600000;
|
|
83630
|
+
var init_pi_session = () => {};
|
|
83631
|
+
|
|
83632
|
+
// src/infrastructure/harnesses/pi-sdk/pi-harness.ts
|
|
83633
|
+
async function loadSdk4() {
|
|
83634
|
+
if (_sdkCache4)
|
|
83635
|
+
return _sdkCache4;
|
|
83636
|
+
if (_sdkLoadError4)
|
|
83637
|
+
throw _sdkLoadError4;
|
|
83638
|
+
try {
|
|
83639
|
+
_sdkCache4 = await importBundledPiSdk();
|
|
83640
|
+
return _sdkCache4;
|
|
83641
|
+
} catch (err) {
|
|
83642
|
+
_sdkLoadError4 = err;
|
|
83643
|
+
throw err;
|
|
83644
|
+
}
|
|
83645
|
+
}
|
|
83646
|
+
function resolveModel2(modelRegistry, model) {
|
|
83647
|
+
if (model) {
|
|
83648
|
+
const slash = model.indexOf("/");
|
|
83649
|
+
if (slash === -1) {
|
|
83650
|
+
return modelRegistry.getAll().find((entry) => entry.id === model);
|
|
83651
|
+
}
|
|
83652
|
+
const provider = model.slice(0, slash);
|
|
83653
|
+
const modelId = model.slice(slash + 1);
|
|
83654
|
+
return modelRegistry.find(provider, modelId);
|
|
83655
|
+
}
|
|
83656
|
+
return modelRegistry.getAvailable()[0];
|
|
83657
|
+
}
|
|
83658
|
+
|
|
83659
|
+
class PiSdkHarness {
|
|
83660
|
+
type = "pi-sdk";
|
|
83661
|
+
displayName = "Pi (SDK)";
|
|
83662
|
+
cwd;
|
|
83663
|
+
closed = false;
|
|
83664
|
+
sessions = new Map;
|
|
83665
|
+
modelRegistry;
|
|
83666
|
+
authStorage;
|
|
83667
|
+
constructor(cwd, modelRegistry, authStorage) {
|
|
83668
|
+
this.cwd = cwd;
|
|
83669
|
+
this.modelRegistry = modelRegistry;
|
|
83670
|
+
this.authStorage = authStorage;
|
|
83671
|
+
}
|
|
83672
|
+
async models() {
|
|
83673
|
+
return this.modelRegistry.getAvailable().map((entry) => ({
|
|
83674
|
+
id: `${entry.provider}/${entry.id}`,
|
|
83675
|
+
name: entry.id,
|
|
83676
|
+
provider: entry.provider
|
|
83677
|
+
}));
|
|
83678
|
+
}
|
|
83679
|
+
async listAgents() {
|
|
83680
|
+
return [{ name: "builder", mode: "primary" }];
|
|
83681
|
+
}
|
|
83682
|
+
async listProviders() {
|
|
83683
|
+
const byProvider = new Map;
|
|
83684
|
+
for (const entry of this.modelRegistry.getAvailable()) {
|
|
83685
|
+
const models = byProvider.get(entry.provider) ?? [];
|
|
83686
|
+
models.push({ modelID: entry.id, name: entry.id });
|
|
83687
|
+
byProvider.set(entry.provider, models);
|
|
83688
|
+
}
|
|
83689
|
+
return [...byProvider.entries()].map(([providerID, models]) => ({
|
|
83690
|
+
providerID,
|
|
83691
|
+
name: providerID,
|
|
83692
|
+
models
|
|
83693
|
+
}));
|
|
83694
|
+
}
|
|
83695
|
+
async newSession(config3) {
|
|
83696
|
+
if (this.closed)
|
|
83697
|
+
throw new Error("Harness is closed");
|
|
83698
|
+
const session2 = await this.createAgentSession(config3.systemPrompt ?? "", config3.model);
|
|
83699
|
+
const piSession = new PiSdkSession({
|
|
83700
|
+
session: session2,
|
|
83701
|
+
opencodeSessionId: session2.sessionId,
|
|
83702
|
+
sessionTitle: config3.title ?? "",
|
|
83703
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
83704
|
+
});
|
|
83705
|
+
this.sessions.set(session2.sessionId, piSession);
|
|
83706
|
+
return piSession;
|
|
83707
|
+
}
|
|
83708
|
+
async resumeSession(sessionId, _options) {
|
|
83709
|
+
if (this.closed)
|
|
83710
|
+
throw new Error("Harness is closed");
|
|
83711
|
+
const existing = this.sessions.get(sessionId);
|
|
83712
|
+
if (existing)
|
|
83713
|
+
return existing;
|
|
83714
|
+
const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk4();
|
|
83715
|
+
const sessions = await SessionManager.list(this.cwd, getPiSessionDir(this.cwd));
|
|
83716
|
+
const match17 = sessions.find((s) => s.id === sessionId);
|
|
83717
|
+
if (!match17) {
|
|
83718
|
+
throw new Error(`Session ${sessionId} not found on the harness`);
|
|
83719
|
+
}
|
|
83720
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
83721
|
+
cwd: this.cwd,
|
|
83722
|
+
agentDir: getAgentDir(),
|
|
83723
|
+
systemPromptOverride: () => ""
|
|
83724
|
+
});
|
|
83725
|
+
await resourceLoader.reload();
|
|
83726
|
+
const resolvedModel = resolveModel2(this.modelRegistry);
|
|
83727
|
+
if (!resolvedModel) {
|
|
83728
|
+
throw new Error("No Pi model available");
|
|
83729
|
+
}
|
|
83730
|
+
const { session: session2 } = await withTimeout(createAgentSession({
|
|
83731
|
+
cwd: this.cwd,
|
|
83732
|
+
model: resolvedModel,
|
|
83733
|
+
sessionManager: SessionManager.open(match17.path, getPiSessionDir(this.cwd)),
|
|
83734
|
+
authStorage: this.authStorage,
|
|
83735
|
+
modelRegistry: this.modelRegistry,
|
|
83736
|
+
resourceLoader
|
|
83737
|
+
}), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
|
|
83738
|
+
const piSession = new PiSdkSession({
|
|
83739
|
+
session: session2,
|
|
83740
|
+
opencodeSessionId: session2.sessionId,
|
|
83741
|
+
sessionTitle: match17.name ?? "",
|
|
83742
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
83743
|
+
});
|
|
83744
|
+
this.sessions.set(session2.sessionId, piSession);
|
|
83745
|
+
return piSession;
|
|
83746
|
+
}
|
|
83747
|
+
async fetchSessionTitle(_opencodeSessionId) {
|
|
83748
|
+
return;
|
|
83749
|
+
}
|
|
83750
|
+
isAlive() {
|
|
83751
|
+
return !this.closed;
|
|
83752
|
+
}
|
|
83753
|
+
async close() {
|
|
83754
|
+
if (this.closed)
|
|
83755
|
+
return;
|
|
83756
|
+
this.closed = true;
|
|
83757
|
+
const closing = [...this.sessions.values()].map((s) => s.close().catch(() => {}));
|
|
83758
|
+
await Promise.all(closing);
|
|
83759
|
+
this.sessions.clear();
|
|
83760
|
+
}
|
|
83761
|
+
async createAgentSession(systemPrompt, model) {
|
|
83762
|
+
const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk4();
|
|
83763
|
+
const resolvedModel = resolveModel2(this.modelRegistry, model ?? DEFAULT_MODEL3);
|
|
83764
|
+
if (!resolvedModel) {
|
|
83765
|
+
throw new Error("No Pi model available — configure provider credentials in ~/.pi/agent/auth.json");
|
|
83766
|
+
}
|
|
83767
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
83768
|
+
cwd: this.cwd,
|
|
83769
|
+
agentDir: getAgentDir(),
|
|
83770
|
+
systemPromptOverride: () => systemPrompt
|
|
83771
|
+
});
|
|
83772
|
+
await resourceLoader.reload();
|
|
83773
|
+
const { session: session2 } = await withTimeout(createAgentSession({
|
|
83774
|
+
cwd: this.cwd,
|
|
83775
|
+
model: resolvedModel,
|
|
83776
|
+
sessionManager: SessionManager.create(getPiSessionDir(this.cwd)),
|
|
83777
|
+
authStorage: this.authStorage,
|
|
83778
|
+
modelRegistry: this.modelRegistry,
|
|
83779
|
+
resourceLoader
|
|
83780
|
+
}), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
|
|
83781
|
+
return session2;
|
|
83782
|
+
}
|
|
83783
|
+
}
|
|
83784
|
+
var SESSION_CREATE_TIMEOUT_MS3 = 60000, DEFAULT_MODEL3 = "opencode/big-pickle", _sdkCache4, _sdkLoadError4, startPiSdkHarness = async (config3) => {
|
|
83785
|
+
try {
|
|
83786
|
+
const { AuthStorage, ModelRegistry } = await loadSdk4();
|
|
83787
|
+
const authStorage = AuthStorage.create();
|
|
83788
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
83789
|
+
if (modelRegistry.getAvailable().length === 0) {
|
|
83790
|
+
throw new Error("No Pi models available");
|
|
83791
|
+
}
|
|
83792
|
+
return new PiSdkHarness(config3.workingDir, modelRegistry, authStorage);
|
|
83793
|
+
} catch (err) {
|
|
83794
|
+
throw new Error(`pi-sdk unavailable: ${formatPiSdkLoadError(err)}`);
|
|
83795
|
+
}
|
|
83796
|
+
};
|
|
83797
|
+
var init_pi_harness = __esm(() => {
|
|
83798
|
+
init_pi_session();
|
|
83799
|
+
init_pi_agent_service();
|
|
83800
|
+
init_pi_sdk_package();
|
|
83801
|
+
});
|
|
83802
|
+
|
|
83803
|
+
// src/infrastructure/harnesses/pi-sdk/event-extractor.ts
|
|
83804
|
+
function createPiSdkChunkExtractor() {
|
|
83805
|
+
return createStandardSdkChunkExtractor();
|
|
83365
83806
|
}
|
|
83807
|
+
var init_event_extractor2 = () => {};
|
|
83808
|
+
|
|
83809
|
+
// src/infrastructure/harnesses/pi-sdk/index.ts
|
|
83810
|
+
var init_pi_sdk2 = __esm(() => {
|
|
83811
|
+
init_pi_harness();
|
|
83812
|
+
init_pi_session();
|
|
83813
|
+
init_event_extractor2();
|
|
83814
|
+
});
|
|
83366
83815
|
|
|
83367
|
-
// src/infrastructure/harnesses/
|
|
83368
|
-
|
|
83816
|
+
// src/infrastructure/harnesses/registry.ts
|
|
83817
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
83818
|
+
async function startBoundHarness(config3) {
|
|
83819
|
+
switch (config3.harnessName) {
|
|
83820
|
+
case "opencode-sdk":
|
|
83821
|
+
return startOpencodeSdkHarness(config3);
|
|
83822
|
+
case "cursor-sdk":
|
|
83823
|
+
return startCursorSdkHarness(config3);
|
|
83824
|
+
case "pi-sdk":
|
|
83825
|
+
return startPiSdkHarness(config3);
|
|
83826
|
+
default: {
|
|
83827
|
+
const _exhaustive = config3.harnessName;
|
|
83828
|
+
throw new Error(`Unsupported direct harness: ${String(_exhaustive)}`);
|
|
83829
|
+
}
|
|
83830
|
+
}
|
|
83831
|
+
}
|
|
83832
|
+
function createChunkExtractor(harnessName) {
|
|
83833
|
+
switch (harnessName) {
|
|
83834
|
+
case "opencode-sdk":
|
|
83835
|
+
return createOpencodeSdkChunkExtractor();
|
|
83836
|
+
case "cursor-sdk":
|
|
83837
|
+
return createCursorSdkChunkExtractor();
|
|
83838
|
+
case "pi-sdk":
|
|
83839
|
+
return createPiSdkChunkExtractor();
|
|
83840
|
+
default:
|
|
83841
|
+
return createStandardSdkChunkExtractor();
|
|
83842
|
+
}
|
|
83843
|
+
}
|
|
83844
|
+
function opencodeOnPath() {
|
|
83845
|
+
try {
|
|
83846
|
+
execSync2("opencode --version", { stdio: "pipe" });
|
|
83847
|
+
return true;
|
|
83848
|
+
} catch {
|
|
83849
|
+
return false;
|
|
83850
|
+
}
|
|
83851
|
+
}
|
|
83852
|
+
async function isCursorSdkInstalled() {
|
|
83853
|
+
if (!process.env.CURSOR_API_KEY?.trim())
|
|
83854
|
+
return false;
|
|
83855
|
+
try {
|
|
83856
|
+
const { importBundledCursorSdk: importBundledCursorSdk2 } = await Promise.resolve().then(() => (init_cursor_sdk_package(), exports_cursor_sdk_package));
|
|
83857
|
+
await importBundledCursorSdk2();
|
|
83858
|
+
return true;
|
|
83859
|
+
} catch {
|
|
83860
|
+
return false;
|
|
83861
|
+
}
|
|
83862
|
+
}
|
|
83863
|
+
async function isPiSdkInstalled() {
|
|
83864
|
+
try {
|
|
83865
|
+
const { importBundledPiSdk: importBundledPiSdk2 } = await Promise.resolve().then(() => (init_pi_sdk_package(), exports_pi_sdk_package));
|
|
83866
|
+
const { ModelRegistry, AuthStorage } = await importBundledPiSdk2();
|
|
83867
|
+
const authStorage = AuthStorage.create();
|
|
83868
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
83869
|
+
return modelRegistry.getAvailable().length > 0;
|
|
83870
|
+
} catch {
|
|
83871
|
+
return false;
|
|
83872
|
+
}
|
|
83873
|
+
}
|
|
83874
|
+
async function listInstalledNativeDirectHarnesses() {
|
|
83875
|
+
const installed2 = [];
|
|
83876
|
+
if (opencodeOnPath())
|
|
83877
|
+
installed2.push("opencode-sdk");
|
|
83878
|
+
if (await isCursorSdkInstalled())
|
|
83879
|
+
installed2.push("cursor-sdk");
|
|
83880
|
+
if (await isPiSdkInstalled())
|
|
83881
|
+
installed2.push("pi-sdk");
|
|
83882
|
+
return installed2;
|
|
83883
|
+
}
|
|
83884
|
+
var init_registry4 = __esm(() => {
|
|
83885
|
+
init_cursor_sdk2();
|
|
83369
83886
|
init_opencode_harness();
|
|
83887
|
+
init_pi_sdk2();
|
|
83888
|
+
});
|
|
83889
|
+
|
|
83890
|
+
// src/commands/machine/daemon-start/direct-harness/command-subscriber.ts
|
|
83891
|
+
function startCommandSubscriber(session2, wsClient2, deps) {
|
|
83892
|
+
const processed = new Set;
|
|
83893
|
+
let processing = false;
|
|
83894
|
+
let pendingDrain = false;
|
|
83895
|
+
const runDrain = () => {
|
|
83896
|
+
if (processing) {
|
|
83897
|
+
pendingDrain = true;
|
|
83898
|
+
return;
|
|
83899
|
+
}
|
|
83900
|
+
processing = true;
|
|
83901
|
+
pendingDrain = false;
|
|
83902
|
+
drain(session2, deps, processed).finally(() => {
|
|
83903
|
+
processing = false;
|
|
83904
|
+
if (pendingDrain)
|
|
83905
|
+
runDrain();
|
|
83906
|
+
});
|
|
83907
|
+
};
|
|
83908
|
+
const unsub = wsClient2.onUpdate(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId }, () => runDrain(), (err) => {
|
|
83909
|
+
console.warn("[direct-harness] Command subscription error:", err instanceof Error ? err.message : String(err));
|
|
83910
|
+
});
|
|
83911
|
+
return { stop: unsub };
|
|
83912
|
+
}
|
|
83913
|
+
async function dispatchPendingCommand(session2, deps, cmd) {
|
|
83914
|
+
switch (cmd.type) {
|
|
83915
|
+
case "refreshCapabilities":
|
|
83916
|
+
await handleRefreshCapabilities(session2, deps, cmd);
|
|
83917
|
+
break;
|
|
83918
|
+
case "refreshSessionTitle":
|
|
83919
|
+
await handleRefreshSessionTitle(session2, deps, cmd);
|
|
83920
|
+
break;
|
|
83921
|
+
case "closeSession":
|
|
83922
|
+
await handleCloseSession(session2, deps, cmd);
|
|
83923
|
+
break;
|
|
83924
|
+
default:
|
|
83925
|
+
await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
|
|
83926
|
+
}
|
|
83927
|
+
}
|
|
83928
|
+
async function processPendingCommand(session2, deps, cmd, now) {
|
|
83929
|
+
if (now - cmd.createdAt > DIRECT_HARNESS_COMMAND_TTL_MS) {
|
|
83930
|
+
console.log(`[direct-harness] Discarding stale command ${cmd._id} (type=${cmd.type}, age=${now - cmd.createdAt}ms)`);
|
|
83931
|
+
await markFailed(session2, cmd._id, "Command expired (TTL)");
|
|
83932
|
+
return;
|
|
83933
|
+
}
|
|
83934
|
+
await dispatchPendingCommand(session2, deps, cmd);
|
|
83935
|
+
}
|
|
83936
|
+
async function drain(session2, deps, processed) {
|
|
83937
|
+
const pending3 = await session2.backend.query(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId });
|
|
83938
|
+
if (!pending3 || pending3.length === 0)
|
|
83939
|
+
return;
|
|
83940
|
+
const now = Date.now();
|
|
83941
|
+
for (const cmd of pending3) {
|
|
83942
|
+
if (processed.has(cmd._id))
|
|
83943
|
+
continue;
|
|
83944
|
+
processed.add(cmd._id);
|
|
83945
|
+
try {
|
|
83946
|
+
await processPendingCommand(session2, deps, cmd, now);
|
|
83947
|
+
} catch (err) {
|
|
83948
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83949
|
+
console.warn(`[direct-harness] Command ${cmd._id} failed: ${message}`);
|
|
83950
|
+
await markFailed(session2, cmd._id, message).catch(() => {});
|
|
83951
|
+
}
|
|
83952
|
+
}
|
|
83953
|
+
}
|
|
83954
|
+
async function handleRefreshCapabilities(session2, deps, cmd) {
|
|
83955
|
+
const payload = cmd.refreshCapabilities;
|
|
83956
|
+
console.log(`[direct-harness] Processing refreshCapabilities for workspace=${cmd.workspaceId}` + (payload ? ` (initiatedBy=${payload.initiatedBy})` : ""));
|
|
83957
|
+
const installed2 = await listInstalledNativeDirectHarnesses();
|
|
83958
|
+
const harnessEntries = [];
|
|
83959
|
+
let cwd = "";
|
|
83960
|
+
for (const harnessName of installed2) {
|
|
83961
|
+
const harness = await deps.lifecycleManager.getOrStart(cmd.workspaceId, harnessName);
|
|
83962
|
+
cwd = harness.cwd;
|
|
83963
|
+
const [agents, providers] = await Promise.all([harness.listAgents(), harness.listProviders()]);
|
|
83964
|
+
harnessEntries.push({
|
|
83965
|
+
name: harness.type,
|
|
83966
|
+
displayName: harness.displayName,
|
|
83967
|
+
agents: [...agents],
|
|
83968
|
+
providers: [...providers]
|
|
83969
|
+
});
|
|
83970
|
+
}
|
|
83971
|
+
const existing = await session2.backend.query(api.daemon.directHarness.capabilities.getForMachine, { sessionId: session2.sessionId, machineId: session2.machineId });
|
|
83972
|
+
const updatedWorkspace = {
|
|
83973
|
+
workspaceId: cmd.workspaceId,
|
|
83974
|
+
cwd,
|
|
83975
|
+
name: cwd,
|
|
83976
|
+
harnesses: harnessEntries
|
|
83977
|
+
};
|
|
83978
|
+
const merged = [
|
|
83979
|
+
...(existing?.workspaces ?? []).filter((ws) => ws.workspaceId !== cmd.workspaceId),
|
|
83980
|
+
updatedWorkspace
|
|
83981
|
+
];
|
|
83982
|
+
await deps.publisher.publish({
|
|
83983
|
+
machineId: session2.machineId,
|
|
83984
|
+
lastSeenAt: Date.now(),
|
|
83985
|
+
workspaces: merged
|
|
83986
|
+
});
|
|
83987
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
83988
|
+
sessionId: session2.sessionId,
|
|
83989
|
+
commandId: cmd._id,
|
|
83990
|
+
status: "done"
|
|
83991
|
+
});
|
|
83992
|
+
console.log(`[direct-harness] Capabilities refreshed for workspace=${cmd.workspaceId}`);
|
|
83993
|
+
}
|
|
83994
|
+
async function handleRefreshSessionTitle(session2, deps, cmd) {
|
|
83995
|
+
const { harnessSessionId } = cmd.refreshSessionTitle ?? {};
|
|
83996
|
+
if (!harnessSessionId) {
|
|
83997
|
+
await markFailed(session2, cmd._id, "refreshSessionTitle: missing harnessSessionId");
|
|
83998
|
+
return;
|
|
83999
|
+
}
|
|
84000
|
+
const sessionRow = await session2.backend.query(api.daemon.directHarness.sessions.getSession, {
|
|
84001
|
+
harnessSessionId
|
|
84002
|
+
});
|
|
84003
|
+
if (!sessionRow?.opencodeSessionId) {
|
|
84004
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84005
|
+
sessionId: session2.sessionId,
|
|
84006
|
+
commandId: cmd._id,
|
|
84007
|
+
status: "done"
|
|
84008
|
+
});
|
|
84009
|
+
return;
|
|
84010
|
+
}
|
|
84011
|
+
const harnessName = sessionRow.harnessName ?? "opencode-sdk";
|
|
84012
|
+
const harness = await deps.lifecycleManager.getOrStart(sessionRow.workspaceId ?? cmd.workspaceId, harnessName);
|
|
84013
|
+
const newTitle = await harness.fetchSessionTitle(sessionRow.opencodeSessionId);
|
|
84014
|
+
if (newTitle) {
|
|
84015
|
+
await session2.backend.mutation(api.daemon.directHarness.sessions.updateSessionTitle, {
|
|
84016
|
+
sessionId: session2.sessionId,
|
|
84017
|
+
harnessSessionId,
|
|
84018
|
+
sessionTitle: newTitle
|
|
84019
|
+
});
|
|
84020
|
+
console.log(`[direct-harness] Refreshed title for session ${harnessSessionId}: "${newTitle}"`);
|
|
84021
|
+
}
|
|
84022
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84023
|
+
sessionId: session2.sessionId,
|
|
84024
|
+
commandId: cmd._id,
|
|
84025
|
+
status: "done"
|
|
84026
|
+
});
|
|
84027
|
+
}
|
|
84028
|
+
async function closeDisconnectedHarnessSession(deps, sessionRow, harnessSessionId) {
|
|
84029
|
+
if (!sessionRow.opencodeSessionId) {
|
|
84030
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84031
|
+
return;
|
|
84032
|
+
}
|
|
84033
|
+
const harnessName = sessionRow.harnessName ?? "opencode-sdk";
|
|
84034
|
+
const harness = await deps.lifecycleManager.getOrStart(sessionRow.workspaceId ?? "", harnessName);
|
|
84035
|
+
const liveSession = await harness.resumeSession(sessionRow.opencodeSessionId, {
|
|
84036
|
+
harnessSessionId
|
|
84037
|
+
});
|
|
84038
|
+
await liveSession.close();
|
|
84039
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84040
|
+
}
|
|
84041
|
+
async function closeHarnessSession(session2, deps, harnessSessionId) {
|
|
84042
|
+
const sessionRow = await session2.backend.query(api.daemon.directHarness.sessions.getSession, {
|
|
84043
|
+
harnessSessionId
|
|
84044
|
+
});
|
|
84045
|
+
if (!sessionRow) {
|
|
84046
|
+
throw new Error(`Harness session ${harnessSessionId} not found`);
|
|
84047
|
+
}
|
|
84048
|
+
if (sessionRow.status === "closed" || sessionRow.status === "failed") {
|
|
84049
|
+
return;
|
|
84050
|
+
}
|
|
84051
|
+
const handle = deps.activeSessions.get(harnessSessionId);
|
|
84052
|
+
if (handle) {
|
|
84053
|
+
await handle.close();
|
|
84054
|
+
deps.activeSessions.delete(harnessSessionId);
|
|
84055
|
+
return;
|
|
84056
|
+
}
|
|
84057
|
+
await closeDisconnectedHarnessSession(deps, sessionRow, harnessSessionId);
|
|
84058
|
+
}
|
|
84059
|
+
async function handleCloseSession(session2, deps, cmd) {
|
|
84060
|
+
const { harnessSessionId } = cmd.closeSession ?? {};
|
|
84061
|
+
if (!harnessSessionId) {
|
|
84062
|
+
await markFailed(session2, cmd._id, "closeSession: missing harnessSessionId");
|
|
84063
|
+
return;
|
|
84064
|
+
}
|
|
84065
|
+
console.log(`[direct-harness] Processing closeSession for harnessSessionId=${harnessSessionId}`);
|
|
84066
|
+
await closeHarnessSession(session2, deps, harnessSessionId);
|
|
84067
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84068
|
+
sessionId: session2.sessionId,
|
|
84069
|
+
commandId: cmd._id,
|
|
84070
|
+
status: "done"
|
|
84071
|
+
});
|
|
84072
|
+
console.log(`[direct-harness] Closed session ${harnessSessionId}`);
|
|
84073
|
+
}
|
|
84074
|
+
async function markFailed(session2, commandId, errorMessage) {
|
|
84075
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84076
|
+
sessionId: session2.sessionId,
|
|
84077
|
+
commandId,
|
|
84078
|
+
status: "failed",
|
|
84079
|
+
errorMessage
|
|
84080
|
+
});
|
|
84081
|
+
}
|
|
84082
|
+
var DIRECT_HARNESS_COMMAND_TTL_MS = 60000;
|
|
84083
|
+
var init_command_subscriber = __esm(() => {
|
|
84084
|
+
init_api3();
|
|
84085
|
+
init_registry4();
|
|
83370
84086
|
});
|
|
83371
84087
|
|
|
84088
|
+
// src/infrastructure/harnesses/harness-key.ts
|
|
84089
|
+
function makeHarnessKey(workspaceId, harnessName) {
|
|
84090
|
+
return `${workspaceId}:${harnessName}`;
|
|
84091
|
+
}
|
|
84092
|
+
function parseHarnessKey(key) {
|
|
84093
|
+
const idx = key.indexOf(":");
|
|
84094
|
+
if (idx === -1)
|
|
84095
|
+
throw new Error(`Invalid harness key: ${key}`);
|
|
84096
|
+
return { workspaceId: key.slice(0, idx), harnessName: key.slice(idx + 1) };
|
|
84097
|
+
}
|
|
84098
|
+
|
|
83372
84099
|
// src/commands/machine/daemon-start/direct-harness/harness-lifecycle-manager.ts
|
|
83373
84100
|
class HarnessLifecycleManager {
|
|
83374
84101
|
harnesses;
|
|
@@ -83383,10 +84110,15 @@ class HarnessLifecycleManager {
|
|
|
83383
84110
|
this.resolveWorkspace = resolveWorkspace;
|
|
83384
84111
|
this.resolvedConvexUrl = resolvedConvexUrl;
|
|
83385
84112
|
}
|
|
83386
|
-
getOrStartHarnessEffect(workspaceId) {
|
|
83387
|
-
const
|
|
83388
|
-
|
|
84113
|
+
getOrStartHarnessEffect(workspaceId, harnessName) {
|
|
84114
|
+
const key = makeHarnessKey(workspaceId, harnessName);
|
|
84115
|
+
const existing = this.harnesses.get(key);
|
|
84116
|
+
if (existing?.isAlive())
|
|
83389
84117
|
return exports_Effect.succeed(existing);
|
|
84118
|
+
if (existing) {
|
|
84119
|
+
existing.close().catch(() => {});
|
|
84120
|
+
this.harnesses.delete(key);
|
|
84121
|
+
}
|
|
83390
84122
|
return exports_Effect.gen(this, function* () {
|
|
83391
84123
|
const workspace = yield* exports_Effect.tryPromise({
|
|
83392
84124
|
try: () => this.resolveWorkspace(workspaceId),
|
|
@@ -83395,23 +84127,23 @@ class HarnessLifecycleManager {
|
|
|
83395
84127
|
if (!workspace) {
|
|
83396
84128
|
return yield* exports_Effect.fail(new Error(`Workspace ${workspaceId} not found — cannot start harness`));
|
|
83397
84129
|
}
|
|
83398
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDD27 Auto-starting
|
|
84130
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD27 Auto-starting ${harnessName} for workspace=${workspaceId} (${workspace.workingDir})`);
|
|
83399
84131
|
const harness = yield* exports_Effect.tryPromise({
|
|
83400
|
-
try: () =>
|
|
83401
|
-
|
|
84132
|
+
try: () => startBoundHarness({
|
|
84133
|
+
harnessName,
|
|
83402
84134
|
workingDir: workspace.workingDir,
|
|
83403
84135
|
workspaceId,
|
|
83404
84136
|
resolvedConvexUrl: this.resolvedConvexUrl
|
|
83405
84137
|
}),
|
|
83406
84138
|
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
83407
84139
|
});
|
|
83408
|
-
this.harnesses.set(
|
|
83409
|
-
this.idleSince.set(
|
|
84140
|
+
this.harnesses.set(key, harness);
|
|
84141
|
+
this.idleSince.set(key, Date.now());
|
|
83410
84142
|
return harness;
|
|
83411
84143
|
});
|
|
83412
84144
|
}
|
|
83413
|
-
async getOrStart(workspaceId) {
|
|
83414
|
-
return exports_Effect.runPromise(this.getOrStartHarnessEffect(workspaceId));
|
|
84145
|
+
async getOrStart(workspaceId, harnessName) {
|
|
84146
|
+
return exports_Effect.runPromise(this.getOrStartHarnessEffect(workspaceId, harnessName));
|
|
83415
84147
|
}
|
|
83416
84148
|
startMonitoring() {
|
|
83417
84149
|
if (this.monitorTimer)
|
|
@@ -83427,28 +84159,30 @@ class HarnessLifecycleManager {
|
|
|
83427
84159
|
}
|
|
83428
84160
|
checkInactivity() {
|
|
83429
84161
|
const now = Date.now();
|
|
83430
|
-
for (const
|
|
83431
|
-
const
|
|
84162
|
+
for (const key of this.harnesses.keys()) {
|
|
84163
|
+
const { workspaceId, harnessName } = parseHarnessKey(key);
|
|
84164
|
+
const hasActiveSessions = this.hasSessionsForHarnessKey(workspaceId, harnessName);
|
|
83432
84165
|
if (hasActiveSessions) {
|
|
83433
|
-
this.idleSince.delete(
|
|
84166
|
+
this.idleSince.delete(key);
|
|
83434
84167
|
} else {
|
|
83435
|
-
const idleStart = this.idleSince.get(
|
|
84168
|
+
const idleStart = this.idleSince.get(key);
|
|
83436
84169
|
if (idleStart === undefined) {
|
|
83437
|
-
this.idleSince.set(
|
|
84170
|
+
this.idleSince.set(key, now);
|
|
83438
84171
|
} else if (now - idleStart >= INACTIVITY_TTL_MS) {
|
|
83439
84172
|
const idleMinutes = Math.round((now - idleStart) / 60000);
|
|
83440
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDD2A Killing idle harness for workspace=${workspaceId} (idle ${idleMinutes}min)`);
|
|
83441
|
-
this.harnesses.get(
|
|
83442
|
-
this.harnesses.delete(
|
|
83443
|
-
this.idleSince.delete(
|
|
84173
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD2A Killing idle harness ${harnessName} for workspace=${workspaceId} (idle ${idleMinutes}min)`);
|
|
84174
|
+
this.harnesses.get(key)?.close().catch(() => {});
|
|
84175
|
+
this.harnesses.delete(key);
|
|
84176
|
+
this.idleSince.delete(key);
|
|
83444
84177
|
}
|
|
83445
84178
|
}
|
|
83446
84179
|
}
|
|
83447
84180
|
}
|
|
83448
|
-
|
|
84181
|
+
hasSessionsForHarnessKey(workspaceId, harnessName) {
|
|
83449
84182
|
for (const handle of this.activeSessions.values()) {
|
|
83450
|
-
if (handle.workspaceId === workspaceId)
|
|
84183
|
+
if (handle.workspaceId === workspaceId && handle.harnessName === harnessName) {
|
|
83451
84184
|
return true;
|
|
84185
|
+
}
|
|
83452
84186
|
}
|
|
83453
84187
|
return false;
|
|
83454
84188
|
}
|
|
@@ -83456,7 +84190,7 @@ class HarnessLifecycleManager {
|
|
|
83456
84190
|
var INACTIVITY_TTL_MS, CHECK_INTERVAL_MS;
|
|
83457
84191
|
var init_harness_lifecycle_manager = __esm(() => {
|
|
83458
84192
|
init_esm();
|
|
83459
|
-
|
|
84193
|
+
init_registry4();
|
|
83460
84194
|
INACTIVITY_TTL_MS = 15 * 60 * 1000;
|
|
83461
84195
|
CHECK_INTERVAL_MS = 60 * 1000;
|
|
83462
84196
|
});
|
|
@@ -83551,6 +84285,7 @@ async function resumeSession(deps, input) {
|
|
|
83551
84285
|
let closed = false;
|
|
83552
84286
|
const handle = {
|
|
83553
84287
|
harnessSessionId,
|
|
84288
|
+
harnessName: input.harnessName,
|
|
83554
84289
|
opencodeSessionId,
|
|
83555
84290
|
workspaceId: input.workspaceId ?? "",
|
|
83556
84291
|
session: session2,
|
|
@@ -83615,14 +84350,16 @@ async function resumeSessionHandle(session2, deps, rowId, info) {
|
|
|
83615
84350
|
return null;
|
|
83616
84351
|
}
|
|
83617
84352
|
const workspaceId = info.workspaceId;
|
|
84353
|
+
const harnessName = info.harnessName;
|
|
83618
84354
|
if (!workspaceId) {
|
|
83619
84355
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: no workspace info`);
|
|
83620
84356
|
return null;
|
|
83621
84357
|
}
|
|
83622
|
-
|
|
84358
|
+
const key = makeHarnessKey(workspaceId, harnessName);
|
|
84359
|
+
let harness = deps.harnesses.get(key);
|
|
83623
84360
|
if (harness && !harness.isAlive()) {
|
|
83624
84361
|
harness.close().catch(() => {});
|
|
83625
|
-
deps.harnesses.delete(
|
|
84362
|
+
deps.harnesses.delete(key);
|
|
83626
84363
|
harness = undefined;
|
|
83627
84364
|
}
|
|
83628
84365
|
if (!harness) {
|
|
@@ -83634,20 +84371,20 @@ async function resumeSessionHandle(session2, deps, rowId, info) {
|
|
|
83634
84371
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: workspace not found`);
|
|
83635
84372
|
return null;
|
|
83636
84373
|
}
|
|
83637
|
-
harness = await
|
|
83638
|
-
|
|
84374
|
+
harness = await startBoundHarness({
|
|
84375
|
+
harnessName,
|
|
83639
84376
|
workingDir: workspace.workingDir,
|
|
83640
84377
|
workspaceId,
|
|
83641
84378
|
resolvedConvexUrl: session2.convexUrl
|
|
83642
84379
|
});
|
|
83643
|
-
deps.harnesses.set(
|
|
84380
|
+
deps.harnesses.set(key, harness);
|
|
83644
84381
|
}
|
|
83645
84382
|
try {
|
|
83646
84383
|
return await resumeSession({
|
|
83647
84384
|
harness,
|
|
83648
84385
|
journalFactory: deps.journalFactory,
|
|
83649
|
-
chunkExtractor:
|
|
83650
|
-
}, { harnessSessionId: rowId, opencodeSessionId, workspaceId });
|
|
84386
|
+
chunkExtractor: createChunkExtractor(harness.type)
|
|
84387
|
+
}, { harnessSessionId: rowId, opencodeSessionId, workspaceId, harnessName });
|
|
83651
84388
|
} catch (err) {
|
|
83652
84389
|
const message = err instanceof Error ? err.message : String(err);
|
|
83653
84390
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: ${message}`);
|
|
@@ -83693,8 +84430,9 @@ async function deliverPendingMessages(handle, deps, rowId, messages, info) {
|
|
|
83693
84430
|
console.warn(`[direct-harness] Prompt failed for session ${rowId} seq=${msg.seq}: ${message}`);
|
|
83694
84431
|
await deps.sessionRepository.markIdle(rowId).catch(() => {});
|
|
83695
84432
|
deps.activeSessions.delete(rowId);
|
|
83696
|
-
if (info?.workspaceId)
|
|
83697
|
-
deps.harnesses.delete(info.workspaceId);
|
|
84433
|
+
if (info?.workspaceId && info.harnessName) {
|
|
84434
|
+
deps.harnesses.delete(makeHarnessKey(info.workspaceId, info.harnessName));
|
|
84435
|
+
}
|
|
83698
84436
|
return;
|
|
83699
84437
|
}
|
|
83700
84438
|
}
|
|
@@ -83721,7 +84459,7 @@ var init_prompt_subscriber = __esm(() => {
|
|
|
83721
84459
|
init_idle_handler();
|
|
83722
84460
|
init_api3();
|
|
83723
84461
|
init_resume_session();
|
|
83724
|
-
|
|
84462
|
+
init_registry4();
|
|
83725
84463
|
});
|
|
83726
84464
|
|
|
83727
84465
|
// src/commands/machine/daemon-start/direct-harness/session-subscriber.ts
|
|
@@ -83746,21 +84484,23 @@ function startSessionSubscriber(daemonSession, wsClient2, deps) {
|
|
|
83746
84484
|
return { stop: unsub };
|
|
83747
84485
|
}
|
|
83748
84486
|
async function getOrCreateHarness(daemonSession, deps, session2, workspace) {
|
|
83749
|
-
|
|
84487
|
+
const harnessName = session2.opencode?.harnessName ?? "opencode-sdk";
|
|
84488
|
+
const key = makeHarnessKey(session2.workspaceId, harnessName);
|
|
84489
|
+
let harness = deps.harnesses.get(key);
|
|
83750
84490
|
if (harness && !harness.isAlive()) {
|
|
83751
|
-
console.warn(`[direct-harness] Harness for workspace ${session2.workspaceId} is no longer alive — restarting`);
|
|
84491
|
+
console.warn(`[direct-harness] Harness ${harnessName} for workspace ${session2.workspaceId} is no longer alive — restarting`);
|
|
83752
84492
|
harness.close().catch(() => {});
|
|
83753
|
-
deps.harnesses.delete(
|
|
84493
|
+
deps.harnesses.delete(key);
|
|
83754
84494
|
harness = undefined;
|
|
83755
84495
|
}
|
|
83756
84496
|
if (!harness) {
|
|
83757
|
-
harness = await
|
|
83758
|
-
|
|
84497
|
+
harness = await startBoundHarness({
|
|
84498
|
+
harnessName,
|
|
83759
84499
|
workingDir: workspace.workingDir,
|
|
83760
84500
|
workspaceId: session2.workspaceId,
|
|
83761
84501
|
resolvedConvexUrl: daemonSession.convexUrl
|
|
83762
84502
|
});
|
|
83763
|
-
deps.harnesses.set(
|
|
84503
|
+
deps.harnesses.set(key, harness);
|
|
83764
84504
|
}
|
|
83765
84505
|
return harness;
|
|
83766
84506
|
}
|
|
@@ -83812,13 +84552,17 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83812
84552
|
await deps.sessionRepository.markFailed(rowId);
|
|
83813
84553
|
return;
|
|
83814
84554
|
}
|
|
84555
|
+
const harnessName = session2.opencode?.harnessName ?? "opencode-sdk";
|
|
83815
84556
|
const harness = await getOrCreateHarness(daemonSession, deps, session2, workspace);
|
|
84557
|
+
const modelConfig = session2.opencode?.lastUsedConfig.model;
|
|
84558
|
+
const model = modelConfig ? `${modelConfig.providerID}/${modelConfig.modelID}` : undefined;
|
|
83816
84559
|
const liveSession = await harness.newSession({
|
|
83817
84560
|
agent: session2.opencode?.lastUsedConfig.agent ?? "build",
|
|
84561
|
+
model,
|
|
83818
84562
|
harnessSessionId: rowId
|
|
83819
84563
|
});
|
|
83820
84564
|
const journal = deps.journalFactory.create(rowId);
|
|
83821
|
-
const extractChunk =
|
|
84565
|
+
const extractChunk = createChunkExtractor(harness.type);
|
|
83822
84566
|
const idleConfig = {
|
|
83823
84567
|
agent: session2.opencode?.lastUsedConfig.agent ?? "build",
|
|
83824
84568
|
model: session2.opencode?.lastUsedConfig.model
|
|
@@ -83833,9 +84577,11 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83833
84577
|
await journal.commit();
|
|
83834
84578
|
await liveSession.close();
|
|
83835
84579
|
await deps.sessionRepository.markClosed(rowId);
|
|
84580
|
+
deps.activeSessions.delete(rowId);
|
|
83836
84581
|
};
|
|
83837
84582
|
const handle = {
|
|
83838
84583
|
harnessSessionId: rowId,
|
|
84584
|
+
harnessName,
|
|
83839
84585
|
opencodeSessionId: liveSession.opencodeSessionId,
|
|
83840
84586
|
workspaceId: session2.workspaceId,
|
|
83841
84587
|
session: liveSession,
|
|
@@ -83873,7 +84619,61 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83873
84619
|
var init_session_subscriber = __esm(() => {
|
|
83874
84620
|
init_idle_handler();
|
|
83875
84621
|
init_api3();
|
|
83876
|
-
|
|
84622
|
+
init_registry4();
|
|
84623
|
+
});
|
|
84624
|
+
|
|
84625
|
+
// src/commands/machine/daemon-start/direct-harness/shutdown-sessions.ts
|
|
84626
|
+
async function listHarnessSessionIdsForShutdown(session2) {
|
|
84627
|
+
const ids3 = new Set;
|
|
84628
|
+
const managed = await session2.backend.query(api.daemon.directHarness.turns.getMachineHarnessSessions, {
|
|
84629
|
+
sessionId: session2.sessionId,
|
|
84630
|
+
machineId: session2.machineId
|
|
84631
|
+
});
|
|
84632
|
+
for (const row of managed) {
|
|
84633
|
+
ids3.add(row.harnessSessionId);
|
|
84634
|
+
}
|
|
84635
|
+
const pending3 = await session2.backend.query(api.daemon.directHarness.sessions.listPendingSessionsForMachine, {
|
|
84636
|
+
sessionId: session2.sessionId,
|
|
84637
|
+
machineId: session2.machineId
|
|
84638
|
+
});
|
|
84639
|
+
for (const row of pending3) {
|
|
84640
|
+
ids3.add(row._id);
|
|
84641
|
+
}
|
|
84642
|
+
return [...ids3];
|
|
84643
|
+
}
|
|
84644
|
+
async function closeAllMachineHarnessSessionsOnShutdown(session2, deps) {
|
|
84645
|
+
const commandDeps = {
|
|
84646
|
+
lifecycleManager: deps.lifecycleManager,
|
|
84647
|
+
publisher: {},
|
|
84648
|
+
activeSessions: deps.activeSessions,
|
|
84649
|
+
sessionRepository: deps.sessionRepository
|
|
84650
|
+
};
|
|
84651
|
+
const harnessSessionIds = await listHarnessSessionIdsForShutdown(session2);
|
|
84652
|
+
if (harnessSessionIds.length === 0)
|
|
84653
|
+
return;
|
|
84654
|
+
console.log(`[direct-harness] Closing ${harnessSessionIds.length} harness session(s) on daemon shutdown...`);
|
|
84655
|
+
for (const harnessSessionId of harnessSessionIds) {
|
|
84656
|
+
try {
|
|
84657
|
+
await session2.backend.mutation(api.daemon.directHarness.turns.markOrphanTurnsFailed, {
|
|
84658
|
+
sessionId: session2.sessionId,
|
|
84659
|
+
machineId: session2.machineId,
|
|
84660
|
+
harnessSessionId
|
|
84661
|
+
});
|
|
84662
|
+
} catch {}
|
|
84663
|
+
try {
|
|
84664
|
+
await closeHarnessSession(session2, commandDeps, harnessSessionId);
|
|
84665
|
+
} catch (err) {
|
|
84666
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84667
|
+
console.warn(`[direct-harness] Failed to close session ${harnessSessionId} on shutdown: ${message}`);
|
|
84668
|
+
try {
|
|
84669
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84670
|
+
} catch {}
|
|
84671
|
+
}
|
|
84672
|
+
}
|
|
84673
|
+
}
|
|
84674
|
+
var init_shutdown_sessions = __esm(() => {
|
|
84675
|
+
init_command_subscriber();
|
|
84676
|
+
init_api3();
|
|
83877
84677
|
});
|
|
83878
84678
|
|
|
83879
84679
|
// src/infrastructure/repos/convex-capabilities-publisher.ts
|
|
@@ -84164,13 +84964,20 @@ function startDirectHarnessSubscriptions(session2, wsClient2, activeSessions, ha
|
|
|
84164
84964
|
publisher: new ConvexCapabilitiesPublisher({
|
|
84165
84965
|
backend: session2.backend,
|
|
84166
84966
|
sessionId: session2.sessionId
|
|
84167
|
-
})
|
|
84967
|
+
}),
|
|
84968
|
+
activeSessions,
|
|
84969
|
+
sessionRepository
|
|
84168
84970
|
});
|
|
84169
84971
|
return {
|
|
84170
84972
|
pendingPromptSubscriptionHandle,
|
|
84171
84973
|
pendingHarnessSessionSubscriptionHandle,
|
|
84172
84974
|
commandSubscriptionHandle,
|
|
84173
|
-
lifecycleManager
|
|
84975
|
+
lifecycleManager,
|
|
84976
|
+
closeSessionsOnShutdown: () => closeAllMachineHarnessSessionsOnShutdown(session2, {
|
|
84977
|
+
lifecycleManager,
|
|
84978
|
+
activeSessions,
|
|
84979
|
+
sessionRepository
|
|
84980
|
+
})
|
|
84174
84981
|
};
|
|
84175
84982
|
}
|
|
84176
84983
|
var init_start_subscriptions = __esm(() => {
|
|
@@ -84178,6 +84985,7 @@ var init_start_subscriptions = __esm(() => {
|
|
|
84178
84985
|
init_harness_lifecycle_manager();
|
|
84179
84986
|
init_prompt_subscriber();
|
|
84180
84987
|
init_session_subscriber();
|
|
84988
|
+
init_shutdown_sessions();
|
|
84181
84989
|
init_api3();
|
|
84182
84990
|
init_convex_capabilities_publisher();
|
|
84183
84991
|
init_convex_output_repository();
|
|
@@ -85191,12 +85999,13 @@ class NativeTaskDeliveryCoordinator {
|
|
|
85191
85999
|
}
|
|
85192
86000
|
}
|
|
85193
86001
|
reconcileAssignedTasks(params) {
|
|
85194
|
-
const { tasks, runtime: runtime4, effectContext: effectContext2, agentMgr, sessionDeps } = params;
|
|
85195
|
-
|
|
85196
|
-
|
|
86002
|
+
const { tasks, runtime: runtime4, effectContext: effectContext2, agentMgr, sessionDeps, machineId } = params;
|
|
86003
|
+
const ledger = this.ledger;
|
|
86004
|
+
for (const row of tasks) {
|
|
86005
|
+
const slot = agentMgr.getSlot(row.chatroomId, row.agentConfig.role);
|
|
85197
86006
|
const harnessSessionId = slot?.harnessSessionId;
|
|
85198
|
-
if (!shouldDeliverNativeTask(
|
|
85199
|
-
ledger
|
|
86007
|
+
if (!shouldDeliverNativeTask(row, {
|
|
86008
|
+
ledger,
|
|
85200
86009
|
harnessSessionId
|
|
85201
86010
|
})) {
|
|
85202
86011
|
continue;
|
|
@@ -85204,14 +86013,24 @@ class NativeTaskDeliveryCoordinator {
|
|
|
85204
86013
|
if (!harnessSessionId) {
|
|
85205
86014
|
continue;
|
|
85206
86015
|
}
|
|
85207
|
-
exports_Runtime.runFork(runtime4)(
|
|
85208
|
-
|
|
85209
|
-
|
|
85210
|
-
|
|
85211
|
-
|
|
85212
|
-
|
|
85213
|
-
|
|
85214
|
-
|
|
86016
|
+
exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
|
|
86017
|
+
const full = yield* exports_Effect.tryPromise(() => sessionDeps.backend.query(api.machines.getAssignedTaskForAction, {
|
|
86018
|
+
sessionId: sessionDeps.sessionId,
|
|
86019
|
+
machineId,
|
|
86020
|
+
taskId: row.taskId,
|
|
86021
|
+
role: row.agentConfig.role
|
|
86022
|
+
}));
|
|
86023
|
+
if (!full)
|
|
86024
|
+
return;
|
|
86025
|
+
yield* runNativeInjectionEffect(full, harnessSessionId, {
|
|
86026
|
+
sessionId: sessionDeps.sessionId,
|
|
86027
|
+
backend: sessionDeps.backend,
|
|
86028
|
+
agentMgr: {
|
|
86029
|
+
resumeTurnForSlot: (args2) => exports_Effect.runPromise(agentMgr.resumeTurnForSlot(args2))
|
|
86030
|
+
},
|
|
86031
|
+
convexUrl: sessionDeps.convexUrl
|
|
86032
|
+
}, ledger);
|
|
86033
|
+
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[NativeTaskDelivery] delivery failed for ${row.agentConfig.role}@${row.chatroomId}: ${getErrorMessage(err)}`)))));
|
|
85215
86034
|
}
|
|
85216
86035
|
}
|
|
85217
86036
|
}
|
|
@@ -85227,6 +86046,7 @@ var init_native_task_delivery_coordinator = __esm(() => {
|
|
|
85227
86046
|
init_esm();
|
|
85228
86047
|
init_native_task_injector_logic();
|
|
85229
86048
|
init_native_task_injector();
|
|
86049
|
+
init_api3();
|
|
85230
86050
|
init_convex_error();
|
|
85231
86051
|
});
|
|
85232
86052
|
|
|
@@ -85557,6 +86377,9 @@ function appendRecentLogLine(slot, line) {
|
|
|
85557
86377
|
}
|
|
85558
86378
|
var RECENT_LOG_LINE_CAP2 = 100;
|
|
85559
86379
|
|
|
86380
|
+
// src/domain/agent-lifecycle/policies/cursor-sdk-session-reopen-retry.ts
|
|
86381
|
+
var CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS = 20, CURSOR_SDK_SESSION_REOPEN_INTERVAL_MS = 5000, CURSOR_SDK_SESSION_REOPEN_REASON = "platform.cursor_sdk_session_reopen";
|
|
86382
|
+
|
|
85560
86383
|
// src/domain/agent-lifecycle/use-cases/handle-turn-completed.ts
|
|
85561
86384
|
async function handleTurnCompleted(deps, input, slot) {
|
|
85562
86385
|
if (await tryAbortResumeStorm(deps, input, slot)) {
|
|
@@ -85956,6 +86779,7 @@ class AgentProcessManager {
|
|
|
85956
86779
|
deps;
|
|
85957
86780
|
slots = new Map;
|
|
85958
86781
|
lastHarnessSessions = new Map;
|
|
86782
|
+
sessionReopenRetryInFlight = new Set;
|
|
85959
86783
|
exitRetryQueue = [];
|
|
85960
86784
|
exitRetryTimer = null;
|
|
85961
86785
|
turnEndQueue = new TurnEndQueue;
|
|
@@ -86305,6 +87129,10 @@ class AgentProcessManager {
|
|
|
86305
87129
|
return;
|
|
86306
87130
|
}
|
|
86307
87131
|
const coldRestartAfterRunError = this.clearHarnessSessionAfterRunError(key, logs);
|
|
87132
|
+
if (harness === "cursor-sdk") {
|
|
87133
|
+
this.retryCursorSdkSessionReopen(opts, ctx, coldRestartAfterRunError);
|
|
87134
|
+
return;
|
|
87135
|
+
}
|
|
86308
87136
|
this.ensureRunning({
|
|
86309
87137
|
chatroomId: opts.chatroomId,
|
|
86310
87138
|
role: opts.role,
|
|
@@ -86322,6 +87150,49 @@ class AgentProcessManager {
|
|
|
86322
87150
|
this.emitStartFailedEvent(opts.role, opts.chatroomId, err.message);
|
|
86323
87151
|
});
|
|
86324
87152
|
}
|
|
87153
|
+
async retryCursorSdkSessionReopen(opts, ctx, coldRestartAfterRunError) {
|
|
87154
|
+
const key = agentKey3(opts.chatroomId, opts.role);
|
|
87155
|
+
if (this.sessionReopenRetryInFlight.has(key)) {
|
|
87156
|
+
return;
|
|
87157
|
+
}
|
|
87158
|
+
this.sessionReopenRetryInFlight.add(key);
|
|
87159
|
+
const harness = ctx.harness;
|
|
87160
|
+
const model = ctx.model;
|
|
87161
|
+
const workingDir = ctx.workingDir;
|
|
87162
|
+
const wantResume = coldRestartAfterRunError ? false : ctx.wantResume ?? true;
|
|
87163
|
+
const storedSessionId = this.lastHarnessSessions.get(key)?.harnessSessionId;
|
|
87164
|
+
let lastError = "unknown";
|
|
87165
|
+
try {
|
|
87166
|
+
for (let attempt = 1;attempt <= CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS; attempt++) {
|
|
87167
|
+
await this.emitSessionReopenRetry(opts.chatroomId, opts.role, attempt, attempt > 1 ? lastError : undefined, storedSessionId);
|
|
87168
|
+
const result = await this.ensureRunning({
|
|
87169
|
+
chatroomId: opts.chatroomId,
|
|
87170
|
+
role: opts.role,
|
|
87171
|
+
agentHarness: harness,
|
|
87172
|
+
model,
|
|
87173
|
+
workingDir,
|
|
87174
|
+
reason: CURSOR_SDK_SESSION_REOPEN_REASON,
|
|
87175
|
+
wantResume
|
|
87176
|
+
});
|
|
87177
|
+
if (result.success) {
|
|
87178
|
+
return;
|
|
87179
|
+
}
|
|
87180
|
+
lastError = result.error ?? "unknown";
|
|
87181
|
+
if (attempt < CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS) {
|
|
87182
|
+
await this.deps.clock.delay(CURSOR_SDK_SESSION_REOPEN_INTERVAL_MS);
|
|
87183
|
+
}
|
|
87184
|
+
}
|
|
87185
|
+
const failureMessage = `cursor-sdk session reopen failed after ${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS} attempts: ${lastError}`;
|
|
87186
|
+
console.log(`[AgentProcessManager] ⛔ ${failureMessage}`);
|
|
87187
|
+
this.emitStartFailedEvent(opts.role, opts.chatroomId, failureMessage);
|
|
87188
|
+
} catch (err) {
|
|
87189
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87190
|
+
console.log(` ⚠️ cursor-sdk session reopen retry loop failed: ${message}`);
|
|
87191
|
+
this.emitStartFailedEvent(opts.role, opts.chatroomId, message);
|
|
87192
|
+
} finally {
|
|
87193
|
+
this.sessionReopenRetryInFlight.delete(key);
|
|
87194
|
+
}
|
|
87195
|
+
}
|
|
86325
87196
|
handlePermanentFailureForRestart(opts, recentLogLines, startFailedAlreadyEmitted) {
|
|
86326
87197
|
const error = isTerminalProviderFailureInLogs(recentLogLines ?? []) ? formatTerminalProviderFailureMessage(recentLogLines ?? []) : formatPermanentHarnessFailureMessage(recentLogLines ?? []);
|
|
86327
87198
|
console.log(`[AgentProcessManager] ⛔ Skipping restart — ${error}`);
|
|
@@ -86637,6 +87508,23 @@ class AgentProcessManager {
|
|
|
86637
87508
|
console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
|
|
86638
87509
|
}
|
|
86639
87510
|
}
|
|
87511
|
+
async emitSessionReopenRetry(chatroomId, role, attempt, error, harnessSessionId) {
|
|
87512
|
+
try {
|
|
87513
|
+
await this.deps.backend.mutation(api.machines.emitSessionReopenRetry, {
|
|
87514
|
+
sessionId: this.deps.sessionId,
|
|
87515
|
+
machineId: this.deps.machineId,
|
|
87516
|
+
chatroomId,
|
|
87517
|
+
role,
|
|
87518
|
+
attempt,
|
|
87519
|
+
maxAttempts: CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS,
|
|
87520
|
+
...error ? { error } : {},
|
|
87521
|
+
...harnessSessionId ? { harnessSessionId } : {}
|
|
87522
|
+
});
|
|
87523
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionReopenRetry for ${role} (attempt ${attempt}/${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS})`);
|
|
87524
|
+
} catch (err) {
|
|
87525
|
+
console.log(` ⚠️ Failed to emit sessionReopenRetry event: ${err.message}`);
|
|
87526
|
+
}
|
|
87527
|
+
}
|
|
86640
87528
|
resetSlotIdle(slot) {
|
|
86641
87529
|
slot.state = "idle";
|
|
86642
87530
|
slot.pendingOperation = undefined;
|
|
@@ -87039,7 +87927,7 @@ class SpawnRateLimiter {
|
|
|
87039
87927
|
this.config = { ...DEFAULT_CONFIG2, ...config3 };
|
|
87040
87928
|
}
|
|
87041
87929
|
tryConsume(chatroomId, reason) {
|
|
87042
|
-
if (reason.startsWith("user.") || reason === "platform.auto_restart_on_new_context") {
|
|
87930
|
+
if (reason.startsWith("user.") || reason === "platform.auto_restart_on_new_context" || reason === "platform.cursor_sdk_session_reopen") {
|
|
87043
87931
|
return { allowed: true };
|
|
87044
87932
|
}
|
|
87045
87933
|
const bucket = this._getOrCreateBucket(chatroomId);
|
|
@@ -87066,13 +87954,16 @@ class SpawnRateLimiter {
|
|
|
87066
87954
|
};
|
|
87067
87955
|
}
|
|
87068
87956
|
_getOrCreateBucket(chatroomId) {
|
|
87069
|
-
|
|
87070
|
-
|
|
87071
|
-
|
|
87072
|
-
lastRefillAt: Date.now()
|
|
87073
|
-
});
|
|
87957
|
+
const existing = this.buckets.get(chatroomId);
|
|
87958
|
+
if (existing) {
|
|
87959
|
+
return existing;
|
|
87074
87960
|
}
|
|
87075
|
-
|
|
87961
|
+
const bucket = {
|
|
87962
|
+
tokens: this.config.initialTokens,
|
|
87963
|
+
lastRefillAt: Date.now()
|
|
87964
|
+
};
|
|
87965
|
+
this.buckets.set(chatroomId, bucket);
|
|
87966
|
+
return bucket;
|
|
87076
87967
|
}
|
|
87077
87968
|
_refill(bucket) {
|
|
87078
87969
|
const now = Date.now();
|
|
@@ -87959,6 +88850,432 @@ var init_task_monitor_logic = __esm(() => {
|
|
|
87959
88850
|
init_predicates();
|
|
87960
88851
|
});
|
|
87961
88852
|
|
|
88853
|
+
// src/infrastructure/incremental-sync/working-snapshot.ts
|
|
88854
|
+
class WorkingSnapshot {
|
|
88855
|
+
opts;
|
|
88856
|
+
rows = new Map;
|
|
88857
|
+
constructor(opts) {
|
|
88858
|
+
this.opts = opts;
|
|
88859
|
+
}
|
|
88860
|
+
replaceAll(rows) {
|
|
88861
|
+
this.rows.clear();
|
|
88862
|
+
for (const row of rows) {
|
|
88863
|
+
this.rows.set(this.opts.rowKey(row), row);
|
|
88864
|
+
}
|
|
88865
|
+
}
|
|
88866
|
+
getByKey(key) {
|
|
88867
|
+
return this.rows.get(key);
|
|
88868
|
+
}
|
|
88869
|
+
getBySignal(signal) {
|
|
88870
|
+
return this.rows.get(this.opts.signalKey(signal));
|
|
88871
|
+
}
|
|
88872
|
+
mergeSignal(signal) {
|
|
88873
|
+
const key = this.opts.signalKey(signal);
|
|
88874
|
+
const merged = this.opts.mergeSignal(this.rows.get(key), signal);
|
|
88875
|
+
if (!merged) {
|
|
88876
|
+
return;
|
|
88877
|
+
}
|
|
88878
|
+
this.rows.set(key, merged);
|
|
88879
|
+
return merged;
|
|
88880
|
+
}
|
|
88881
|
+
}
|
|
88882
|
+
|
|
88883
|
+
// src/commands/machine/daemon-start/task-monitor-snapshot.ts
|
|
88884
|
+
function taskSnapshotKey(taskId, role) {
|
|
88885
|
+
return `${taskId}:${role}`;
|
|
88886
|
+
}
|
|
88887
|
+
function mergeSignalIntoTaskSnapshot(existing, signal) {
|
|
88888
|
+
if (!existing) {
|
|
88889
|
+
return;
|
|
88890
|
+
}
|
|
88891
|
+
return {
|
|
88892
|
+
...existing,
|
|
88893
|
+
status: signal.status,
|
|
88894
|
+
agentConfig: {
|
|
88895
|
+
...existing.agentConfig,
|
|
88896
|
+
spawnedAgentPid: signal.spawnedAgentPid ?? existing.agentConfig.spawnedAgentPid,
|
|
88897
|
+
desiredState: signal.desiredState ?? existing.agentConfig.desiredState
|
|
88898
|
+
},
|
|
88899
|
+
participant: {
|
|
88900
|
+
lastSeenAction: signal.lastSeenAction ?? existing.participant?.lastSeenAction ?? null,
|
|
88901
|
+
lastSeenAt: existing.participant?.lastSeenAt ?? null,
|
|
88902
|
+
lastStatus: existing.participant?.lastStatus ?? null
|
|
88903
|
+
}
|
|
88904
|
+
};
|
|
88905
|
+
}
|
|
88906
|
+
function createTaskMonitorSnapshot() {
|
|
88907
|
+
return new WorkingSnapshot(taskMonitorSnapshotOptions);
|
|
88908
|
+
}
|
|
88909
|
+
var taskMonitorSnapshotOptions;
|
|
88910
|
+
var init_task_monitor_snapshot = __esm(() => {
|
|
88911
|
+
taskMonitorSnapshotOptions = {
|
|
88912
|
+
rowKey: (row) => taskSnapshotKey(row.taskId, row.agentConfig.role),
|
|
88913
|
+
signalKey: (signal) => taskSnapshotKey(signal.taskId, signal.role),
|
|
88914
|
+
mergeSignal: mergeSignalIntoTaskSnapshot
|
|
88915
|
+
};
|
|
88916
|
+
});
|
|
88917
|
+
|
|
88918
|
+
// src/infrastructure/incremental-sync/layers.ts
|
|
88919
|
+
var IntervalClock, IntervalClockLive;
|
|
88920
|
+
var init_layers = __esm(() => {
|
|
88921
|
+
init_esm();
|
|
88922
|
+
IntervalClock = class IntervalClock extends exports_Context.Tag("IntervalClock")() {
|
|
88923
|
+
};
|
|
88924
|
+
IntervalClockLive = exports_Layer.succeed(IntervalClock, {
|
|
88925
|
+
sleep: (ms) => exports_Effect.promise(() => new Promise((resolve5) => setTimeout(resolve5, ms)))
|
|
88926
|
+
});
|
|
88927
|
+
});
|
|
88928
|
+
|
|
88929
|
+
// src/infrastructure/incremental-sync/message-buffer.ts
|
|
88930
|
+
class MessageBuffer {
|
|
88931
|
+
keyOf;
|
|
88932
|
+
queue = [];
|
|
88933
|
+
inBuffer = new Set;
|
|
88934
|
+
inFlight = new Map;
|
|
88935
|
+
recentlyAcked = new Map;
|
|
88936
|
+
dedupe;
|
|
88937
|
+
dedupeTtlMs;
|
|
88938
|
+
maxSize;
|
|
88939
|
+
constructor(config3, keyOf) {
|
|
88940
|
+
this.keyOf = keyOf;
|
|
88941
|
+
this.dedupe = config3.dedupe ?? true;
|
|
88942
|
+
this.dedupeTtlMs = config3.dedupeTtlMs ?? 0;
|
|
88943
|
+
this.maxSize = config3.maxSize;
|
|
88944
|
+
}
|
|
88945
|
+
enqueue(items) {
|
|
88946
|
+
this.pruneRecentlyAcked();
|
|
88947
|
+
let enqueued = 0;
|
|
88948
|
+
for (const item of items) {
|
|
88949
|
+
const key = this.keyOf(item);
|
|
88950
|
+
if (this.dedupe && this.shouldSkip(key)) {
|
|
88951
|
+
continue;
|
|
88952
|
+
}
|
|
88953
|
+
this.queue.push({ key, item });
|
|
88954
|
+
this.inBuffer.add(key);
|
|
88955
|
+
enqueued++;
|
|
88956
|
+
}
|
|
88957
|
+
if (enqueued > 0) {
|
|
88958
|
+
this.queue.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
88959
|
+
}
|
|
88960
|
+
this.enforceMaxSize();
|
|
88961
|
+
return enqueued;
|
|
88962
|
+
}
|
|
88963
|
+
dequeue() {
|
|
88964
|
+
this.pruneRecentlyAcked();
|
|
88965
|
+
const next4 = this.queue.shift();
|
|
88966
|
+
if (!next4)
|
|
88967
|
+
return;
|
|
88968
|
+
this.inBuffer.delete(next4.key);
|
|
88969
|
+
this.inFlight.set(next4.key, next4.item);
|
|
88970
|
+
return next4.item;
|
|
88971
|
+
}
|
|
88972
|
+
ack(key) {
|
|
88973
|
+
this.inFlight.delete(key);
|
|
88974
|
+
if (this.dedupe) {
|
|
88975
|
+
this.recentlyAcked.set(key, Date.now());
|
|
88976
|
+
}
|
|
88977
|
+
}
|
|
88978
|
+
nack(key, requeue = false) {
|
|
88979
|
+
const item = this.inFlight.get(key);
|
|
88980
|
+
this.inFlight.delete(key);
|
|
88981
|
+
if (requeue && item !== undefined) {
|
|
88982
|
+
this.enqueue([item]);
|
|
88983
|
+
}
|
|
88984
|
+
}
|
|
88985
|
+
size() {
|
|
88986
|
+
return this.queue.length;
|
|
88987
|
+
}
|
|
88988
|
+
highKeyOf(items) {
|
|
88989
|
+
if (items.length === 0)
|
|
88990
|
+
return null;
|
|
88991
|
+
let high = null;
|
|
88992
|
+
for (const item of items) {
|
|
88993
|
+
const key = this.keyOf(item);
|
|
88994
|
+
if (high === null || key > high) {
|
|
88995
|
+
high = key;
|
|
88996
|
+
}
|
|
88997
|
+
}
|
|
88998
|
+
return high;
|
|
88999
|
+
}
|
|
89000
|
+
shouldSkip(key) {
|
|
89001
|
+
if (this.inBuffer.has(key) || this.inFlight.has(key)) {
|
|
89002
|
+
return true;
|
|
89003
|
+
}
|
|
89004
|
+
if (this.dedupeTtlMs > 0) {
|
|
89005
|
+
const ackedAt = this.recentlyAcked.get(key);
|
|
89006
|
+
if (ackedAt !== undefined && Date.now() - ackedAt < this.dedupeTtlMs) {
|
|
89007
|
+
return true;
|
|
89008
|
+
}
|
|
89009
|
+
} else if (this.recentlyAcked.has(key)) {
|
|
89010
|
+
return true;
|
|
89011
|
+
}
|
|
89012
|
+
return false;
|
|
89013
|
+
}
|
|
89014
|
+
pruneRecentlyAcked() {
|
|
89015
|
+
if (this.dedupeTtlMs <= 0)
|
|
89016
|
+
return;
|
|
89017
|
+
const now = Date.now();
|
|
89018
|
+
for (const [key, ackedAt] of this.recentlyAcked) {
|
|
89019
|
+
if (now - ackedAt >= this.dedupeTtlMs) {
|
|
89020
|
+
this.recentlyAcked.delete(key);
|
|
89021
|
+
}
|
|
89022
|
+
}
|
|
89023
|
+
}
|
|
89024
|
+
enforceMaxSize() {
|
|
89025
|
+
while (this.queue.length > this.maxSize) {
|
|
89026
|
+
const dropped = this.queue.shift();
|
|
89027
|
+
if (dropped) {
|
|
89028
|
+
this.inBuffer.delete(dropped.key);
|
|
89029
|
+
console.warn(`[incremental-sync] Buffer full — dropped oldest unacked item (key=${dropped.key})`);
|
|
89030
|
+
}
|
|
89031
|
+
}
|
|
89032
|
+
}
|
|
89033
|
+
}
|
|
89034
|
+
|
|
89035
|
+
// src/infrastructure/incremental-sync/resolve-high-key.ts
|
|
89036
|
+
function resolveHighKey(def, buffer, page) {
|
|
89037
|
+
if (page.highKey !== null)
|
|
89038
|
+
return page.highKey;
|
|
89039
|
+
const fromItems = buffer.highKeyOf(page.items);
|
|
89040
|
+
if (fromItems !== null)
|
|
89041
|
+
return fromItems;
|
|
89042
|
+
if (def.itemToKey && page.items.length > 0) {
|
|
89043
|
+
let high = null;
|
|
89044
|
+
for (const item of page.items) {
|
|
89045
|
+
const key = def.itemToKey(item);
|
|
89046
|
+
if (high === null || key > high)
|
|
89047
|
+
high = key;
|
|
89048
|
+
}
|
|
89049
|
+
return high;
|
|
89050
|
+
}
|
|
89051
|
+
return null;
|
|
89052
|
+
}
|
|
89053
|
+
|
|
89054
|
+
// src/infrastructure/incremental-sync/subscribe-loop.ts
|
|
89055
|
+
function startSubscribeLoop(opts) {
|
|
89056
|
+
let afterKey = opts.initialAfterKey ?? null;
|
|
89057
|
+
let stopped = false;
|
|
89058
|
+
let unsubscribe;
|
|
89059
|
+
let drainInFlight = false;
|
|
89060
|
+
const subscribe = () => {
|
|
89061
|
+
if (stopped)
|
|
89062
|
+
return;
|
|
89063
|
+
unsubscribe?.();
|
|
89064
|
+
unsubscribe = opts.wsClient.onUpdate(opts.target.query, opts.target.buildArgs(opts.args, afterKey, opts.config.limit), (result) => {
|
|
89065
|
+
if (stopped)
|
|
89066
|
+
return;
|
|
89067
|
+
const page = opts.target.parsePage(result);
|
|
89068
|
+
drainPages(page);
|
|
89069
|
+
}, (err) => {
|
|
89070
|
+
opts.onError?.(err);
|
|
89071
|
+
});
|
|
89072
|
+
};
|
|
89073
|
+
const fetchPage = async (cursor) => {
|
|
89074
|
+
const result = await opts.wsClient.query(opts.target.query, opts.target.buildArgs(opts.args, cursor, opts.config.limit));
|
|
89075
|
+
return opts.target.parsePage(result);
|
|
89076
|
+
};
|
|
89077
|
+
const drainPages = async (initialPage) => {
|
|
89078
|
+
if (stopped || drainInFlight)
|
|
89079
|
+
return;
|
|
89080
|
+
drainInFlight = true;
|
|
89081
|
+
try {
|
|
89082
|
+
const cursorAtStart = afterKey;
|
|
89083
|
+
let page = initialPage;
|
|
89084
|
+
while (!stopped && page.items.length > 0) {
|
|
89085
|
+
opts.buffer.enqueue(page.items);
|
|
89086
|
+
const nextKey = resolveHighKey(opts.def, opts.buffer, page);
|
|
89087
|
+
if (nextKey === null) {
|
|
89088
|
+
break;
|
|
89089
|
+
}
|
|
89090
|
+
afterKey = nextKey;
|
|
89091
|
+
if (!page.hasMore) {
|
|
89092
|
+
break;
|
|
89093
|
+
}
|
|
89094
|
+
page = await fetchPage(afterKey);
|
|
89095
|
+
}
|
|
89096
|
+
if (!stopped && afterKey !== cursorAtStart) {
|
|
89097
|
+
subscribe();
|
|
89098
|
+
}
|
|
89099
|
+
} catch (err) {
|
|
89100
|
+
opts.onError?.(err);
|
|
89101
|
+
} finally {
|
|
89102
|
+
drainInFlight = false;
|
|
89103
|
+
}
|
|
89104
|
+
};
|
|
89105
|
+
subscribe();
|
|
89106
|
+
return {
|
|
89107
|
+
stop: () => {
|
|
89108
|
+
stopped = true;
|
|
89109
|
+
unsubscribe?.();
|
|
89110
|
+
unsubscribe = undefined;
|
|
89111
|
+
},
|
|
89112
|
+
getAfterKey: () => afterKey
|
|
89113
|
+
};
|
|
89114
|
+
}
|
|
89115
|
+
var init_subscribe_loop = () => {};
|
|
89116
|
+
|
|
89117
|
+
// src/infrastructure/incremental-sync/feed-runtime.ts
|
|
89118
|
+
function runWorkerLoop(feedName, buffer, keyOf, onItem) {
|
|
89119
|
+
return exports_Effect.gen(function* () {
|
|
89120
|
+
while (true) {
|
|
89121
|
+
const item = buffer.dequeue();
|
|
89122
|
+
if (item === undefined) {
|
|
89123
|
+
yield* exports_Effect.sleep("10 millis");
|
|
89124
|
+
continue;
|
|
89125
|
+
}
|
|
89126
|
+
const key = keyOf(item);
|
|
89127
|
+
const ctx = {
|
|
89128
|
+
item,
|
|
89129
|
+
feedName,
|
|
89130
|
+
ack: () => buffer.ack(key),
|
|
89131
|
+
nack: (opts) => buffer.nack(key, opts?.requeue ?? false)
|
|
89132
|
+
};
|
|
89133
|
+
yield* onItem(ctx).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89134
|
+
}
|
|
89135
|
+
});
|
|
89136
|
+
}
|
|
89137
|
+
var runIncrementalSubscribeLive = (opts) => exports_Effect.gen(function* () {
|
|
89138
|
+
const buffer = new MessageBuffer(opts.buffer, opts.def.itemKey);
|
|
89139
|
+
const subscribeHandle = startSubscribeLoop({
|
|
89140
|
+
wsClient: opts.wsClient,
|
|
89141
|
+
target: opts.target,
|
|
89142
|
+
args: opts.args,
|
|
89143
|
+
buffer,
|
|
89144
|
+
def: opts.def,
|
|
89145
|
+
config: opts.subscribe,
|
|
89146
|
+
initialAfterKey: opts.initialAfterKey ?? null,
|
|
89147
|
+
onError: opts.onError
|
|
89148
|
+
});
|
|
89149
|
+
const workerFiber = yield* exports_Effect.forkDaemon(runWorkerLoop(opts.def.name, buffer, opts.def.itemKey, opts.onItem));
|
|
89150
|
+
return {
|
|
89151
|
+
buffer,
|
|
89152
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89153
|
+
subscribeHandle.stop();
|
|
89154
|
+
yield* exports_Fiber.interrupt(workerFiber);
|
|
89155
|
+
})
|
|
89156
|
+
};
|
|
89157
|
+
}), runReconcilePoll = (opts) => exports_Effect.gen(function* () {
|
|
89158
|
+
const clock4 = yield* IntervalClock;
|
|
89159
|
+
const backoffCfg = opts.backoff ?? { initialMs: 1000, maxMs: 30000 };
|
|
89160
|
+
let backoffMs = opts.intervalMs;
|
|
89161
|
+
let stopped = false;
|
|
89162
|
+
const loopFiber = yield* exports_Effect.forkDaemon(exports_Effect.gen(function* () {
|
|
89163
|
+
while (!stopped) {
|
|
89164
|
+
const pollOutcome = yield* exports_Effect.either(exports_Effect.tryPromise(() => opts.poll(opts.args)));
|
|
89165
|
+
if (pollOutcome._tag === "Left") {
|
|
89166
|
+
backoffMs = Math.min(backoffMs === opts.intervalMs ? backoffCfg.initialMs : backoffMs * 2, backoffCfg.maxMs);
|
|
89167
|
+
yield* clock4.sleep(backoffMs);
|
|
89168
|
+
continue;
|
|
89169
|
+
}
|
|
89170
|
+
yield* opts.onResult(pollOutcome.right);
|
|
89171
|
+
backoffMs = opts.intervalMs;
|
|
89172
|
+
yield* clock4.sleep(opts.intervalMs);
|
|
89173
|
+
}
|
|
89174
|
+
}));
|
|
89175
|
+
return {
|
|
89176
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89177
|
+
stopped = true;
|
|
89178
|
+
yield* exports_Fiber.interrupt(loopFiber);
|
|
89179
|
+
})
|
|
89180
|
+
};
|
|
89181
|
+
}), runReconcilePollLive = (opts) => runReconcilePoll(opts).pipe(exports_Effect.provide(IntervalClockLive));
|
|
89182
|
+
var init_feed_runtime = __esm(() => {
|
|
89183
|
+
init_esm();
|
|
89184
|
+
init_layers();
|
|
89185
|
+
init_subscribe_loop();
|
|
89186
|
+
});
|
|
89187
|
+
|
|
89188
|
+
// src/infrastructure/incremental-sync/dual-channel-feed.ts
|
|
89189
|
+
var runDualChannelFeedLive = (opts) => exports_Effect.gen(function* () {
|
|
89190
|
+
const initial = yield* exports_Effect.tryPromise(() => opts.fetchReconcile()).pipe(exports_Effect.orElseSucceed(() => null));
|
|
89191
|
+
if (initial !== null) {
|
|
89192
|
+
const rows = opts.extractReconcileRows(initial);
|
|
89193
|
+
opts.snapshot.replaceAll(rows);
|
|
89194
|
+
if (rows.length > 0) {
|
|
89195
|
+
yield* opts.onReconcileRows(rows).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89196
|
+
}
|
|
89197
|
+
}
|
|
89198
|
+
const seedKey = yield* exports_Effect.tryPromise(() => opts.seedCursor()).pipe(exports_Effect.orElseSucceed(() => null));
|
|
89199
|
+
const hydrateFromReconcile = async () => {
|
|
89200
|
+
const result = await opts.fetchReconcile();
|
|
89201
|
+
opts.snapshot.replaceAll(opts.extractReconcileRows(result));
|
|
89202
|
+
};
|
|
89203
|
+
const resolveRowForSignal = async (signal) => {
|
|
89204
|
+
const merged = opts.snapshot.mergeSignal(signal);
|
|
89205
|
+
if (merged) {
|
|
89206
|
+
return merged;
|
|
89207
|
+
}
|
|
89208
|
+
await hydrateFromReconcile();
|
|
89209
|
+
return opts.snapshot.mergeSignal(signal) ?? opts.snapshot.getBySignal(signal);
|
|
89210
|
+
};
|
|
89211
|
+
const signalHandle = yield* runIncrementalSubscribeLive({
|
|
89212
|
+
wsClient: opts.wsClient,
|
|
89213
|
+
def: opts.def,
|
|
89214
|
+
target: opts.target,
|
|
89215
|
+
args: opts.args,
|
|
89216
|
+
buffer: opts.buffer,
|
|
89217
|
+
subscribe: opts.subscribe,
|
|
89218
|
+
initialAfterKey: seedKey,
|
|
89219
|
+
onError: opts.onSubscribeError,
|
|
89220
|
+
onItem: ({ item: signal, ack }) => exports_Effect.gen(function* () {
|
|
89221
|
+
ack();
|
|
89222
|
+
if (opts.isStopped()) {
|
|
89223
|
+
return;
|
|
89224
|
+
}
|
|
89225
|
+
const row = yield* exports_Effect.tryPromise(() => resolveRowForSignal(signal));
|
|
89226
|
+
if (!row) {
|
|
89227
|
+
return;
|
|
89228
|
+
}
|
|
89229
|
+
yield* opts.onSignalRow(row).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89230
|
+
})
|
|
89231
|
+
});
|
|
89232
|
+
const reconcileHandle = yield* runReconcilePollLive({
|
|
89233
|
+
name: `${opts.name}-reconcile`,
|
|
89234
|
+
poll: () => opts.fetchReconcile(),
|
|
89235
|
+
args: undefined,
|
|
89236
|
+
intervalMs: opts.reconcileIntervalMs,
|
|
89237
|
+
onResult: (result) => exports_Effect.gen(function* () {
|
|
89238
|
+
const rows = opts.extractReconcileRows(result);
|
|
89239
|
+
opts.snapshot.replaceAll(rows);
|
|
89240
|
+
yield* opts.onReconcileRows(rows).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89241
|
+
})
|
|
89242
|
+
});
|
|
89243
|
+
return {
|
|
89244
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89245
|
+
yield* signalHandle.stop();
|
|
89246
|
+
yield* reconcileHandle.stop();
|
|
89247
|
+
})
|
|
89248
|
+
};
|
|
89249
|
+
});
|
|
89250
|
+
var init_dual_channel_feed = __esm(() => {
|
|
89251
|
+
init_esm();
|
|
89252
|
+
init_feed_runtime();
|
|
89253
|
+
});
|
|
89254
|
+
|
|
89255
|
+
// src/infrastructure/incremental-sync/feeds/assigned-task-signals.ts
|
|
89256
|
+
var assignedTaskSignalsFeedDef, assignedTaskSignalsSubscribeTarget, ASSIGNED_TASK_SIGNAL_FEED_LIMIT = 50, ASSIGNED_TASK_SIGNAL_FEED_BUFFER, ASSIGNED_TASK_RECONCILE_INTERVAL_MS = 15000;
|
|
89257
|
+
var init_assigned_task_signals = __esm(() => {
|
|
89258
|
+
init_api3();
|
|
89259
|
+
assignedTaskSignalsFeedDef = {
|
|
89260
|
+
name: "assigned-task-signals",
|
|
89261
|
+
itemKey: (item) => item.revisionKey
|
|
89262
|
+
};
|
|
89263
|
+
assignedTaskSignalsSubscribeTarget = {
|
|
89264
|
+
query: api.machines.subscribeAssignedTaskSignalsSince,
|
|
89265
|
+
buildArgs: (args2, afterKey, limit) => ({
|
|
89266
|
+
sessionId: args2.sessionId,
|
|
89267
|
+
machineId: args2.machineId,
|
|
89268
|
+
afterKey: afterKey ?? undefined,
|
|
89269
|
+
limit
|
|
89270
|
+
}),
|
|
89271
|
+
parsePage: (result) => result
|
|
89272
|
+
};
|
|
89273
|
+
ASSIGNED_TASK_SIGNAL_FEED_BUFFER = {
|
|
89274
|
+
maxSize: 200,
|
|
89275
|
+
dedupe: true
|
|
89276
|
+
};
|
|
89277
|
+
});
|
|
89278
|
+
|
|
87962
89279
|
// src/commands/machine/daemon-start/task-monitor.ts
|
|
87963
89280
|
function resolveTaskWantResume(task) {
|
|
87964
89281
|
return compressContextToWantResume(parseCompressContext(task.taskContent ?? ""));
|
|
@@ -87971,7 +89288,7 @@ function buildCliNudgeLogLine(task) {
|
|
|
87971
89288
|
const wantResume = resolveTaskWantResume(task);
|
|
87972
89289
|
return `[TaskMonitor] nudging ${role}@${chatroomId} — pending task ${task.taskId}, lastSeenAction=${lastSeenAction}, compress_context=${compressMode}, wantResume=${wantResume}`;
|
|
87973
89290
|
}
|
|
87974
|
-
function
|
|
89291
|
+
function resolveTaskRunnerContextFromFull(task) {
|
|
87975
89292
|
const { chatroomId, agentConfig } = task;
|
|
87976
89293
|
const { role } = agentConfig;
|
|
87977
89294
|
const workingDir = agentConfig.workingDir;
|
|
@@ -87986,7 +89303,7 @@ function resolveTaskRunnerContext(task) {
|
|
|
87986
89303
|
};
|
|
87987
89304
|
}
|
|
87988
89305
|
function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
|
|
87989
|
-
const ctx =
|
|
89306
|
+
const ctx = resolveTaskRunnerContextFromFull(task);
|
|
87990
89307
|
if (!ctx)
|
|
87991
89308
|
return;
|
|
87992
89309
|
const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
|
|
@@ -88004,7 +89321,7 @@ function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
|
|
|
88004
89321
|
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] nudge failed for ${role}@${chatroomId}: ${getErrorMessage(err)}`)))));
|
|
88005
89322
|
}
|
|
88006
89323
|
function runNativeReviveEffect(task, runtime4, effectContext2, agentMgr) {
|
|
88007
|
-
const ctx =
|
|
89324
|
+
const ctx = resolveTaskRunnerContextFromFull(task);
|
|
88008
89325
|
if (!ctx)
|
|
88009
89326
|
return;
|
|
88010
89327
|
const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
|
|
@@ -88021,63 +89338,124 @@ function runNativeReviveEffect(task, runtime4, effectContext2, agentMgr) {
|
|
|
88021
89338
|
});
|
|
88022
89339
|
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] native revive failed for ${role}@${chatroomId}: ${getErrorMessage(err)}`)))));
|
|
88023
89340
|
}
|
|
89341
|
+
async function fetchTaskForAction(sessionDeps, machineId, snapshotRow) {
|
|
89342
|
+
const result = await sessionDeps.backend.query(api.machines.getAssignedTaskForAction, {
|
|
89343
|
+
sessionId: sessionDeps.sessionId,
|
|
89344
|
+
machineId,
|
|
89345
|
+
taskId: snapshotRow.taskId,
|
|
89346
|
+
role: snapshotRow.agentConfig.role
|
|
89347
|
+
});
|
|
89348
|
+
return result;
|
|
89349
|
+
}
|
|
88024
89350
|
function runCliNudgeEffect(task, runtime4, effectContext2, agentMgr) {
|
|
88025
89351
|
console.log(buildCliNudgeLogLine(task));
|
|
88026
89352
|
executeCliNudge(task, runtime4, effectContext2, agentMgr);
|
|
88027
89353
|
}
|
|
88028
|
-
function nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr) {
|
|
88029
|
-
for (const
|
|
88030
|
-
|
|
89354
|
+
async function nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
89355
|
+
for (const row of listTasksReadyForNudge(tasks, now, cooldown)) {
|
|
89356
|
+
const full = await fetchTaskForAction(sessionDeps, machineId, row);
|
|
89357
|
+
if (!full)
|
|
89358
|
+
continue;
|
|
89359
|
+
runCliNudgeEffect(full, runtime4, effectContext2, agentMgr);
|
|
88031
89360
|
}
|
|
88032
89361
|
}
|
|
88033
|
-
function reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr) {
|
|
88034
|
-
for (const
|
|
88035
|
-
|
|
89362
|
+
async function reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
89363
|
+
for (const row of listNativeTasksNeedingRevive(tasks, localHealth, now, cooldown)) {
|
|
89364
|
+
const full = await fetchTaskForAction(sessionDeps, machineId, row);
|
|
89365
|
+
if (!full)
|
|
89366
|
+
continue;
|
|
89367
|
+
runNativeReviveEffect(full, runtime4, effectContext2, agentMgr);
|
|
88036
89368
|
}
|
|
88037
89369
|
}
|
|
88038
|
-
function processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps) {
|
|
89370
|
+
async function processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps, machineId, pass) {
|
|
89371
|
+
if (tasks.length === 0)
|
|
89372
|
+
return;
|
|
88039
89373
|
const now = Date.now();
|
|
88040
89374
|
const localHealth = {
|
|
88041
89375
|
getSlot: (chatroomId, role) => agentMgr.getSlot(chatroomId, role),
|
|
88042
89376
|
isPidAlive: (pid) => isProcessAlive((p) => process.kill(p, 0), pid)
|
|
88043
89377
|
};
|
|
88044
|
-
reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr);
|
|
89378
|
+
await reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
88045
89379
|
getNativeTaskDeliveryCoordinator().reconcileAssignedTasks({
|
|
88046
89380
|
tasks,
|
|
88047
89381
|
runtime: runtime4,
|
|
88048
89382
|
effectContext: effectContext2,
|
|
88049
89383
|
agentMgr,
|
|
88050
|
-
sessionDeps
|
|
89384
|
+
sessionDeps,
|
|
89385
|
+
machineId
|
|
88051
89386
|
});
|
|
88052
|
-
|
|
89387
|
+
if (pass === "reconcile") {
|
|
89388
|
+
await nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
89389
|
+
}
|
|
88053
89390
|
}
|
|
88054
|
-
var
|
|
89391
|
+
var startTaskMonitorEffect = (wsClient2) => exports_Effect.gen(function* () {
|
|
88055
89392
|
const session2 = yield* DaemonSessionService;
|
|
88056
89393
|
const agentMgr = yield* DaemonAgentProcessManagerService;
|
|
88057
89394
|
const effectContext2 = yield* exports_Effect.context();
|
|
88058
89395
|
const runtime4 = yield* exports_Effect.runtime();
|
|
88059
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Starting task-monitor
|
|
89396
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Starting task-monitor (incremental subscribe)`);
|
|
88060
89397
|
const cooldown = new NudgeCooldown;
|
|
89398
|
+
const snapshot = createTaskMonitorSnapshot();
|
|
88061
89399
|
let stopped = false;
|
|
89400
|
+
let monitorPassInFlight = false;
|
|
88062
89401
|
const sessionDeps = {
|
|
88063
89402
|
sessionId: session2.sessionId,
|
|
88064
89403
|
convexUrl: session2.convexUrl,
|
|
89404
|
+
machineId: session2.machineId,
|
|
88065
89405
|
backend: {
|
|
88066
89406
|
mutation: (fn2, args2) => session2.backend.mutation(fn2, args2),
|
|
88067
89407
|
query: (fn2, args2) => session2.backend.query(fn2, args2)
|
|
88068
89408
|
}
|
|
88069
89409
|
};
|
|
88070
|
-
const
|
|
88071
|
-
if (stopped ||
|
|
89410
|
+
const runMonitorPass = (tasks, pass) => {
|
|
89411
|
+
if (stopped || monitorPassInFlight || tasks.length === 0)
|
|
88072
89412
|
return;
|
|
88073
|
-
|
|
89413
|
+
monitorPassInFlight = true;
|
|
89414
|
+
processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps, session2.machineId, pass).finally(() => {
|
|
89415
|
+
monitorPassInFlight = false;
|
|
89416
|
+
});
|
|
88074
89417
|
};
|
|
88075
|
-
const
|
|
89418
|
+
const queryReconcileSnapshots = () => session2.backend.query(api.machines.listAssignedTasksForReconcile, {
|
|
89419
|
+
sessionId: session2.sessionId,
|
|
89420
|
+
machineId: session2.machineId
|
|
89421
|
+
});
|
|
89422
|
+
const feedHandle = yield* runDualChannelFeedLive({
|
|
89423
|
+
name: "assigned-tasks",
|
|
89424
|
+
wsClient: wsClient2,
|
|
89425
|
+
def: assignedTaskSignalsFeedDef,
|
|
89426
|
+
target: assignedTaskSignalsSubscribeTarget,
|
|
89427
|
+
args: {
|
|
89428
|
+
sessionId: session2.sessionId,
|
|
89429
|
+
machineId: session2.machineId
|
|
89430
|
+
},
|
|
89431
|
+
buffer: ASSIGNED_TASK_SIGNAL_FEED_BUFFER,
|
|
89432
|
+
subscribe: { limit: ASSIGNED_TASK_SIGNAL_FEED_LIMIT },
|
|
89433
|
+
snapshot,
|
|
89434
|
+
seedCursor: async () => {
|
|
89435
|
+
const seedPage = await session2.backend.query(api.machines.subscribeAssignedTaskSignalsSince, {
|
|
89436
|
+
sessionId: session2.sessionId,
|
|
89437
|
+
machineId: session2.machineId,
|
|
89438
|
+
limit: ASSIGNED_TASK_SIGNAL_FEED_LIMIT
|
|
89439
|
+
});
|
|
89440
|
+
return seedPage?.highKey ?? null;
|
|
89441
|
+
},
|
|
89442
|
+
fetchReconcile: () => queryReconcileSnapshots(),
|
|
89443
|
+
extractReconcileRows: (result) => result?.tasks ?? [],
|
|
89444
|
+
reconcileIntervalMs: ASSIGNED_TASK_RECONCILE_INTERVAL_MS,
|
|
89445
|
+
isStopped: () => stopped,
|
|
89446
|
+
onSubscribeError: (err) => console.warn(`[${formatTimestamp()}] ⚠️ Task signal subscription error: ${getErrorMessage(err)}`),
|
|
89447
|
+
onSignalRow: (row) => exports_Effect.sync(() => {
|
|
89448
|
+
runMonitorPass([row], "signal");
|
|
89449
|
+
}),
|
|
89450
|
+
onReconcileRows: (tasks) => exports_Effect.sync(() => {
|
|
89451
|
+
runMonitorPass([...tasks], "reconcile");
|
|
89452
|
+
})
|
|
89453
|
+
});
|
|
88076
89454
|
return {
|
|
88077
89455
|
stop() {
|
|
88078
89456
|
stopped = true;
|
|
88079
|
-
|
|
88080
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Task-monitor
|
|
89457
|
+
exports_Effect.runPromise(feedHandle.stop());
|
|
89458
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Task-monitor stopped`);
|
|
88081
89459
|
}
|
|
88082
89460
|
};
|
|
88083
89461
|
});
|
|
@@ -88087,7 +89465,10 @@ var init_task_monitor = __esm(() => {
|
|
|
88087
89465
|
init_daemon_services();
|
|
88088
89466
|
init_native_task_delivery_coordinator();
|
|
88089
89467
|
init_task_monitor_logic();
|
|
89468
|
+
init_task_monitor_snapshot();
|
|
88090
89469
|
init_api3();
|
|
89470
|
+
init_dual_channel_feed();
|
|
89471
|
+
init_assigned_task_signals();
|
|
88091
89472
|
init_convex_error();
|
|
88092
89473
|
});
|
|
88093
89474
|
|
|
@@ -88290,6 +89671,11 @@ function handleRefreshCapabilitiesEffect(event, tracker) {
|
|
|
88290
89671
|
})));
|
|
88291
89672
|
});
|
|
88292
89673
|
}
|
|
89674
|
+
function forkDaemonSyncBatch(runtime4, effectContext2, failureLabel) {
|
|
89675
|
+
exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
|
|
89676
|
+
concurrency: "unbounded"
|
|
89677
|
+
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ ${failureLabel}: ${getErrorMessage(err)}`)))));
|
|
89678
|
+
}
|
|
88293
89679
|
var GIT_PUSH_ACTIONS, commandEventHandlers, dispatchCommandEventEffect = (event, tracker) => {
|
|
88294
89680
|
const factory2 = commandEventHandlers[event.type];
|
|
88295
89681
|
return factory2 != null ? factory2(event, tracker) : exports_Effect.void;
|
|
@@ -88348,9 +89734,7 @@ var init_command_loop = __esm(() => {
|
|
|
88348
89734
|
heartbeatCount++;
|
|
88349
89735
|
console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
|
|
88350
89736
|
if (!observedSyncEnabled) {
|
|
88351
|
-
|
|
88352
|
-
concurrency: "unbounded"
|
|
88353
|
-
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Heartbeat sync failed: ${getErrorMessage(err)}`)))));
|
|
89737
|
+
forkDaemonSyncBatch(runtime4, effectContext2, "Heartbeat sync failed");
|
|
88354
89738
|
}
|
|
88355
89739
|
}).catch((err) => {
|
|
88356
89740
|
console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
|
|
@@ -88367,14 +89751,13 @@ var init_command_loop = __esm(() => {
|
|
|
88367
89751
|
let pendingHarnessSessionSubscriptionHandle = null;
|
|
88368
89752
|
let commandSubscriptionHandle = null;
|
|
88369
89753
|
let lifecycleManager = null;
|
|
89754
|
+
let closeDirectHarnessSessionsOnShutdown = null;
|
|
88370
89755
|
const activeSessions = new Map;
|
|
88371
89756
|
const harnesses = new Map;
|
|
88372
89757
|
if (observedSyncEnabled) {
|
|
88373
89758
|
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
|
|
88374
89759
|
} else {
|
|
88375
|
-
|
|
88376
|
-
concurrency: "unbounded"
|
|
88377
|
-
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Startup sync failed: ${getErrorMessage(err)}`)))));
|
|
89760
|
+
forkDaemonSyncBatch(runtime4, effectContext2, "Startup sync failed");
|
|
88378
89761
|
}
|
|
88379
89762
|
const PROCESS_KILL_TIMEOUT_MS = 6000;
|
|
88380
89763
|
const CLOSE_TIMEOUT_MS = 3000;
|
|
@@ -88442,8 +89825,12 @@ var init_command_loop = __esm(() => {
|
|
|
88442
89825
|
await withTimeout2(exports_Effect.runPromise(onDaemonShutdownEffect.pipe(exports_Effect.provide(effectContext2))), PROCESS_KILL_TIMEOUT_MS);
|
|
88443
89826
|
};
|
|
88444
89827
|
const closeAllSessionsAndHarnesses = async () => {
|
|
88445
|
-
|
|
88446
|
-
await withTimeout2(
|
|
89828
|
+
if (closeDirectHarnessSessionsOnShutdown) {
|
|
89829
|
+
await withTimeout2(closeDirectHarnessSessionsOnShutdown(), PROCESS_KILL_TIMEOUT_MS);
|
|
89830
|
+
} else {
|
|
89831
|
+
for (const handle of activeSessions.values()) {
|
|
89832
|
+
await withTimeout2(handle.close(), CLOSE_TIMEOUT_MS);
|
|
89833
|
+
}
|
|
88447
89834
|
}
|
|
88448
89835
|
for (const harness of harnesses.values()) {
|
|
88449
89836
|
await withTimeout2(harness.close(), CLOSE_TIMEOUT_MS);
|
|
@@ -88473,7 +89860,7 @@ var init_command_loop = __esm(() => {
|
|
|
88473
89860
|
if (observedSyncEnabled) {
|
|
88474
89861
|
observedSyncSubscriptionHandle = yield* startObservedSyncSubscriptionEffect(wsClient2);
|
|
88475
89862
|
}
|
|
88476
|
-
const taskMonitorHandle = yield*
|
|
89863
|
+
const taskMonitorHandle = yield* startTaskMonitorEffect(wsClient2);
|
|
88477
89864
|
logObserverSubscriptionHandle = startLogObserverSubscription({ sessionId: session2.sessionId, machineId: session2.machineId }, wsClient2);
|
|
88478
89865
|
if (featureFlags.directHarnessWorkers) {
|
|
88479
89866
|
const handles = startDirectHarnessSubscriptions({
|
|
@@ -88486,6 +89873,7 @@ var init_command_loop = __esm(() => {
|
|
|
88486
89873
|
pendingHarnessSessionSubscriptionHandle = handles.pendingHarnessSessionSubscriptionHandle;
|
|
88487
89874
|
commandSubscriptionHandle = handles.commandSubscriptionHandle;
|
|
88488
89875
|
lifecycleManager = handles.lifecycleManager;
|
|
89876
|
+
closeDirectHarnessSessionsOnShutdown = handles.closeSessionsOnShutdown;
|
|
88489
89877
|
}
|
|
88490
89878
|
console.log(`
|
|
88491
89879
|
Listening for commands...`);
|
|
@@ -88672,8 +90060,8 @@ import * as os2 from "os";
|
|
|
88672
90060
|
import * as path4 from "path";
|
|
88673
90061
|
async function isChatroomInstalledDefault() {
|
|
88674
90062
|
try {
|
|
88675
|
-
const { execSync:
|
|
88676
|
-
|
|
90063
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
90064
|
+
execSync3("chatroom --version", { stdio: "pipe" });
|
|
88677
90065
|
return true;
|
|
88678
90066
|
} catch {
|
|
88679
90067
|
return false;
|
|
@@ -89587,4 +90975,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
89587
90975
|
});
|
|
89588
90976
|
program2.parse();
|
|
89589
90977
|
|
|
89590
|
-
//# debugId=
|
|
90978
|
+
//# debugId=00A3989CD8BAD93D64756E2164756E21
|