chatroom-cli 1.57.3 → 1.58.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1881 -390
- package/dist/index.js.map +61 -39
- 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";
|
|
@@ -31671,7 +31677,7 @@ function isClassifiableHarnessLogLine(line) {
|
|
|
31671
31677
|
return true;
|
|
31672
31678
|
if (line.includes(" error]"))
|
|
31673
31679
|
return true;
|
|
31674
|
-
if (
|
|
31680
|
+
if (line.includes(" run-error]"))
|
|
31675
31681
|
return true;
|
|
31676
31682
|
return false;
|
|
31677
31683
|
}
|
|
@@ -31710,7 +31716,7 @@ function formatTerminalProviderFailureMessage(logLines) {
|
|
|
31710
31716
|
`).trim();
|
|
31711
31717
|
return blob ? `Provider rate limit or quota error (non-retryable): ${blob.slice(-500)}` : "Provider rate limit or quota error (non-retryable)";
|
|
31712
31718
|
}
|
|
31713
|
-
var QUOTA_PHRASES, PROVIDER_ERROR_NAMES
|
|
31719
|
+
var QUOTA_PHRASES, PROVIDER_ERROR_NAMES;
|
|
31714
31720
|
var init_terminal_provider_error = __esm(() => {
|
|
31715
31721
|
QUOTA_PHRASES = [
|
|
31716
31722
|
"usagelimit",
|
|
@@ -31724,7 +31730,6 @@ var init_terminal_provider_error = __esm(() => {
|
|
|
31724
31730
|
"exceeded your weekly"
|
|
31725
31731
|
];
|
|
31726
31732
|
PROVIDER_ERROR_NAMES = ["ai_apicallerror", "ai_retryerror"];
|
|
31727
|
-
HARNESS_LOG_PREFIX = /^\[[^\]]+\] role:/;
|
|
31728
31733
|
});
|
|
31729
31734
|
|
|
31730
31735
|
// src/infrastructure/services/remote-agents/opencode-sdk/session-event-forwarder.ts
|
|
@@ -32520,6 +32525,12 @@ var init_opencode_sdk = __esm(() => {
|
|
|
32520
32525
|
});
|
|
32521
32526
|
|
|
32522
32527
|
// src/infrastructure/services/remote-agents/pi-sdk/pi-sdk-package.ts
|
|
32528
|
+
var exports_pi_sdk_package = {};
|
|
32529
|
+
__export(exports_pi_sdk_package, {
|
|
32530
|
+
importBundledPiSdk: () => importBundledPiSdk,
|
|
32531
|
+
getBundledPiSdkVersion: () => getBundledPiSdkVersion,
|
|
32532
|
+
formatPiSdkLoadError: () => formatPiSdkLoadError
|
|
32533
|
+
});
|
|
32523
32534
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
32524
32535
|
import { dirname as dirname4, join as join9 } from "node:path";
|
|
32525
32536
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -75550,21 +75561,38 @@ Put your **complete** deliverable in the handoff message — not in session text
|
|
|
75550
75561
|
---`;
|
|
75551
75562
|
}
|
|
75552
75563
|
|
|
75564
|
+
// ../../services/backend/prompts/utils/code-change-verification.ts
|
|
75565
|
+
var CODE_CHANGE_VERIFICATION_COMMAND = "pnpm typecheck && pnpm test", CODE_CHANGE_VERIFICATION_CONFIRMATION;
|
|
75566
|
+
var init_code_change_verification = __esm(() => {
|
|
75567
|
+
CODE_CHANGE_VERIFICATION_CONFIRMATION = `- [ ] I confirm that I have run \`${CODE_CHANGE_VERIFICATION_COMMAND}\` (only required if code changes were made)`;
|
|
75568
|
+
});
|
|
75569
|
+
|
|
75553
75570
|
// ../../services/backend/prompts/teams/duo/handoff-templates/builder-to-planner.ts
|
|
75554
75571
|
function getBuilderToPlannerHandoffTemplate() {
|
|
75555
75572
|
return `${getHandoffRecipientVisibilityCallout("planner")}
|
|
75556
75573
|
|
|
75557
|
-
**Handoff Template (Builder → Planner)** — paste into the handoff message. Fill in EVERY section
|
|
75574
|
+
**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
75575
|
|
|
75559
75576
|
\`\`\`markdown
|
|
75560
75577
|
## Summary
|
|
75561
75578
|
<what was implemented or attempted, in plain terms>
|
|
75562
75579
|
|
|
75563
|
-
##
|
|
75580
|
+
## Template Disclosure Confirmation
|
|
75581
|
+
- [ ] I confirm that I have seen this template at the start of this task, before implementing or modifying any code
|
|
75582
|
+
|
|
75583
|
+
## Proof of Principle
|
|
75584
|
+
<!-- Demonstrate adherence to:
|
|
75585
|
+
- Organization & Maintainability: a small change in requirements should result in a small change in code in a small number of files and folders.
|
|
75586
|
+
- 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.
|
|
75587
|
+
-->
|
|
75588
|
+
<how this work follows the principles above — localized changes, readable structure, correctness provable from source then tests>
|
|
75589
|
+
|
|
75590
|
+
## Proof of Completion
|
|
75564
75591
|
- \`path/to/file.ts\` — <what changed and why>
|
|
75592
|
+
<evidence the goal was met — list every file you modified>
|
|
75565
75593
|
|
|
75566
|
-
## Verification
|
|
75567
|
-
|
|
75594
|
+
## Code Change Verification
|
|
75595
|
+
${CODE_CHANGE_VERIFICATION_CONFIRMATION}
|
|
75568
75596
|
|
|
75569
75597
|
## Blockers / questions
|
|
75570
75598
|
<anything needing planner decision, or "Not Applicable">
|
|
@@ -75573,18 +75601,18 @@ function getBuilderToPlannerHandoffTemplate() {
|
|
|
75573
75601
|
<specific areas for planner to check, or "Not Applicable">
|
|
75574
75602
|
\`\`\``;
|
|
75575
75603
|
}
|
|
75576
|
-
var init_builder_to_planner = () => {
|
|
75604
|
+
var init_builder_to_planner = __esm(() => {
|
|
75605
|
+
init_code_change_verification();
|
|
75606
|
+
});
|
|
75577
75607
|
|
|
75578
75608
|
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-to-builder.ts
|
|
75579
75609
|
function getPlannerToBuilderHandoffTemplate(nativeIntegration = false) {
|
|
75580
|
-
const sessionManagement = nativeIntegration ?
|
|
75581
|
-
|
|
75582
|
-
**CLI harnesses** (all others): in-session compaction is NOT supported. \`new_session\` requires a hard restart — the daemon stops the agent, cold-starts it, and the agent must rejoin via \`get-next-task\`. \`none\` resumes the prior session (\`wantResume=true\`).`;
|
|
75610
|
+
const sessionManagement = nativeIntegration ? `\`compact\` runs in-session context compaction via the SDK runtime. \`new_session\` starts a completely new session within the same process (not compaction). \`none\` continues the prior session. Tasks continue via injection.` : `\`compact\` is NOT supported — use \`none\` or \`new_session\`. \`new_session\` requires a hard restart (daemon stops agent, cold-starts, agent rejoins via \`get-next-task\`). \`none\` resumes prior session (\`wantResume=true\`).`;
|
|
75583
75611
|
return `${getHandoffRecipientVisibilityCallout("builder")}
|
|
75584
75612
|
|
|
75585
75613
|
**Delegation Brief (Planner → Builder)** — paste into the handoff message and fill in EVERY field. No field is optional: if a section does not apply, write \`Not Applicable\` (do not delete the section).
|
|
75586
75614
|
|
|
75587
|
-
**Division of labor:** You (planner) own architecture and API shape. The builder implements exactly what you specify
|
|
75615
|
+
**Division of labor:** You (planner) own architecture and API shape. The builder implements exactly what you specify and does not redesign or invent alternatives unless blocked.
|
|
75588
75616
|
|
|
75589
75617
|
**Detail bar:** Specify down to **every file** the builder will create or modify (full repo paths). Include code snippets — types, signatures, stubs, or target implementations — until a competent builder **cannot misinterpret** what to write. Vague layers ("update the backend", "fix the component") are not acceptable.
|
|
75590
75618
|
|
|
@@ -75636,23 +75664,23 @@ Cross-file types, interfaces, or patterns that apply beyond a single file. Write
|
|
|
75636
75664
|
|
|
75637
75665
|
## Requirements (acceptance criteria)
|
|
75638
75666
|
- <verifiable outcome the builder can self-check>
|
|
75639
|
-
- Verify: \`pnpm typecheck && pnpm test\`
|
|
75640
75667
|
|
|
75641
75668
|
## What to avoid
|
|
75642
75669
|
- <anti-patterns, recurring mistakes, or scope creep for this slice — be explicit>
|
|
75643
75670
|
- <e.g. "Do not add new abstractions", "Do not refactor unrelated files", "Do not change existing public APIs", or "Not Applicable">
|
|
75644
75671
|
|
|
75645
75672
|
## Skills to activate
|
|
75646
|
-
- <e.g. chatroom skill activate
|
|
75673
|
+
- <e.g. chatroom skill activate code-review --chatroom-id=<id> --role=builder, or "Not Applicable">
|
|
75647
75674
|
|
|
75648
75675
|
## Out of scope
|
|
75649
75676
|
- <files or areas the builder must NOT touch in this slice, or "Not Applicable">
|
|
75650
75677
|
|
|
75651
|
-
## Session
|
|
75652
|
-
Valid values: \`
|
|
75653
|
-
- \`new_session\` — start a fresh agent session (default)
|
|
75678
|
+
## Session Augmentation
|
|
75679
|
+
Valid values: \`none\` | \`compact\` | \`new_session\`
|
|
75654
75680
|
- \`none\` — continue prior session context
|
|
75655
|
-
|
|
75681
|
+
- \`compact\` — run in-session context compaction (native SDK harnesses only)
|
|
75682
|
+
- \`new_session\` — start a completely new session (default)
|
|
75683
|
+
// data:agent.session_augmentation=new_session
|
|
75656
75684
|
|
|
75657
75685
|
${sessionManagement}
|
|
75658
75686
|
|
|
@@ -75684,6 +75712,15 @@ function getPlannerToUserReportTemplate() {
|
|
|
75684
75712
|
- \`path/to/file.ts\` — <what changed and why>
|
|
75685
75713
|
<evidence the goal was met — list every file you (or the builder) modified>
|
|
75686
75714
|
|
|
75715
|
+
## Backlog Tasks Implemented
|
|
75716
|
+
- \`backlog-item-id\` — <backlog item title/summary and how this work addresses it>
|
|
75717
|
+
<List every backlog item this work implemented. Write \`Not Applicable\` if no backlog items were in scope.>
|
|
75718
|
+
|
|
75719
|
+
## Backlog Pending User Review Confirmation
|
|
75720
|
+
- [ ] 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
|
|
75721
|
+
- PR URL(s): <link to PR(s), or \`Not Applicable\` if no PR was raised>
|
|
75722
|
+
- If no backlog items apply, write \`Not Applicable\` for the checkbox and explain in one line
|
|
75723
|
+
|
|
75687
75724
|
## Key Technical Decisions
|
|
75688
75725
|
- <schema design, modules, interfaces, domain entities — what you chose and why, or "Not Applicable">
|
|
75689
75726
|
|
|
@@ -75701,14 +75738,16 @@ flowchart TD
|
|
|
75701
75738
|
A[Component] --> B[Component]
|
|
75702
75739
|
\`\`\`
|
|
75703
75740
|
|
|
75704
|
-
## Verification
|
|
75705
|
-
|
|
75741
|
+
## Code Change Verification
|
|
75742
|
+
${CODE_CHANGE_VERIFICATION_CONFIRMATION}
|
|
75706
75743
|
|
|
75707
75744
|
## Notes / Next steps
|
|
75708
75745
|
<anything the user should know, follow-ups, or open questions, or "Not Applicable">
|
|
75709
75746
|
\`\`\``;
|
|
75710
75747
|
}
|
|
75711
|
-
var init_planner_to_user = () => {
|
|
75748
|
+
var init_planner_to_user = __esm(() => {
|
|
75749
|
+
init_code_change_verification();
|
|
75750
|
+
});
|
|
75712
75751
|
|
|
75713
75752
|
// ../../services/backend/prompts/teams/duo/handoff-templates/index.ts
|
|
75714
75753
|
function getDuoHandoffTemplate(query) {
|
|
@@ -75758,9 +75797,6 @@ flowchart TD
|
|
|
75758
75797
|
A[Component] --> B[Component]
|
|
75759
75798
|
\`\`\`
|
|
75760
75799
|
|
|
75761
|
-
## Verification
|
|
75762
|
-
- \`pnpm typecheck && pnpm test\` — <result>
|
|
75763
|
-
|
|
75764
75800
|
## Notes / Next steps
|
|
75765
75801
|
<anything the user should know, follow-ups, or open questions, or "Not Applicable">
|
|
75766
75802
|
\`\`\``;
|
|
@@ -76013,16 +76049,11 @@ var init_fromContext = __esm(() => {
|
|
|
76013
76049
|
init_planner();
|
|
76014
76050
|
init_solo();
|
|
76015
76051
|
});
|
|
76016
|
-
// ../../services/backend/prompts/cli/sections/handoff-templates-preview.ts
|
|
76017
|
-
var init_handoff_templates_preview = __esm(() => {
|
|
76018
|
-
init_handoff_templates3();
|
|
76019
|
-
});
|
|
76020
76052
|
|
|
76021
76053
|
// ../../services/backend/prompts/native/task-started-content.ts
|
|
76022
76054
|
var init_task_started_content = __esm(() => {
|
|
76023
76055
|
init_new();
|
|
76024
76056
|
});
|
|
76025
|
-
|
|
76026
76057
|
// ../../services/backend/prompts/sections/classification-guide.ts
|
|
76027
76058
|
var init_classification_guide = __esm(() => {
|
|
76028
76059
|
init_cli();
|
|
@@ -76050,6 +76081,8 @@ var init_current_classification = () => {};
|
|
|
76050
76081
|
var init_getting_started = __esm(() => {
|
|
76051
76082
|
init_getting_started_content();
|
|
76052
76083
|
});
|
|
76084
|
+
// ../../services/backend/src/domain/usecase/skills/modules/attachments/index.ts
|
|
76085
|
+
var init_attachments = () => {};
|
|
76053
76086
|
|
|
76054
76087
|
// ../../services/backend/prompts/cli/backlog/command.ts
|
|
76055
76088
|
var init_command3 = __esm(() => {
|
|
@@ -76064,18 +76097,11 @@ var init_backlog = __esm(() => {
|
|
|
76064
76097
|
// ../../services/backend/src/domain/usecase/skills/modules/code-review/index.ts
|
|
76065
76098
|
var init_code_review = () => {};
|
|
76066
76099
|
|
|
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
76100
|
// ../../services/backend/src/domain/usecase/skills/registry.ts
|
|
76074
76101
|
var init_registry3 = __esm(() => {
|
|
76102
|
+
init_attachments();
|
|
76075
76103
|
init_backlog();
|
|
76076
76104
|
init_code_review();
|
|
76077
|
-
init_development_workflow();
|
|
76078
|
-
init_software_engineering();
|
|
76079
76105
|
});
|
|
76080
76106
|
|
|
76081
76107
|
// ../../services/backend/prompts/sections/glossary.ts
|
|
@@ -76102,20 +76128,15 @@ var init_glossary = __esm(() => {
|
|
|
76102
76128
|
linkedSkillId: "backlog"
|
|
76103
76129
|
},
|
|
76104
76130
|
{
|
|
76105
|
-
term: "
|
|
76106
|
-
definition: "
|
|
76107
|
-
linkedSkillId: "
|
|
76131
|
+
term: "attachments",
|
|
76132
|
+
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.",
|
|
76133
|
+
linkedSkillId: "attachments"
|
|
76108
76134
|
},
|
|
76109
76135
|
{
|
|
76110
76136
|
term: "code-review",
|
|
76111
76137
|
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
76138
|
linkedSkillId: "code-review"
|
|
76113
76139
|
},
|
|
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
76140
|
{
|
|
76120
76141
|
term: "structural-decisions",
|
|
76121
76142
|
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 +76415,6 @@ var init_generator = __esm(() => {
|
|
|
76394
76415
|
init_command();
|
|
76395
76416
|
init_reminder();
|
|
76396
76417
|
init_fromContext();
|
|
76397
|
-
init_handoff_templates_preview();
|
|
76398
76418
|
init_classification_guide();
|
|
76399
76419
|
init_commands_reference();
|
|
76400
76420
|
init_current_classification();
|
|
@@ -77401,6 +77421,52 @@ function readFileContent(filePath, optionName) {
|
|
|
77401
77421
|
}
|
|
77402
77422
|
var init_file_content = () => {};
|
|
77403
77423
|
|
|
77424
|
+
// ../../services/backend/prompts/attachments/render-delivery-attachments.ts
|
|
77425
|
+
function renderSnippetAttachment(snippet) {
|
|
77426
|
+
return [
|
|
77427
|
+
` <attachment reference="${snippet.reference}">`,
|
|
77428
|
+
` <snippet file-source="${snippet.fileSource}">`,
|
|
77429
|
+
` <user-selected-content>`,
|
|
77430
|
+
snippet.selectedContent,
|
|
77431
|
+
` </user-selected-content>`,
|
|
77432
|
+
` </snippet>`,
|
|
77433
|
+
` </attachment>`
|
|
77434
|
+
];
|
|
77435
|
+
}
|
|
77436
|
+
function renderBacklogAttachments(items, ctx) {
|
|
77437
|
+
const lines = [];
|
|
77438
|
+
for (const item of items) {
|
|
77439
|
+
lines.push(` <attachment type="backlog-item">`);
|
|
77440
|
+
lines.push(` - [${item.status.toUpperCase()}] ${item.content}`);
|
|
77441
|
+
lines.push(` ID: ${item._id}`);
|
|
77442
|
+
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>`);
|
|
77443
|
+
lines.push(` </attachment>`);
|
|
77444
|
+
}
|
|
77445
|
+
return lines;
|
|
77446
|
+
}
|
|
77447
|
+
function renderSnippetAttachments(snippets) {
|
|
77448
|
+
const lines = [];
|
|
77449
|
+
for (const snippet of snippets) {
|
|
77450
|
+
lines.push(...renderSnippetAttachment(snippet));
|
|
77451
|
+
}
|
|
77452
|
+
return lines;
|
|
77453
|
+
}
|
|
77454
|
+
function renderDeliveryAttachmentsBlock(input, ctx) {
|
|
77455
|
+
const backlogLines = input.attachedBacklogItems?.length ? DELIVERY_ATTACHMENT_RENDERERS.backlog(input.attachedBacklogItems, ctx) : [];
|
|
77456
|
+
const snippetLines = input.attachedSnippets?.length ? DELIVERY_ATTACHMENT_RENDERERS.snippet(input.attachedSnippets, ctx) : [];
|
|
77457
|
+
if (backlogLines.length === 0 && snippetLines.length === 0)
|
|
77458
|
+
return [];
|
|
77459
|
+
return ["", "<attachments>", ...backlogLines, ...snippetLines, "</attachments>"];
|
|
77460
|
+
}
|
|
77461
|
+
var DELIVERY_ATTACHMENT_RENDERERS;
|
|
77462
|
+
var init_render_delivery_attachments = __esm(() => {
|
|
77463
|
+
DELIVERY_ATTACHMENT_RENDERERS = {
|
|
77464
|
+
backlog: renderBacklogAttachments,
|
|
77465
|
+
snippet: renderSnippetAttachments,
|
|
77466
|
+
message: () => []
|
|
77467
|
+
};
|
|
77468
|
+
});
|
|
77469
|
+
|
|
77404
77470
|
// src/commands/task/read/render.ts
|
|
77405
77471
|
function detectBacklogDivergence(contextContent, attachedIds) {
|
|
77406
77472
|
if (!contextContent || attachedIds.length === 0) {
|
|
@@ -77409,19 +77475,10 @@ function detectBacklogDivergence(contextContent, attachedIds) {
|
|
|
77409
77475
|
return attachedIds.filter((id3) => !contextContent.includes(id3));
|
|
77410
77476
|
}
|
|
77411
77477
|
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;
|
|
77478
|
+
return renderDeliveryAttachmentsBlock({
|
|
77479
|
+
attachedBacklogItems: input.attachedBacklogItems,
|
|
77480
|
+
attachedSnippets: input.attachedSnippets
|
|
77481
|
+
}, { chatroomId, role, mode: "task-read" });
|
|
77425
77482
|
}
|
|
77426
77483
|
function renderDivergenceWarnings(input) {
|
|
77427
77484
|
const ctx = input.context;
|
|
@@ -77476,6 +77533,9 @@ ${content}`);
|
|
|
77476
77533
|
return lines.join(`
|
|
77477
77534
|
`);
|
|
77478
77535
|
}
|
|
77536
|
+
var init_render = __esm(() => {
|
|
77537
|
+
init_render_delivery_attachments();
|
|
77538
|
+
});
|
|
77479
77539
|
|
|
77480
77540
|
// src/commands/task/read/index.ts
|
|
77481
77541
|
var exports_read = {};
|
|
@@ -77503,6 +77563,18 @@ function validateTaskId(taskId) {
|
|
|
77503
77563
|
}
|
|
77504
77564
|
return exports_Effect.void;
|
|
77505
77565
|
}
|
|
77566
|
+
function buildTaskPromptFromReadResult(result, chatroomId, role) {
|
|
77567
|
+
return renderTaskPrompt({
|
|
77568
|
+
taskId: result.taskId,
|
|
77569
|
+
status: result.status,
|
|
77570
|
+
content: result.content,
|
|
77571
|
+
chatroomId,
|
|
77572
|
+
role,
|
|
77573
|
+
context: result.context && typeof result.context === "object" ? result.context : undefined,
|
|
77574
|
+
attachedBacklogItems: Array.isArray(result.attachedBacklogItems) ? result.attachedBacklogItems : undefined,
|
|
77575
|
+
attachedSnippets: Array.isArray(result.attachedSnippets) ? result.attachedSnippets : undefined
|
|
77576
|
+
});
|
|
77577
|
+
}
|
|
77506
77578
|
function handleTaskReadError(err) {
|
|
77507
77579
|
return exports_Effect.sync(() => {
|
|
77508
77580
|
const handler = taskReadErrorHandlers[err._tag];
|
|
@@ -77535,19 +77607,12 @@ var taskReadEffect = (chatroomId, options) => exports_Effect.gen(function* () {
|
|
|
77535
77607
|
taskId
|
|
77536
77608
|
}).pipe(exports_Effect.mapError((cause3) => ({ _tag: "MutationFailed", cause: cause3 })));
|
|
77537
77609
|
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
|
-
}));
|
|
77610
|
+
console.log(buildTaskPromptFromReadResult(result, chatroomId, role));
|
|
77547
77611
|
});
|
|
77548
77612
|
}), taskReadErrorHandlers;
|
|
77549
77613
|
var init_read = __esm(() => {
|
|
77550
77614
|
init_esm();
|
|
77615
|
+
init_render();
|
|
77551
77616
|
init_api3();
|
|
77552
77617
|
init_storage();
|
|
77553
77618
|
init_client2();
|
|
@@ -82679,161 +82744,311 @@ var init_commit_detail_sync = __esm(() => {
|
|
|
82679
82744
|
seenShas = new Map;
|
|
82680
82745
|
});
|
|
82681
82746
|
|
|
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
|
-
}
|
|
82747
|
+
// src/infrastructure/harnesses/cursor-sdk/cursor-session.ts
|
|
82748
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
82710
82749
|
|
|
82711
|
-
|
|
82712
|
-
|
|
82713
|
-
|
|
82714
|
-
|
|
82715
|
-
|
|
82716
|
-
|
|
82717
|
-
|
|
82718
|
-
|
|
82750
|
+
class CursorSdkSession {
|
|
82751
|
+
opencodeSessionId;
|
|
82752
|
+
_sessionTitle;
|
|
82753
|
+
get sessionTitle() {
|
|
82754
|
+
return this._sessionTitle;
|
|
82755
|
+
}
|
|
82756
|
+
agent;
|
|
82757
|
+
onClose;
|
|
82758
|
+
listeners = new Set;
|
|
82759
|
+
closed = false;
|
|
82760
|
+
turnCount = 0;
|
|
82761
|
+
constructor(options) {
|
|
82762
|
+
this.agent = options.agent;
|
|
82763
|
+
this.opencodeSessionId = options.opencodeSessionId;
|
|
82764
|
+
this._sessionTitle = options.sessionTitle;
|
|
82765
|
+
this.onClose = options.onClose;
|
|
82766
|
+
}
|
|
82767
|
+
setTitle(title) {
|
|
82768
|
+
this._sessionTitle = title;
|
|
82769
|
+
}
|
|
82770
|
+
async prompt(input) {
|
|
82771
|
+
if (this.closed)
|
|
82772
|
+
throw new Error("Session is closed");
|
|
82773
|
+
const text = input.parts.map((p) => p.text).join(`
|
|
82774
|
+
`);
|
|
82775
|
+
const messageId = randomUUID3();
|
|
82776
|
+
const isFirstTurn = this.turnCount === 0;
|
|
82777
|
+
this.turnCount += 1;
|
|
82778
|
+
const modelId = input.model ? resolveModelFromPrompt(input.model.providerID, input.model.modelID) : undefined;
|
|
82779
|
+
const run3 = await withTimeout(this.agent.send(text, {
|
|
82780
|
+
local: { force: isFirstTurn },
|
|
82781
|
+
idempotencyKey: randomUUID3(),
|
|
82782
|
+
...modelId ? { model: { id: modelId } } : {}
|
|
82783
|
+
}), SEND_TIMEOUT_MS2, "agent.send");
|
|
82784
|
+
for await (const message of run3.stream()) {
|
|
82785
|
+
if (this.closed)
|
|
82786
|
+
break;
|
|
82787
|
+
this.emitFromSdkMessage(message, messageId);
|
|
82788
|
+
}
|
|
82789
|
+
const result = await withTimeout(run3.wait(), RUN_WAIT_TIMEOUT_MS2, "run.wait");
|
|
82790
|
+
if (result.status === "error") {
|
|
82791
|
+
const detail = typeof result.result === "string" && result.result.trim().length > 0 ? result.result.trim() : `run ${result.id} failed`;
|
|
82792
|
+
throw new Error(detail);
|
|
82793
|
+
}
|
|
82794
|
+
this.emit({ type: "session.idle", payload: {}, timestamp: Date.now() });
|
|
82795
|
+
}
|
|
82796
|
+
onEvent(listener) {
|
|
82797
|
+
this.listeners.add(listener);
|
|
82798
|
+
return () => {
|
|
82799
|
+
this.listeners.delete(listener);
|
|
82800
|
+
};
|
|
82801
|
+
}
|
|
82802
|
+
async close() {
|
|
82803
|
+
if (this.closed)
|
|
82719
82804
|
return;
|
|
82805
|
+
this.closed = true;
|
|
82806
|
+
try {
|
|
82807
|
+
this.agent.close();
|
|
82808
|
+
} catch {}
|
|
82809
|
+
this.onClose?.(this.opencodeSessionId);
|
|
82810
|
+
this.listeners.clear();
|
|
82811
|
+
}
|
|
82812
|
+
emit(event) {
|
|
82813
|
+
for (const listener of this.listeners) {
|
|
82814
|
+
listener(event);
|
|
82720
82815
|
}
|
|
82721
|
-
|
|
82722
|
-
|
|
82723
|
-
|
|
82724
|
-
|
|
82725
|
-
|
|
82726
|
-
|
|
82816
|
+
}
|
|
82817
|
+
emitDelta(messageId, delta, partType) {
|
|
82818
|
+
if (!delta)
|
|
82819
|
+
return;
|
|
82820
|
+
this.emit({
|
|
82821
|
+
type: "message.part.delta",
|
|
82822
|
+
payload: { messageID: messageId, delta, partType },
|
|
82823
|
+
timestamp: Date.now()
|
|
82727
82824
|
});
|
|
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
82825
|
}
|
|
82745
|
-
|
|
82746
|
-
|
|
82747
|
-
|
|
82748
|
-
|
|
82749
|
-
|
|
82750
|
-
|
|
82826
|
+
emitFromSdkMessage(message, messageId) {
|
|
82827
|
+
switch (message.type) {
|
|
82828
|
+
case "assistant":
|
|
82829
|
+
for (const block of message.message.content) {
|
|
82830
|
+
if (block.type === "text" && block.text) {
|
|
82831
|
+
this.emitDelta(messageId, block.text, "text");
|
|
82832
|
+
}
|
|
82833
|
+
}
|
|
82834
|
+
break;
|
|
82835
|
+
case "thinking":
|
|
82836
|
+
if (message.text) {
|
|
82837
|
+
this.emitDelta(messageId, message.text, "reasoning");
|
|
82838
|
+
}
|
|
82839
|
+
break;
|
|
82840
|
+
default:
|
|
82841
|
+
break;
|
|
82842
|
+
}
|
|
82751
82843
|
}
|
|
82752
|
-
await dispatchPendingCommand(session2, deps, cmd);
|
|
82753
82844
|
}
|
|
82754
|
-
|
|
82755
|
-
|
|
82756
|
-
|
|
82757
|
-
|
|
82758
|
-
|
|
82759
|
-
|
|
82760
|
-
|
|
82761
|
-
|
|
82762
|
-
|
|
82763
|
-
|
|
82764
|
-
|
|
82765
|
-
|
|
82766
|
-
|
|
82767
|
-
|
|
82768
|
-
|
|
82769
|
-
|
|
82845
|
+
function resolveModelFromPrompt(providerID, modelID) {
|
|
82846
|
+
if (providerID === "cursor")
|
|
82847
|
+
return resolveCursorSdkModel(modelID);
|
|
82848
|
+
return resolveCursorSdkModel(`${providerID}/${modelID}`);
|
|
82849
|
+
}
|
|
82850
|
+
var SEND_TIMEOUT_MS2 = 60000, RUN_WAIT_TIMEOUT_MS2 = 3600000;
|
|
82851
|
+
var init_cursor_session = () => {};
|
|
82852
|
+
|
|
82853
|
+
// src/infrastructure/harnesses/cursor-sdk/cursor-harness.ts
|
|
82854
|
+
async function loadSdk3() {
|
|
82855
|
+
if (_sdkCache3)
|
|
82856
|
+
return _sdkCache3;
|
|
82857
|
+
if (_sdkLoadError3)
|
|
82858
|
+
throw _sdkLoadError3;
|
|
82859
|
+
try {
|
|
82860
|
+
_sdkCache3 = await importBundledCursorSdk();
|
|
82861
|
+
return _sdkCache3;
|
|
82862
|
+
} catch (err) {
|
|
82863
|
+
_sdkLoadError3 = err;
|
|
82864
|
+
throw err;
|
|
82770
82865
|
}
|
|
82771
82866
|
}
|
|
82772
|
-
|
|
82773
|
-
|
|
82774
|
-
|
|
82775
|
-
|
|
82776
|
-
|
|
82777
|
-
|
|
82778
|
-
|
|
82779
|
-
|
|
82780
|
-
|
|
82781
|
-
|
|
82867
|
+
|
|
82868
|
+
class CursorSdkHarness {
|
|
82869
|
+
type = "cursor-sdk";
|
|
82870
|
+
displayName = "Cursor (SDK)";
|
|
82871
|
+
cwd;
|
|
82872
|
+
closed = false;
|
|
82873
|
+
sessions = new Map;
|
|
82874
|
+
constructor(cwd) {
|
|
82875
|
+
this.cwd = cwd;
|
|
82876
|
+
}
|
|
82877
|
+
async models() {
|
|
82878
|
+
const providers = await this.listProviders();
|
|
82879
|
+
const models = [];
|
|
82880
|
+
for (const provider of providers) {
|
|
82881
|
+
for (const model of provider.models) {
|
|
82882
|
+
models.push({
|
|
82883
|
+
id: `${provider.providerID}/${model.modelID}`,
|
|
82884
|
+
name: model.name,
|
|
82885
|
+
provider: provider.name
|
|
82886
|
+
});
|
|
82887
|
+
}
|
|
82782
82888
|
}
|
|
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;
|
|
82889
|
+
return models;
|
|
82796
82890
|
}
|
|
82797
|
-
|
|
82798
|
-
|
|
82799
|
-
}
|
|
82800
|
-
|
|
82801
|
-
|
|
82802
|
-
|
|
82803
|
-
|
|
82804
|
-
|
|
82891
|
+
async listAgents() {
|
|
82892
|
+
return [{ name: "builder", mode: "primary" }];
|
|
82893
|
+
}
|
|
82894
|
+
async listProviders() {
|
|
82895
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82896
|
+
if (!apiKey)
|
|
82897
|
+
return [];
|
|
82898
|
+
const { Cursor } = await loadSdk3();
|
|
82899
|
+
const listed = await withTimeout(Cursor.models.list({ apiKey }), MODELS_LIST_TIMEOUT_MS2, "Cursor.models.list");
|
|
82900
|
+
const modelIds = normalizeCursorSdkListedModels(listed.map((m) => m.id).filter((id3) => id3.length > 0));
|
|
82901
|
+
return [
|
|
82902
|
+
{
|
|
82903
|
+
providerID: "cursor",
|
|
82904
|
+
name: "Cursor",
|
|
82905
|
+
models: modelIds.map((modelID) => ({ modelID, name: modelID }))
|
|
82906
|
+
}
|
|
82907
|
+
];
|
|
82908
|
+
}
|
|
82909
|
+
async newSession(config3) {
|
|
82910
|
+
if (this.closed)
|
|
82911
|
+
throw new Error("Harness is closed");
|
|
82912
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82913
|
+
if (!apiKey)
|
|
82914
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82915
|
+
const modelId = resolveCursorSdkModel(config3.model ?? DEFAULT_MODEL2);
|
|
82916
|
+
const { Agent } = await loadSdk3();
|
|
82917
|
+
const agent = await withTimeout(Agent.create({
|
|
82918
|
+
apiKey,
|
|
82919
|
+
model: { id: modelId },
|
|
82920
|
+
local: { cwd: this.cwd, settingSources: [] }
|
|
82921
|
+
}), AGENT_CREATE_TIMEOUT_MS2, "Agent.create");
|
|
82922
|
+
const session2 = new CursorSdkSession({
|
|
82923
|
+
agent,
|
|
82924
|
+
opencodeSessionId: agent.agentId,
|
|
82925
|
+
sessionTitle: config3.title ?? "",
|
|
82926
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
82805
82927
|
});
|
|
82806
|
-
|
|
82928
|
+
this.sessions.set(agent.agentId, session2);
|
|
82929
|
+
return session2;
|
|
82807
82930
|
}
|
|
82808
|
-
|
|
82809
|
-
|
|
82810
|
-
|
|
82811
|
-
|
|
82812
|
-
|
|
82813
|
-
|
|
82814
|
-
|
|
82931
|
+
async resumeSession(sessionId, _options) {
|
|
82932
|
+
if (this.closed)
|
|
82933
|
+
throw new Error("Harness is closed");
|
|
82934
|
+
const existing = this.sessions.get(sessionId);
|
|
82935
|
+
if (existing)
|
|
82936
|
+
return existing;
|
|
82937
|
+
const apiKey = process.env.CURSOR_API_KEY?.trim();
|
|
82938
|
+
if (!apiKey)
|
|
82939
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82940
|
+
const { Agent } = await loadSdk3();
|
|
82941
|
+
const agent = await withTimeout(Agent.resume(sessionId, {
|
|
82942
|
+
apiKey,
|
|
82943
|
+
model: { id: DEFAULT_MODEL2 },
|
|
82944
|
+
local: { cwd: this.cwd, settingSources: [] }
|
|
82945
|
+
}), AGENT_CREATE_TIMEOUT_MS2, "Agent.resume");
|
|
82946
|
+
const session2 = new CursorSdkSession({
|
|
82947
|
+
agent,
|
|
82948
|
+
opencodeSessionId: agent.agentId,
|
|
82949
|
+
sessionTitle: "",
|
|
82950
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
82815
82951
|
});
|
|
82816
|
-
|
|
82952
|
+
this.sessions.set(agent.agentId, session2);
|
|
82953
|
+
return session2;
|
|
82954
|
+
}
|
|
82955
|
+
async fetchSessionTitle(_opencodeSessionId) {
|
|
82956
|
+
return;
|
|
82957
|
+
}
|
|
82958
|
+
isAlive() {
|
|
82959
|
+
return !this.closed;
|
|
82960
|
+
}
|
|
82961
|
+
async close() {
|
|
82962
|
+
if (this.closed)
|
|
82963
|
+
return;
|
|
82964
|
+
this.closed = true;
|
|
82965
|
+
const closing = [...this.sessions.values()].map((s) => s.close().catch(() => {}));
|
|
82966
|
+
await Promise.all(closing);
|
|
82967
|
+
this.sessions.clear();
|
|
82817
82968
|
}
|
|
82818
|
-
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
82819
|
-
sessionId: session2.sessionId,
|
|
82820
|
-
commandId: cmd._id,
|
|
82821
|
-
status: "done"
|
|
82822
|
-
});
|
|
82823
82969
|
}
|
|
82824
|
-
|
|
82825
|
-
|
|
82826
|
-
|
|
82827
|
-
|
|
82828
|
-
|
|
82829
|
-
|
|
82830
|
-
|
|
82970
|
+
var DEFAULT_MODEL2 = "composer-2.5", MODELS_LIST_TIMEOUT_MS2 = 60000, AGENT_CREATE_TIMEOUT_MS2 = 60000, _sdkCache3, _sdkLoadError3, startCursorSdkHarness = async (config3) => {
|
|
82971
|
+
try {
|
|
82972
|
+
await loadSdk3();
|
|
82973
|
+
} catch (err) {
|
|
82974
|
+
throw new Error(`cursor-sdk unavailable: ${formatCursorSdkLoadError(err)}`);
|
|
82975
|
+
}
|
|
82976
|
+
if (!process.env.CURSOR_API_KEY?.trim()) {
|
|
82977
|
+
throw new Error("CURSOR_API_KEY is not set");
|
|
82978
|
+
}
|
|
82979
|
+
return new CursorSdkHarness(config3.workingDir);
|
|
82980
|
+
};
|
|
82981
|
+
var init_cursor_harness = __esm(() => {
|
|
82982
|
+
init_cursor_session();
|
|
82983
|
+
init_cursor_sdk_package();
|
|
82984
|
+
});
|
|
82985
|
+
|
|
82986
|
+
// src/infrastructure/harnesses/shared-chunk-extractor.ts
|
|
82987
|
+
function createStandardSdkChunkExtractor() {
|
|
82988
|
+
return function extract(event) {
|
|
82989
|
+
if (event.type !== "message.part.delta")
|
|
82990
|
+
return null;
|
|
82991
|
+
const payload = event.payload;
|
|
82992
|
+
const delta = payload?.delta;
|
|
82993
|
+
if (!delta || delta.length === 0)
|
|
82994
|
+
return null;
|
|
82995
|
+
const messageId = payload?.messageID;
|
|
82996
|
+
if (!messageId)
|
|
82997
|
+
return null;
|
|
82998
|
+
return {
|
|
82999
|
+
content: delta,
|
|
83000
|
+
messageId,
|
|
83001
|
+
partType: payload?.partType ?? "text"
|
|
83002
|
+
};
|
|
83003
|
+
};
|
|
82831
83004
|
}
|
|
82832
|
-
|
|
82833
|
-
|
|
82834
|
-
|
|
83005
|
+
|
|
83006
|
+
// src/infrastructure/harnesses/cursor-sdk/event-extractor.ts
|
|
83007
|
+
function createCursorSdkChunkExtractor() {
|
|
83008
|
+
return createStandardSdkChunkExtractor();
|
|
83009
|
+
}
|
|
83010
|
+
var init_event_extractor = () => {};
|
|
83011
|
+
|
|
83012
|
+
// src/infrastructure/harnesses/cursor-sdk/index.ts
|
|
83013
|
+
var init_cursor_sdk2 = __esm(() => {
|
|
83014
|
+
init_cursor_harness();
|
|
83015
|
+
init_cursor_session();
|
|
83016
|
+
init_event_extractor();
|
|
82835
83017
|
});
|
|
82836
83018
|
|
|
83019
|
+
// src/infrastructure/harnesses/opencode-sdk/event-extractor.ts
|
|
83020
|
+
function createOpencodeSdkChunkExtractor() {
|
|
83021
|
+
const partMap = new Map;
|
|
83022
|
+
return function extract(event) {
|
|
83023
|
+
if (event.type === "message.part.updated") {
|
|
83024
|
+
const payload = event.payload;
|
|
83025
|
+
const part = payload?.part;
|
|
83026
|
+
if (part?.id && part?.messageID) {
|
|
83027
|
+
const partType = part.type === "reasoning" ? "reasoning" : "text";
|
|
83028
|
+
partMap.set(part.id, { messageId: part.messageID, partType });
|
|
83029
|
+
const delta = payload?.delta;
|
|
83030
|
+
if (delta && delta.length > 0) {
|
|
83031
|
+
return { content: delta, messageId: part.messageID, partType };
|
|
83032
|
+
}
|
|
83033
|
+
}
|
|
83034
|
+
return null;
|
|
83035
|
+
}
|
|
83036
|
+
if (event.type === "message.part.delta") {
|
|
83037
|
+
const payload = event.payload;
|
|
83038
|
+
const delta = payload?.delta;
|
|
83039
|
+
if (!delta || delta.length === 0)
|
|
83040
|
+
return null;
|
|
83041
|
+
const messageId = payload?.messageID;
|
|
83042
|
+
if (!messageId)
|
|
83043
|
+
return null;
|
|
83044
|
+
const partID = payload?.partID;
|
|
83045
|
+
const partType = (partID ? partMap.get(partID)?.partType : undefined) ?? "text";
|
|
83046
|
+
return { content: delta, messageId, partType };
|
|
83047
|
+
}
|
|
83048
|
+
return null;
|
|
83049
|
+
};
|
|
83050
|
+
}
|
|
83051
|
+
|
|
82837
83052
|
// src/infrastructure/harnesses/opencode-sdk/sse-event-buffer.ts
|
|
82838
83053
|
var SseEventBuffer;
|
|
82839
83054
|
var init_sse_event_buffer = __esm(() => {
|
|
@@ -83331,44 +83546,560 @@ var init_opencode_harness = __esm(() => {
|
|
|
83331
83546
|
init_spawn_env();
|
|
83332
83547
|
});
|
|
83333
83548
|
|
|
83334
|
-
// src/infrastructure/harnesses/
|
|
83335
|
-
|
|
83336
|
-
|
|
83337
|
-
|
|
83338
|
-
|
|
83339
|
-
|
|
83340
|
-
|
|
83341
|
-
|
|
83342
|
-
|
|
83343
|
-
|
|
83344
|
-
|
|
83345
|
-
|
|
83346
|
-
|
|
83347
|
-
|
|
83348
|
-
|
|
83349
|
-
|
|
83549
|
+
// src/infrastructure/harnesses/pi-sdk/pi-session.ts
|
|
83550
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
83551
|
+
|
|
83552
|
+
class PiSdkSession {
|
|
83553
|
+
opencodeSessionId;
|
|
83554
|
+
_sessionTitle;
|
|
83555
|
+
get sessionTitle() {
|
|
83556
|
+
return this._sessionTitle;
|
|
83557
|
+
}
|
|
83558
|
+
session;
|
|
83559
|
+
onClose;
|
|
83560
|
+
listeners = new Set;
|
|
83561
|
+
closed = false;
|
|
83562
|
+
unsubscribe;
|
|
83563
|
+
constructor(options) {
|
|
83564
|
+
this.session = options.session;
|
|
83565
|
+
this.opencodeSessionId = options.opencodeSessionId;
|
|
83566
|
+
this._sessionTitle = options.sessionTitle;
|
|
83567
|
+
this.onClose = options.onClose;
|
|
83568
|
+
}
|
|
83569
|
+
setTitle(title) {
|
|
83570
|
+
this._sessionTitle = title;
|
|
83571
|
+
}
|
|
83572
|
+
async prompt(input) {
|
|
83573
|
+
if (this.closed)
|
|
83574
|
+
throw new Error("Session is closed");
|
|
83575
|
+
const text = input.parts.map((p) => p.text).join(`
|
|
83576
|
+
`);
|
|
83577
|
+
const messageId = randomUUID4();
|
|
83578
|
+
const onSessionEvent = (event) => {
|
|
83579
|
+
if (this.closed)
|
|
83580
|
+
return;
|
|
83581
|
+
this.emitFromPiEvent(event, messageId);
|
|
83582
|
+
};
|
|
83583
|
+
this.unsubscribe?.();
|
|
83584
|
+
this.unsubscribe = this.session.subscribe(onSessionEvent);
|
|
83585
|
+
await withTimeout(this.session.prompt(text), PROMPT_TIMEOUT_MS2, "session.prompt");
|
|
83586
|
+
this.emit({ type: "session.idle", payload: {}, timestamp: Date.now() });
|
|
83587
|
+
}
|
|
83588
|
+
onEvent(listener) {
|
|
83589
|
+
this.listeners.add(listener);
|
|
83590
|
+
return () => {
|
|
83591
|
+
this.listeners.delete(listener);
|
|
83592
|
+
};
|
|
83593
|
+
}
|
|
83594
|
+
async close() {
|
|
83595
|
+
if (this.closed)
|
|
83596
|
+
return;
|
|
83597
|
+
this.closed = true;
|
|
83598
|
+
this.unsubscribe?.();
|
|
83599
|
+
try {
|
|
83600
|
+
await this.session.abort();
|
|
83601
|
+
} catch {}
|
|
83602
|
+
try {
|
|
83603
|
+
this.session.dispose();
|
|
83604
|
+
} catch {}
|
|
83605
|
+
this.onClose?.(this.opencodeSessionId);
|
|
83606
|
+
this.listeners.clear();
|
|
83607
|
+
}
|
|
83608
|
+
emit(event) {
|
|
83609
|
+
for (const listener of this.listeners) {
|
|
83610
|
+
listener(event);
|
|
83350
83611
|
}
|
|
83351
|
-
|
|
83352
|
-
|
|
83353
|
-
|
|
83354
|
-
|
|
83355
|
-
|
|
83356
|
-
|
|
83357
|
-
|
|
83358
|
-
|
|
83359
|
-
|
|
83360
|
-
|
|
83361
|
-
|
|
83612
|
+
}
|
|
83613
|
+
emitDelta(messageId, delta, partType) {
|
|
83614
|
+
if (!delta)
|
|
83615
|
+
return;
|
|
83616
|
+
this.emit({
|
|
83617
|
+
type: "message.part.delta",
|
|
83618
|
+
payload: { messageID: messageId, delta, partType },
|
|
83619
|
+
timestamp: Date.now()
|
|
83620
|
+
});
|
|
83621
|
+
}
|
|
83622
|
+
emitFromPiEvent(event, messageId) {
|
|
83623
|
+
if (event.type !== "message_update")
|
|
83624
|
+
return;
|
|
83625
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
83626
|
+
if (assistantEvent.type === "text_delta") {
|
|
83627
|
+
this.emitDelta(messageId, assistantEvent.delta, "text");
|
|
83628
|
+
} else if (assistantEvent.type === "thinking_delta") {
|
|
83629
|
+
this.emitDelta(messageId, assistantEvent.delta, "reasoning");
|
|
83362
83630
|
}
|
|
83363
|
-
|
|
83364
|
-
|
|
83631
|
+
}
|
|
83632
|
+
}
|
|
83633
|
+
var PROMPT_TIMEOUT_MS2 = 3600000;
|
|
83634
|
+
var init_pi_session = () => {};
|
|
83635
|
+
|
|
83636
|
+
// src/infrastructure/harnesses/pi-sdk/pi-harness.ts
|
|
83637
|
+
async function loadSdk4() {
|
|
83638
|
+
if (_sdkCache4)
|
|
83639
|
+
return _sdkCache4;
|
|
83640
|
+
if (_sdkLoadError4)
|
|
83641
|
+
throw _sdkLoadError4;
|
|
83642
|
+
try {
|
|
83643
|
+
_sdkCache4 = await importBundledPiSdk();
|
|
83644
|
+
return _sdkCache4;
|
|
83645
|
+
} catch (err) {
|
|
83646
|
+
_sdkLoadError4 = err;
|
|
83647
|
+
throw err;
|
|
83648
|
+
}
|
|
83649
|
+
}
|
|
83650
|
+
function resolveModel2(modelRegistry, model) {
|
|
83651
|
+
if (model) {
|
|
83652
|
+
const slash = model.indexOf("/");
|
|
83653
|
+
if (slash === -1) {
|
|
83654
|
+
return modelRegistry.getAll().find((entry) => entry.id === model);
|
|
83655
|
+
}
|
|
83656
|
+
const provider = model.slice(0, slash);
|
|
83657
|
+
const modelId = model.slice(slash + 1);
|
|
83658
|
+
return modelRegistry.find(provider, modelId);
|
|
83659
|
+
}
|
|
83660
|
+
return modelRegistry.getAvailable()[0];
|
|
83661
|
+
}
|
|
83662
|
+
|
|
83663
|
+
class PiSdkHarness {
|
|
83664
|
+
type = "pi-sdk";
|
|
83665
|
+
displayName = "Pi (SDK)";
|
|
83666
|
+
cwd;
|
|
83667
|
+
closed = false;
|
|
83668
|
+
sessions = new Map;
|
|
83669
|
+
modelRegistry;
|
|
83670
|
+
authStorage;
|
|
83671
|
+
constructor(cwd, modelRegistry, authStorage) {
|
|
83672
|
+
this.cwd = cwd;
|
|
83673
|
+
this.modelRegistry = modelRegistry;
|
|
83674
|
+
this.authStorage = authStorage;
|
|
83675
|
+
}
|
|
83676
|
+
async models() {
|
|
83677
|
+
return this.modelRegistry.getAvailable().map((entry) => ({
|
|
83678
|
+
id: `${entry.provider}/${entry.id}`,
|
|
83679
|
+
name: entry.id,
|
|
83680
|
+
provider: entry.provider
|
|
83681
|
+
}));
|
|
83682
|
+
}
|
|
83683
|
+
async listAgents() {
|
|
83684
|
+
return [{ name: "builder", mode: "primary" }];
|
|
83685
|
+
}
|
|
83686
|
+
async listProviders() {
|
|
83687
|
+
const byProvider = new Map;
|
|
83688
|
+
for (const entry of this.modelRegistry.getAvailable()) {
|
|
83689
|
+
const models = byProvider.get(entry.provider) ?? [];
|
|
83690
|
+
models.push({ modelID: entry.id, name: entry.id });
|
|
83691
|
+
byProvider.set(entry.provider, models);
|
|
83692
|
+
}
|
|
83693
|
+
return [...byProvider.entries()].map(([providerID, models]) => ({
|
|
83694
|
+
providerID,
|
|
83695
|
+
name: providerID,
|
|
83696
|
+
models
|
|
83697
|
+
}));
|
|
83698
|
+
}
|
|
83699
|
+
async newSession(config3) {
|
|
83700
|
+
if (this.closed)
|
|
83701
|
+
throw new Error("Harness is closed");
|
|
83702
|
+
const session2 = await this.createAgentSession(config3.systemPrompt ?? "", config3.model);
|
|
83703
|
+
const piSession = new PiSdkSession({
|
|
83704
|
+
session: session2,
|
|
83705
|
+
opencodeSessionId: session2.sessionId,
|
|
83706
|
+
sessionTitle: config3.title ?? "",
|
|
83707
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
83708
|
+
});
|
|
83709
|
+
this.sessions.set(session2.sessionId, piSession);
|
|
83710
|
+
return piSession;
|
|
83711
|
+
}
|
|
83712
|
+
async resumeSession(sessionId, _options) {
|
|
83713
|
+
if (this.closed)
|
|
83714
|
+
throw new Error("Harness is closed");
|
|
83715
|
+
const existing = this.sessions.get(sessionId);
|
|
83716
|
+
if (existing)
|
|
83717
|
+
return existing;
|
|
83718
|
+
const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk4();
|
|
83719
|
+
const sessions = await SessionManager.list(this.cwd, getPiSessionDir(this.cwd));
|
|
83720
|
+
const match17 = sessions.find((s) => s.id === sessionId);
|
|
83721
|
+
if (!match17) {
|
|
83722
|
+
throw new Error(`Session ${sessionId} not found on the harness`);
|
|
83723
|
+
}
|
|
83724
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
83725
|
+
cwd: this.cwd,
|
|
83726
|
+
agentDir: getAgentDir(),
|
|
83727
|
+
systemPromptOverride: () => ""
|
|
83728
|
+
});
|
|
83729
|
+
await resourceLoader.reload();
|
|
83730
|
+
const resolvedModel = resolveModel2(this.modelRegistry);
|
|
83731
|
+
if (!resolvedModel) {
|
|
83732
|
+
throw new Error("No Pi model available");
|
|
83733
|
+
}
|
|
83734
|
+
const { session: session2 } = await withTimeout(createAgentSession({
|
|
83735
|
+
cwd: this.cwd,
|
|
83736
|
+
model: resolvedModel,
|
|
83737
|
+
sessionManager: SessionManager.open(match17.path, getPiSessionDir(this.cwd)),
|
|
83738
|
+
authStorage: this.authStorage,
|
|
83739
|
+
modelRegistry: this.modelRegistry,
|
|
83740
|
+
resourceLoader
|
|
83741
|
+
}), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
|
|
83742
|
+
const piSession = new PiSdkSession({
|
|
83743
|
+
session: session2,
|
|
83744
|
+
opencodeSessionId: session2.sessionId,
|
|
83745
|
+
sessionTitle: match17.name ?? "",
|
|
83746
|
+
onClose: (id3) => this.sessions.delete(id3)
|
|
83747
|
+
});
|
|
83748
|
+
this.sessions.set(session2.sessionId, piSession);
|
|
83749
|
+
return piSession;
|
|
83750
|
+
}
|
|
83751
|
+
async fetchSessionTitle(_opencodeSessionId) {
|
|
83752
|
+
return;
|
|
83753
|
+
}
|
|
83754
|
+
isAlive() {
|
|
83755
|
+
return !this.closed;
|
|
83756
|
+
}
|
|
83757
|
+
async close() {
|
|
83758
|
+
if (this.closed)
|
|
83759
|
+
return;
|
|
83760
|
+
this.closed = true;
|
|
83761
|
+
const closing = [...this.sessions.values()].map((s) => s.close().catch(() => {}));
|
|
83762
|
+
await Promise.all(closing);
|
|
83763
|
+
this.sessions.clear();
|
|
83764
|
+
}
|
|
83765
|
+
async createAgentSession(systemPrompt, model) {
|
|
83766
|
+
const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager } = await loadSdk4();
|
|
83767
|
+
const resolvedModel = resolveModel2(this.modelRegistry, model ?? DEFAULT_MODEL3);
|
|
83768
|
+
if (!resolvedModel) {
|
|
83769
|
+
throw new Error("No Pi model available — configure provider credentials in ~/.pi/agent/auth.json");
|
|
83770
|
+
}
|
|
83771
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
83772
|
+
cwd: this.cwd,
|
|
83773
|
+
agentDir: getAgentDir(),
|
|
83774
|
+
systemPromptOverride: () => systemPrompt
|
|
83775
|
+
});
|
|
83776
|
+
await resourceLoader.reload();
|
|
83777
|
+
const { session: session2 } = await withTimeout(createAgentSession({
|
|
83778
|
+
cwd: this.cwd,
|
|
83779
|
+
model: resolvedModel,
|
|
83780
|
+
sessionManager: SessionManager.create(getPiSessionDir(this.cwd)),
|
|
83781
|
+
authStorage: this.authStorage,
|
|
83782
|
+
modelRegistry: this.modelRegistry,
|
|
83783
|
+
resourceLoader
|
|
83784
|
+
}), SESSION_CREATE_TIMEOUT_MS3, "createAgentSession");
|
|
83785
|
+
return session2;
|
|
83786
|
+
}
|
|
83365
83787
|
}
|
|
83788
|
+
var SESSION_CREATE_TIMEOUT_MS3 = 60000, DEFAULT_MODEL3 = "opencode/big-pickle", _sdkCache4, _sdkLoadError4, startPiSdkHarness = async (config3) => {
|
|
83789
|
+
try {
|
|
83790
|
+
const { AuthStorage, ModelRegistry } = await loadSdk4();
|
|
83791
|
+
const authStorage = AuthStorage.create();
|
|
83792
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
83793
|
+
if (modelRegistry.getAvailable().length === 0) {
|
|
83794
|
+
throw new Error("No Pi models available");
|
|
83795
|
+
}
|
|
83796
|
+
return new PiSdkHarness(config3.workingDir, modelRegistry, authStorage);
|
|
83797
|
+
} catch (err) {
|
|
83798
|
+
throw new Error(`pi-sdk unavailable: ${formatPiSdkLoadError(err)}`);
|
|
83799
|
+
}
|
|
83800
|
+
};
|
|
83801
|
+
var init_pi_harness = __esm(() => {
|
|
83802
|
+
init_pi_session();
|
|
83803
|
+
init_pi_agent_service();
|
|
83804
|
+
init_pi_sdk_package();
|
|
83805
|
+
});
|
|
83806
|
+
|
|
83807
|
+
// src/infrastructure/harnesses/pi-sdk/event-extractor.ts
|
|
83808
|
+
function createPiSdkChunkExtractor() {
|
|
83809
|
+
return createStandardSdkChunkExtractor();
|
|
83810
|
+
}
|
|
83811
|
+
var init_event_extractor2 = () => {};
|
|
83812
|
+
|
|
83813
|
+
// src/infrastructure/harnesses/pi-sdk/index.ts
|
|
83814
|
+
var init_pi_sdk2 = __esm(() => {
|
|
83815
|
+
init_pi_harness();
|
|
83816
|
+
init_pi_session();
|
|
83817
|
+
init_event_extractor2();
|
|
83818
|
+
});
|
|
83366
83819
|
|
|
83367
|
-
// src/infrastructure/harnesses/
|
|
83368
|
-
|
|
83820
|
+
// src/infrastructure/harnesses/registry.ts
|
|
83821
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
83822
|
+
async function startBoundHarness(config3) {
|
|
83823
|
+
switch (config3.harnessName) {
|
|
83824
|
+
case "opencode-sdk":
|
|
83825
|
+
return startOpencodeSdkHarness(config3);
|
|
83826
|
+
case "cursor-sdk":
|
|
83827
|
+
return startCursorSdkHarness(config3);
|
|
83828
|
+
case "pi-sdk":
|
|
83829
|
+
return startPiSdkHarness(config3);
|
|
83830
|
+
default: {
|
|
83831
|
+
const _exhaustive = config3.harnessName;
|
|
83832
|
+
throw new Error(`Unsupported direct harness: ${String(_exhaustive)}`);
|
|
83833
|
+
}
|
|
83834
|
+
}
|
|
83835
|
+
}
|
|
83836
|
+
function createChunkExtractor(harnessName) {
|
|
83837
|
+
switch (harnessName) {
|
|
83838
|
+
case "opencode-sdk":
|
|
83839
|
+
return createOpencodeSdkChunkExtractor();
|
|
83840
|
+
case "cursor-sdk":
|
|
83841
|
+
return createCursorSdkChunkExtractor();
|
|
83842
|
+
case "pi-sdk":
|
|
83843
|
+
return createPiSdkChunkExtractor();
|
|
83844
|
+
default:
|
|
83845
|
+
return createStandardSdkChunkExtractor();
|
|
83846
|
+
}
|
|
83847
|
+
}
|
|
83848
|
+
function opencodeOnPath() {
|
|
83849
|
+
try {
|
|
83850
|
+
execSync2("opencode --version", { stdio: "pipe" });
|
|
83851
|
+
return true;
|
|
83852
|
+
} catch {
|
|
83853
|
+
return false;
|
|
83854
|
+
}
|
|
83855
|
+
}
|
|
83856
|
+
async function isCursorSdkInstalled() {
|
|
83857
|
+
if (!process.env.CURSOR_API_KEY?.trim())
|
|
83858
|
+
return false;
|
|
83859
|
+
try {
|
|
83860
|
+
const { importBundledCursorSdk: importBundledCursorSdk2 } = await Promise.resolve().then(() => (init_cursor_sdk_package(), exports_cursor_sdk_package));
|
|
83861
|
+
await importBundledCursorSdk2();
|
|
83862
|
+
return true;
|
|
83863
|
+
} catch {
|
|
83864
|
+
return false;
|
|
83865
|
+
}
|
|
83866
|
+
}
|
|
83867
|
+
async function isPiSdkInstalled() {
|
|
83868
|
+
try {
|
|
83869
|
+
const { importBundledPiSdk: importBundledPiSdk2 } = await Promise.resolve().then(() => (init_pi_sdk_package(), exports_pi_sdk_package));
|
|
83870
|
+
const { ModelRegistry, AuthStorage } = await importBundledPiSdk2();
|
|
83871
|
+
const authStorage = AuthStorage.create();
|
|
83872
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
83873
|
+
return modelRegistry.getAvailable().length > 0;
|
|
83874
|
+
} catch {
|
|
83875
|
+
return false;
|
|
83876
|
+
}
|
|
83877
|
+
}
|
|
83878
|
+
async function listInstalledNativeDirectHarnesses() {
|
|
83879
|
+
const installed2 = [];
|
|
83880
|
+
if (opencodeOnPath())
|
|
83881
|
+
installed2.push("opencode-sdk");
|
|
83882
|
+
if (await isCursorSdkInstalled())
|
|
83883
|
+
installed2.push("cursor-sdk");
|
|
83884
|
+
if (await isPiSdkInstalled())
|
|
83885
|
+
installed2.push("pi-sdk");
|
|
83886
|
+
return installed2;
|
|
83887
|
+
}
|
|
83888
|
+
var init_registry4 = __esm(() => {
|
|
83889
|
+
init_cursor_sdk2();
|
|
83369
83890
|
init_opencode_harness();
|
|
83891
|
+
init_pi_sdk2();
|
|
83892
|
+
});
|
|
83893
|
+
|
|
83894
|
+
// src/commands/machine/daemon-start/direct-harness/command-subscriber.ts
|
|
83895
|
+
function startCommandSubscriber(session2, wsClient2, deps) {
|
|
83896
|
+
const processed = new Set;
|
|
83897
|
+
let processing = false;
|
|
83898
|
+
let pendingDrain = false;
|
|
83899
|
+
const runDrain = () => {
|
|
83900
|
+
if (processing) {
|
|
83901
|
+
pendingDrain = true;
|
|
83902
|
+
return;
|
|
83903
|
+
}
|
|
83904
|
+
processing = true;
|
|
83905
|
+
pendingDrain = false;
|
|
83906
|
+
drain(session2, deps, processed).finally(() => {
|
|
83907
|
+
processing = false;
|
|
83908
|
+
if (pendingDrain)
|
|
83909
|
+
runDrain();
|
|
83910
|
+
});
|
|
83911
|
+
};
|
|
83912
|
+
const unsub = wsClient2.onUpdate(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId }, () => runDrain(), (err) => {
|
|
83913
|
+
console.warn("[direct-harness] Command subscription error:", err instanceof Error ? err.message : String(err));
|
|
83914
|
+
});
|
|
83915
|
+
return { stop: unsub };
|
|
83916
|
+
}
|
|
83917
|
+
async function dispatchPendingCommand(session2, deps, cmd) {
|
|
83918
|
+
switch (cmd.type) {
|
|
83919
|
+
case "refreshCapabilities":
|
|
83920
|
+
await handleRefreshCapabilities(session2, deps, cmd);
|
|
83921
|
+
break;
|
|
83922
|
+
case "refreshSessionTitle":
|
|
83923
|
+
await handleRefreshSessionTitle(session2, deps, cmd);
|
|
83924
|
+
break;
|
|
83925
|
+
case "closeSession":
|
|
83926
|
+
await handleCloseSession(session2, deps, cmd);
|
|
83927
|
+
break;
|
|
83928
|
+
default:
|
|
83929
|
+
await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
|
|
83930
|
+
}
|
|
83931
|
+
}
|
|
83932
|
+
async function processPendingCommand(session2, deps, cmd, now) {
|
|
83933
|
+
if (now - cmd.createdAt > DIRECT_HARNESS_COMMAND_TTL_MS) {
|
|
83934
|
+
console.log(`[direct-harness] Discarding stale command ${cmd._id} (type=${cmd.type}, age=${now - cmd.createdAt}ms)`);
|
|
83935
|
+
await markFailed(session2, cmd._id, "Command expired (TTL)");
|
|
83936
|
+
return;
|
|
83937
|
+
}
|
|
83938
|
+
await dispatchPendingCommand(session2, deps, cmd);
|
|
83939
|
+
}
|
|
83940
|
+
async function drain(session2, deps, processed) {
|
|
83941
|
+
const pending3 = await session2.backend.query(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId });
|
|
83942
|
+
if (!pending3 || pending3.length === 0)
|
|
83943
|
+
return;
|
|
83944
|
+
const now = Date.now();
|
|
83945
|
+
for (const cmd of pending3) {
|
|
83946
|
+
if (processed.has(cmd._id))
|
|
83947
|
+
continue;
|
|
83948
|
+
processed.add(cmd._id);
|
|
83949
|
+
try {
|
|
83950
|
+
await processPendingCommand(session2, deps, cmd, now);
|
|
83951
|
+
} catch (err) {
|
|
83952
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83953
|
+
console.warn(`[direct-harness] Command ${cmd._id} failed: ${message}`);
|
|
83954
|
+
await markFailed(session2, cmd._id, message).catch(() => {});
|
|
83955
|
+
}
|
|
83956
|
+
}
|
|
83957
|
+
}
|
|
83958
|
+
async function handleRefreshCapabilities(session2, deps, cmd) {
|
|
83959
|
+
const payload = cmd.refreshCapabilities;
|
|
83960
|
+
console.log(`[direct-harness] Processing refreshCapabilities for workspace=${cmd.workspaceId}` + (payload ? ` (initiatedBy=${payload.initiatedBy})` : ""));
|
|
83961
|
+
const installed2 = await listInstalledNativeDirectHarnesses();
|
|
83962
|
+
const harnessEntries = [];
|
|
83963
|
+
let cwd = "";
|
|
83964
|
+
for (const harnessName of installed2) {
|
|
83965
|
+
const harness = await deps.lifecycleManager.getOrStart(cmd.workspaceId, harnessName);
|
|
83966
|
+
cwd = harness.cwd;
|
|
83967
|
+
const [agents, providers] = await Promise.all([harness.listAgents(), harness.listProviders()]);
|
|
83968
|
+
harnessEntries.push({
|
|
83969
|
+
name: harness.type,
|
|
83970
|
+
displayName: harness.displayName,
|
|
83971
|
+
agents: [...agents],
|
|
83972
|
+
providers: [...providers]
|
|
83973
|
+
});
|
|
83974
|
+
}
|
|
83975
|
+
const existing = await session2.backend.query(api.daemon.directHarness.capabilities.getForMachine, { sessionId: session2.sessionId, machineId: session2.machineId });
|
|
83976
|
+
const updatedWorkspace = {
|
|
83977
|
+
workspaceId: cmd.workspaceId,
|
|
83978
|
+
cwd,
|
|
83979
|
+
name: cwd,
|
|
83980
|
+
harnesses: harnessEntries
|
|
83981
|
+
};
|
|
83982
|
+
const merged = [
|
|
83983
|
+
...(existing?.workspaces ?? []).filter((ws) => ws.workspaceId !== cmd.workspaceId),
|
|
83984
|
+
updatedWorkspace
|
|
83985
|
+
];
|
|
83986
|
+
await deps.publisher.publish({
|
|
83987
|
+
machineId: session2.machineId,
|
|
83988
|
+
lastSeenAt: Date.now(),
|
|
83989
|
+
workspaces: merged
|
|
83990
|
+
});
|
|
83991
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
83992
|
+
sessionId: session2.sessionId,
|
|
83993
|
+
commandId: cmd._id,
|
|
83994
|
+
status: "done"
|
|
83995
|
+
});
|
|
83996
|
+
console.log(`[direct-harness] Capabilities refreshed for workspace=${cmd.workspaceId}`);
|
|
83997
|
+
}
|
|
83998
|
+
async function handleRefreshSessionTitle(session2, deps, cmd) {
|
|
83999
|
+
const { harnessSessionId } = cmd.refreshSessionTitle ?? {};
|
|
84000
|
+
if (!harnessSessionId) {
|
|
84001
|
+
await markFailed(session2, cmd._id, "refreshSessionTitle: missing harnessSessionId");
|
|
84002
|
+
return;
|
|
84003
|
+
}
|
|
84004
|
+
const sessionRow = await session2.backend.query(api.daemon.directHarness.sessions.getSession, {
|
|
84005
|
+
harnessSessionId
|
|
84006
|
+
});
|
|
84007
|
+
if (!sessionRow?.opencodeSessionId) {
|
|
84008
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84009
|
+
sessionId: session2.sessionId,
|
|
84010
|
+
commandId: cmd._id,
|
|
84011
|
+
status: "done"
|
|
84012
|
+
});
|
|
84013
|
+
return;
|
|
84014
|
+
}
|
|
84015
|
+
const harnessName = sessionRow.harnessName ?? "opencode-sdk";
|
|
84016
|
+
const harness = await deps.lifecycleManager.getOrStart(sessionRow.workspaceId ?? cmd.workspaceId, harnessName);
|
|
84017
|
+
const newTitle = await harness.fetchSessionTitle(sessionRow.opencodeSessionId);
|
|
84018
|
+
if (newTitle) {
|
|
84019
|
+
await session2.backend.mutation(api.daemon.directHarness.sessions.updateSessionTitle, {
|
|
84020
|
+
sessionId: session2.sessionId,
|
|
84021
|
+
harnessSessionId,
|
|
84022
|
+
sessionTitle: newTitle
|
|
84023
|
+
});
|
|
84024
|
+
console.log(`[direct-harness] Refreshed title for session ${harnessSessionId}: "${newTitle}"`);
|
|
84025
|
+
}
|
|
84026
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84027
|
+
sessionId: session2.sessionId,
|
|
84028
|
+
commandId: cmd._id,
|
|
84029
|
+
status: "done"
|
|
84030
|
+
});
|
|
84031
|
+
}
|
|
84032
|
+
async function closeDisconnectedHarnessSession(deps, sessionRow, harnessSessionId) {
|
|
84033
|
+
if (!sessionRow.opencodeSessionId) {
|
|
84034
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84035
|
+
return;
|
|
84036
|
+
}
|
|
84037
|
+
const harnessName = sessionRow.harnessName ?? "opencode-sdk";
|
|
84038
|
+
const harness = await deps.lifecycleManager.getOrStart(sessionRow.workspaceId ?? "", harnessName);
|
|
84039
|
+
const liveSession = await harness.resumeSession(sessionRow.opencodeSessionId, {
|
|
84040
|
+
harnessSessionId
|
|
84041
|
+
});
|
|
84042
|
+
await liveSession.close();
|
|
84043
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84044
|
+
}
|
|
84045
|
+
async function closeHarnessSession(session2, deps, harnessSessionId) {
|
|
84046
|
+
const sessionRow = await session2.backend.query(api.daemon.directHarness.sessions.getSession, {
|
|
84047
|
+
harnessSessionId
|
|
84048
|
+
});
|
|
84049
|
+
if (!sessionRow) {
|
|
84050
|
+
throw new Error(`Harness session ${harnessSessionId} not found`);
|
|
84051
|
+
}
|
|
84052
|
+
if (sessionRow.status === "closed" || sessionRow.status === "failed") {
|
|
84053
|
+
return;
|
|
84054
|
+
}
|
|
84055
|
+
const handle = deps.activeSessions.get(harnessSessionId);
|
|
84056
|
+
if (handle) {
|
|
84057
|
+
await handle.close();
|
|
84058
|
+
deps.activeSessions.delete(harnessSessionId);
|
|
84059
|
+
return;
|
|
84060
|
+
}
|
|
84061
|
+
await closeDisconnectedHarnessSession(deps, sessionRow, harnessSessionId);
|
|
84062
|
+
}
|
|
84063
|
+
async function handleCloseSession(session2, deps, cmd) {
|
|
84064
|
+
const { harnessSessionId } = cmd.closeSession ?? {};
|
|
84065
|
+
if (!harnessSessionId) {
|
|
84066
|
+
await markFailed(session2, cmd._id, "closeSession: missing harnessSessionId");
|
|
84067
|
+
return;
|
|
84068
|
+
}
|
|
84069
|
+
console.log(`[direct-harness] Processing closeSession for harnessSessionId=${harnessSessionId}`);
|
|
84070
|
+
await closeHarnessSession(session2, deps, harnessSessionId);
|
|
84071
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84072
|
+
sessionId: session2.sessionId,
|
|
84073
|
+
commandId: cmd._id,
|
|
84074
|
+
status: "done"
|
|
84075
|
+
});
|
|
84076
|
+
console.log(`[direct-harness] Closed session ${harnessSessionId}`);
|
|
84077
|
+
}
|
|
84078
|
+
async function markFailed(session2, commandId, errorMessage) {
|
|
84079
|
+
await session2.backend.mutation(api.daemon.directHarness.commands.updateCommandStatus, {
|
|
84080
|
+
sessionId: session2.sessionId,
|
|
84081
|
+
commandId,
|
|
84082
|
+
status: "failed",
|
|
84083
|
+
errorMessage
|
|
84084
|
+
});
|
|
84085
|
+
}
|
|
84086
|
+
var DIRECT_HARNESS_COMMAND_TTL_MS = 60000;
|
|
84087
|
+
var init_command_subscriber = __esm(() => {
|
|
84088
|
+
init_api3();
|
|
84089
|
+
init_registry4();
|
|
83370
84090
|
});
|
|
83371
84091
|
|
|
84092
|
+
// src/infrastructure/harnesses/harness-key.ts
|
|
84093
|
+
function makeHarnessKey(workspaceId, harnessName) {
|
|
84094
|
+
return `${workspaceId}:${harnessName}`;
|
|
84095
|
+
}
|
|
84096
|
+
function parseHarnessKey(key) {
|
|
84097
|
+
const idx = key.indexOf(":");
|
|
84098
|
+
if (idx === -1)
|
|
84099
|
+
throw new Error(`Invalid harness key: ${key}`);
|
|
84100
|
+
return { workspaceId: key.slice(0, idx), harnessName: key.slice(idx + 1) };
|
|
84101
|
+
}
|
|
84102
|
+
|
|
83372
84103
|
// src/commands/machine/daemon-start/direct-harness/harness-lifecycle-manager.ts
|
|
83373
84104
|
class HarnessLifecycleManager {
|
|
83374
84105
|
harnesses;
|
|
@@ -83383,10 +84114,15 @@ class HarnessLifecycleManager {
|
|
|
83383
84114
|
this.resolveWorkspace = resolveWorkspace;
|
|
83384
84115
|
this.resolvedConvexUrl = resolvedConvexUrl;
|
|
83385
84116
|
}
|
|
83386
|
-
getOrStartHarnessEffect(workspaceId) {
|
|
83387
|
-
const
|
|
83388
|
-
|
|
84117
|
+
getOrStartHarnessEffect(workspaceId, harnessName) {
|
|
84118
|
+
const key = makeHarnessKey(workspaceId, harnessName);
|
|
84119
|
+
const existing = this.harnesses.get(key);
|
|
84120
|
+
if (existing?.isAlive())
|
|
83389
84121
|
return exports_Effect.succeed(existing);
|
|
84122
|
+
if (existing) {
|
|
84123
|
+
existing.close().catch(() => {});
|
|
84124
|
+
this.harnesses.delete(key);
|
|
84125
|
+
}
|
|
83390
84126
|
return exports_Effect.gen(this, function* () {
|
|
83391
84127
|
const workspace = yield* exports_Effect.tryPromise({
|
|
83392
84128
|
try: () => this.resolveWorkspace(workspaceId),
|
|
@@ -83395,23 +84131,23 @@ class HarnessLifecycleManager {
|
|
|
83395
84131
|
if (!workspace) {
|
|
83396
84132
|
return yield* exports_Effect.fail(new Error(`Workspace ${workspaceId} not found — cannot start harness`));
|
|
83397
84133
|
}
|
|
83398
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDD27 Auto-starting
|
|
84134
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD27 Auto-starting ${harnessName} for workspace=${workspaceId} (${workspace.workingDir})`);
|
|
83399
84135
|
const harness = yield* exports_Effect.tryPromise({
|
|
83400
|
-
try: () =>
|
|
83401
|
-
|
|
84136
|
+
try: () => startBoundHarness({
|
|
84137
|
+
harnessName,
|
|
83402
84138
|
workingDir: workspace.workingDir,
|
|
83403
84139
|
workspaceId,
|
|
83404
84140
|
resolvedConvexUrl: this.resolvedConvexUrl
|
|
83405
84141
|
}),
|
|
83406
84142
|
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
83407
84143
|
});
|
|
83408
|
-
this.harnesses.set(
|
|
83409
|
-
this.idleSince.set(
|
|
84144
|
+
this.harnesses.set(key, harness);
|
|
84145
|
+
this.idleSince.set(key, Date.now());
|
|
83410
84146
|
return harness;
|
|
83411
84147
|
});
|
|
83412
84148
|
}
|
|
83413
|
-
async getOrStart(workspaceId) {
|
|
83414
|
-
return exports_Effect.runPromise(this.getOrStartHarnessEffect(workspaceId));
|
|
84149
|
+
async getOrStart(workspaceId, harnessName) {
|
|
84150
|
+
return exports_Effect.runPromise(this.getOrStartHarnessEffect(workspaceId, harnessName));
|
|
83415
84151
|
}
|
|
83416
84152
|
startMonitoring() {
|
|
83417
84153
|
if (this.monitorTimer)
|
|
@@ -83427,28 +84163,30 @@ class HarnessLifecycleManager {
|
|
|
83427
84163
|
}
|
|
83428
84164
|
checkInactivity() {
|
|
83429
84165
|
const now = Date.now();
|
|
83430
|
-
for (const
|
|
83431
|
-
const
|
|
84166
|
+
for (const key of this.harnesses.keys()) {
|
|
84167
|
+
const { workspaceId, harnessName } = parseHarnessKey(key);
|
|
84168
|
+
const hasActiveSessions = this.hasSessionsForHarnessKey(workspaceId, harnessName);
|
|
83432
84169
|
if (hasActiveSessions) {
|
|
83433
|
-
this.idleSince.delete(
|
|
84170
|
+
this.idleSince.delete(key);
|
|
83434
84171
|
} else {
|
|
83435
|
-
const idleStart = this.idleSince.get(
|
|
84172
|
+
const idleStart = this.idleSince.get(key);
|
|
83436
84173
|
if (idleStart === undefined) {
|
|
83437
|
-
this.idleSince.set(
|
|
84174
|
+
this.idleSince.set(key, now);
|
|
83438
84175
|
} else if (now - idleStart >= INACTIVITY_TTL_MS) {
|
|
83439
84176
|
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(
|
|
84177
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD2A Killing idle harness ${harnessName} for workspace=${workspaceId} (idle ${idleMinutes}min)`);
|
|
84178
|
+
this.harnesses.get(key)?.close().catch(() => {});
|
|
84179
|
+
this.harnesses.delete(key);
|
|
84180
|
+
this.idleSince.delete(key);
|
|
83444
84181
|
}
|
|
83445
84182
|
}
|
|
83446
84183
|
}
|
|
83447
84184
|
}
|
|
83448
|
-
|
|
84185
|
+
hasSessionsForHarnessKey(workspaceId, harnessName) {
|
|
83449
84186
|
for (const handle of this.activeSessions.values()) {
|
|
83450
|
-
if (handle.workspaceId === workspaceId)
|
|
84187
|
+
if (handle.workspaceId === workspaceId && handle.harnessName === harnessName) {
|
|
83451
84188
|
return true;
|
|
84189
|
+
}
|
|
83452
84190
|
}
|
|
83453
84191
|
return false;
|
|
83454
84192
|
}
|
|
@@ -83456,7 +84194,7 @@ class HarnessLifecycleManager {
|
|
|
83456
84194
|
var INACTIVITY_TTL_MS, CHECK_INTERVAL_MS;
|
|
83457
84195
|
var init_harness_lifecycle_manager = __esm(() => {
|
|
83458
84196
|
init_esm();
|
|
83459
|
-
|
|
84197
|
+
init_registry4();
|
|
83460
84198
|
INACTIVITY_TTL_MS = 15 * 60 * 1000;
|
|
83461
84199
|
CHECK_INTERVAL_MS = 60 * 1000;
|
|
83462
84200
|
});
|
|
@@ -83551,6 +84289,7 @@ async function resumeSession(deps, input) {
|
|
|
83551
84289
|
let closed = false;
|
|
83552
84290
|
const handle = {
|
|
83553
84291
|
harnessSessionId,
|
|
84292
|
+
harnessName: input.harnessName,
|
|
83554
84293
|
opencodeSessionId,
|
|
83555
84294
|
workspaceId: input.workspaceId ?? "",
|
|
83556
84295
|
session: session2,
|
|
@@ -83615,14 +84354,16 @@ async function resumeSessionHandle(session2, deps, rowId, info) {
|
|
|
83615
84354
|
return null;
|
|
83616
84355
|
}
|
|
83617
84356
|
const workspaceId = info.workspaceId;
|
|
84357
|
+
const harnessName = info.harnessName;
|
|
83618
84358
|
if (!workspaceId) {
|
|
83619
84359
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: no workspace info`);
|
|
83620
84360
|
return null;
|
|
83621
84361
|
}
|
|
83622
|
-
|
|
84362
|
+
const key = makeHarnessKey(workspaceId, harnessName);
|
|
84363
|
+
let harness = deps.harnesses.get(key);
|
|
83623
84364
|
if (harness && !harness.isAlive()) {
|
|
83624
84365
|
harness.close().catch(() => {});
|
|
83625
|
-
deps.harnesses.delete(
|
|
84366
|
+
deps.harnesses.delete(key);
|
|
83626
84367
|
harness = undefined;
|
|
83627
84368
|
}
|
|
83628
84369
|
if (!harness) {
|
|
@@ -83634,20 +84375,20 @@ async function resumeSessionHandle(session2, deps, rowId, info) {
|
|
|
83634
84375
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: workspace not found`);
|
|
83635
84376
|
return null;
|
|
83636
84377
|
}
|
|
83637
|
-
harness = await
|
|
83638
|
-
|
|
84378
|
+
harness = await startBoundHarness({
|
|
84379
|
+
harnessName,
|
|
83639
84380
|
workingDir: workspace.workingDir,
|
|
83640
84381
|
workspaceId,
|
|
83641
84382
|
resolvedConvexUrl: session2.convexUrl
|
|
83642
84383
|
});
|
|
83643
|
-
deps.harnesses.set(
|
|
84384
|
+
deps.harnesses.set(key, harness);
|
|
83644
84385
|
}
|
|
83645
84386
|
try {
|
|
83646
84387
|
return await resumeSession({
|
|
83647
84388
|
harness,
|
|
83648
84389
|
journalFactory: deps.journalFactory,
|
|
83649
|
-
chunkExtractor:
|
|
83650
|
-
}, { harnessSessionId: rowId, opencodeSessionId, workspaceId });
|
|
84390
|
+
chunkExtractor: createChunkExtractor(harness.type)
|
|
84391
|
+
}, { harnessSessionId: rowId, opencodeSessionId, workspaceId, harnessName });
|
|
83651
84392
|
} catch (err) {
|
|
83652
84393
|
const message = err instanceof Error ? err.message : String(err);
|
|
83653
84394
|
console.warn(`[direct-harness] Cannot resume session ${rowId}: ${message}`);
|
|
@@ -83693,8 +84434,9 @@ async function deliverPendingMessages(handle, deps, rowId, messages, info) {
|
|
|
83693
84434
|
console.warn(`[direct-harness] Prompt failed for session ${rowId} seq=${msg.seq}: ${message}`);
|
|
83694
84435
|
await deps.sessionRepository.markIdle(rowId).catch(() => {});
|
|
83695
84436
|
deps.activeSessions.delete(rowId);
|
|
83696
|
-
if (info?.workspaceId)
|
|
83697
|
-
deps.harnesses.delete(info.workspaceId);
|
|
84437
|
+
if (info?.workspaceId && info.harnessName) {
|
|
84438
|
+
deps.harnesses.delete(makeHarnessKey(info.workspaceId, info.harnessName));
|
|
84439
|
+
}
|
|
83698
84440
|
return;
|
|
83699
84441
|
}
|
|
83700
84442
|
}
|
|
@@ -83721,7 +84463,7 @@ var init_prompt_subscriber = __esm(() => {
|
|
|
83721
84463
|
init_idle_handler();
|
|
83722
84464
|
init_api3();
|
|
83723
84465
|
init_resume_session();
|
|
83724
|
-
|
|
84466
|
+
init_registry4();
|
|
83725
84467
|
});
|
|
83726
84468
|
|
|
83727
84469
|
// src/commands/machine/daemon-start/direct-harness/session-subscriber.ts
|
|
@@ -83746,21 +84488,23 @@ function startSessionSubscriber(daemonSession, wsClient2, deps) {
|
|
|
83746
84488
|
return { stop: unsub };
|
|
83747
84489
|
}
|
|
83748
84490
|
async function getOrCreateHarness(daemonSession, deps, session2, workspace) {
|
|
83749
|
-
|
|
84491
|
+
const harnessName = session2.opencode?.harnessName ?? "opencode-sdk";
|
|
84492
|
+
const key = makeHarnessKey(session2.workspaceId, harnessName);
|
|
84493
|
+
let harness = deps.harnesses.get(key);
|
|
83750
84494
|
if (harness && !harness.isAlive()) {
|
|
83751
|
-
console.warn(`[direct-harness] Harness for workspace ${session2.workspaceId} is no longer alive — restarting`);
|
|
84495
|
+
console.warn(`[direct-harness] Harness ${harnessName} for workspace ${session2.workspaceId} is no longer alive — restarting`);
|
|
83752
84496
|
harness.close().catch(() => {});
|
|
83753
|
-
deps.harnesses.delete(
|
|
84497
|
+
deps.harnesses.delete(key);
|
|
83754
84498
|
harness = undefined;
|
|
83755
84499
|
}
|
|
83756
84500
|
if (!harness) {
|
|
83757
|
-
harness = await
|
|
83758
|
-
|
|
84501
|
+
harness = await startBoundHarness({
|
|
84502
|
+
harnessName,
|
|
83759
84503
|
workingDir: workspace.workingDir,
|
|
83760
84504
|
workspaceId: session2.workspaceId,
|
|
83761
84505
|
resolvedConvexUrl: daemonSession.convexUrl
|
|
83762
84506
|
});
|
|
83763
|
-
deps.harnesses.set(
|
|
84507
|
+
deps.harnesses.set(key, harness);
|
|
83764
84508
|
}
|
|
83765
84509
|
return harness;
|
|
83766
84510
|
}
|
|
@@ -83812,13 +84556,17 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83812
84556
|
await deps.sessionRepository.markFailed(rowId);
|
|
83813
84557
|
return;
|
|
83814
84558
|
}
|
|
84559
|
+
const harnessName = session2.opencode?.harnessName ?? "opencode-sdk";
|
|
83815
84560
|
const harness = await getOrCreateHarness(daemonSession, deps, session2, workspace);
|
|
84561
|
+
const modelConfig = session2.opencode?.lastUsedConfig.model;
|
|
84562
|
+
const model = modelConfig ? `${modelConfig.providerID}/${modelConfig.modelID}` : undefined;
|
|
83816
84563
|
const liveSession = await harness.newSession({
|
|
83817
84564
|
agent: session2.opencode?.lastUsedConfig.agent ?? "build",
|
|
84565
|
+
model,
|
|
83818
84566
|
harnessSessionId: rowId
|
|
83819
84567
|
});
|
|
83820
84568
|
const journal = deps.journalFactory.create(rowId);
|
|
83821
|
-
const extractChunk =
|
|
84569
|
+
const extractChunk = createChunkExtractor(harness.type);
|
|
83822
84570
|
const idleConfig = {
|
|
83823
84571
|
agent: session2.opencode?.lastUsedConfig.agent ?? "build",
|
|
83824
84572
|
model: session2.opencode?.lastUsedConfig.model
|
|
@@ -83833,9 +84581,11 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83833
84581
|
await journal.commit();
|
|
83834
84582
|
await liveSession.close();
|
|
83835
84583
|
await deps.sessionRepository.markClosed(rowId);
|
|
84584
|
+
deps.activeSessions.delete(rowId);
|
|
83836
84585
|
};
|
|
83837
84586
|
const handle = {
|
|
83838
84587
|
harnessSessionId: rowId,
|
|
84588
|
+
harnessName,
|
|
83839
84589
|
opencodeSessionId: liveSession.opencodeSessionId,
|
|
83840
84590
|
workspaceId: session2.workspaceId,
|
|
83841
84591
|
session: liveSession,
|
|
@@ -83873,7 +84623,61 @@ async function processOne(daemonSession, deps, session2) {
|
|
|
83873
84623
|
var init_session_subscriber = __esm(() => {
|
|
83874
84624
|
init_idle_handler();
|
|
83875
84625
|
init_api3();
|
|
83876
|
-
|
|
84626
|
+
init_registry4();
|
|
84627
|
+
});
|
|
84628
|
+
|
|
84629
|
+
// src/commands/machine/daemon-start/direct-harness/shutdown-sessions.ts
|
|
84630
|
+
async function listHarnessSessionIdsForShutdown(session2) {
|
|
84631
|
+
const ids3 = new Set;
|
|
84632
|
+
const managed = await session2.backend.query(api.daemon.directHarness.turns.getMachineHarnessSessions, {
|
|
84633
|
+
sessionId: session2.sessionId,
|
|
84634
|
+
machineId: session2.machineId
|
|
84635
|
+
});
|
|
84636
|
+
for (const row of managed) {
|
|
84637
|
+
ids3.add(row.harnessSessionId);
|
|
84638
|
+
}
|
|
84639
|
+
const pending3 = await session2.backend.query(api.daemon.directHarness.sessions.listPendingSessionsForMachine, {
|
|
84640
|
+
sessionId: session2.sessionId,
|
|
84641
|
+
machineId: session2.machineId
|
|
84642
|
+
});
|
|
84643
|
+
for (const row of pending3) {
|
|
84644
|
+
ids3.add(row._id);
|
|
84645
|
+
}
|
|
84646
|
+
return [...ids3];
|
|
84647
|
+
}
|
|
84648
|
+
async function closeAllMachineHarnessSessionsOnShutdown(session2, deps) {
|
|
84649
|
+
const commandDeps = {
|
|
84650
|
+
lifecycleManager: deps.lifecycleManager,
|
|
84651
|
+
publisher: {},
|
|
84652
|
+
activeSessions: deps.activeSessions,
|
|
84653
|
+
sessionRepository: deps.sessionRepository
|
|
84654
|
+
};
|
|
84655
|
+
const harnessSessionIds = await listHarnessSessionIdsForShutdown(session2);
|
|
84656
|
+
if (harnessSessionIds.length === 0)
|
|
84657
|
+
return;
|
|
84658
|
+
console.log(`[direct-harness] Closing ${harnessSessionIds.length} harness session(s) on daemon shutdown...`);
|
|
84659
|
+
for (const harnessSessionId of harnessSessionIds) {
|
|
84660
|
+
try {
|
|
84661
|
+
await session2.backend.mutation(api.daemon.directHarness.turns.markOrphanTurnsFailed, {
|
|
84662
|
+
sessionId: session2.sessionId,
|
|
84663
|
+
machineId: session2.machineId,
|
|
84664
|
+
harnessSessionId
|
|
84665
|
+
});
|
|
84666
|
+
} catch {}
|
|
84667
|
+
try {
|
|
84668
|
+
await closeHarnessSession(session2, commandDeps, harnessSessionId);
|
|
84669
|
+
} catch (err) {
|
|
84670
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84671
|
+
console.warn(`[direct-harness] Failed to close session ${harnessSessionId} on shutdown: ${message}`);
|
|
84672
|
+
try {
|
|
84673
|
+
await deps.sessionRepository.markClosed(harnessSessionId);
|
|
84674
|
+
} catch {}
|
|
84675
|
+
}
|
|
84676
|
+
}
|
|
84677
|
+
}
|
|
84678
|
+
var init_shutdown_sessions = __esm(() => {
|
|
84679
|
+
init_command_subscriber();
|
|
84680
|
+
init_api3();
|
|
83877
84681
|
});
|
|
83878
84682
|
|
|
83879
84683
|
// src/infrastructure/repos/convex-capabilities-publisher.ts
|
|
@@ -84164,13 +84968,20 @@ function startDirectHarnessSubscriptions(session2, wsClient2, activeSessions, ha
|
|
|
84164
84968
|
publisher: new ConvexCapabilitiesPublisher({
|
|
84165
84969
|
backend: session2.backend,
|
|
84166
84970
|
sessionId: session2.sessionId
|
|
84167
|
-
})
|
|
84971
|
+
}),
|
|
84972
|
+
activeSessions,
|
|
84973
|
+
sessionRepository
|
|
84168
84974
|
});
|
|
84169
84975
|
return {
|
|
84170
84976
|
pendingPromptSubscriptionHandle,
|
|
84171
84977
|
pendingHarnessSessionSubscriptionHandle,
|
|
84172
84978
|
commandSubscriptionHandle,
|
|
84173
|
-
lifecycleManager
|
|
84979
|
+
lifecycleManager,
|
|
84980
|
+
closeSessionsOnShutdown: () => closeAllMachineHarnessSessionsOnShutdown(session2, {
|
|
84981
|
+
lifecycleManager,
|
|
84982
|
+
activeSessions,
|
|
84983
|
+
sessionRepository
|
|
84984
|
+
})
|
|
84174
84985
|
};
|
|
84175
84986
|
}
|
|
84176
84987
|
var init_start_subscriptions = __esm(() => {
|
|
@@ -84178,6 +84989,7 @@ var init_start_subscriptions = __esm(() => {
|
|
|
84178
84989
|
init_harness_lifecycle_manager();
|
|
84179
84990
|
init_prompt_subscriber();
|
|
84180
84991
|
init_session_subscriber();
|
|
84992
|
+
init_shutdown_sessions();
|
|
84181
84993
|
init_api3();
|
|
84182
84994
|
init_convex_capabilities_publisher();
|
|
84183
84995
|
init_convex_output_repository();
|
|
@@ -84981,6 +85793,9 @@ function isInjectableNativeAction(action) {
|
|
|
84981
85793
|
return true;
|
|
84982
85794
|
return action === NATIVE_WAITING_ACTION;
|
|
84983
85795
|
}
|
|
85796
|
+
function shouldEmitNativeWaitingOnTurnEnd(lastStatus) {
|
|
85797
|
+
return lastStatus !== "task.acknowledged" && lastStatus !== "task.inProgress";
|
|
85798
|
+
}
|
|
84984
85799
|
function isNativeIdleAfterTaskComplete(participant) {
|
|
84985
85800
|
return participant.lastSeenAction === NATIVE_TASK_INJECTED_ACTION && participant.lastStatus === "task.completed";
|
|
84986
85801
|
}
|
|
@@ -85050,13 +85865,21 @@ function shouldDeliverNativeTask(task, opts) {
|
|
|
85050
85865
|
return isInjectableNativeAction(task.participant?.lastSeenAction) || isNativeIdleAfterTaskComplete(task.participant ?? {}) || isNativeAcknowledgedInjectionRetry(task);
|
|
85051
85866
|
}
|
|
85052
85867
|
function buildNativeInjectionPrompt(params) {
|
|
85053
|
-
const { taskDeliveryOutput,
|
|
85054
|
-
if (
|
|
85868
|
+
const { taskDeliveryOutput, augmentationMode } = params;
|
|
85869
|
+
if (augmentationMode === "compact") {
|
|
85055
85870
|
return [
|
|
85056
85871
|
"⚠️ Context was compacted. Run `chatroom get-system-prompt` only if role instructions are missing.",
|
|
85057
85872
|
"",
|
|
85058
85873
|
taskDeliveryOutput
|
|
85059
85874
|
].join(`
|
|
85875
|
+
`);
|
|
85876
|
+
}
|
|
85877
|
+
if (augmentationMode === "new_session") {
|
|
85878
|
+
return [
|
|
85879
|
+
"⚠️ Starting a new agent session. Run `chatroom get-system-prompt` to reload role instructions if needed.",
|
|
85880
|
+
"",
|
|
85881
|
+
taskDeliveryOutput
|
|
85882
|
+
].join(`
|
|
85060
85883
|
`);
|
|
85061
85884
|
}
|
|
85062
85885
|
return taskDeliveryOutput;
|
|
@@ -85066,18 +85889,23 @@ var init_native_task_injector_logic = __esm(() => {
|
|
|
85066
85889
|
init_native_integration();
|
|
85067
85890
|
});
|
|
85068
85891
|
|
|
85069
|
-
// ../../services/backend/src/domain/
|
|
85892
|
+
// ../../services/backend/src/domain/entities/team-agent-settings.ts
|
|
85893
|
+
function roleSupportsSessionAugmentation(role) {
|
|
85894
|
+
const normalized = role.toLowerCase();
|
|
85895
|
+
return SESSION_AUGMENTATION_ROLES.some((r) => r === normalized);
|
|
85896
|
+
}
|
|
85897
|
+
var SESSION_AUGMENTATION_ROLES;
|
|
85898
|
+
var init_team_agent_settings = __esm(() => {
|
|
85899
|
+
SESSION_AUGMENTATION_ROLES = ["builder"];
|
|
85900
|
+
});
|
|
85901
|
+
|
|
85902
|
+
// ../../services/backend/src/domain/handoff/parse-session-augmentation.ts
|
|
85070
85903
|
function findSectionIndex(content, headings) {
|
|
85071
85904
|
const indices = headings.map((heading) => content.indexOf(heading)).filter((idx) => idx !== -1);
|
|
85072
85905
|
return indices.length === 0 ? -1 : Math.min(...indices);
|
|
85073
85906
|
}
|
|
85074
85907
|
function normalizeMode(raw) {
|
|
85075
|
-
|
|
85076
|
-
if (value === "reset")
|
|
85077
|
-
return "new_session";
|
|
85078
|
-
if (value === "none")
|
|
85079
|
-
return value;
|
|
85080
|
-
return DEFAULT_MODE;
|
|
85908
|
+
return MODE_ALIASES[raw.toLowerCase()] ?? DEFAULT_MODE;
|
|
85081
85909
|
}
|
|
85082
85910
|
function extractSectionBody(content, sectionIdx) {
|
|
85083
85911
|
const matchedHeading = SECTION_HEADINGS.find((h) => content.indexOf(h, sectionIdx) === sectionIdx) ?? SECTION_HEADINGS[0];
|
|
@@ -85085,7 +85913,7 @@ function extractSectionBody(content, sectionIdx) {
|
|
|
85085
85913
|
const nextHeading = afterSection.slice(matchedHeading.length).search(/\n## /);
|
|
85086
85914
|
return nextHeading === -1 ? afterSection : afterSection.slice(0, matchedHeading.length + nextHeading);
|
|
85087
85915
|
}
|
|
85088
|
-
function
|
|
85916
|
+
function parseSessionAugmentation(handoffContent) {
|
|
85089
85917
|
const sectionIdx = findSectionIndex(handoffContent, SECTION_HEADINGS);
|
|
85090
85918
|
if (sectionIdx === -1)
|
|
85091
85919
|
return DEFAULT_MODE;
|
|
@@ -85094,13 +85922,33 @@ function parseCompressContext(handoffContent) {
|
|
|
85094
85922
|
return DEFAULT_MODE;
|
|
85095
85923
|
return normalizeMode(match17[1]);
|
|
85096
85924
|
}
|
|
85097
|
-
function
|
|
85098
|
-
|
|
85925
|
+
function resolveSessionAugmentationForRole(handoffContent, role) {
|
|
85926
|
+
if (!roleSupportsSessionAugmentation(role)) {
|
|
85927
|
+
return "none";
|
|
85928
|
+
}
|
|
85929
|
+
return parseSessionAugmentation(handoffContent);
|
|
85930
|
+
}
|
|
85931
|
+
function sessionAugmentationToWantResume(mode) {
|
|
85932
|
+
return mode === "none" || mode === "compact";
|
|
85933
|
+
}
|
|
85934
|
+
function sessionAugmentationNewSessionStarted(mode) {
|
|
85935
|
+
return mode === "new_session";
|
|
85099
85936
|
}
|
|
85100
|
-
var SECTION_HEADINGS, DATA_TAG, DEFAULT_MODE = "new_session";
|
|
85101
|
-
var
|
|
85102
|
-
|
|
85103
|
-
|
|
85937
|
+
var SECTION_HEADINGS, DATA_TAG, DEFAULT_MODE = "new_session", MODE_ALIASES;
|
|
85938
|
+
var init_parse_session_augmentation = __esm(() => {
|
|
85939
|
+
init_team_agent_settings();
|
|
85940
|
+
SECTION_HEADINGS = [
|
|
85941
|
+
"## Session Augmentation",
|
|
85942
|
+
"## Session Management",
|
|
85943
|
+
"## Restart new context"
|
|
85944
|
+
];
|
|
85945
|
+
DATA_TAG = /\/\/\s*data:agent\.(?:session_augmentation|compress_context)=(none|compact|new_session|reset)\b/i;
|
|
85946
|
+
MODE_ALIASES = {
|
|
85947
|
+
reset: "new_session",
|
|
85948
|
+
none: "none",
|
|
85949
|
+
compact: "compact",
|
|
85950
|
+
new_session: "new_session"
|
|
85951
|
+
};
|
|
85104
85952
|
});
|
|
85105
85953
|
|
|
85106
85954
|
// src/commands/machine/daemon-start/native-task-injector.ts
|
|
@@ -85144,9 +85992,10 @@ function runNativeInjectionEffect(task, harnessSessionId, deps, ledger) {
|
|
|
85144
85992
|
return yield* exports_Effect.fail(deliveryResult.left);
|
|
85145
85993
|
}
|
|
85146
85994
|
const delivery = deliveryResult.right;
|
|
85995
|
+
const augmentationMode = resolveSessionAugmentationForRole(taskContent, role);
|
|
85147
85996
|
const prompt = buildNativeInjectionPrompt({
|
|
85148
85997
|
taskDeliveryOutput: delivery.fullCliOutput,
|
|
85149
|
-
|
|
85998
|
+
augmentationMode
|
|
85150
85999
|
});
|
|
85151
86000
|
yield* exports_Effect.tryPromise({
|
|
85152
86001
|
try: () => deps.backend.mutation(api.participants.join, {
|
|
@@ -85158,6 +86007,21 @@ function runNativeInjectionEffect(task, harnessSessionId, deps, ledger) {
|
|
|
85158
86007
|
}),
|
|
85159
86008
|
catch: (err) => err
|
|
85160
86009
|
}).pipe(exports_Effect.tapError(() => exports_Effect.sync(() => ledger.clearDelivery(taskId, harnessSessionId))));
|
|
86010
|
+
if (roleSupportsSessionAugmentation(role)) {
|
|
86011
|
+
yield* exports_Effect.tryPromise({
|
|
86012
|
+
try: () => deps.backend.mutation(api.machines.emitSessionAugmented, {
|
|
86013
|
+
sessionId: deps.sessionId,
|
|
86014
|
+
machineId: deps.machineId,
|
|
86015
|
+
chatroomId,
|
|
86016
|
+
role,
|
|
86017
|
+
taskId,
|
|
86018
|
+
mode: augmentationMode,
|
|
86019
|
+
newSessionStarted: sessionAugmentationNewSessionStarted(augmentationMode),
|
|
86020
|
+
harnessSessionId
|
|
86021
|
+
}),
|
|
86022
|
+
catch: (err) => err
|
|
86023
|
+
}).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
86024
|
+
}
|
|
85161
86025
|
const resumeResult = yield* exports_Effect.tryPromise({
|
|
85162
86026
|
try: () => deps.agentMgr.resumeTurnForSlot({ chatroomId, role, prompt }),
|
|
85163
86027
|
catch: (err) => err
|
|
@@ -85172,7 +86036,8 @@ function runNativeInjectionEffect(task, harnessSessionId, deps, ledger) {
|
|
|
85172
86036
|
}
|
|
85173
86037
|
var init_native_task_injector = __esm(() => {
|
|
85174
86038
|
init_participant();
|
|
85175
|
-
|
|
86039
|
+
init_team_agent_settings();
|
|
86040
|
+
init_parse_session_augmentation();
|
|
85176
86041
|
init_esm();
|
|
85177
86042
|
init_native_task_injector_logic();
|
|
85178
86043
|
init_api3();
|
|
@@ -85191,12 +86056,13 @@ class NativeTaskDeliveryCoordinator {
|
|
|
85191
86056
|
}
|
|
85192
86057
|
}
|
|
85193
86058
|
reconcileAssignedTasks(params) {
|
|
85194
|
-
const { tasks, runtime: runtime4, effectContext: effectContext2, agentMgr, sessionDeps } = params;
|
|
85195
|
-
|
|
85196
|
-
|
|
86059
|
+
const { tasks, runtime: runtime4, effectContext: effectContext2, agentMgr, sessionDeps, machineId } = params;
|
|
86060
|
+
const ledger = this.ledger;
|
|
86061
|
+
for (const row of tasks) {
|
|
86062
|
+
const slot = agentMgr.getSlot(row.chatroomId, row.agentConfig.role);
|
|
85197
86063
|
const harnessSessionId = slot?.harnessSessionId;
|
|
85198
|
-
if (!shouldDeliverNativeTask(
|
|
85199
|
-
ledger
|
|
86064
|
+
if (!shouldDeliverNativeTask(row, {
|
|
86065
|
+
ledger,
|
|
85200
86066
|
harnessSessionId
|
|
85201
86067
|
})) {
|
|
85202
86068
|
continue;
|
|
@@ -85204,14 +86070,25 @@ class NativeTaskDeliveryCoordinator {
|
|
|
85204
86070
|
if (!harnessSessionId) {
|
|
85205
86071
|
continue;
|
|
85206
86072
|
}
|
|
85207
|
-
exports_Runtime.runFork(runtime4)(
|
|
85208
|
-
|
|
85209
|
-
|
|
85210
|
-
|
|
85211
|
-
|
|
85212
|
-
|
|
85213
|
-
|
|
85214
|
-
|
|
86073
|
+
exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
|
|
86074
|
+
const full = yield* exports_Effect.tryPromise(() => sessionDeps.backend.query(api.machines.getAssignedTaskForAction, {
|
|
86075
|
+
sessionId: sessionDeps.sessionId,
|
|
86076
|
+
machineId,
|
|
86077
|
+
taskId: row.taskId,
|
|
86078
|
+
role: row.agentConfig.role
|
|
86079
|
+
}));
|
|
86080
|
+
if (!full)
|
|
86081
|
+
return;
|
|
86082
|
+
yield* runNativeInjectionEffect(full, harnessSessionId, {
|
|
86083
|
+
sessionId: sessionDeps.sessionId,
|
|
86084
|
+
machineId: sessionDeps.machineId,
|
|
86085
|
+
backend: sessionDeps.backend,
|
|
86086
|
+
agentMgr: {
|
|
86087
|
+
resumeTurnForSlot: (args2) => exports_Effect.runPromise(agentMgr.resumeTurnForSlot(args2))
|
|
86088
|
+
},
|
|
86089
|
+
convexUrl: sessionDeps.convexUrl
|
|
86090
|
+
}, ledger);
|
|
86091
|
+
}).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
86092
|
}
|
|
85216
86093
|
}
|
|
85217
86094
|
}
|
|
@@ -85227,6 +86104,7 @@ var init_native_task_delivery_coordinator = __esm(() => {
|
|
|
85227
86104
|
init_esm();
|
|
85228
86105
|
init_native_task_injector_logic();
|
|
85229
86106
|
init_native_task_injector();
|
|
86107
|
+
init_api3();
|
|
85230
86108
|
init_convex_error();
|
|
85231
86109
|
});
|
|
85232
86110
|
|
|
@@ -85401,24 +86279,12 @@ var init_agent_lifecycle = __esm(() => {
|
|
|
85401
86279
|
init_restart_decision();
|
|
85402
86280
|
});
|
|
85403
86281
|
|
|
85404
|
-
// src/domain/agent-lifecycle/policies/cursor-sdk-run-error.ts
|
|
85405
|
-
function isCursorSdkRunErrorInLogs(logLines) {
|
|
85406
|
-
return logLines.some((line) => line.includes(" run-error]"));
|
|
85407
|
-
}
|
|
85408
|
-
function formatCursorSdkRunErrorMessage(logLines) {
|
|
85409
|
-
const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
|
|
85410
|
-
return line?.trim() ?? "Cursor SDK run failed";
|
|
85411
|
-
}
|
|
85412
|
-
|
|
85413
86282
|
// src/commands/machine/daemon-start/native-harness-session-exit.ts
|
|
85414
86283
|
function isNativeHarnessSessionDiscardedOnExit(ctx) {
|
|
85415
|
-
const { harness, harnessSessionId, stopReason,
|
|
86284
|
+
const { harness, harnessSessionId, stopReason, supportsDaemonMemoryResume } = ctx;
|
|
85416
86285
|
if (!harness || !harnessSessionId) {
|
|
85417
86286
|
return false;
|
|
85418
86287
|
}
|
|
85419
|
-
if (isCursorSdkRunErrorInLogs(recentLogLines ?? [])) {
|
|
85420
|
-
return true;
|
|
85421
|
-
}
|
|
85422
86288
|
if (!supportsDaemonMemoryResume) {
|
|
85423
86289
|
return true;
|
|
85424
86290
|
}
|
|
@@ -85557,6 +86423,18 @@ function appendRecentLogLine(slot, line) {
|
|
|
85557
86423
|
}
|
|
85558
86424
|
var RECENT_LOG_LINE_CAP2 = 100;
|
|
85559
86425
|
|
|
86426
|
+
// src/domain/agent-lifecycle/policies/cursor-sdk-run-error.ts
|
|
86427
|
+
function isCursorSdkRunErrorInLogs(logLines) {
|
|
86428
|
+
return logLines.some((line) => line.includes(" run-error]"));
|
|
86429
|
+
}
|
|
86430
|
+
function formatCursorSdkRunErrorMessage(logLines) {
|
|
86431
|
+
const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
|
|
86432
|
+
return line?.trim() ?? "Cursor SDK run failed";
|
|
86433
|
+
}
|
|
86434
|
+
|
|
86435
|
+
// src/domain/agent-lifecycle/policies/cursor-sdk-session-reopen-retry.ts
|
|
86436
|
+
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", CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS = 3;
|
|
86437
|
+
|
|
85560
86438
|
// src/domain/agent-lifecycle/use-cases/handle-turn-completed.ts
|
|
85561
86439
|
async function handleTurnCompleted(deps, input, slot) {
|
|
85562
86440
|
if (await tryAbortResumeStorm(deps, input, slot)) {
|
|
@@ -85956,6 +86834,7 @@ class AgentProcessManager {
|
|
|
85956
86834
|
deps;
|
|
85957
86835
|
slots = new Map;
|
|
85958
86836
|
lastHarnessSessions = new Map;
|
|
86837
|
+
sessionReopenRetryInFlight = new Set;
|
|
85959
86838
|
exitRetryQueue = [];
|
|
85960
86839
|
exitRetryTimer = null;
|
|
85961
86840
|
turnEndQueue = new TurnEndQueue;
|
|
@@ -86164,8 +87043,10 @@ class AgentProcessManager {
|
|
|
86164
87043
|
console.log(`[AgentProcessManager] ⛔ Terminal provider error for ${opts.role} — emitted agent.startFailed`);
|
|
86165
87044
|
return;
|
|
86166
87045
|
}
|
|
86167
|
-
await this.emitNativeWaiting(opts.chatroomId, opts.role, opts.harness);
|
|
86168
|
-
|
|
87046
|
+
const emitted = await this.emitNativeWaiting(opts.chatroomId, opts.role, opts.harness, "turn-end");
|
|
87047
|
+
if (emitted) {
|
|
87048
|
+
console.log(`[AgentProcessManager] ✅ Native harness idle for ${opts.role} (native:waiting)`);
|
|
87049
|
+
}
|
|
86169
87050
|
}
|
|
86170
87051
|
async handleExit(opts) {
|
|
86171
87052
|
const key = agentKey3(opts.chatroomId, opts.role);
|
|
@@ -86227,14 +87108,10 @@ class AgentProcessManager {
|
|
|
86227
87108
|
});
|
|
86228
87109
|
}
|
|
86229
87110
|
async preserveHarnessSessionOnExit(key, slot, ctx) {
|
|
86230
|
-
const { harness, harnessSessionId, stopReason
|
|
87111
|
+
const { harness, harnessSessionId, stopReason } = ctx;
|
|
86231
87112
|
if (!harness || !harnessSessionId) {
|
|
86232
87113
|
return;
|
|
86233
87114
|
}
|
|
86234
|
-
if (isCursorSdkRunErrorInLogs(recentLogLines ?? [])) {
|
|
86235
|
-
this.clearLastHarnessSession(key);
|
|
86236
|
-
return;
|
|
86237
|
-
}
|
|
86238
87115
|
const service3 = this.deps.agentServices.get(harness);
|
|
86239
87116
|
if (!service3?.resumeFromDaemonMemory) {
|
|
86240
87117
|
return;
|
|
@@ -86284,17 +87161,15 @@ class AgentProcessManager {
|
|
|
86284
87161
|
}
|
|
86285
87162
|
this.maybeRestartAgent(opts, ctx);
|
|
86286
87163
|
}
|
|
86287
|
-
|
|
87164
|
+
hasCursorSdkRunErrorInContext(recentLogLines) {
|
|
86288
87165
|
if (!isCursorSdkRunErrorInLogs(recentLogLines)) {
|
|
86289
87166
|
return false;
|
|
86290
87167
|
}
|
|
86291
|
-
console.log(`[AgentProcessManager] cursor-sdk run-error
|
|
86292
|
-
this.clearLastHarnessSession(key);
|
|
87168
|
+
console.log(`[AgentProcessManager] cursor-sdk run-error detected: ${formatCursorSdkRunErrorMessage(recentLogLines)}`);
|
|
86293
87169
|
return true;
|
|
86294
87170
|
}
|
|
86295
87171
|
maybeRestartAgent(opts, ctx) {
|
|
86296
87172
|
const { harness, model, workingDir, recentLogLines } = ctx;
|
|
86297
|
-
const key = agentKey3(opts.chatroomId, opts.role);
|
|
86298
87173
|
const logs = recentLogLines ?? [];
|
|
86299
87174
|
if (!harness || !workingDir) {
|
|
86300
87175
|
console.log(`[AgentProcessManager] ⚠️ Cannot restart — missing harness or workingDir ` + `(role: ${opts.role}, harness: ${harness ?? "none"}, workingDir: ${workingDir ?? "none"})`);
|
|
@@ -86304,7 +87179,11 @@ class AgentProcessManager {
|
|
|
86304
87179
|
this.handlePermanentFailureForRestart(opts, recentLogLines, ctx.terminalProviderFailureHandled);
|
|
86305
87180
|
return;
|
|
86306
87181
|
}
|
|
86307
|
-
const
|
|
87182
|
+
const hadRunError = this.hasCursorSdkRunErrorInContext(logs);
|
|
87183
|
+
if (harness === "cursor-sdk") {
|
|
87184
|
+
this.retryCursorSdkSessionReopen(opts, ctx, hadRunError);
|
|
87185
|
+
return;
|
|
87186
|
+
}
|
|
86308
87187
|
this.ensureRunning({
|
|
86309
87188
|
chatroomId: opts.chatroomId,
|
|
86310
87189
|
role: opts.role,
|
|
@@ -86312,7 +87191,7 @@ class AgentProcessManager {
|
|
|
86312
87191
|
model,
|
|
86313
87192
|
workingDir,
|
|
86314
87193
|
reason: "platform.crash_recovery",
|
|
86315
|
-
wantResume:
|
|
87194
|
+
wantResume: hadRunError ? false : ctx.wantResume ?? true
|
|
86316
87195
|
}).then((result) => {
|
|
86317
87196
|
if (!result.success) {
|
|
86318
87197
|
console.log(`[AgentProcessManager] ⚠️ Agent restart did not complete for ${opts.role}: ${result.error ?? "unknown"}`);
|
|
@@ -86322,6 +87201,72 @@ class AgentProcessManager {
|
|
|
86322
87201
|
this.emitStartFailedEvent(opts.role, opts.chatroomId, err.message);
|
|
86323
87202
|
});
|
|
86324
87203
|
}
|
|
87204
|
+
clearHarnessSessionAfterResumePhaseFailure(key, opts) {
|
|
87205
|
+
const stored = this.lastHarnessSessions.get(key);
|
|
87206
|
+
this.clearLastHarnessSession(key);
|
|
87207
|
+
if (stored?.harnessSessionId) {
|
|
87208
|
+
notifyNativeSessionLost({
|
|
87209
|
+
chatroomId: opts.chatroomId,
|
|
87210
|
+
role: opts.role,
|
|
87211
|
+
harnessSessionId: stored.harnessSessionId
|
|
87212
|
+
});
|
|
87213
|
+
}
|
|
87214
|
+
}
|
|
87215
|
+
resolveCursorSdkReopenWantResume(hadRunError, attempt, ctx) {
|
|
87216
|
+
if (hadRunError && attempt <= CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS) {
|
|
87217
|
+
return true;
|
|
87218
|
+
}
|
|
87219
|
+
if (hadRunError) {
|
|
87220
|
+
return false;
|
|
87221
|
+
}
|
|
87222
|
+
return ctx.wantResume ?? true;
|
|
87223
|
+
}
|
|
87224
|
+
async retryCursorSdkSessionReopen(opts, ctx, hadRunError) {
|
|
87225
|
+
const key = agentKey3(opts.chatroomId, opts.role);
|
|
87226
|
+
if (this.sessionReopenRetryInFlight.has(key)) {
|
|
87227
|
+
return;
|
|
87228
|
+
}
|
|
87229
|
+
this.sessionReopenRetryInFlight.add(key);
|
|
87230
|
+
const harness = ctx.harness;
|
|
87231
|
+
const model = ctx.model;
|
|
87232
|
+
const workingDir = ctx.workingDir;
|
|
87233
|
+
let lastError = "unknown";
|
|
87234
|
+
try {
|
|
87235
|
+
for (let attempt = 1;attempt <= CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS; attempt++) {
|
|
87236
|
+
if (hadRunError && attempt === CURSOR_SDK_SESSION_RESUME_FIRST_ATTEMPTS + 1) {
|
|
87237
|
+
this.clearHarnessSessionAfterResumePhaseFailure(key, opts);
|
|
87238
|
+
}
|
|
87239
|
+
const wantResume = this.resolveCursorSdkReopenWantResume(hadRunError, attempt, ctx);
|
|
87240
|
+
const storedSessionId = this.lastHarnessSessions.get(key)?.harnessSessionId;
|
|
87241
|
+
await this.emitSessionReopenRetry(opts.chatroomId, opts.role, attempt, attempt > 1 ? lastError : undefined, storedSessionId);
|
|
87242
|
+
const result = await this.ensureRunning({
|
|
87243
|
+
chatroomId: opts.chatroomId,
|
|
87244
|
+
role: opts.role,
|
|
87245
|
+
agentHarness: harness,
|
|
87246
|
+
model,
|
|
87247
|
+
workingDir,
|
|
87248
|
+
reason: CURSOR_SDK_SESSION_REOPEN_REASON,
|
|
87249
|
+
wantResume
|
|
87250
|
+
});
|
|
87251
|
+
if (result.success) {
|
|
87252
|
+
return;
|
|
87253
|
+
}
|
|
87254
|
+
lastError = result.error ?? "unknown";
|
|
87255
|
+
if (attempt < CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS) {
|
|
87256
|
+
await this.deps.clock.delay(CURSOR_SDK_SESSION_REOPEN_INTERVAL_MS);
|
|
87257
|
+
}
|
|
87258
|
+
}
|
|
87259
|
+
const failureMessage = `cursor-sdk session reopen failed after ${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS} attempts: ${lastError}`;
|
|
87260
|
+
console.log(`[AgentProcessManager] ⛔ ${failureMessage}`);
|
|
87261
|
+
this.emitStartFailedEvent(opts.role, opts.chatroomId, failureMessage);
|
|
87262
|
+
} catch (err) {
|
|
87263
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87264
|
+
console.log(` ⚠️ cursor-sdk session reopen retry loop failed: ${message}`);
|
|
87265
|
+
this.emitStartFailedEvent(opts.role, opts.chatroomId, message);
|
|
87266
|
+
} finally {
|
|
87267
|
+
this.sessionReopenRetryInFlight.delete(key);
|
|
87268
|
+
}
|
|
87269
|
+
}
|
|
86325
87270
|
handlePermanentFailureForRestart(opts, recentLogLines, startFailedAlreadyEmitted) {
|
|
86326
87271
|
const error = isTerminalProviderFailureInLogs(recentLogLines ?? []) ? formatTerminalProviderFailureMessage(recentLogLines ?? []) : formatPermanentHarnessFailureMessage(recentLogLines ?? []);
|
|
86327
87272
|
console.log(`[AgentProcessManager] ⛔ Skipping restart — ${error}`);
|
|
@@ -86474,13 +87419,6 @@ class AgentProcessManager {
|
|
|
86474
87419
|
try {
|
|
86475
87420
|
await this.killExistingBeforeSpawn(opts.chatroomId, opts.role);
|
|
86476
87421
|
const result = await this.doEnsureRunning(key, slot, opts);
|
|
86477
|
-
if (!result.success && opts.reason === "platform.auto_restart_on_new_context") {
|
|
86478
|
-
console.log(`[AgentProcessManager] Context auto-restart failed (${result.error ?? "unknown"}), ` + `attempting crash recovery (rate-limited)`);
|
|
86479
|
-
return await this.doEnsureRunning(key, slot, {
|
|
86480
|
-
...opts,
|
|
86481
|
-
reason: "platform.crash_recovery"
|
|
86482
|
-
});
|
|
86483
|
-
}
|
|
86484
87422
|
return result;
|
|
86485
87423
|
} finally {
|
|
86486
87424
|
if (slot.pendingOperation) {
|
|
@@ -86637,6 +87575,23 @@ class AgentProcessManager {
|
|
|
86637
87575
|
console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
|
|
86638
87576
|
}
|
|
86639
87577
|
}
|
|
87578
|
+
async emitSessionReopenRetry(chatroomId, role, attempt, error, harnessSessionId) {
|
|
87579
|
+
try {
|
|
87580
|
+
await this.deps.backend.mutation(api.machines.emitSessionReopenRetry, {
|
|
87581
|
+
sessionId: this.deps.sessionId,
|
|
87582
|
+
machineId: this.deps.machineId,
|
|
87583
|
+
chatroomId,
|
|
87584
|
+
role,
|
|
87585
|
+
attempt,
|
|
87586
|
+
maxAttempts: CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS,
|
|
87587
|
+
...error ? { error } : {},
|
|
87588
|
+
...harnessSessionId ? { harnessSessionId } : {}
|
|
87589
|
+
});
|
|
87590
|
+
console.log(`[AgentProcessManager] ✅ Emitted agent.sessionReopenRetry for ${role} (attempt ${attempt}/${CURSOR_SDK_SESSION_REOPEN_MAX_ATTEMPTS})`);
|
|
87591
|
+
} catch (err) {
|
|
87592
|
+
console.log(` ⚠️ Failed to emit sessionReopenRetry event: ${err.message}`);
|
|
87593
|
+
}
|
|
87594
|
+
}
|
|
86640
87595
|
resetSlotIdle(slot) {
|
|
86641
87596
|
slot.state = "idle";
|
|
86642
87597
|
slot.pendingOperation = undefined;
|
|
@@ -86856,9 +87811,25 @@ class AgentProcessManager {
|
|
|
86856
87811
|
this.registerSpawnCallbacks(slot, opts, spawnResult, pid);
|
|
86857
87812
|
await this.emitNativeWaiting(opts.chatroomId, opts.role, opts.agentHarness);
|
|
86858
87813
|
}
|
|
86859
|
-
async emitNativeWaiting(chatroomId, role, harness) {
|
|
87814
|
+
async emitNativeWaiting(chatroomId, role, harness, reason = "spawn") {
|
|
86860
87815
|
if (!getHarnessCapabilities(harness).supportsNativeIntegration)
|
|
86861
|
-
return;
|
|
87816
|
+
return false;
|
|
87817
|
+
if (reason === "turn-end") {
|
|
87818
|
+
try {
|
|
87819
|
+
const participant = await this.deps.backend.query(api.participants.getByRole, {
|
|
87820
|
+
sessionId: this.deps.sessionId,
|
|
87821
|
+
chatroomId,
|
|
87822
|
+
role
|
|
87823
|
+
});
|
|
87824
|
+
if (!shouldEmitNativeWaitingOnTurnEnd(participant?.lastStatus)) {
|
|
87825
|
+
console.log(`[AgentProcessManager] Skipping native:waiting for ${role} — active work (${participant?.lastStatus})`);
|
|
87826
|
+
return false;
|
|
87827
|
+
}
|
|
87828
|
+
} catch (err) {
|
|
87829
|
+
console.log(` ⚠️ Failed to check status before native:waiting for ${role}: ${err.message}`);
|
|
87830
|
+
return false;
|
|
87831
|
+
}
|
|
87832
|
+
}
|
|
86862
87833
|
try {
|
|
86863
87834
|
await this.deps.backend.mutation(api.participants.join, {
|
|
86864
87835
|
sessionId: this.deps.sessionId,
|
|
@@ -86866,8 +87837,10 @@ class AgentProcessManager {
|
|
|
86866
87837
|
role,
|
|
86867
87838
|
action: NATIVE_WAITING_ACTION
|
|
86868
87839
|
});
|
|
87840
|
+
return true;
|
|
86869
87841
|
} catch (err) {
|
|
86870
87842
|
console.log(` ⚠️ Failed to emit native:waiting for ${role}: ${err.message}`);
|
|
87843
|
+
return false;
|
|
86871
87844
|
}
|
|
86872
87845
|
}
|
|
86873
87846
|
async doEnsureRunning(key, slot, opts) {
|
|
@@ -87021,11 +87994,13 @@ var init_agent_process_manager = __esm(() => {
|
|
|
87021
87994
|
init_api3();
|
|
87022
87995
|
init_orphan_tracker();
|
|
87023
87996
|
init_native_harness_session_exit();
|
|
87997
|
+
init_native_task_delivery_coordinator();
|
|
87024
87998
|
init_agent_lifecycle();
|
|
87025
87999
|
init_abort_resume_storm();
|
|
87026
88000
|
init_classify_resume_storm_reason();
|
|
87027
88001
|
init_terminal_provider_error();
|
|
87028
88002
|
init_handle_turn_completed();
|
|
88003
|
+
init_predicates();
|
|
87029
88004
|
init_spawn_policy();
|
|
87030
88005
|
init_agent_lifecycle_runtime();
|
|
87031
88006
|
init_agent_lifecycle_types();
|
|
@@ -87039,7 +88014,7 @@ class SpawnRateLimiter {
|
|
|
87039
88014
|
this.config = { ...DEFAULT_CONFIG2, ...config3 };
|
|
87040
88015
|
}
|
|
87041
88016
|
tryConsume(chatroomId, reason) {
|
|
87042
|
-
if (reason.startsWith("user.") || reason === "platform.
|
|
88017
|
+
if (reason.startsWith("user.") || reason === "platform.cursor_sdk_session_reopen") {
|
|
87043
88018
|
return { allowed: true };
|
|
87044
88019
|
}
|
|
87045
88020
|
const bucket = this._getOrCreateBucket(chatroomId);
|
|
@@ -87066,13 +88041,16 @@ class SpawnRateLimiter {
|
|
|
87066
88041
|
};
|
|
87067
88042
|
}
|
|
87068
88043
|
_getOrCreateBucket(chatroomId) {
|
|
87069
|
-
|
|
87070
|
-
|
|
87071
|
-
|
|
87072
|
-
lastRefillAt: Date.now()
|
|
87073
|
-
});
|
|
88044
|
+
const existing = this.buckets.get(chatroomId);
|
|
88045
|
+
if (existing) {
|
|
88046
|
+
return existing;
|
|
87074
88047
|
}
|
|
87075
|
-
|
|
88048
|
+
const bucket = {
|
|
88049
|
+
tokens: this.config.initialTokens,
|
|
88050
|
+
lastRefillAt: Date.now()
|
|
88051
|
+
};
|
|
88052
|
+
this.buckets.set(chatroomId, bucket);
|
|
88053
|
+
return bucket;
|
|
87076
88054
|
}
|
|
87077
88055
|
_refill(bucket) {
|
|
87078
88056
|
const now = Date.now();
|
|
@@ -87959,19 +88937,445 @@ var init_task_monitor_logic = __esm(() => {
|
|
|
87959
88937
|
init_predicates();
|
|
87960
88938
|
});
|
|
87961
88939
|
|
|
88940
|
+
// src/infrastructure/incremental-sync/working-snapshot.ts
|
|
88941
|
+
class WorkingSnapshot {
|
|
88942
|
+
opts;
|
|
88943
|
+
rows = new Map;
|
|
88944
|
+
constructor(opts) {
|
|
88945
|
+
this.opts = opts;
|
|
88946
|
+
}
|
|
88947
|
+
replaceAll(rows) {
|
|
88948
|
+
this.rows.clear();
|
|
88949
|
+
for (const row of rows) {
|
|
88950
|
+
this.rows.set(this.opts.rowKey(row), row);
|
|
88951
|
+
}
|
|
88952
|
+
}
|
|
88953
|
+
getByKey(key) {
|
|
88954
|
+
return this.rows.get(key);
|
|
88955
|
+
}
|
|
88956
|
+
getBySignal(signal) {
|
|
88957
|
+
return this.rows.get(this.opts.signalKey(signal));
|
|
88958
|
+
}
|
|
88959
|
+
mergeSignal(signal) {
|
|
88960
|
+
const key = this.opts.signalKey(signal);
|
|
88961
|
+
const merged = this.opts.mergeSignal(this.rows.get(key), signal);
|
|
88962
|
+
if (!merged) {
|
|
88963
|
+
return;
|
|
88964
|
+
}
|
|
88965
|
+
this.rows.set(key, merged);
|
|
88966
|
+
return merged;
|
|
88967
|
+
}
|
|
88968
|
+
}
|
|
88969
|
+
|
|
88970
|
+
// src/commands/machine/daemon-start/task-monitor-snapshot.ts
|
|
88971
|
+
function taskSnapshotKey(taskId, role) {
|
|
88972
|
+
return `${taskId}:${role}`;
|
|
88973
|
+
}
|
|
88974
|
+
function mergeSignalIntoTaskSnapshot(existing, signal) {
|
|
88975
|
+
if (!existing) {
|
|
88976
|
+
return;
|
|
88977
|
+
}
|
|
88978
|
+
return {
|
|
88979
|
+
...existing,
|
|
88980
|
+
status: signal.status,
|
|
88981
|
+
agentConfig: {
|
|
88982
|
+
...existing.agentConfig,
|
|
88983
|
+
spawnedAgentPid: signal.spawnedAgentPid ?? existing.agentConfig.spawnedAgentPid,
|
|
88984
|
+
desiredState: signal.desiredState ?? existing.agentConfig.desiredState
|
|
88985
|
+
},
|
|
88986
|
+
participant: {
|
|
88987
|
+
lastSeenAction: signal.lastSeenAction ?? existing.participant?.lastSeenAction ?? null,
|
|
88988
|
+
lastSeenAt: existing.participant?.lastSeenAt ?? null,
|
|
88989
|
+
lastStatus: existing.participant?.lastStatus ?? null
|
|
88990
|
+
}
|
|
88991
|
+
};
|
|
88992
|
+
}
|
|
88993
|
+
function createTaskMonitorSnapshot() {
|
|
88994
|
+
return new WorkingSnapshot(taskMonitorSnapshotOptions);
|
|
88995
|
+
}
|
|
88996
|
+
var taskMonitorSnapshotOptions;
|
|
88997
|
+
var init_task_monitor_snapshot = __esm(() => {
|
|
88998
|
+
taskMonitorSnapshotOptions = {
|
|
88999
|
+
rowKey: (row) => taskSnapshotKey(row.taskId, row.agentConfig.role),
|
|
89000
|
+
signalKey: (signal) => taskSnapshotKey(signal.taskId, signal.role),
|
|
89001
|
+
mergeSignal: mergeSignalIntoTaskSnapshot
|
|
89002
|
+
};
|
|
89003
|
+
});
|
|
89004
|
+
|
|
89005
|
+
// src/infrastructure/incremental-sync/layers.ts
|
|
89006
|
+
var IntervalClock, IntervalClockLive;
|
|
89007
|
+
var init_layers = __esm(() => {
|
|
89008
|
+
init_esm();
|
|
89009
|
+
IntervalClock = class IntervalClock extends exports_Context.Tag("IntervalClock")() {
|
|
89010
|
+
};
|
|
89011
|
+
IntervalClockLive = exports_Layer.succeed(IntervalClock, {
|
|
89012
|
+
sleep: (ms) => exports_Effect.promise(() => new Promise((resolve5) => setTimeout(resolve5, ms)))
|
|
89013
|
+
});
|
|
89014
|
+
});
|
|
89015
|
+
|
|
89016
|
+
// src/infrastructure/incremental-sync/message-buffer.ts
|
|
89017
|
+
class MessageBuffer {
|
|
89018
|
+
keyOf;
|
|
89019
|
+
queue = [];
|
|
89020
|
+
inBuffer = new Set;
|
|
89021
|
+
inFlight = new Map;
|
|
89022
|
+
recentlyAcked = new Map;
|
|
89023
|
+
dedupe;
|
|
89024
|
+
dedupeTtlMs;
|
|
89025
|
+
maxSize;
|
|
89026
|
+
constructor(config3, keyOf) {
|
|
89027
|
+
this.keyOf = keyOf;
|
|
89028
|
+
this.dedupe = config3.dedupe ?? true;
|
|
89029
|
+
this.dedupeTtlMs = config3.dedupeTtlMs ?? 0;
|
|
89030
|
+
this.maxSize = config3.maxSize;
|
|
89031
|
+
}
|
|
89032
|
+
enqueue(items) {
|
|
89033
|
+
this.pruneRecentlyAcked();
|
|
89034
|
+
let enqueued = 0;
|
|
89035
|
+
for (const item of items) {
|
|
89036
|
+
const key = this.keyOf(item);
|
|
89037
|
+
if (this.dedupe && this.shouldSkip(key)) {
|
|
89038
|
+
continue;
|
|
89039
|
+
}
|
|
89040
|
+
this.queue.push({ key, item });
|
|
89041
|
+
this.inBuffer.add(key);
|
|
89042
|
+
enqueued++;
|
|
89043
|
+
}
|
|
89044
|
+
if (enqueued > 0) {
|
|
89045
|
+
this.queue.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
89046
|
+
}
|
|
89047
|
+
this.enforceMaxSize();
|
|
89048
|
+
return enqueued;
|
|
89049
|
+
}
|
|
89050
|
+
dequeue() {
|
|
89051
|
+
this.pruneRecentlyAcked();
|
|
89052
|
+
const next4 = this.queue.shift();
|
|
89053
|
+
if (!next4)
|
|
89054
|
+
return;
|
|
89055
|
+
this.inBuffer.delete(next4.key);
|
|
89056
|
+
this.inFlight.set(next4.key, next4.item);
|
|
89057
|
+
return next4.item;
|
|
89058
|
+
}
|
|
89059
|
+
ack(key) {
|
|
89060
|
+
this.inFlight.delete(key);
|
|
89061
|
+
if (this.dedupe) {
|
|
89062
|
+
this.recentlyAcked.set(key, Date.now());
|
|
89063
|
+
}
|
|
89064
|
+
}
|
|
89065
|
+
nack(key, requeue = false) {
|
|
89066
|
+
const item = this.inFlight.get(key);
|
|
89067
|
+
this.inFlight.delete(key);
|
|
89068
|
+
if (requeue && item !== undefined) {
|
|
89069
|
+
this.enqueue([item]);
|
|
89070
|
+
}
|
|
89071
|
+
}
|
|
89072
|
+
size() {
|
|
89073
|
+
return this.queue.length;
|
|
89074
|
+
}
|
|
89075
|
+
highKeyOf(items) {
|
|
89076
|
+
if (items.length === 0)
|
|
89077
|
+
return null;
|
|
89078
|
+
let high = null;
|
|
89079
|
+
for (const item of items) {
|
|
89080
|
+
const key = this.keyOf(item);
|
|
89081
|
+
if (high === null || key > high) {
|
|
89082
|
+
high = key;
|
|
89083
|
+
}
|
|
89084
|
+
}
|
|
89085
|
+
return high;
|
|
89086
|
+
}
|
|
89087
|
+
shouldSkip(key) {
|
|
89088
|
+
if (this.inBuffer.has(key) || this.inFlight.has(key)) {
|
|
89089
|
+
return true;
|
|
89090
|
+
}
|
|
89091
|
+
if (this.dedupeTtlMs > 0) {
|
|
89092
|
+
const ackedAt = this.recentlyAcked.get(key);
|
|
89093
|
+
if (ackedAt !== undefined && Date.now() - ackedAt < this.dedupeTtlMs) {
|
|
89094
|
+
return true;
|
|
89095
|
+
}
|
|
89096
|
+
} else if (this.recentlyAcked.has(key)) {
|
|
89097
|
+
return true;
|
|
89098
|
+
}
|
|
89099
|
+
return false;
|
|
89100
|
+
}
|
|
89101
|
+
pruneRecentlyAcked() {
|
|
89102
|
+
if (this.dedupeTtlMs <= 0)
|
|
89103
|
+
return;
|
|
89104
|
+
const now = Date.now();
|
|
89105
|
+
for (const [key, ackedAt] of this.recentlyAcked) {
|
|
89106
|
+
if (now - ackedAt >= this.dedupeTtlMs) {
|
|
89107
|
+
this.recentlyAcked.delete(key);
|
|
89108
|
+
}
|
|
89109
|
+
}
|
|
89110
|
+
}
|
|
89111
|
+
enforceMaxSize() {
|
|
89112
|
+
while (this.queue.length > this.maxSize) {
|
|
89113
|
+
const dropped = this.queue.shift();
|
|
89114
|
+
if (dropped) {
|
|
89115
|
+
this.inBuffer.delete(dropped.key);
|
|
89116
|
+
console.warn(`[incremental-sync] Buffer full — dropped oldest unacked item (key=${dropped.key})`);
|
|
89117
|
+
}
|
|
89118
|
+
}
|
|
89119
|
+
}
|
|
89120
|
+
}
|
|
89121
|
+
|
|
89122
|
+
// src/infrastructure/incremental-sync/resolve-high-key.ts
|
|
89123
|
+
function resolveHighKey(def, buffer, page) {
|
|
89124
|
+
if (page.highKey !== null)
|
|
89125
|
+
return page.highKey;
|
|
89126
|
+
const fromItems = buffer.highKeyOf(page.items);
|
|
89127
|
+
if (fromItems !== null)
|
|
89128
|
+
return fromItems;
|
|
89129
|
+
if (def.itemToKey && page.items.length > 0) {
|
|
89130
|
+
let high = null;
|
|
89131
|
+
for (const item of page.items) {
|
|
89132
|
+
const key = def.itemToKey(item);
|
|
89133
|
+
if (high === null || key > high)
|
|
89134
|
+
high = key;
|
|
89135
|
+
}
|
|
89136
|
+
return high;
|
|
89137
|
+
}
|
|
89138
|
+
return null;
|
|
89139
|
+
}
|
|
89140
|
+
|
|
89141
|
+
// src/infrastructure/incremental-sync/subscribe-loop.ts
|
|
89142
|
+
function startSubscribeLoop(opts) {
|
|
89143
|
+
let afterKey = opts.initialAfterKey ?? null;
|
|
89144
|
+
let stopped = false;
|
|
89145
|
+
let unsubscribe;
|
|
89146
|
+
let drainInFlight = false;
|
|
89147
|
+
const subscribe = () => {
|
|
89148
|
+
if (stopped)
|
|
89149
|
+
return;
|
|
89150
|
+
unsubscribe?.();
|
|
89151
|
+
unsubscribe = opts.wsClient.onUpdate(opts.target.query, opts.target.buildArgs(opts.args, afterKey, opts.config.limit), (result) => {
|
|
89152
|
+
if (stopped)
|
|
89153
|
+
return;
|
|
89154
|
+
const page = opts.target.parsePage(result);
|
|
89155
|
+
drainPages(page);
|
|
89156
|
+
}, (err) => {
|
|
89157
|
+
opts.onError?.(err);
|
|
89158
|
+
});
|
|
89159
|
+
};
|
|
89160
|
+
const fetchPage = async (cursor) => {
|
|
89161
|
+
const result = await opts.wsClient.query(opts.target.query, opts.target.buildArgs(opts.args, cursor, opts.config.limit));
|
|
89162
|
+
return opts.target.parsePage(result);
|
|
89163
|
+
};
|
|
89164
|
+
const drainPages = async (initialPage) => {
|
|
89165
|
+
if (stopped || drainInFlight)
|
|
89166
|
+
return;
|
|
89167
|
+
drainInFlight = true;
|
|
89168
|
+
try {
|
|
89169
|
+
const cursorAtStart = afterKey;
|
|
89170
|
+
let page = initialPage;
|
|
89171
|
+
while (!stopped && page.items.length > 0) {
|
|
89172
|
+
opts.buffer.enqueue(page.items);
|
|
89173
|
+
const nextKey = resolveHighKey(opts.def, opts.buffer, page);
|
|
89174
|
+
if (nextKey === null) {
|
|
89175
|
+
break;
|
|
89176
|
+
}
|
|
89177
|
+
afterKey = nextKey;
|
|
89178
|
+
if (!page.hasMore) {
|
|
89179
|
+
break;
|
|
89180
|
+
}
|
|
89181
|
+
page = await fetchPage(afterKey);
|
|
89182
|
+
}
|
|
89183
|
+
if (!stopped && afterKey !== cursorAtStart) {
|
|
89184
|
+
subscribe();
|
|
89185
|
+
}
|
|
89186
|
+
} catch (err) {
|
|
89187
|
+
opts.onError?.(err);
|
|
89188
|
+
} finally {
|
|
89189
|
+
drainInFlight = false;
|
|
89190
|
+
}
|
|
89191
|
+
};
|
|
89192
|
+
subscribe();
|
|
89193
|
+
return {
|
|
89194
|
+
stop: () => {
|
|
89195
|
+
stopped = true;
|
|
89196
|
+
unsubscribe?.();
|
|
89197
|
+
unsubscribe = undefined;
|
|
89198
|
+
},
|
|
89199
|
+
getAfterKey: () => afterKey
|
|
89200
|
+
};
|
|
89201
|
+
}
|
|
89202
|
+
var init_subscribe_loop = () => {};
|
|
89203
|
+
|
|
89204
|
+
// src/infrastructure/incremental-sync/feed-runtime.ts
|
|
89205
|
+
function runWorkerLoop(feedName, buffer, keyOf, onItem) {
|
|
89206
|
+
return exports_Effect.gen(function* () {
|
|
89207
|
+
while (true) {
|
|
89208
|
+
const item = buffer.dequeue();
|
|
89209
|
+
if (item === undefined) {
|
|
89210
|
+
yield* exports_Effect.sleep("10 millis");
|
|
89211
|
+
continue;
|
|
89212
|
+
}
|
|
89213
|
+
const key = keyOf(item);
|
|
89214
|
+
const ctx = {
|
|
89215
|
+
item,
|
|
89216
|
+
feedName,
|
|
89217
|
+
ack: () => buffer.ack(key),
|
|
89218
|
+
nack: (opts) => buffer.nack(key, opts?.requeue ?? false)
|
|
89219
|
+
};
|
|
89220
|
+
yield* onItem(ctx).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89221
|
+
}
|
|
89222
|
+
});
|
|
89223
|
+
}
|
|
89224
|
+
var runIncrementalSubscribeLive = (opts) => exports_Effect.gen(function* () {
|
|
89225
|
+
const buffer = new MessageBuffer(opts.buffer, opts.def.itemKey);
|
|
89226
|
+
const subscribeHandle = startSubscribeLoop({
|
|
89227
|
+
wsClient: opts.wsClient,
|
|
89228
|
+
target: opts.target,
|
|
89229
|
+
args: opts.args,
|
|
89230
|
+
buffer,
|
|
89231
|
+
def: opts.def,
|
|
89232
|
+
config: opts.subscribe,
|
|
89233
|
+
initialAfterKey: opts.initialAfterKey ?? null,
|
|
89234
|
+
onError: opts.onError
|
|
89235
|
+
});
|
|
89236
|
+
const workerFiber = yield* exports_Effect.forkDaemon(runWorkerLoop(opts.def.name, buffer, opts.def.itemKey, opts.onItem));
|
|
89237
|
+
return {
|
|
89238
|
+
buffer,
|
|
89239
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89240
|
+
subscribeHandle.stop();
|
|
89241
|
+
yield* exports_Fiber.interrupt(workerFiber);
|
|
89242
|
+
})
|
|
89243
|
+
};
|
|
89244
|
+
}), runReconcilePoll = (opts) => exports_Effect.gen(function* () {
|
|
89245
|
+
const clock4 = yield* IntervalClock;
|
|
89246
|
+
const backoffCfg = opts.backoff ?? { initialMs: 1000, maxMs: 30000 };
|
|
89247
|
+
let backoffMs = opts.intervalMs;
|
|
89248
|
+
let stopped = false;
|
|
89249
|
+
const loopFiber = yield* exports_Effect.forkDaemon(exports_Effect.gen(function* () {
|
|
89250
|
+
while (!stopped) {
|
|
89251
|
+
const pollOutcome = yield* exports_Effect.either(exports_Effect.tryPromise(() => opts.poll(opts.args)));
|
|
89252
|
+
if (pollOutcome._tag === "Left") {
|
|
89253
|
+
backoffMs = Math.min(backoffMs === opts.intervalMs ? backoffCfg.initialMs : backoffMs * 2, backoffCfg.maxMs);
|
|
89254
|
+
yield* clock4.sleep(backoffMs);
|
|
89255
|
+
continue;
|
|
89256
|
+
}
|
|
89257
|
+
yield* opts.onResult(pollOutcome.right);
|
|
89258
|
+
backoffMs = opts.intervalMs;
|
|
89259
|
+
yield* clock4.sleep(opts.intervalMs);
|
|
89260
|
+
}
|
|
89261
|
+
}));
|
|
89262
|
+
return {
|
|
89263
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89264
|
+
stopped = true;
|
|
89265
|
+
yield* exports_Fiber.interrupt(loopFiber);
|
|
89266
|
+
})
|
|
89267
|
+
};
|
|
89268
|
+
}), runReconcilePollLive = (opts) => runReconcilePoll(opts).pipe(exports_Effect.provide(IntervalClockLive));
|
|
89269
|
+
var init_feed_runtime = __esm(() => {
|
|
89270
|
+
init_esm();
|
|
89271
|
+
init_layers();
|
|
89272
|
+
init_subscribe_loop();
|
|
89273
|
+
});
|
|
89274
|
+
|
|
89275
|
+
// src/infrastructure/incremental-sync/dual-channel-feed.ts
|
|
89276
|
+
var runDualChannelFeedLive = (opts) => exports_Effect.gen(function* () {
|
|
89277
|
+
const initial = yield* exports_Effect.tryPromise(() => opts.fetchReconcile()).pipe(exports_Effect.orElseSucceed(() => null));
|
|
89278
|
+
if (initial !== null) {
|
|
89279
|
+
const rows = opts.extractReconcileRows(initial);
|
|
89280
|
+
opts.snapshot.replaceAll(rows);
|
|
89281
|
+
if (rows.length > 0) {
|
|
89282
|
+
yield* opts.onReconcileRows(rows).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89283
|
+
}
|
|
89284
|
+
}
|
|
89285
|
+
const seedKey = yield* exports_Effect.tryPromise(() => opts.seedCursor()).pipe(exports_Effect.orElseSucceed(() => null));
|
|
89286
|
+
const hydrateFromReconcile = async () => {
|
|
89287
|
+
const result = await opts.fetchReconcile();
|
|
89288
|
+
opts.snapshot.replaceAll(opts.extractReconcileRows(result));
|
|
89289
|
+
};
|
|
89290
|
+
const resolveRowForSignal = async (signal) => {
|
|
89291
|
+
const merged = opts.snapshot.mergeSignal(signal);
|
|
89292
|
+
if (merged) {
|
|
89293
|
+
return merged;
|
|
89294
|
+
}
|
|
89295
|
+
await hydrateFromReconcile();
|
|
89296
|
+
return opts.snapshot.mergeSignal(signal) ?? opts.snapshot.getBySignal(signal);
|
|
89297
|
+
};
|
|
89298
|
+
const signalHandle = yield* runIncrementalSubscribeLive({
|
|
89299
|
+
wsClient: opts.wsClient,
|
|
89300
|
+
def: opts.def,
|
|
89301
|
+
target: opts.target,
|
|
89302
|
+
args: opts.args,
|
|
89303
|
+
buffer: opts.buffer,
|
|
89304
|
+
subscribe: opts.subscribe,
|
|
89305
|
+
initialAfterKey: seedKey,
|
|
89306
|
+
onError: opts.onSubscribeError,
|
|
89307
|
+
onItem: ({ item: signal, ack }) => exports_Effect.gen(function* () {
|
|
89308
|
+
ack();
|
|
89309
|
+
if (opts.isStopped()) {
|
|
89310
|
+
return;
|
|
89311
|
+
}
|
|
89312
|
+
const row = yield* exports_Effect.tryPromise(() => resolveRowForSignal(signal));
|
|
89313
|
+
if (!row) {
|
|
89314
|
+
return;
|
|
89315
|
+
}
|
|
89316
|
+
yield* opts.onSignalRow(row).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89317
|
+
})
|
|
89318
|
+
});
|
|
89319
|
+
const reconcileHandle = yield* runReconcilePollLive({
|
|
89320
|
+
name: `${opts.name}-reconcile`,
|
|
89321
|
+
poll: () => opts.fetchReconcile(),
|
|
89322
|
+
args: undefined,
|
|
89323
|
+
intervalMs: opts.reconcileIntervalMs,
|
|
89324
|
+
onResult: (result) => exports_Effect.gen(function* () {
|
|
89325
|
+
const rows = opts.extractReconcileRows(result);
|
|
89326
|
+
opts.snapshot.replaceAll(rows);
|
|
89327
|
+
yield* opts.onReconcileRows(rows).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89328
|
+
})
|
|
89329
|
+
});
|
|
89330
|
+
return {
|
|
89331
|
+
stop: () => exports_Effect.gen(function* () {
|
|
89332
|
+
yield* signalHandle.stop();
|
|
89333
|
+
yield* reconcileHandle.stop();
|
|
89334
|
+
})
|
|
89335
|
+
};
|
|
89336
|
+
});
|
|
89337
|
+
var init_dual_channel_feed = __esm(() => {
|
|
89338
|
+
init_esm();
|
|
89339
|
+
init_feed_runtime();
|
|
89340
|
+
});
|
|
89341
|
+
|
|
89342
|
+
// src/infrastructure/incremental-sync/feeds/assigned-task-signals.ts
|
|
89343
|
+
var assignedTaskSignalsFeedDef, assignedTaskSignalsSubscribeTarget, ASSIGNED_TASK_SIGNAL_FEED_LIMIT = 50, ASSIGNED_TASK_SIGNAL_FEED_BUFFER, ASSIGNED_TASK_RECONCILE_INTERVAL_MS = 15000;
|
|
89344
|
+
var init_assigned_task_signals = __esm(() => {
|
|
89345
|
+
init_api3();
|
|
89346
|
+
assignedTaskSignalsFeedDef = {
|
|
89347
|
+
name: "assigned-task-signals",
|
|
89348
|
+
itemKey: (item) => item.revisionKey
|
|
89349
|
+
};
|
|
89350
|
+
assignedTaskSignalsSubscribeTarget = {
|
|
89351
|
+
query: api.machines.subscribeAssignedTaskSignalsSince,
|
|
89352
|
+
buildArgs: (args2, afterKey, limit) => ({
|
|
89353
|
+
sessionId: args2.sessionId,
|
|
89354
|
+
machineId: args2.machineId,
|
|
89355
|
+
afterKey: afterKey ?? undefined,
|
|
89356
|
+
limit
|
|
89357
|
+
}),
|
|
89358
|
+
parsePage: (result) => result
|
|
89359
|
+
};
|
|
89360
|
+
ASSIGNED_TASK_SIGNAL_FEED_BUFFER = {
|
|
89361
|
+
maxSize: 200,
|
|
89362
|
+
dedupe: true
|
|
89363
|
+
};
|
|
89364
|
+
});
|
|
89365
|
+
|
|
87962
89366
|
// src/commands/machine/daemon-start/task-monitor.ts
|
|
87963
89367
|
function resolveTaskWantResume(task) {
|
|
87964
|
-
return
|
|
89368
|
+
return sessionAugmentationToWantResume(resolveSessionAugmentationForRole(task.taskContent ?? "", task.agentConfig.role));
|
|
87965
89369
|
}
|
|
87966
89370
|
function buildCliNudgeLogLine(task) {
|
|
87967
89371
|
const { chatroomId, agentConfig } = task;
|
|
87968
89372
|
const { role } = agentConfig;
|
|
87969
89373
|
const lastSeenAction = task.participant?.lastSeenAction ?? "unknown";
|
|
87970
|
-
const
|
|
89374
|
+
const augmentationMode = resolveSessionAugmentationForRole(task.taskContent ?? "", role);
|
|
87971
89375
|
const wantResume = resolveTaskWantResume(task);
|
|
87972
|
-
return `[TaskMonitor] nudging ${role}@${chatroomId} — pending task ${task.taskId}, lastSeenAction=${lastSeenAction},
|
|
89376
|
+
return `[TaskMonitor] nudging ${role}@${chatroomId} — pending task ${task.taskId}, lastSeenAction=${lastSeenAction}, session_augmentation=${augmentationMode}, wantResume=${wantResume}`;
|
|
87973
89377
|
}
|
|
87974
|
-
function
|
|
89378
|
+
function resolveTaskRunnerContextFromFull(task) {
|
|
87975
89379
|
const { chatroomId, agentConfig } = task;
|
|
87976
89380
|
const { role } = agentConfig;
|
|
87977
89381
|
const workingDir = agentConfig.workingDir;
|
|
@@ -87985,11 +89389,12 @@ function resolveTaskRunnerContext(task) {
|
|
|
87985
89389
|
wantResume: resolveTaskWantResume(task)
|
|
87986
89390
|
};
|
|
87987
89391
|
}
|
|
87988
|
-
function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
|
|
87989
|
-
const ctx =
|
|
89392
|
+
function executeCliNudge(task, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
89393
|
+
const ctx = resolveTaskRunnerContextFromFull(task);
|
|
87990
89394
|
if (!ctx)
|
|
87991
89395
|
return;
|
|
87992
89396
|
const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
|
|
89397
|
+
const augmentationMode = resolveSessionAugmentationForRole(task.taskContent ?? "", role);
|
|
87993
89398
|
exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
|
|
87994
89399
|
yield* agentMgr.stop({ chatroomId, role, reason: "platform.task_monitor_nudge" });
|
|
87995
89400
|
yield* agentMgr.ensureRunning({
|
|
@@ -88001,10 +89406,24 @@ function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
|
|
|
88001
89406
|
reason: "platform.task_monitor_nudge",
|
|
88002
89407
|
wantResume
|
|
88003
89408
|
});
|
|
89409
|
+
if (roleSupportsSessionAugmentation(role)) {
|
|
89410
|
+
yield* exports_Effect.tryPromise({
|
|
89411
|
+
try: () => sessionDeps.backend.mutation(api.machines.emitSessionAugmented, {
|
|
89412
|
+
sessionId: sessionDeps.sessionId,
|
|
89413
|
+
machineId,
|
|
89414
|
+
chatroomId,
|
|
89415
|
+
role,
|
|
89416
|
+
taskId: task.taskId,
|
|
89417
|
+
mode: augmentationMode,
|
|
89418
|
+
newSessionStarted: sessionAugmentationNewSessionStarted(augmentationMode)
|
|
89419
|
+
}),
|
|
89420
|
+
catch: (err) => err
|
|
89421
|
+
}).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
89422
|
+
}
|
|
88004
89423
|
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] nudge failed for ${role}@${chatroomId}: ${getErrorMessage(err)}`)))));
|
|
88005
89424
|
}
|
|
88006
89425
|
function runNativeReviveEffect(task, runtime4, effectContext2, agentMgr) {
|
|
88007
|
-
const ctx =
|
|
89426
|
+
const ctx = resolveTaskRunnerContextFromFull(task);
|
|
88008
89427
|
if (!ctx)
|
|
88009
89428
|
return;
|
|
88010
89429
|
const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
|
|
@@ -88021,73 +89440,138 @@ function runNativeReviveEffect(task, runtime4, effectContext2, agentMgr) {
|
|
|
88021
89440
|
});
|
|
88022
89441
|
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] native revive failed for ${role}@${chatroomId}: ${getErrorMessage(err)}`)))));
|
|
88023
89442
|
}
|
|
88024
|
-
function
|
|
89443
|
+
async function fetchTaskForAction(sessionDeps, machineId, snapshotRow) {
|
|
89444
|
+
const result = await sessionDeps.backend.query(api.machines.getAssignedTaskForAction, {
|
|
89445
|
+
sessionId: sessionDeps.sessionId,
|
|
89446
|
+
machineId,
|
|
89447
|
+
taskId: snapshotRow.taskId,
|
|
89448
|
+
role: snapshotRow.agentConfig.role
|
|
89449
|
+
});
|
|
89450
|
+
return result;
|
|
89451
|
+
}
|
|
89452
|
+
function runCliNudgeEffect(task, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
88025
89453
|
console.log(buildCliNudgeLogLine(task));
|
|
88026
|
-
executeCliNudge(task, runtime4, effectContext2, agentMgr);
|
|
89454
|
+
executeCliNudge(task, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
88027
89455
|
}
|
|
88028
|
-
function nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr) {
|
|
88029
|
-
for (const
|
|
88030
|
-
|
|
89456
|
+
async function nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
89457
|
+
for (const row of listTasksReadyForNudge(tasks, now, cooldown)) {
|
|
89458
|
+
const full = await fetchTaskForAction(sessionDeps, machineId, row);
|
|
89459
|
+
if (!full)
|
|
89460
|
+
continue;
|
|
89461
|
+
runCliNudgeEffect(full, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
88031
89462
|
}
|
|
88032
89463
|
}
|
|
88033
|
-
function reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr) {
|
|
88034
|
-
for (const
|
|
88035
|
-
|
|
89464
|
+
async function reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId) {
|
|
89465
|
+
for (const row of listNativeTasksNeedingRevive(tasks, localHealth, now, cooldown)) {
|
|
89466
|
+
const full = await fetchTaskForAction(sessionDeps, machineId, row);
|
|
89467
|
+
if (!full)
|
|
89468
|
+
continue;
|
|
89469
|
+
runNativeReviveEffect(full, runtime4, effectContext2, agentMgr);
|
|
88036
89470
|
}
|
|
88037
89471
|
}
|
|
88038
|
-
function processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps) {
|
|
89472
|
+
async function processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps, machineId, pass) {
|
|
89473
|
+
if (tasks.length === 0)
|
|
89474
|
+
return;
|
|
88039
89475
|
const now = Date.now();
|
|
88040
89476
|
const localHealth = {
|
|
88041
89477
|
getSlot: (chatroomId, role) => agentMgr.getSlot(chatroomId, role),
|
|
88042
89478
|
isPidAlive: (pid) => isProcessAlive((p) => process.kill(p, 0), pid)
|
|
88043
89479
|
};
|
|
88044
|
-
reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr);
|
|
89480
|
+
await reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
88045
89481
|
getNativeTaskDeliveryCoordinator().reconcileAssignedTasks({
|
|
88046
89482
|
tasks,
|
|
88047
89483
|
runtime: runtime4,
|
|
88048
89484
|
effectContext: effectContext2,
|
|
88049
89485
|
agentMgr,
|
|
88050
|
-
sessionDeps
|
|
89486
|
+
sessionDeps,
|
|
89487
|
+
machineId
|
|
88051
89488
|
});
|
|
88052
|
-
|
|
89489
|
+
if (pass === "reconcile") {
|
|
89490
|
+
await nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr, sessionDeps, machineId);
|
|
89491
|
+
}
|
|
88053
89492
|
}
|
|
88054
|
-
var
|
|
89493
|
+
var startTaskMonitorEffect = (wsClient2) => exports_Effect.gen(function* () {
|
|
88055
89494
|
const session2 = yield* DaemonSessionService;
|
|
88056
89495
|
const agentMgr = yield* DaemonAgentProcessManagerService;
|
|
88057
89496
|
const effectContext2 = yield* exports_Effect.context();
|
|
88058
89497
|
const runtime4 = yield* exports_Effect.runtime();
|
|
88059
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Starting task-monitor
|
|
89498
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Starting task-monitor (incremental subscribe)`);
|
|
88060
89499
|
const cooldown = new NudgeCooldown;
|
|
89500
|
+
const snapshot = createTaskMonitorSnapshot();
|
|
88061
89501
|
let stopped = false;
|
|
89502
|
+
let monitorPassInFlight = false;
|
|
88062
89503
|
const sessionDeps = {
|
|
88063
89504
|
sessionId: session2.sessionId,
|
|
88064
89505
|
convexUrl: session2.convexUrl,
|
|
89506
|
+
machineId: session2.machineId,
|
|
88065
89507
|
backend: {
|
|
88066
89508
|
mutation: (fn2, args2) => session2.backend.mutation(fn2, args2),
|
|
88067
89509
|
query: (fn2, args2) => session2.backend.query(fn2, args2)
|
|
88068
89510
|
}
|
|
88069
89511
|
};
|
|
88070
|
-
const
|
|
88071
|
-
if (stopped ||
|
|
89512
|
+
const runMonitorPass = (tasks, pass) => {
|
|
89513
|
+
if (stopped || monitorPassInFlight || tasks.length === 0)
|
|
88072
89514
|
return;
|
|
88073
|
-
|
|
89515
|
+
monitorPassInFlight = true;
|
|
89516
|
+
processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps, session2.machineId, pass).finally(() => {
|
|
89517
|
+
monitorPassInFlight = false;
|
|
89518
|
+
});
|
|
88074
89519
|
};
|
|
88075
|
-
const
|
|
89520
|
+
const queryReconcileSnapshots = () => session2.backend.query(api.machines.listAssignedTasksForReconcile, {
|
|
89521
|
+
sessionId: session2.sessionId,
|
|
89522
|
+
machineId: session2.machineId
|
|
89523
|
+
});
|
|
89524
|
+
const feedHandle = yield* runDualChannelFeedLive({
|
|
89525
|
+
name: "assigned-tasks",
|
|
89526
|
+
wsClient: wsClient2,
|
|
89527
|
+
def: assignedTaskSignalsFeedDef,
|
|
89528
|
+
target: assignedTaskSignalsSubscribeTarget,
|
|
89529
|
+
args: {
|
|
89530
|
+
sessionId: session2.sessionId,
|
|
89531
|
+
machineId: session2.machineId
|
|
89532
|
+
},
|
|
89533
|
+
buffer: ASSIGNED_TASK_SIGNAL_FEED_BUFFER,
|
|
89534
|
+
subscribe: { limit: ASSIGNED_TASK_SIGNAL_FEED_LIMIT },
|
|
89535
|
+
snapshot,
|
|
89536
|
+
seedCursor: async () => {
|
|
89537
|
+
const seedPage = await session2.backend.query(api.machines.subscribeAssignedTaskSignalsSince, {
|
|
89538
|
+
sessionId: session2.sessionId,
|
|
89539
|
+
machineId: session2.machineId,
|
|
89540
|
+
limit: ASSIGNED_TASK_SIGNAL_FEED_LIMIT
|
|
89541
|
+
});
|
|
89542
|
+
return seedPage?.highKey ?? null;
|
|
89543
|
+
},
|
|
89544
|
+
fetchReconcile: () => queryReconcileSnapshots(),
|
|
89545
|
+
extractReconcileRows: (result) => result?.tasks ?? [],
|
|
89546
|
+
reconcileIntervalMs: ASSIGNED_TASK_RECONCILE_INTERVAL_MS,
|
|
89547
|
+
isStopped: () => stopped,
|
|
89548
|
+
onSubscribeError: (err) => console.warn(`[${formatTimestamp()}] ⚠️ Task signal subscription error: ${getErrorMessage(err)}`),
|
|
89549
|
+
onSignalRow: (row) => exports_Effect.sync(() => {
|
|
89550
|
+
runMonitorPass([row], "signal");
|
|
89551
|
+
}),
|
|
89552
|
+
onReconcileRows: (tasks) => exports_Effect.sync(() => {
|
|
89553
|
+
runMonitorPass([...tasks], "reconcile");
|
|
89554
|
+
})
|
|
89555
|
+
});
|
|
88076
89556
|
return {
|
|
88077
89557
|
stop() {
|
|
88078
89558
|
stopped = true;
|
|
88079
|
-
|
|
88080
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Task-monitor
|
|
89559
|
+
exports_Effect.runPromise(feedHandle.stop());
|
|
89560
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCCB Task-monitor stopped`);
|
|
88081
89561
|
}
|
|
88082
89562
|
};
|
|
88083
89563
|
});
|
|
88084
89564
|
var init_task_monitor = __esm(() => {
|
|
88085
|
-
|
|
89565
|
+
init_team_agent_settings();
|
|
89566
|
+
init_parse_session_augmentation();
|
|
88086
89567
|
init_esm();
|
|
88087
89568
|
init_daemon_services();
|
|
88088
89569
|
init_native_task_delivery_coordinator();
|
|
88089
89570
|
init_task_monitor_logic();
|
|
89571
|
+
init_task_monitor_snapshot();
|
|
88090
89572
|
init_api3();
|
|
89573
|
+
init_dual_channel_feed();
|
|
89574
|
+
init_assigned_task_signals();
|
|
88091
89575
|
init_convex_error();
|
|
88092
89576
|
});
|
|
88093
89577
|
|
|
@@ -88290,6 +89774,11 @@ function handleRefreshCapabilitiesEffect(event, tracker) {
|
|
|
88290
89774
|
})));
|
|
88291
89775
|
});
|
|
88292
89776
|
}
|
|
89777
|
+
function forkDaemonSyncBatch(runtime4, effectContext2, failureLabel) {
|
|
89778
|
+
exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
|
|
89779
|
+
concurrency: "unbounded"
|
|
89780
|
+
}).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ ${failureLabel}: ${getErrorMessage(err)}`)))));
|
|
89781
|
+
}
|
|
88293
89782
|
var GIT_PUSH_ACTIONS, commandEventHandlers, dispatchCommandEventEffect = (event, tracker) => {
|
|
88294
89783
|
const factory2 = commandEventHandlers[event.type];
|
|
88295
89784
|
return factory2 != null ? factory2(event, tracker) : exports_Effect.void;
|
|
@@ -88348,9 +89837,7 @@ var init_command_loop = __esm(() => {
|
|
|
88348
89837
|
heartbeatCount++;
|
|
88349
89838
|
console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
|
|
88350
89839
|
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)}`)))));
|
|
89840
|
+
forkDaemonSyncBatch(runtime4, effectContext2, "Heartbeat sync failed");
|
|
88354
89841
|
}
|
|
88355
89842
|
}).catch((err) => {
|
|
88356
89843
|
console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
|
|
@@ -88367,14 +89854,13 @@ var init_command_loop = __esm(() => {
|
|
|
88367
89854
|
let pendingHarnessSessionSubscriptionHandle = null;
|
|
88368
89855
|
let commandSubscriptionHandle = null;
|
|
88369
89856
|
let lifecycleManager = null;
|
|
89857
|
+
let closeDirectHarnessSessionsOnShutdown = null;
|
|
88370
89858
|
const activeSessions = new Map;
|
|
88371
89859
|
const harnesses = new Map;
|
|
88372
89860
|
if (observedSyncEnabled) {
|
|
88373
89861
|
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
|
|
88374
89862
|
} 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)}`)))));
|
|
89863
|
+
forkDaemonSyncBatch(runtime4, effectContext2, "Startup sync failed");
|
|
88378
89864
|
}
|
|
88379
89865
|
const PROCESS_KILL_TIMEOUT_MS = 6000;
|
|
88380
89866
|
const CLOSE_TIMEOUT_MS = 3000;
|
|
@@ -88442,8 +89928,12 @@ var init_command_loop = __esm(() => {
|
|
|
88442
89928
|
await withTimeout2(exports_Effect.runPromise(onDaemonShutdownEffect.pipe(exports_Effect.provide(effectContext2))), PROCESS_KILL_TIMEOUT_MS);
|
|
88443
89929
|
};
|
|
88444
89930
|
const closeAllSessionsAndHarnesses = async () => {
|
|
88445
|
-
|
|
88446
|
-
await withTimeout2(
|
|
89931
|
+
if (closeDirectHarnessSessionsOnShutdown) {
|
|
89932
|
+
await withTimeout2(closeDirectHarnessSessionsOnShutdown(), PROCESS_KILL_TIMEOUT_MS);
|
|
89933
|
+
} else {
|
|
89934
|
+
for (const handle of activeSessions.values()) {
|
|
89935
|
+
await withTimeout2(handle.close(), CLOSE_TIMEOUT_MS);
|
|
89936
|
+
}
|
|
88447
89937
|
}
|
|
88448
89938
|
for (const harness of harnesses.values()) {
|
|
88449
89939
|
await withTimeout2(harness.close(), CLOSE_TIMEOUT_MS);
|
|
@@ -88473,7 +89963,7 @@ var init_command_loop = __esm(() => {
|
|
|
88473
89963
|
if (observedSyncEnabled) {
|
|
88474
89964
|
observedSyncSubscriptionHandle = yield* startObservedSyncSubscriptionEffect(wsClient2);
|
|
88475
89965
|
}
|
|
88476
|
-
const taskMonitorHandle = yield*
|
|
89966
|
+
const taskMonitorHandle = yield* startTaskMonitorEffect(wsClient2);
|
|
88477
89967
|
logObserverSubscriptionHandle = startLogObserverSubscription({ sessionId: session2.sessionId, machineId: session2.machineId }, wsClient2);
|
|
88478
89968
|
if (featureFlags.directHarnessWorkers) {
|
|
88479
89969
|
const handles = startDirectHarnessSubscriptions({
|
|
@@ -88486,6 +89976,7 @@ var init_command_loop = __esm(() => {
|
|
|
88486
89976
|
pendingHarnessSessionSubscriptionHandle = handles.pendingHarnessSessionSubscriptionHandle;
|
|
88487
89977
|
commandSubscriptionHandle = handles.commandSubscriptionHandle;
|
|
88488
89978
|
lifecycleManager = handles.lifecycleManager;
|
|
89979
|
+
closeDirectHarnessSessionsOnShutdown = handles.closeSessionsOnShutdown;
|
|
88489
89980
|
}
|
|
88490
89981
|
console.log(`
|
|
88491
89982
|
Listening for commands...`);
|
|
@@ -88672,8 +90163,8 @@ import * as os2 from "os";
|
|
|
88672
90163
|
import * as path4 from "path";
|
|
88673
90164
|
async function isChatroomInstalledDefault() {
|
|
88674
90165
|
try {
|
|
88675
|
-
const { execSync:
|
|
88676
|
-
|
|
90166
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
90167
|
+
execSync3("chatroom --version", { stdio: "pipe" });
|
|
88677
90168
|
return true;
|
|
88678
90169
|
} catch {
|
|
88679
90170
|
return false;
|
|
@@ -89587,4 +91078,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
89587
91078
|
});
|
|
89588
91079
|
program2.parse();
|
|
89589
91080
|
|
|
89590
|
-
//# debugId=
|
|
91081
|
+
//# debugId=D22ED479E2182D5A64756E2164756E21
|