@vibedeckx/linux-x64 0.3.27 → 0.3.29
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/bin.js +411 -62
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -186844,7 +186844,9 @@ var mapAgentSession = (row) => ({
|
|
|
186844
186844
|
last_completed_at: row.last_completed_at,
|
|
186845
186845
|
favorited_at: row.favorited_at,
|
|
186846
186846
|
native_session_id: row.native_session_id,
|
|
186847
|
-
history_epoch: row.history_epoch
|
|
186847
|
+
history_epoch: row.history_epoch,
|
|
186848
|
+
branched_from_session_id: row.branched_from_session_id,
|
|
186849
|
+
branched_from_entry_index: row.branched_from_entry_index
|
|
186848
186850
|
});
|
|
186849
186851
|
var parseActivityTimestamp = (value) => {
|
|
186850
186852
|
const explicitZone = /(?:Z|[+-]\d\d:\d\d)$/i.test(value);
|
|
@@ -187007,6 +187009,7 @@ var mapRemoteCreationIntent = (row) => ({
|
|
|
187007
187009
|
var mapRemoteReviewerCreationIntent = (row) => ({
|
|
187008
187010
|
...row,
|
|
187009
187011
|
review_span: row.review_span,
|
|
187012
|
+
review_context_mode: row.review_context_mode ?? null,
|
|
187010
187013
|
status: row.status
|
|
187011
187014
|
});
|
|
187012
187015
|
var nowActivityAt = () => sql`cast((julianday('now') - 2440587.5) * 86400000 as integer)`;
|
|
@@ -187231,6 +187234,9 @@ var createAgentSessionRepos = (kdb, h) => ({
|
|
|
187231
187234
|
// Toggle favorite without touching updated_at — favoriting is a passive
|
|
187232
187235
|
// bookmark, not a "this session was active" signal, so it must not
|
|
187233
187236
|
// disturb the dropdown's recency ordering.
|
|
187237
|
+
setBranchedFrom: async (id, sourceSessionId, entryIndex) => {
|
|
187238
|
+
await kdb.updateTable("agent_sessions").set({ branched_from_session_id: sourceSessionId, branched_from_entry_index: entryIndex }).where("id", "=", id).execute();
|
|
187239
|
+
},
|
|
187234
187240
|
setFavorited: async (id, favorited) => {
|
|
187235
187241
|
await kdb.updateTable("agent_sessions").set({ favorited_at: favorited ? Date.now() : null }).where("id", "=", id).execute();
|
|
187236
187242
|
},
|
|
@@ -187549,6 +187555,7 @@ var createAgentSessionRepos = (kdb, h) => ({
|
|
|
187549
187555
|
review_focus: intent.reviewFocus ?? null,
|
|
187550
187556
|
source_turn_end_index: intent.sourceTurnEndIndex ?? null,
|
|
187551
187557
|
review_span: intent.reviewSpan,
|
|
187558
|
+
review_context_mode: intent.reviewContextMode ?? null,
|
|
187552
187559
|
agent_type: intent.agentType,
|
|
187553
187560
|
intent_brief: intent.intentBrief ?? null,
|
|
187554
187561
|
user_id: intent.userId ?? null,
|
|
@@ -187558,7 +187565,7 @@ var createAgentSessionRepos = (kdb, h) => ({
|
|
|
187558
187565
|
updated_at: h.nowMs()
|
|
187559
187566
|
}).onConflict((oc) => oc.column("local_reviewer_session_id").doNothing()).execute();
|
|
187560
187567
|
const row = await trx.selectFrom("remote_reviewer_creation_intents").selectAll().where("local_reviewer_session_id", "=", intent.localReviewerSessionId).executeTakeFirstOrThrow();
|
|
187561
|
-
const sameIdentity = row.remote_reviewer_session_id === intent.remoteReviewerSessionId && row.remote_run_id === intent.remoteRunId && row.project_id === intent.projectId && row.remote_server_id === intent.remoteServerId && (row.branch ?? "") === (intent.branch ?? "") && row.remote_path === intent.remotePath && row.source_remote_session_id === intent.sourceRemoteSessionId && row.review_focus === (intent.reviewFocus ?? null) && row.source_turn_end_index === (intent.sourceTurnEndIndex ?? null) && row.review_span === intent.reviewSpan && row.agent_type === intent.agentType && row.intent_brief === (intent.intentBrief ?? null) && row.user_id === (intent.userId ?? null);
|
|
187568
|
+
const sameIdentity = row.remote_reviewer_session_id === intent.remoteReviewerSessionId && row.remote_run_id === intent.remoteRunId && row.project_id === intent.projectId && row.remote_server_id === intent.remoteServerId && (row.branch ?? "") === (intent.branch ?? "") && row.remote_path === intent.remotePath && row.source_remote_session_id === intent.sourceRemoteSessionId && row.review_focus === (intent.reviewFocus ?? null) && row.source_turn_end_index === (intent.sourceTurnEndIndex ?? null) && row.review_span === intent.reviewSpan && (row.review_context_mode ?? null) === (intent.reviewContextMode ?? null) && row.agent_type === intent.agentType && row.intent_brief === (intent.intentBrief ?? null) && row.user_id === (intent.userId ?? null);
|
|
187562
187569
|
if (!sameIdentity) {
|
|
187563
187570
|
throw new Error(`Remote reviewer creation intent ${intent.localReviewerSessionId} has conflicting identity`);
|
|
187564
187571
|
}
|
|
@@ -203758,6 +203765,8 @@ var tightenWorkspaceCheckoutForeignKeys = (db) => {
|
|
|
203758
203765
|
favorited_at INTEGER DEFAULT NULL,
|
|
203759
203766
|
native_session_id TEXT DEFAULT NULL,
|
|
203760
203767
|
history_epoch INTEGER NOT NULL DEFAULT 0,
|
|
203768
|
+
branched_from_session_id TEXT DEFAULT NULL,
|
|
203769
|
+
branched_from_entry_index INTEGER DEFAULT NULL,
|
|
203761
203770
|
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
203762
203771
|
FOREIGN KEY (workspace_checkout_id) REFERENCES workspace_checkouts(id)
|
|
203763
203772
|
DEFERRABLE INITIALLY DEFERRED
|
|
@@ -203765,10 +203774,12 @@ var tightenWorkspaceCheckoutForeignKeys = (db) => {
|
|
|
203765
203774
|
INSERT INTO agent_sessions_fk_new
|
|
203766
203775
|
(id, project_id, branch, workspace_checkout_id, status, permission_mode, agent_type,
|
|
203767
203776
|
title, model, created_at, updated_at, activity_at, last_user_message_at,
|
|
203768
|
-
last_completed_at, favorited_at, native_session_id, history_epoch
|
|
203777
|
+
last_completed_at, favorited_at, native_session_id, history_epoch,
|
|
203778
|
+
branched_from_session_id, branched_from_entry_index)
|
|
203769
203779
|
SELECT id, project_id, branch, workspace_checkout_id, status, permission_mode, agent_type,
|
|
203770
203780
|
title, model, created_at, updated_at, activity_at, last_user_message_at,
|
|
203771
|
-
last_completed_at, favorited_at, native_session_id, history_epoch
|
|
203781
|
+
last_completed_at, favorited_at, native_session_id, history_epoch,
|
|
203782
|
+
branched_from_session_id, branched_from_entry_index
|
|
203772
203783
|
FROM agent_sessions;
|
|
203773
203784
|
DROP TABLE agent_sessions;
|
|
203774
203785
|
ALTER TABLE agent_sessions_fk_new RENAME TO agent_sessions;
|
|
@@ -204179,6 +204190,7 @@ var initializeSchema = (db) => {
|
|
|
204179
204190
|
review_focus TEXT,
|
|
204180
204191
|
source_turn_end_index INTEGER,
|
|
204181
204192
|
review_span TEXT NOT NULL CHECK (review_span IN ('this_turn', 'session_start')),
|
|
204193
|
+
review_context_mode TEXT CHECK (review_context_mode IN ('briefed', 'blind')),
|
|
204182
204194
|
agent_type TEXT NOT NULL,
|
|
204183
204195
|
intent_brief TEXT,
|
|
204184
204196
|
user_id TEXT,
|
|
@@ -204595,6 +204607,11 @@ var initializeSchema = (db) => {
|
|
|
204595
204607
|
coalesce(cast((julianday(created_at) - 2440587.5) * 86400000 as integer), 0)
|
|
204596
204608
|
)`);
|
|
204597
204609
|
}
|
|
204610
|
+
const sessionBranchedFromInfo = db.prepare("PRAGMA table_info(agent_sessions)").all();
|
|
204611
|
+
if (!sessionBranchedFromInfo.some((col) => col.name === "branched_from_session_id")) {
|
|
204612
|
+
db.exec("ALTER TABLE agent_sessions ADD COLUMN branched_from_session_id TEXT DEFAULT NULL");
|
|
204613
|
+
db.exec("ALTER TABLE agent_sessions ADD COLUMN branched_from_entry_index INTEGER DEFAULT NULL");
|
|
204614
|
+
}
|
|
204598
204615
|
db.exec(`
|
|
204599
204616
|
CREATE INDEX IF NOT EXISTS idx_agent_sessions_project_branch
|
|
204600
204617
|
ON agent_sessions(project_id, branch);
|
|
@@ -205344,6 +205361,10 @@ var initializeSchema = (db) => {
|
|
|
205344
205361
|
if (!workflowRunsInfo.some((col) => col.name === "review_span")) {
|
|
205345
205362
|
db.exec("ALTER TABLE workflow_runs ADD COLUMN review_span TEXT NOT NULL DEFAULT 'this_turn'");
|
|
205346
205363
|
}
|
|
205364
|
+
const reviewerIntentsInfo = db.prepare("PRAGMA table_info(remote_reviewer_creation_intents)").all();
|
|
205365
|
+
if (!reviewerIntentsInfo.some((col) => col.name === "review_context_mode")) {
|
|
205366
|
+
db.exec("ALTER TABLE remote_reviewer_creation_intents ADD COLUMN review_context_mode TEXT CHECK (review_context_mode IN ('briefed', 'blind'))");
|
|
205367
|
+
}
|
|
205347
205368
|
db.exec(`
|
|
205348
205369
|
CREATE TABLE IF NOT EXISTS user_settings (
|
|
205349
205370
|
user_id TEXT NOT NULL,
|
|
@@ -207173,7 +207194,7 @@ function sessionMilestoneForTurnEnd(opts) {
|
|
|
207173
207194
|
workflow_run_id: null,
|
|
207174
207195
|
created_at: opts.createdAt
|
|
207175
207196
|
};
|
|
207176
|
-
if (opts.outcome === "completed") {
|
|
207197
|
+
if (opts.outcome === "completed" || opts.outcome === "completed_with_pending_tasks") {
|
|
207177
207198
|
return {
|
|
207178
207199
|
...base,
|
|
207179
207200
|
id: sessionResultReadyId(opts.sessionId, opts.entryIndex),
|
|
@@ -207312,6 +207333,25 @@ var ClaudeCodeProvider = class {
|
|
|
207312
207333
|
formatUserInput(content, _sessionId) {
|
|
207313
207334
|
return serializeUserInput(content);
|
|
207314
207335
|
}
|
|
207336
|
+
/**
|
|
207337
|
+
* `stop_task` control_request — the documented Agent SDK primitive for
|
|
207338
|
+
* killing one background task, verified live against claude 2.1.238: the CLI
|
|
207339
|
+
* answers `control_response { subtype: "success" }`, empties its task list
|
|
207340
|
+
* and the process really dies.
|
|
207341
|
+
*
|
|
207342
|
+
* `request_id` is namespaced by task id so a response could be correlated;
|
|
207343
|
+
* nothing reads control_response today, and it does not need to — the
|
|
207344
|
+
* authoritative `background_tasks_changed` snapshot that follows is what
|
|
207345
|
+
* updates the ledger. An older CLI that does not know the subtype answers an
|
|
207346
|
+
* error and simply changes nothing.
|
|
207347
|
+
*/
|
|
207348
|
+
formatStopBackgroundTask(taskId, _sessionId) {
|
|
207349
|
+
return JSON.stringify({
|
|
207350
|
+
type: "control_request",
|
|
207351
|
+
request_id: `stop_task-${taskId}`,
|
|
207352
|
+
request: { subtype: "stop_task", task_id: taskId }
|
|
207353
|
+
}) + "\n";
|
|
207354
|
+
}
|
|
207315
207355
|
// Lifecycle hooks are no-ops for Claude (stateless per-session)
|
|
207316
207356
|
onSessionCreated(_sessionId, _permissionMode) {
|
|
207317
207357
|
}
|
|
@@ -229427,7 +229467,7 @@ function isResidentProcessInScope(candidate, scope) {
|
|
|
229427
229467
|
return candidate.projectId === scope.projectId && candidate.branch === scope.branch;
|
|
229428
229468
|
}
|
|
229429
229469
|
function isIdleResidentProcess(candidate) {
|
|
229430
|
-
return candidate.processAlive && !candidate.dormant && candidate.status !== "running" && candidate.
|
|
229470
|
+
return candidate.processAlive && !candidate.dormant && candidate.status !== "running" && !candidate.backgroundTasksProtect;
|
|
229431
229471
|
}
|
|
229432
229472
|
function pickIdleResidentEvictionCandidate(candidates, scope) {
|
|
229433
229473
|
return candidates.filter((candidate) => !scope || isResidentProcessInScope(candidate, scope)).filter(isIdleResidentProcess).sort((a, b2) => a.lastActiveAt - b2.lastActiveAt)[0] ?? null;
|
|
@@ -229449,7 +229489,12 @@ var ResidentProcessLimitError = class extends Error {
|
|
|
229449
229489
|
|
|
229450
229490
|
// src/turn-completion.ts
|
|
229451
229491
|
var COMPLETION_GRACE_MS = 1500;
|
|
229492
|
+
var PARK_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
229452
229493
|
var TurnCompletionLedger = class {
|
|
229494
|
+
constructor(parkTimeoutMs = PARK_TIMEOUT_MS) {
|
|
229495
|
+
this.parkTimeoutMs = parkTimeoutMs;
|
|
229496
|
+
}
|
|
229497
|
+
parkTimeoutMs;
|
|
229453
229498
|
/** Live background tasks by harness task_id (same id may restart). */
|
|
229454
229499
|
tasks = /* @__PURE__ */ new Map();
|
|
229455
229500
|
/** Held completion candidate — the latest success result, if any. */
|
|
@@ -229462,6 +229507,23 @@ var TurnCompletionLedger = class {
|
|
|
229462
229507
|
* grace delay (the common case).
|
|
229463
229508
|
*/
|
|
229464
229509
|
sawBackgroundActivity = false;
|
|
229510
|
+
/**
|
|
229511
|
+
* When the held candidate was parked behind live background tasks — the
|
|
229512
|
+
* moment the agent stopped working and only tasks kept the turn open. The
|
|
229513
|
+
* park deadline counts from here, not from when a task started: the number
|
|
229514
|
+
* that matters to the user is "how long since the agent answered".
|
|
229515
|
+
*/
|
|
229516
|
+
parkedSince = null;
|
|
229517
|
+
/** Task ids the user vouched for; they stop counting toward the deadline. */
|
|
229518
|
+
sanctioned = /* @__PURE__ */ new Set();
|
|
229519
|
+
/**
|
|
229520
|
+
* Whether a park deadline already expired and closed the turn. Live tasks
|
|
229521
|
+
* normally shield the session from being reclaimed — hibernating would kill
|
|
229522
|
+
* a real build and the auto-resume that reads it — but that shield must not
|
|
229523
|
+
* outlast the deadline, or one stuck task pins a resident process slot
|
|
229524
|
+
* forever and new sessions on the branch get turned away.
|
|
229525
|
+
*/
|
|
229526
|
+
parkDeadlineExpired = false;
|
|
229465
229527
|
get pendingTaskCount() {
|
|
229466
229528
|
return this.tasks.size;
|
|
229467
229529
|
}
|
|
@@ -229470,17 +229532,61 @@ var TurnCompletionLedger = class {
|
|
|
229470
229532
|
}
|
|
229471
229533
|
/** Live tasks in first-seen order — the payload the UI renders. */
|
|
229472
229534
|
get backgroundTasks() {
|
|
229473
|
-
return [...this.tasks.values()]
|
|
229535
|
+
return [...this.tasks.values()].map(
|
|
229536
|
+
(task) => this.sanctioned.has(task.taskId) ? { ...task, sanctioned: true } : task
|
|
229537
|
+
);
|
|
229538
|
+
}
|
|
229539
|
+
/**
|
|
229540
|
+
* When the parked turn will be committed anyway, or null if nothing is
|
|
229541
|
+
* parked or every live task has been vouched for. Timer-free by design: the
|
|
229542
|
+
* caller re-reads this after each mutation and syncs its own timer, so the
|
|
229543
|
+
* ledger stays a pure state machine.
|
|
229544
|
+
*/
|
|
229545
|
+
get parkDeadlineAt() {
|
|
229546
|
+
if (this.pending === null || this.parkedSince === null) return null;
|
|
229547
|
+
const allVouchedFor = [...this.tasks.keys()].every((id) => this.sanctioned.has(id));
|
|
229548
|
+
return allVouchedFor ? null : this.parkedSince + this.parkTimeoutMs;
|
|
229549
|
+
}
|
|
229550
|
+
/**
|
|
229551
|
+
* The user vouched for a task: stop counting it toward the deadline. This
|
|
229552
|
+
* restores the original behavior for that task — wait for it, let the
|
|
229553
|
+
* auto-resume close the turn — but now as an explicit choice.
|
|
229554
|
+
*/
|
|
229555
|
+
sanction(taskId) {
|
|
229556
|
+
if (this.tasks.has(taskId)) this.sanctioned.add(taskId);
|
|
229557
|
+
}
|
|
229558
|
+
/**
|
|
229559
|
+
* The deadline expired: commit the parked candidate. Deliberately uses the
|
|
229560
|
+
* ORIGINAL payload — its duration/cost/tokens describe the turn the agent
|
|
229561
|
+
* actually ran, not the time spent waiting on a stuck task.
|
|
229562
|
+
*/
|
|
229563
|
+
parkDeadlineElapsed() {
|
|
229564
|
+
if (this.pending === null) return { kind: "none" };
|
|
229565
|
+
this.parkDeadlineExpired = true;
|
|
229566
|
+
return this.commitHeld();
|
|
229567
|
+
}
|
|
229568
|
+
/**
|
|
229569
|
+
* Whether live background tasks should still shield this session from
|
|
229570
|
+
* resident-process reclamation. True while they are plausibly doing real
|
|
229571
|
+
* work; false once the deadline judged them anomalous — unless the user
|
|
229572
|
+
* vouched for every one of them, which restores the shield along with the
|
|
229573
|
+
* waiting behavior it protects.
|
|
229574
|
+
*/
|
|
229575
|
+
get backgroundTasksProtectSession() {
|
|
229576
|
+
if (this.tasks.size === 0) return false;
|
|
229577
|
+
if (!this.parkDeadlineExpired) return true;
|
|
229578
|
+
return [...this.tasks.keys()].every((id) => this.sanctioned.has(id));
|
|
229474
229579
|
}
|
|
229475
229580
|
taskStarted(task, now3) {
|
|
229476
229581
|
this.upsert(task, this.tasks.get(task.taskId), now3);
|
|
229477
229582
|
this.sawBackgroundActivity = true;
|
|
229478
|
-
return this.rearmIfHeld();
|
|
229583
|
+
return this.rearmIfHeld(now3);
|
|
229479
229584
|
}
|
|
229480
|
-
taskFinished(taskId) {
|
|
229585
|
+
taskFinished(taskId, now3) {
|
|
229481
229586
|
this.tasks.delete(taskId);
|
|
229587
|
+
this.sanctioned.delete(taskId);
|
|
229482
229588
|
this.sawBackgroundActivity = true;
|
|
229483
|
-
return this.rearmIfHeld();
|
|
229589
|
+
return this.rearmIfHeld(now3);
|
|
229484
229590
|
}
|
|
229485
229591
|
/** Authoritative snapshot from `system/background_tasks_changed`. */
|
|
229486
229592
|
taskListChanged(tasks, now3) {
|
|
@@ -229489,8 +229595,11 @@ var TurnCompletionLedger = class {
|
|
|
229489
229595
|
for (const task of tasks) {
|
|
229490
229596
|
this.upsert(task, previous.get(task.taskId), now3);
|
|
229491
229597
|
}
|
|
229598
|
+
for (const id of this.sanctioned) {
|
|
229599
|
+
if (!this.tasks.has(id)) this.sanctioned.delete(id);
|
|
229600
|
+
}
|
|
229492
229601
|
if (tasks.length > 0) this.sawBackgroundActivity = true;
|
|
229493
|
-
return this.rearmIfHeld();
|
|
229602
|
+
return this.rearmIfHeld(now3);
|
|
229494
229603
|
}
|
|
229495
229604
|
/**
|
|
229496
229605
|
* The process emitted turn activity: if a completion was held, it was an
|
|
@@ -229502,8 +229611,10 @@ var TurnCompletionLedger = class {
|
|
|
229502
229611
|
* the race against the grace window.
|
|
229503
229612
|
*/
|
|
229504
229613
|
noteTurnActivity() {
|
|
229614
|
+
this.parkDeadlineExpired = false;
|
|
229505
229615
|
if (this.pending === null) return { kind: "none" };
|
|
229506
229616
|
this.pending = null;
|
|
229617
|
+
this.parkedSince = null;
|
|
229507
229618
|
this.generation++;
|
|
229508
229619
|
return { kind: "cancel" };
|
|
229509
229620
|
}
|
|
@@ -229519,9 +229630,11 @@ var TurnCompletionLedger = class {
|
|
|
229519
229630
|
this.sawBackgroundActivity = false;
|
|
229520
229631
|
return this.noteTurnActivity();
|
|
229521
229632
|
}
|
|
229522
|
-
successResult(payload) {
|
|
229633
|
+
successResult(payload, now3) {
|
|
229523
229634
|
this.generation++;
|
|
229524
229635
|
if (this.tasks.size > 0) {
|
|
229636
|
+
this.parkedSince = now3;
|
|
229637
|
+
this.parkDeadlineExpired = false;
|
|
229525
229638
|
this.pending = payload;
|
|
229526
229639
|
return { kind: "cancel" };
|
|
229527
229640
|
}
|
|
@@ -229535,6 +229648,7 @@ var TurnCompletionLedger = class {
|
|
|
229535
229648
|
errorResult() {
|
|
229536
229649
|
if (this.pending === null) return { kind: "none" };
|
|
229537
229650
|
this.pending = null;
|
|
229651
|
+
this.parkedSince = null;
|
|
229538
229652
|
this.generation++;
|
|
229539
229653
|
return { kind: "cancel" };
|
|
229540
229654
|
}
|
|
@@ -229560,7 +229674,10 @@ var TurnCompletionLedger = class {
|
|
|
229560
229674
|
/** Full reset (fresh spawn / stop / hibernate / agent switch). */
|
|
229561
229675
|
reset() {
|
|
229562
229676
|
this.tasks.clear();
|
|
229677
|
+
this.sanctioned.clear();
|
|
229563
229678
|
this.pending = null;
|
|
229679
|
+
this.parkedSince = null;
|
|
229680
|
+
this.parkDeadlineExpired = false;
|
|
229564
229681
|
this.generation++;
|
|
229565
229682
|
this.sawBackgroundActivity = false;
|
|
229566
229683
|
}
|
|
@@ -229569,10 +229686,13 @@ var TurnCompletionLedger = class {
|
|
|
229569
229686
|
* have no resume behind it), so they delay the commit rather than cancel
|
|
229570
229687
|
* it — and while tasks are still live the candidate stays parked with no
|
|
229571
229688
|
* timer at all (only an empty set can complete a turn). */
|
|
229572
|
-
rearmIfHeld() {
|
|
229689
|
+
rearmIfHeld(now3) {
|
|
229573
229690
|
if (this.pending === null) return { kind: "none" };
|
|
229574
229691
|
this.generation++;
|
|
229575
|
-
if (this.tasks.size > 0)
|
|
229692
|
+
if (this.tasks.size > 0) {
|
|
229693
|
+
this.parkedSince ??= now3;
|
|
229694
|
+
return { kind: "cancel" };
|
|
229695
|
+
}
|
|
229576
229696
|
return { kind: "schedule", generation: this.generation };
|
|
229577
229697
|
}
|
|
229578
229698
|
/**
|
|
@@ -229593,6 +229713,7 @@ var TurnCompletionLedger = class {
|
|
|
229593
229713
|
commitHeld() {
|
|
229594
229714
|
const payload = this.pending;
|
|
229595
229715
|
this.pending = null;
|
|
229716
|
+
this.parkedSince = null;
|
|
229596
229717
|
this.generation++;
|
|
229597
229718
|
return { kind: "commit", payload };
|
|
229598
229719
|
}
|
|
@@ -229673,10 +229794,13 @@ var AgentSessionManager = class {
|
|
|
229673
229794
|
retentionDeleting = /* @__PURE__ */ new Set();
|
|
229674
229795
|
/** Grace window before committing a held completion (injectable for tests). */
|
|
229675
229796
|
completionGraceMs;
|
|
229797
|
+
/** Bound on a parked completion (injectable for tests). */
|
|
229798
|
+
parkTimeoutMs;
|
|
229676
229799
|
workflowSuppressionCheck = null;
|
|
229677
229800
|
constructor(storage2, opts) {
|
|
229678
229801
|
this.storage = storage2;
|
|
229679
229802
|
this.completionGraceMs = opts?.completionGraceMs ?? COMPLETION_GRACE_MS;
|
|
229803
|
+
this.parkTimeoutMs = opts?.parkTimeoutMs ?? PARK_TIMEOUT_MS;
|
|
229680
229804
|
}
|
|
229681
229805
|
async resolveSessionWorktreePath(session, legacyProjectPath) {
|
|
229682
229806
|
if (!session.workspaceCheckoutId) {
|
|
@@ -229951,7 +230075,7 @@ var AgentSessionManager = class {
|
|
|
229951
230075
|
processAlive: this.isProcessAlive(session),
|
|
229952
230076
|
status: session.status,
|
|
229953
230077
|
dormant: session.dormant,
|
|
229954
|
-
|
|
230078
|
+
backgroundTasksProtect: session.completion.backgroundTasksProtectSession,
|
|
229955
230079
|
lastActiveAt: session.lastActiveAt,
|
|
229956
230080
|
projectId: session.projectId,
|
|
229957
230081
|
branch: session.branch
|
|
@@ -230099,8 +230223,9 @@ var AgentSessionManager = class {
|
|
|
230099
230223
|
crossRemoteMcp: opts.crossRemoteMcp,
|
|
230100
230224
|
agentType,
|
|
230101
230225
|
model,
|
|
230102
|
-
completion: new TurnCompletionLedger(),
|
|
230226
|
+
completion: new TurnCompletionLedger(this.parkTimeoutMs),
|
|
230103
230227
|
graceTimer: null,
|
|
230228
|
+
parkTimer: null,
|
|
230104
230229
|
eventChain: Promise.resolve(),
|
|
230105
230230
|
bgSpawnHintsThisTurn: 0,
|
|
230106
230231
|
taskStartedThisTurn: 0,
|
|
@@ -230357,11 +230482,78 @@ var AgentSessionManager = class {
|
|
|
230357
230482
|
} else if (action.kind === "schedule") {
|
|
230358
230483
|
this.armGraceTimer(session, action.generation);
|
|
230359
230484
|
}
|
|
230485
|
+
this.syncParkTimer(session);
|
|
230486
|
+
}
|
|
230487
|
+
/**
|
|
230488
|
+
* Keep the park timer in step with the ledger's deadline.
|
|
230489
|
+
*
|
|
230490
|
+
* Driven by ledger STATE rather than by an action kind, because the deadline
|
|
230491
|
+
* survives across many actions (every task event returns `cancel` while a
|
|
230492
|
+
* completion stays parked) and can also be lifted without any action at all
|
|
230493
|
+
* when the user vouches for the last unvouched task. Re-reading the state
|
|
230494
|
+
* after each mutation is the only way the two can't drift.
|
|
230495
|
+
*/
|
|
230496
|
+
syncParkTimer(session) {
|
|
230497
|
+
const deadlineAt = session.completion.parkDeadlineAt;
|
|
230498
|
+
if (deadlineAt === null) {
|
|
230499
|
+
if (session.parkTimer) {
|
|
230500
|
+
clearTimeout(session.parkTimer);
|
|
230501
|
+
session.parkTimer = null;
|
|
230502
|
+
}
|
|
230503
|
+
return;
|
|
230504
|
+
}
|
|
230505
|
+
if (session.parkTimer) return;
|
|
230506
|
+
const timer = setTimeout(() => {
|
|
230507
|
+
session.parkTimer = null;
|
|
230508
|
+
this.enqueueSessionWork(session, async () => {
|
|
230509
|
+
const action = session.completion.parkDeadlineElapsed();
|
|
230510
|
+
if (action.kind !== "commit") return;
|
|
230511
|
+
console.log(
|
|
230512
|
+
`[AgentSession] parked completion exceeded ${this.parkTimeoutMs}ms with ${session.completion.pendingTaskCount} background task(s) still running \u2014 committing the turn anyway (session=${session.id})`
|
|
230513
|
+
);
|
|
230514
|
+
await this.commitCompletion(session, action.payload, "completed_with_pending_tasks");
|
|
230515
|
+
this.broadcastBackgroundTasks(session);
|
|
230516
|
+
}, "completion-park-deadline");
|
|
230517
|
+
}, Math.max(0, deadlineAt - Date.now()));
|
|
230518
|
+
timer.unref?.();
|
|
230519
|
+
session.parkTimer = timer;
|
|
230520
|
+
}
|
|
230521
|
+
/**
|
|
230522
|
+
* The user vouched for a background task: it stops counting toward the park
|
|
230523
|
+
* deadline, restoring the original wait-for-auto-resume behavior for that
|
|
230524
|
+
* task alone — now as an explicit choice rather than a silent assumption.
|
|
230525
|
+
*/
|
|
230526
|
+
/**
|
|
230527
|
+
* Ask the agent to stop one background task.
|
|
230528
|
+
*
|
|
230529
|
+
* Returns "unsupported" for agents with no such primitive (Codex), so the
|
|
230530
|
+
* caller can say "stop the session instead" rather than showing a dead
|
|
230531
|
+
* button. On success nothing is updated here: the CLI's own
|
|
230532
|
+
* `background_tasks_changed` snapshot drains the ledger, which then commits
|
|
230533
|
+
* the parked turn through the normal path.
|
|
230534
|
+
*/
|
|
230535
|
+
stopBackgroundTask(sessionId, taskId) {
|
|
230536
|
+
const session = this.sessions.get(sessionId);
|
|
230537
|
+
if (!session?.process?.stdin) return "not_found";
|
|
230538
|
+
const frame = getProvider(session.agentType).formatStopBackgroundTask?.(taskId, sessionId);
|
|
230539
|
+
if (!frame) return "unsupported";
|
|
230540
|
+
session.process.stdin.write(frame);
|
|
230541
|
+
console.log(`[AgentSession] stop_task sent for ${taskId} (session=${sessionId})`);
|
|
230542
|
+
return "ok";
|
|
230543
|
+
}
|
|
230544
|
+
sanctionBackgroundTask(sessionId, taskId) {
|
|
230545
|
+
const session = this.sessions.get(sessionId);
|
|
230546
|
+
if (!session) return false;
|
|
230547
|
+
session.completion.sanction(taskId);
|
|
230548
|
+
this.syncParkTimer(session);
|
|
230549
|
+
this.broadcastBackgroundTasks(session);
|
|
230550
|
+
return true;
|
|
230360
230551
|
}
|
|
230361
230552
|
/** Discard all turn-completion state (fresh spawn / stop / hibernate / agent switch). */
|
|
230362
230553
|
resetCompletion(session) {
|
|
230363
230554
|
this.clearGraceTimer(session);
|
|
230364
230555
|
session.completion.reset();
|
|
230556
|
+
this.syncParkTimer(session);
|
|
230365
230557
|
this.broadcastBackgroundTasks(session);
|
|
230366
230558
|
}
|
|
230367
230559
|
/**
|
|
@@ -230376,21 +230568,32 @@ var AgentSessionManager = class {
|
|
|
230376
230568
|
* stateless (no patch application, no ordering assumptions).
|
|
230377
230569
|
*/
|
|
230378
230570
|
broadcastBackgroundTasks(session) {
|
|
230379
|
-
this.broadcastRaw(session.id,
|
|
230571
|
+
this.broadcastRaw(session.id, this.backgroundTasksMessage(session));
|
|
230572
|
+
}
|
|
230573
|
+
/**
|
|
230574
|
+
* Reported rather than inferred client-side: whether a single task can be
|
|
230575
|
+
* stopped is a property of the agent (Claude Code has `stop_task`, Codex has
|
|
230576
|
+
* nothing equivalent), and the server is the only side that knows. A client
|
|
230577
|
+
* guessing from the agent type would drift the day Codex gains one.
|
|
230578
|
+
*/
|
|
230579
|
+
backgroundTasksMessage(session) {
|
|
230580
|
+
return {
|
|
230380
230581
|
backgroundTasks: {
|
|
230381
230582
|
tasks: session.completion.backgroundTasks,
|
|
230382
|
-
turnParked: session.completion.hasPendingCompletion
|
|
230583
|
+
turnParked: session.completion.hasPendingCompletion,
|
|
230584
|
+
parkDeadlineAt: session.completion.parkDeadlineAt,
|
|
230585
|
+
canStopTasks: !!getProvider(session.agentType).formatStopBackgroundTask
|
|
230383
230586
|
}
|
|
230384
|
-
}
|
|
230587
|
+
};
|
|
230385
230588
|
}
|
|
230386
|
-
async commitCompletion(session, payload) {
|
|
230589
|
+
async commitCompletion(session, payload, outcome = "completed") {
|
|
230387
230590
|
const sessionId = session.id;
|
|
230388
230591
|
console.log(`[AgentSession] taskCompleted: sessionId=${sessionId}, eventBus=${!!this.eventBus}, projectId=${session.projectId}, branch=${session.branch}`);
|
|
230389
230592
|
const completedAt = Date.now();
|
|
230390
230593
|
if (!session.skipDb) {
|
|
230391
230594
|
await this.storage.agentSessions.markCompleted(sessionId, completedAt);
|
|
230392
230595
|
}
|
|
230393
|
-
const turnEndEntryIndex = await this.endActiveTurn(session,
|
|
230596
|
+
const turnEndEntryIndex = await this.endActiveTurn(session, outcome);
|
|
230394
230597
|
this.broadcastBackgroundTasks(session);
|
|
230395
230598
|
const summaryText = extractLastAssistantText(session.store.entries);
|
|
230396
230599
|
this.broadcastRaw(sessionId, {
|
|
@@ -230582,7 +230785,7 @@ var AgentSessionManager = class {
|
|
|
230582
230785
|
console.log(`[AgentSession] Background task started: ${event.taskId} (${event.taskType ?? "?"}) \u2014 ${session.completion.pendingTaskCount} pending in ${sessionId}`);
|
|
230583
230786
|
break;
|
|
230584
230787
|
case "task_finished":
|
|
230585
|
-
this.applyCompletionTimerAction(session, session.completion.taskFinished(event.taskId));
|
|
230788
|
+
this.applyCompletionTimerAction(session, session.completion.taskFinished(event.taskId, timestamp));
|
|
230586
230789
|
this.broadcastBackgroundTasks(session);
|
|
230587
230790
|
console.log(`[AgentSession] Background task finished: ${event.taskId} (${event.status ?? "?"}) \u2014 ${session.completion.pendingTaskCount} pending in ${sessionId}`);
|
|
230588
230791
|
break;
|
|
@@ -230652,7 +230855,7 @@ var AgentSessionManager = class {
|
|
|
230652
230855
|
cost_usd: event.cost_usd,
|
|
230653
230856
|
input_tokens: event.input_tokens,
|
|
230654
230857
|
output_tokens: event.output_tokens
|
|
230655
|
-
});
|
|
230858
|
+
}, timestamp);
|
|
230656
230859
|
if (action.kind === "commit") {
|
|
230657
230860
|
await this.commitCompletion(session, action.payload);
|
|
230658
230861
|
} else {
|
|
@@ -230983,6 +231186,7 @@ var AgentSessionManager = class {
|
|
|
230983
231186
|
reset: opts.historyEpoch !== void 0 && opts.historyEpoch !== session.historyEpoch
|
|
230984
231187
|
}
|
|
230985
231188
|
}));
|
|
231189
|
+
ws.send(JSON.stringify(this.backgroundTasksMessage(session)));
|
|
230986
231190
|
for (const patch of session.store.patches) {
|
|
230987
231191
|
const entryIndices = patch.flatMap((op) => {
|
|
230988
231192
|
const match2 = op.path.match(/^\/entries\/(\d+)$/);
|
|
@@ -230995,13 +231199,6 @@ var AgentSessionManager = class {
|
|
|
230995
231199
|
ws.send(JSON.stringify({ Ready: true, historyEpoch: session.historyEpoch }));
|
|
230996
231200
|
const statusPatch = ConversationPatch.updateStatus(session.status);
|
|
230997
231201
|
ws.send(JSON.stringify({ JsonPatch: statusPatch }));
|
|
230998
|
-
const tasksMsg = {
|
|
230999
|
-
backgroundTasks: {
|
|
231000
|
-
tasks: session.completion.backgroundTasks,
|
|
231001
|
-
turnParked: session.completion.hasPendingCompletion
|
|
231002
|
-
}
|
|
231003
|
-
};
|
|
231004
|
-
ws.send(JSON.stringify(tasksMsg));
|
|
231005
231202
|
return () => {
|
|
231006
231203
|
session.subscribers.delete(ws);
|
|
231007
231204
|
};
|
|
@@ -231800,8 +231997,11 @@ var AgentSessionManager = class {
|
|
|
231800
231997
|
permissionMode,
|
|
231801
231998
|
agentType: dbSession.agent_type || "claude-code",
|
|
231802
231999
|
model: dbSession.model ?? null,
|
|
231803
|
-
|
|
232000
|
+
branchedFromSessionId: dbSession.branched_from_session_id ?? null,
|
|
232001
|
+
branchedFromEntryIndex: dbSession.branched_from_entry_index ?? null,
|
|
232002
|
+
completion: new TurnCompletionLedger(this.parkTimeoutMs),
|
|
231804
232003
|
graceTimer: null,
|
|
232004
|
+
parkTimer: null,
|
|
231805
232005
|
eventChain: Promise.resolve(),
|
|
231806
232006
|
bgSpawnHintsThisTurn: 0,
|
|
231807
232007
|
taskStartedThisTurn: 0,
|
|
@@ -231925,6 +232125,14 @@ var AgentSessionManager = class {
|
|
|
231925
232125
|
if (existingRuntime && opts.crossRemoteMcp) {
|
|
231926
232126
|
existingRuntime.crossRemoteMcp = opts.crossRemoteMcp;
|
|
231927
232127
|
}
|
|
232128
|
+
if (!existingBranch.branched_from_session_id) {
|
|
232129
|
+
const repairedEntryIndex = entryRows[entryRows.length - 1].entry_index;
|
|
232130
|
+
await this.storage.agentSessions.setBranchedFrom(newId, sourceSessionId, repairedEntryIndex);
|
|
232131
|
+
if (existingRuntime) {
|
|
232132
|
+
existingRuntime.branchedFromSessionId = sourceSessionId;
|
|
232133
|
+
existingRuntime.branchedFromEntryIndex = repairedEntryIndex;
|
|
232134
|
+
}
|
|
232135
|
+
}
|
|
231928
232136
|
return { ok: true, sessionId: newId };
|
|
231929
232137
|
}
|
|
231930
232138
|
}
|
|
@@ -231958,6 +232166,8 @@ var AgentSessionManager = class {
|
|
|
231958
232166
|
for (const row of entryRows) {
|
|
231959
232167
|
await this.storage.agentSessions.upsertEntry(newId, row.entry_index, row.data);
|
|
231960
232168
|
}
|
|
232169
|
+
const branchedFromEntryIndex = opts.upToEntryIndex ?? entryRows[entryRows.length - 1].entry_index;
|
|
232170
|
+
await this.storage.agentSessions.setBranchedFrom(newId, sourceSessionId, branchedFromEntryIndex);
|
|
231961
232171
|
let baseTitle = sourceRow?.title ?? null;
|
|
231962
232172
|
if (!baseTitle) {
|
|
231963
232173
|
for (const row of entryRows) {
|
|
@@ -231992,15 +232202,18 @@ var AgentSessionManager = class {
|
|
|
231992
232202
|
permissionMode,
|
|
231993
232203
|
agentType,
|
|
231994
232204
|
model,
|
|
231995
|
-
completion: new TurnCompletionLedger(),
|
|
232205
|
+
completion: new TurnCompletionLedger(this.parkTimeoutMs),
|
|
231996
232206
|
graceTimer: null,
|
|
232207
|
+
parkTimer: null,
|
|
231997
232208
|
eventChain: Promise.resolve(),
|
|
231998
232209
|
bgSpawnHintsThisTurn: 0,
|
|
231999
232210
|
taskStartedThisTurn: 0,
|
|
232000
232211
|
lastActiveAt: Date.now(),
|
|
232001
232212
|
turnOpenSince: null,
|
|
232002
232213
|
turnDisposition: null,
|
|
232003
|
-
crossRemoteMcp: opts.crossRemoteMcp
|
|
232214
|
+
crossRemoteMcp: opts.crossRemoteMcp,
|
|
232215
|
+
branchedFromSessionId: sourceSessionId,
|
|
232216
|
+
branchedFromEntryIndex
|
|
232004
232217
|
};
|
|
232005
232218
|
this.sessions.set(newId, branched);
|
|
232006
232219
|
await this.emitDerivedBranchActivity(projectId, branch);
|
|
@@ -233190,6 +233403,7 @@ async function createRemoteWorkflowReviewer(deps, params) {
|
|
|
233190
233403
|
reviewFocus: params.reviewFocus ?? null,
|
|
233191
233404
|
sourceTurnEndIndex: params.sourceTurnEndIndex ?? null,
|
|
233192
233405
|
reviewSpan: params.reviewSpan,
|
|
233406
|
+
reviewContextMode: params.reviewContextMode ?? null,
|
|
233193
233407
|
agentType: params.reviewerAgentType,
|
|
233194
233408
|
intentBrief: params.intentBrief ?? null,
|
|
233195
233409
|
userId: params.userId ?? null
|
|
@@ -233205,6 +233419,7 @@ async function createRemoteWorkflowReviewer(deps, params) {
|
|
|
233205
233419
|
reviewFocus: params.reviewFocus,
|
|
233206
233420
|
sourceTurnEndIndex: params.sourceTurnEndIndex,
|
|
233207
233421
|
reviewSpan: params.reviewSpan,
|
|
233422
|
+
reviewContextMode: params.reviewContextMode,
|
|
233208
233423
|
reviewerAgentType: params.reviewerAgentType,
|
|
233209
233424
|
intentBrief: params.intentBrief,
|
|
233210
233425
|
runId: remoteRunId,
|
|
@@ -233396,6 +233611,7 @@ function recoverPendingRemoteReviewerOnce(deps, intent) {
|
|
|
233396
233611
|
reviewFocus: intent.review_focus ?? void 0,
|
|
233397
233612
|
sourceTurnEndIndex: intent.source_turn_end_index ?? void 0,
|
|
233398
233613
|
reviewSpan: intent.review_span,
|
|
233614
|
+
reviewContextMode: intent.review_context_mode ?? void 0,
|
|
233399
233615
|
reviewerAgentType: intent.agent_type,
|
|
233400
233616
|
intentBrief: intent.intent_brief ?? void 0,
|
|
233401
233617
|
userId: intent.user_id ?? void 0,
|
|
@@ -239487,13 +239703,17 @@ var VERDICT_INSTRUCTIONS = [
|
|
|
239487
239703
|
"3. Non-blocking notes \u2014 style and polish, briefly, clearly separated from the blocking list."
|
|
239488
239704
|
];
|
|
239489
239705
|
function buildReviewerPrompt(opts) {
|
|
239490
|
-
const
|
|
239491
|
-
const
|
|
239492
|
-
const
|
|
239706
|
+
const blind = opts.blind === true;
|
|
239707
|
+
const intent = !blind && opts.originalIntent !== opts.taskContext ? opts.originalIntent : null;
|
|
239708
|
+
const brief = blind ? null : opts.intentBrief || null;
|
|
239709
|
+
const taskContext = blind ? null : opts.taskContext;
|
|
239710
|
+
const selfReport = blind ? null : opts.authorSelfReport;
|
|
239711
|
+
const hasExcerpt = Boolean(intent || taskContext || selfReport);
|
|
239493
239712
|
const scope = opts.scope && opts.scope.changedFiles.length > 0 ? opts.scope : null;
|
|
239494
|
-
const noDiffWithAnalysis = Boolean(
|
|
239713
|
+
const noDiffWithAnalysis = Boolean(selfReport) && selfReport.trim().length >= SELF_REPORT_MIN_CHARS;
|
|
239495
239714
|
return [
|
|
239496
239715
|
"You are a code reviewer agent. Another agent just completed work in this workspace; review it critically and independently.",
|
|
239716
|
+
blind ? '\n## Independent review\nBy design you have been given no context from the conversation that produced this work \u2014 no task statement, no author summary. Infer the intent from the change itself, the repository, and its history, and open your verdict message by stating that inferred intent in one or two sentences. Do not assume any agreement, exemption, or constraint that is not evidenced in the repository; if a behavior looks wrong but could plausibly be intentional, report it marked "possibly intended \u2014 needs author confirmation" rather than staying silent.' : null,
|
|
239497
239717
|
brief ? `
|
|
239498
239718
|
## Intent brief (distilled from the source conversation)
|
|
239499
239719
|
${brief}` : null,
|
|
@@ -239503,10 +239723,10 @@ ${intent}` : null,
|
|
|
239503
239723
|
// Deliberately not titled "Original task": in confirmation-style
|
|
239504
239724
|
// conversations the latest message is often just "ok" — informative as
|
|
239505
239725
|
// the user's last word, misleading as a statement of the task.
|
|
239506
|
-
!brief &&
|
|
239726
|
+
!brief && taskContext ? `
|
|
239507
239727
|
## Latest user message (verbatim)
|
|
239508
|
-
${
|
|
239509
|
-
selfReportSection(
|
|
239728
|
+
${taskContext}` : null,
|
|
239729
|
+
selfReportSection(selfReport),
|
|
239510
239730
|
opts.reviewFocus ? `
|
|
239511
239731
|
## Review focus (from the user)
|
|
239512
239732
|
${opts.reviewFocus}` : null,
|
|
@@ -239523,14 +239743,17 @@ Confine your review to these files and changes. Other uncommitted or pre-existin
|
|
|
239523
239743
|
"- Do NOT modify any files \u2014 you are in read-only review mode.",
|
|
239524
239744
|
"- Inspect the actual workspace state yourself: read the relevant files, run `git diff`, `git status` and `git log`.",
|
|
239525
239745
|
reviewTargetPromptLine(opts.target),
|
|
239526
|
-
noDiffWithAnalysis ? "- Judge correctness and completeness against the task. For this analysis/plan turn the work under review is the reasoning and the proposal, not code quality of a diff. Be specific: reference files and lines." : "- Judge correctness, completeness against the task, and code quality. Be specific: reference files and lines.",
|
|
239746
|
+
blind ? "- Judge correctness and code quality on the change's own evidence \u2014 there is no task statement to judge completeness against. Be specific: reference files and lines." : noDiffWithAnalysis ? "- Judge correctness and completeness against the task. For this analysis/plan turn the work under review is the reasoning and the proposal, not code quality of a diff. Be specific: reference files and lines." : "- Judge correctness, completeness against the task, and code quality. Be specific: reference files and lines.",
|
|
239527
239747
|
// These two only make sense against a distilled brief: tier 2 has no
|
|
239528
239748
|
// [settled]/[tentative] marks and no stated scope, and implying it does
|
|
239529
239749
|
// would suppress findings on the strength of data that doesn't exist.
|
|
239530
239750
|
brief ? "- Where the brief marks a decision, non-goal, or accepted limitation as [settled], do not re-raise the choice itself as a finding; DO report concrete consequences it causes \u2014 failure of the core goal, or a correctness, security, or data loss problem. Items marked [tentative] (or unmarked) get normal review. A violated hard constraint is always blocking." : null,
|
|
239531
239751
|
brief ? "- Do not propose enhancements beyond the brief's stated scope \u2014 scope expansion is a product decision, not a review finding." : null,
|
|
239532
239752
|
...VERDICT_INSTRUCTIONS,
|
|
239533
|
-
|
|
239753
|
+
// "deliberately withheld" vs the tier-3 "was unavailable": both are
|
|
239754
|
+
// workspace-only prompts, but post-hoc attribution must be able to tell a
|
|
239755
|
+
// user choice from a degradation.
|
|
239756
|
+
blind ? "\n(review context: independent review \u2014 session context deliberately withheld; live workspace only)" : brief ? opts.authorSelfReport ? "\n(review context: distilled intent brief + author self-report + live workspace)" : "\n(review context: distilled intent brief + live workspace)" : hasExcerpt ? "\n(review context: deterministic excerpt of the source conversation + live workspace)" : "\n(review context: live workspace only \u2014 the source conversation was unavailable)"
|
|
239534
239757
|
].filter((l) => l !== null).join("\n");
|
|
239535
239758
|
}
|
|
239536
239759
|
function reviewTargetPromptLine(target) {
|
|
@@ -239699,6 +239922,7 @@ var WorkflowEngine = class {
|
|
|
239699
239922
|
sessionId: null,
|
|
239700
239923
|
title: null,
|
|
239701
239924
|
agentType: null,
|
|
239925
|
+
lastActiveAt: null,
|
|
239702
239926
|
reason
|
|
239703
239927
|
});
|
|
239704
239928
|
const source = await this.storage.agentSessions.getById(sourceSessionId);
|
|
@@ -239731,6 +239955,7 @@ var WorkflowEngine = class {
|
|
|
239731
239955
|
sessionId: reviewer.id,
|
|
239732
239956
|
title: reviewer.title ?? null,
|
|
239733
239957
|
agentType: reviewer.agent_type,
|
|
239958
|
+
lastActiveAt: reviewerProjection.lastActiveAt,
|
|
239734
239959
|
reason: null
|
|
239735
239960
|
};
|
|
239736
239961
|
}
|
|
@@ -239741,6 +239966,9 @@ var WorkflowEngine = class {
|
|
|
239741
239966
|
if (opts.reviewerSessionId && opts.newReviewerSessionId) {
|
|
239742
239967
|
throw new WorkflowError("reviewer-unavailable", "\u4E0D\u80FD\u540C\u65F6\u590D\u7528\u548C\u65B0\u5EFA reviewer session");
|
|
239743
239968
|
}
|
|
239969
|
+
if (opts.blind && opts.reviewerSessionId) {
|
|
239970
|
+
throw new WorkflowError("reviewer-unavailable", "blind review \u4E0D\u80FD\u590D\u7528\u5DF2\u6709 reviewer session");
|
|
239971
|
+
}
|
|
239744
239972
|
const runId = opts.runId ?? randomUUID6();
|
|
239745
239973
|
const existingRun = opts.runId ? await this.storage.workflowRuns.getById(runId) : void 0;
|
|
239746
239974
|
if (existingRun) {
|
|
@@ -239902,6 +240130,7 @@ var WorkflowEngine = class {
|
|
|
239902
240130
|
originalIntent: extractFirstUserMessage(entries),
|
|
239903
240131
|
authorSelfReport: extractAuthorSelfReport(entries, turnEndIndex),
|
|
239904
240132
|
intentBrief: opts.intentBrief ?? null,
|
|
240133
|
+
blind: opts.blind,
|
|
239905
240134
|
reviewFocus: opts.reviewFocus ?? null,
|
|
239906
240135
|
target,
|
|
239907
240136
|
scope
|
|
@@ -247419,6 +247648,25 @@ var routes11 = async (fastify2) => {
|
|
|
247419
247648
|
}
|
|
247420
247649
|
return reader(session.id, "runtime");
|
|
247421
247650
|
}
|
|
247651
|
+
function sendBackFields(session) {
|
|
247652
|
+
const parentId = session.branchedFromSessionId;
|
|
247653
|
+
if (!parentId) return {};
|
|
247654
|
+
return {
|
|
247655
|
+
branchedFromSessionId: parentId,
|
|
247656
|
+
branchedFromAvailable: fastify2.agentSessionManager.getSession(parentId) != null,
|
|
247657
|
+
// Entry indices survive the branch copy unchanged, so this tells the UI
|
|
247658
|
+
// which dividers are inherited history (≤) vs the branch's own turns (>)
|
|
247659
|
+
// — send-back only makes sense on the latter.
|
|
247660
|
+
...session.branchedFromEntryIndex != null ? { branchedFromEntryIndex: session.branchedFromEntryIndex } : {}
|
|
247661
|
+
};
|
|
247662
|
+
}
|
|
247663
|
+
async function mapRemoteSendBackFields(remoteServerId, session) {
|
|
247664
|
+
const { branchedFromSessionId: workerParentId, branchedFromAvailable, branchedFromEntryIndex, ...rest } = session;
|
|
247665
|
+
if (typeof workerParentId !== "string") return rest;
|
|
247666
|
+
const mapping = await fastify2.storage.remoteSessionMappings.getByRemote?.(remoteServerId, workerParentId);
|
|
247667
|
+
if (!mapping) return rest;
|
|
247668
|
+
return { ...rest, branchedFromSessionId: mapping.local_session_id, branchedFromAvailable, branchedFromEntryIndex };
|
|
247669
|
+
}
|
|
247422
247670
|
async function performLocalBranch(sourceSessionId, userId, opts) {
|
|
247423
247671
|
const sourceRow = await fastify2.storage.agentSessions.getById(sourceSessionId);
|
|
247424
247672
|
const sourceProjection = sourceRow ? await projectLocalSessionIdentity(sourceRow) : void 0;
|
|
@@ -247454,7 +247702,8 @@ var routes11 = async (fastify2) => {
|
|
|
247454
247702
|
permissionMode: session?.permissionMode || "edit",
|
|
247455
247703
|
agentType: session?.agentType || "claude-code",
|
|
247456
247704
|
model: session?.model ?? null,
|
|
247457
|
-
title: dbRow?.title ?? null
|
|
247705
|
+
title: dbRow?.title ?? null,
|
|
247706
|
+
...session ? sendBackFields(session) : {}
|
|
247458
247707
|
},
|
|
247459
247708
|
messages
|
|
247460
247709
|
}
|
|
@@ -247542,7 +247791,8 @@ var routes11 = async (fastify2) => {
|
|
|
247542
247791
|
workspaceCheckoutId: session?.workspaceCheckoutId ?? null,
|
|
247543
247792
|
worktreePath: projection?.worktreePath ?? session?.checkoutPath ?? null,
|
|
247544
247793
|
checkoutDeletedAt: projection?.checkoutDeletedAt ?? null,
|
|
247545
|
-
processAlive: session ? fastify2.agentSessionManager.getSessionProcessAlive(sessionId) : false
|
|
247794
|
+
processAlive: session ? fastify2.agentSessionManager.getSessionProcessAlive(sessionId) : false,
|
|
247795
|
+
...session ? sendBackFields(session) : {}
|
|
247546
247796
|
},
|
|
247547
247797
|
messages: historyWindow ? historyWindow.entries.map((entry) => entry.message) : messages,
|
|
247548
247798
|
...historyWindow ? { historyWindow } : {}
|
|
@@ -247692,7 +247942,8 @@ var routes11 = async (fastify2) => {
|
|
|
247692
247942
|
model: active.model ?? null,
|
|
247693
247943
|
workspaceCheckoutId: active.workspaceCheckoutId,
|
|
247694
247944
|
worktreePath: active.checkoutPath,
|
|
247695
|
-
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(sessionId)
|
|
247945
|
+
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(sessionId),
|
|
247946
|
+
...sendBackFields(active)
|
|
247696
247947
|
},
|
|
247697
247948
|
messages: fastify2.agentSessionManager.getMessages(sessionId)
|
|
247698
247949
|
});
|
|
@@ -248016,7 +248267,7 @@ var routes11 = async (fastify2) => {
|
|
|
248016
248267
|
}
|
|
248017
248268
|
return reply.code(200).send({
|
|
248018
248269
|
session: {
|
|
248019
|
-
...remoteData.session,
|
|
248270
|
+
...await mapRemoteSendBackFields(agentMode, remoteData.session),
|
|
248020
248271
|
id: localSessionId,
|
|
248021
248272
|
projectId: req.params.projectId
|
|
248022
248273
|
},
|
|
@@ -248058,7 +248309,8 @@ var routes11 = async (fastify2) => {
|
|
|
248058
248309
|
permissionMode: session?.permissionMode || "edit",
|
|
248059
248310
|
agentType: session?.agentType || "claude-code",
|
|
248060
248311
|
model: session?.model ?? null,
|
|
248061
|
-
processAlive: session ? fastify2.agentSessionManager.getSessionProcessAlive(sessionId) : false
|
|
248312
|
+
processAlive: session ? fastify2.agentSessionManager.getSessionProcessAlive(sessionId) : false,
|
|
248313
|
+
...session ? sendBackFields(session) : {}
|
|
248062
248314
|
},
|
|
248063
248315
|
messages: historyWindow ? historyWindow.entries.map((entry) => entry.message) : messages,
|
|
248064
248316
|
...historyWindow ? { historyWindow } : {}
|
|
@@ -248195,7 +248447,7 @@ var routes11 = async (fastify2) => {
|
|
|
248195
248447
|
return reply.code(200).send({
|
|
248196
248448
|
...remoteData,
|
|
248197
248449
|
session: {
|
|
248198
|
-
...remoteData.session,
|
|
248450
|
+
...await mapRemoteSendBackFields(remoteInfo.remoteServerId, remoteData.session),
|
|
248199
248451
|
id: req.params.sessionId,
|
|
248200
248452
|
projectId: registered?.workspace.project_id ?? mapping?.project_id,
|
|
248201
248453
|
branch: registered ? registered.workspace.branch === "" ? null : registered.workspace.branch : mapping?.branch,
|
|
@@ -248240,7 +248492,8 @@ var routes11 = async (fastify2) => {
|
|
|
248240
248492
|
permissionMode: session.permissionMode,
|
|
248241
248493
|
agentType: session.agentType || "claude-code",
|
|
248242
248494
|
model: session.model ?? null,
|
|
248243
|
-
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(session.id)
|
|
248495
|
+
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(session.id),
|
|
248496
|
+
...sendBackFields(session)
|
|
248244
248497
|
},
|
|
248245
248498
|
messages
|
|
248246
248499
|
});
|
|
@@ -248271,7 +248524,7 @@ var routes11 = async (fastify2) => {
|
|
|
248271
248524
|
return reply.code(200).send({
|
|
248272
248525
|
...data,
|
|
248273
248526
|
session: data.session ? {
|
|
248274
|
-
...data.session,
|
|
248527
|
+
...await mapRemoteSendBackFields(remoteInfo.remoteServerId, data.session),
|
|
248275
248528
|
id: req.params.sessionId,
|
|
248276
248529
|
projectId: projectIdFromRemoteSessionId(req.params.sessionId, remoteInfo),
|
|
248277
248530
|
branch: remoteInfo.branch ?? null
|
|
@@ -248309,7 +248562,8 @@ var routes11 = async (fastify2) => {
|
|
|
248309
248562
|
permissionMode: session.permissionMode,
|
|
248310
248563
|
agentType: session.agentType,
|
|
248311
248564
|
model: session.model,
|
|
248312
|
-
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(session.id)
|
|
248565
|
+
processAlive: fastify2.agentSessionManager.getSessionProcessAlive(session.id),
|
|
248566
|
+
...sendBackFields(session)
|
|
248313
248567
|
}
|
|
248314
248568
|
});
|
|
248315
248569
|
});
|
|
@@ -248753,6 +249007,68 @@ var routes11 = async (fastify2) => {
|
|
|
248753
249007
|
});
|
|
248754
249008
|
}
|
|
248755
249009
|
);
|
|
249010
|
+
fastify2.post(
|
|
249011
|
+
"/api/agent-sessions/:sessionId/background-tasks/:taskId/keep",
|
|
249012
|
+
async (req, reply) => {
|
|
249013
|
+
const userId = requireUserFacingUserId(req, reply);
|
|
249014
|
+
if (userId === null) return;
|
|
249015
|
+
const { sessionId, taskId } = req.params;
|
|
249016
|
+
if (sessionId.startsWith("remote-")) {
|
|
249017
|
+
const remoteInfo = await getAuthorizedRemoteSessionInfo(sessionId, userId);
|
|
249018
|
+
if (!remoteInfo) {
|
|
249019
|
+
return reply.code(404).send({ error: "Remote session not found" });
|
|
249020
|
+
}
|
|
249021
|
+
const result = await proxyAuto(
|
|
249022
|
+
remoteInfo.remoteServerId,
|
|
249023
|
+
"POST",
|
|
249024
|
+
`/api/agent-sessions/${remoteInfo.remoteSessionId}/background-tasks/${encodeURIComponent(taskId)}/keep`,
|
|
249025
|
+
{}
|
|
249026
|
+
);
|
|
249027
|
+
return reply.code(proxyStatus(result)).send(result.data);
|
|
249028
|
+
}
|
|
249029
|
+
const row = await fastify2.storage.agentSessions.getById(sessionId);
|
|
249030
|
+
if (!row || !await fastify2.storage.projects.getById(row.project_id, userId)) {
|
|
249031
|
+
return reply.code(404).send({ error: "Session not found" });
|
|
249032
|
+
}
|
|
249033
|
+
if (!fastify2.agentSessionManager.sanctionBackgroundTask(sessionId, taskId)) {
|
|
249034
|
+
return reply.code(404).send({ error: "Session not running" });
|
|
249035
|
+
}
|
|
249036
|
+
return reply.code(200).send({ success: true });
|
|
249037
|
+
}
|
|
249038
|
+
);
|
|
249039
|
+
fastify2.post(
|
|
249040
|
+
"/api/agent-sessions/:sessionId/background-tasks/:taskId/stop",
|
|
249041
|
+
async (req, reply) => {
|
|
249042
|
+
const userId = requireUserFacingUserId(req, reply);
|
|
249043
|
+
if (userId === null) return;
|
|
249044
|
+
const { sessionId, taskId } = req.params;
|
|
249045
|
+
if (sessionId.startsWith("remote-")) {
|
|
249046
|
+
const remoteInfo = await getAuthorizedRemoteSessionInfo(sessionId, userId);
|
|
249047
|
+
if (!remoteInfo) {
|
|
249048
|
+
return reply.code(404).send({ error: "Remote session not found" });
|
|
249049
|
+
}
|
|
249050
|
+
const result = await proxyAuto(
|
|
249051
|
+
remoteInfo.remoteServerId,
|
|
249052
|
+
"POST",
|
|
249053
|
+
`/api/agent-sessions/${remoteInfo.remoteSessionId}/background-tasks/${encodeURIComponent(taskId)}/stop`,
|
|
249054
|
+
{}
|
|
249055
|
+
);
|
|
249056
|
+
return reply.code(proxyStatus(result)).send(result.data);
|
|
249057
|
+
}
|
|
249058
|
+
const row = await fastify2.storage.agentSessions.getById(sessionId);
|
|
249059
|
+
if (!row || !await fastify2.storage.projects.getById(row.project_id, userId)) {
|
|
249060
|
+
return reply.code(404).send({ error: "Session not found" });
|
|
249061
|
+
}
|
|
249062
|
+
const outcome = fastify2.agentSessionManager.stopBackgroundTask(sessionId, taskId);
|
|
249063
|
+
if (outcome === "not_found") {
|
|
249064
|
+
return reply.code(404).send({ error: "Session not running" });
|
|
249065
|
+
}
|
|
249066
|
+
if (outcome === "unsupported") {
|
|
249067
|
+
return reply.code(501).send({ error: "This agent cannot stop a single background task \u2014 stop the session instead" });
|
|
249068
|
+
}
|
|
249069
|
+
return reply.code(200).send({ success: true });
|
|
249070
|
+
}
|
|
249071
|
+
);
|
|
248756
249072
|
fastify2.post(
|
|
248757
249073
|
"/api/agent-sessions/:sessionId/branch",
|
|
248758
249074
|
async (req, reply) => {
|
|
@@ -250307,6 +250623,10 @@ function parseReviewSpan(raw) {
|
|
|
250307
250623
|
if (raw === void 0) return "this_turn";
|
|
250308
250624
|
return raw === "this_turn" || raw === "session_start" ? raw : null;
|
|
250309
250625
|
}
|
|
250626
|
+
function parseReviewContextMode(raw) {
|
|
250627
|
+
if (raw === void 0) return "briefed";
|
|
250628
|
+
return raw === "briefed" || raw === "blind" ? raw : null;
|
|
250629
|
+
}
|
|
250310
250630
|
function normalizeIntentBrief(raw) {
|
|
250311
250631
|
if (!raw?.trim()) return void 0;
|
|
250312
250632
|
return raw.length > 8e3 ? raw.slice(0, 8e3) + "\u2026" : raw;
|
|
@@ -250414,12 +250734,18 @@ async function routes20(fastify2) {
|
|
|
250414
250734
|
return reply.code(400).send({ error: "reviewerSessionId and reviewerAgentType are mutually exclusive" });
|
|
250415
250735
|
}
|
|
250416
250736
|
const reviewerSessionId = reviewerSessionIdRaw?.trim();
|
|
250737
|
+
const reviewContextMode = parseReviewContextMode(req.body?.reviewContextMode);
|
|
250738
|
+
if (reviewContextMode === null) return reply.code(400).send({ error: "reviewContextMode must be one of: briefed, blind" });
|
|
250739
|
+
const blind = reviewContextMode === "blind";
|
|
250740
|
+
if (blind && reviewerSessionId) {
|
|
250741
|
+
return reply.code(400).send({ error: "blind review requires a new reviewer session" });
|
|
250742
|
+
}
|
|
250417
250743
|
const intentBriefRaw = req.body?.intentBrief;
|
|
250418
250744
|
if (intentBriefRaw !== void 0 && typeof intentBriefRaw !== "string") {
|
|
250419
250745
|
return reply.code(400).send({ error: "intentBrief must be a string" });
|
|
250420
250746
|
}
|
|
250421
250747
|
const clientProvidedBrief = intentBriefRaw !== void 0;
|
|
250422
|
-
const clientBrief = normalizeIntentBrief(intentBriefRaw);
|
|
250748
|
+
const clientBrief = blind ? void 0 : normalizeIntentBrief(intentBriefRaw);
|
|
250423
250749
|
if (sourceSessionId.startsWith("remote-")) {
|
|
250424
250750
|
const remoteInfo = fastify2.remoteSessionMap.get(sourceSessionId);
|
|
250425
250751
|
if (!remoteInfo) return reply.code(404).send({ error: "Session not found" });
|
|
@@ -250442,7 +250768,7 @@ async function routes20(fastify2) {
|
|
|
250442
250768
|
bareReviewerSessionId = reviewerInfo.remoteSessionId;
|
|
250443
250769
|
}
|
|
250444
250770
|
let intentBrief2 = clientBrief;
|
|
250445
|
-
if (!clientProvidedBrief && !bareReviewerSessionId) {
|
|
250771
|
+
if (!clientProvidedBrief && !bareReviewerSessionId && !blind) {
|
|
250446
250772
|
intentBrief2 = await distillIntentBrief(userId, sourceSessionId);
|
|
250447
250773
|
}
|
|
250448
250774
|
if (reviewerSessionId && !await fastify2.remoteNotificationSync.prepareForNewTurn(reviewerSessionId)) {
|
|
@@ -250486,6 +250812,10 @@ async function routes20(fastify2) {
|
|
|
250486
250812
|
reviewFocus,
|
|
250487
250813
|
sourceTurnEndIndex,
|
|
250488
250814
|
reviewSpan,
|
|
250815
|
+
// Additive tunnel field: a worker that predates it ignores the flag
|
|
250816
|
+
// and runs a briefed (tier-2) review — the reviewer prompt's
|
|
250817
|
+
// trailing "(review context: …)" line records what actually ran.
|
|
250818
|
+
reviewContextMode,
|
|
250489
250819
|
reviewerAgentType: reviewerAgentType ?? "claude-code",
|
|
250490
250820
|
intentBrief: intentBrief2,
|
|
250491
250821
|
userId
|
|
@@ -250586,7 +250916,7 @@ async function routes20(fastify2) {
|
|
|
250586
250916
|
return reply.code(400).send({ error: "branch does not match source session" });
|
|
250587
250917
|
}
|
|
250588
250918
|
let intentBrief = clientBrief;
|
|
250589
|
-
if (!clientProvidedBrief && !reviewerSessionId) {
|
|
250919
|
+
if (!clientProvidedBrief && !reviewerSessionId && !blind) {
|
|
250590
250920
|
intentBrief = await distillIntentBrief(userId, sourceSessionId);
|
|
250591
250921
|
}
|
|
250592
250922
|
try {
|
|
@@ -250599,7 +250929,8 @@ async function routes20(fastify2) {
|
|
|
250599
250929
|
reviewSpan,
|
|
250600
250930
|
reviewerAgentType,
|
|
250601
250931
|
reviewerSessionId,
|
|
250602
|
-
intentBrief
|
|
250932
|
+
intentBrief,
|
|
250933
|
+
blind
|
|
250603
250934
|
});
|
|
250604
250935
|
return reply.code(201).send({ run: run2 });
|
|
250605
250936
|
} catch (err) {
|
|
@@ -250816,11 +251147,14 @@ async function routes20(fastify2) {
|
|
|
250816
251147
|
if (!sourceSessionId) return reply.code(400).send({ error: "sourceSessionId is required" });
|
|
250817
251148
|
const reviewSpan = parseReviewSpan(req.body?.reviewSpan);
|
|
250818
251149
|
if (reviewSpan === null) return reply.code(400).send({ error: "reviewSpan must be one of: this_turn, session_start" });
|
|
251150
|
+
const reviewContextMode = parseReviewContextMode(req.body?.reviewContextMode);
|
|
251151
|
+
if (reviewContextMode === null) return reply.code(400).send({ error: "reviewContextMode must be one of: briefed, blind" });
|
|
251152
|
+
const blind = reviewContextMode === "blind";
|
|
250819
251153
|
const intentBriefRaw = req.body?.intentBrief;
|
|
250820
251154
|
if (intentBriefRaw !== void 0 && typeof intentBriefRaw !== "string") {
|
|
250821
251155
|
return reply.code(400).send({ error: "intentBrief must be a string" });
|
|
250822
251156
|
}
|
|
250823
|
-
const intentBrief = normalizeIntentBrief(intentBriefRaw);
|
|
251157
|
+
const intentBrief = blind ? void 0 : normalizeIntentBrief(intentBriefRaw);
|
|
250824
251158
|
const reviewerAgentType = parseReviewerAgentType(req.body?.reviewerAgentType);
|
|
250825
251159
|
if (reviewerAgentType === null) return reply.code(400).send({ error: "reviewerAgentType must be one of: claude-code, codex" });
|
|
250826
251160
|
const reviewerSessionIdRaw = req.body?.reviewerSessionId;
|
|
@@ -250858,6 +251192,7 @@ async function routes20(fastify2) {
|
|
|
250858
251192
|
reviewerAgentType,
|
|
250859
251193
|
reviewerSessionId,
|
|
250860
251194
|
intentBrief,
|
|
251195
|
+
blind,
|
|
250861
251196
|
runId: runId || void 0,
|
|
250862
251197
|
newReviewerSessionId: newReviewerSessionId || void 0
|
|
250863
251198
|
});
|
|
@@ -251770,6 +252105,11 @@ var routes23 = async (fastify2) => {
|
|
|
251770
252105
|
try {
|
|
251771
252106
|
const message = JSON.parse(data.toString());
|
|
251772
252107
|
if (message.type === "input" || message.type === "resize") {
|
|
252108
|
+
if (message.type === "resize") {
|
|
252109
|
+
console.log(
|
|
252110
|
+
`[WebSocket] resize ${processId} \u2192 ${message.cols}x${message.rows} ip=${req.ip} ua=${req.headers["user-agent"] ?? "?"}`
|
|
252111
|
+
);
|
|
252112
|
+
}
|
|
251773
252113
|
handle.handleInput(message);
|
|
251774
252114
|
}
|
|
251775
252115
|
} catch (error48) {
|
|
@@ -251845,6 +252185,9 @@ var routes23 = async (fastify2) => {
|
|
|
251845
252185
|
} else if (msg.type === "input") {
|
|
251846
252186
|
handleInputMap.get(msg.processId)?.({ type: "input", data: msg.data });
|
|
251847
252187
|
} else if (msg.type === "resize") {
|
|
252188
|
+
console.log(
|
|
252189
|
+
`[ExecutorMux] resize ${msg.processId} \u2192 ${msg.cols}x${msg.rows} ip=${req.ip} ua=${req.headers["user-agent"] ?? "?"}`
|
|
252190
|
+
);
|
|
251848
252191
|
handleInputMap.get(msg.processId)?.({ type: "resize", cols: msg.cols, rows: msg.rows });
|
|
251849
252192
|
}
|
|
251850
252193
|
} catch (error48) {
|
|
@@ -251936,6 +252279,12 @@ var routes23 = async (fastify2) => {
|
|
|
251936
252279
|
}));
|
|
251937
252280
|
} catch {
|
|
251938
252281
|
}
|
|
252282
|
+
if (cacheEntry.backgroundTasks !== null) {
|
|
252283
|
+
try {
|
|
252284
|
+
socket.send(cacheEntry.backgroundTasks);
|
|
252285
|
+
} catch {
|
|
252286
|
+
}
|
|
252287
|
+
}
|
|
251939
252288
|
for (const raw of cacheEntry.messages) {
|
|
251940
252289
|
if (replayAfter >= 0) {
|
|
251941
252290
|
try {
|
|
@@ -251959,12 +252308,6 @@ var routes23 = async (fastify2) => {
|
|
|
251959
252308
|
socket.send(JSON.stringify({ Ready: true, historyEpoch: cacheEntry.historyEpoch ?? void 0 }));
|
|
251960
252309
|
} catch {
|
|
251961
252310
|
}
|
|
251962
|
-
if (cacheEntry.backgroundTasks !== null) {
|
|
251963
|
-
try {
|
|
251964
|
-
socket.send(cacheEntry.backgroundTasks);
|
|
251965
|
-
} catch {
|
|
251966
|
-
}
|
|
251967
|
-
}
|
|
251968
252311
|
if (cacheEntry.finished) {
|
|
251969
252312
|
try {
|
|
251970
252313
|
socket.send(JSON.stringify({ finished: true }));
|
|
@@ -260532,6 +260875,12 @@ var WORKER_CAPABILITIES = {
|
|
|
260532
260875
|
"http:POST /api/agent-sessions/:param/restart": { since: "0.2.0", summary: "\u91CD\u542F\u4F1A\u8BDD\u8FDB\u7A0B" },
|
|
260533
260876
|
"http:POST /api/agent-sessions/:param/agent-type": { since: "0.2.0", summary: "\u5207\u6362 agent \u7C7B\u578B" },
|
|
260534
260877
|
"http:POST /api/agent-sessions/:param/model": { since: "0.2.0", summary: "\u5207\u6362\u4F1A\u8BDD\u6A21\u578B" },
|
|
260878
|
+
// Additive: an older worker 404s it, and the UI degrades by hiding the
|
|
260879
|
+
// "keep running" button — that worker has no park deadline to defuse.
|
|
260880
|
+
"http:POST /api/agent-sessions/:param/background-tasks/:param/keep": { since: "0.3.28", summary: "\u4E3A\u540E\u53F0\u4EFB\u52A1\u80CC\u4E66,\u514D\u4E8E\u8D85\u65F6\u6536\u5C3E" },
|
|
260881
|
+
// Additive alongside /keep, and degrades the same way: an older worker 404s
|
|
260882
|
+
// and the UI hides the button.
|
|
260883
|
+
"http:POST /api/agent-sessions/:param/background-tasks/:param/stop": { since: "0.3.28", summary: "\u505C\u6B62\u5355\u4E2A\u540E\u53F0\u4EFB\u52A1" },
|
|
260535
260884
|
"http:POST /api/path/agent-sessions/:param/branch": { since: "0.2.0", summary: "\u4ECE\u5386\u53F2\u5206\u53C9\u4F1A\u8BDD" },
|
|
260536
260885
|
"http:POST /api/agent-sessions/:param/switch-mode": { since: "0.2.0", summary: "\u6743\u9650\u6A21\u5F0F\u5207\u6362" },
|
|
260537
260886
|
"http:POST /api/agent-sessions/:param/accept-plan": { since: "0.2.0", summary: "\u63A5\u53D7\u8BA1\u5212(\u9000\u51FA plan \u6A21\u5F0F)" },
|