chatroom-cli 1.128.0 → 1.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +585 -55
- package/dist/index.js.map +28 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20251,6 +20251,16 @@ var init_agent_process_manager_service = __esm(() => {
|
|
|
20251
20251
|
init_check_agent_slot();
|
|
20252
20252
|
});
|
|
20253
20253
|
|
|
20254
|
+
// ../../services/backend/config/reliability.ts
|
|
20255
|
+
var HARNESS_SESSION_READY_TIMEOUT_MS = 5000, NATIVE_DELIVERY_RECONCILE_MS = 1e4, DAEMON_HEARTBEAT_INTERVAL_MS, DAEMON_HEARTBEAT_TTL_MS, AGENT_REQUEST_DEADLINE_MS = 120000, MACHINE_COMMAND_LEASE_RENEWAL_INTERVAL_MS = 20000, MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS, SCOPE_TARGET_STOP_TIMEOUT_MS = 1e4, AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS = 120000, CONNECTION_CLOSE_REQUEST_TTL_MS, ENHANCER_TERMINAL_JOB_RETENTION_MS;
|
|
20256
|
+
var init_reliability = __esm(() => {
|
|
20257
|
+
DAEMON_HEARTBEAT_INTERVAL_MS = 5 * 60000;
|
|
20258
|
+
DAEMON_HEARTBEAT_TTL_MS = 6 * DAEMON_HEARTBEAT_INTERVAL_MS;
|
|
20259
|
+
MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS = 5 * 60000;
|
|
20260
|
+
CONNECTION_CLOSE_REQUEST_TTL_MS = 10 * 60000;
|
|
20261
|
+
ENHANCER_TERMINAL_JOB_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
20262
|
+
});
|
|
20263
|
+
|
|
20254
20264
|
// ../shared/src/domain/team-kind.ts
|
|
20255
20265
|
var TEAM_PRESET_IDS;
|
|
20256
20266
|
var init_team_kind = __esm(() => {
|
|
@@ -20393,6 +20403,13 @@ var init_agent_role = __esm(() => {
|
|
|
20393
20403
|
researcher: {
|
|
20394
20404
|
tags: ["ephemeral" /* Ephemeral */],
|
|
20395
20405
|
teams: [{ teamId: "duo", order: 25 }]
|
|
20406
|
+
},
|
|
20407
|
+
verifier: {
|
|
20408
|
+
tags: ["ephemeral" /* Ephemeral */],
|
|
20409
|
+
teams: [
|
|
20410
|
+
{ teamId: "duo", order: 45 },
|
|
20411
|
+
{ teamId: "solo", order: 45 }
|
|
20412
|
+
]
|
|
20396
20413
|
}
|
|
20397
20414
|
};
|
|
20398
20415
|
AGENT_ROLE_DEFINITIONS = Object.fromEntries(Object.entries(roleMetadata).map(([role, metadata]) => [role, { role, ...metadata }]));
|
|
@@ -35229,8 +35246,10 @@ var init_agent_lifecycle_fact = __esm(() => {
|
|
|
35229
35246
|
function isDeliverableTaskStatus(status) {
|
|
35230
35247
|
return status === "pending" || status === "acknowledged";
|
|
35231
35248
|
}
|
|
35249
|
+
var ACTIVE_TASK_STATUSES;
|
|
35232
35250
|
var init_assigned_task = __esm(() => {
|
|
35233
35251
|
init_agent_role();
|
|
35252
|
+
ACTIVE_TASK_STATUSES = ["pending", "acknowledged", "in_progress"];
|
|
35234
35253
|
});
|
|
35235
35254
|
|
|
35236
35255
|
// src/daemon/infrastructure/local/harness/services/spawn-prompt.ts
|
|
@@ -35292,6 +35311,17 @@ var init_native_task_injector_logic = __esm(() => {
|
|
|
35292
35311
|
};
|
|
35293
35312
|
});
|
|
35294
35313
|
|
|
35314
|
+
// src/daemon/services/task-service/domain/usecase/uncovered-turn-nudge.ts
|
|
35315
|
+
function buildUncoveredTurnNudgePrompt(args2) {
|
|
35316
|
+
return [
|
|
35317
|
+
`Your previous turn ended, but task ${args2.taskId} has no handoff recorded.`,
|
|
35318
|
+
"If the work is complete, run `chatroom handoff` for this task now, as instructed in the task.",
|
|
35319
|
+
"If you are waiting on something (for example a background command or CI), do not end your turn: wait for it in the foreground, then run `chatroom handoff`.",
|
|
35320
|
+
"If you cannot do either, reply with one line saying what you are waiting on."
|
|
35321
|
+
].join(`
|
|
35322
|
+
`);
|
|
35323
|
+
}
|
|
35324
|
+
|
|
35295
35325
|
// ../../services/backend/src/domain/entities/participant.ts
|
|
35296
35326
|
var NATIVE_WAITING_ACTION = "native:waiting", NATIVE_TASK_INJECTED_ACTION = "native:task-injected", GET_NEXT_TASK_STARTED_ACTION = "get-next-task:started";
|
|
35297
35327
|
|
|
@@ -36324,16 +36354,6 @@ var init_parse_session_augmentation = __esm(() => {
|
|
|
36324
36354
|
init_team_agent_settings();
|
|
36325
36355
|
});
|
|
36326
36356
|
|
|
36327
|
-
// ../../services/backend/config/reliability.ts
|
|
36328
|
-
var HARNESS_SESSION_READY_TIMEOUT_MS = 5000, NATIVE_DELIVERY_RECONCILE_MS = 1e4, DAEMON_HEARTBEAT_INTERVAL_MS, DAEMON_HEARTBEAT_TTL_MS, AGENT_REQUEST_DEADLINE_MS = 120000, MACHINE_COMMAND_LEASE_RENEWAL_INTERVAL_MS = 20000, MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS, SCOPE_TARGET_STOP_TIMEOUT_MS = 1e4, AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS = 120000, CONNECTION_CLOSE_REQUEST_TTL_MS, ENHANCER_TERMINAL_JOB_RETENTION_MS;
|
|
36329
|
-
var init_reliability = __esm(() => {
|
|
36330
|
-
DAEMON_HEARTBEAT_INTERVAL_MS = 5 * 60000;
|
|
36331
|
-
DAEMON_HEARTBEAT_TTL_MS = 6 * DAEMON_HEARTBEAT_INTERVAL_MS;
|
|
36332
|
-
MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS = 5 * 60000;
|
|
36333
|
-
CONNECTION_CLOSE_REQUEST_TTL_MS = 10 * 60000;
|
|
36334
|
-
ENHANCER_TERMINAL_JOB_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
36335
|
-
});
|
|
36336
|
-
|
|
36337
36357
|
// src/daemon/services/task-service/service/native-cold-session-before-inject.ts
|
|
36338
36358
|
function sleep3(ms) {
|
|
36339
36359
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -36603,7 +36623,7 @@ function resumeHarnessWithPrompt(task, deps, prompt, harnessSessionId) {
|
|
|
36603
36623
|
taskId,
|
|
36604
36624
|
error: error51
|
|
36605
36625
|
});
|
|
36606
|
-
return;
|
|
36626
|
+
return yield* exports_Effect.fail(resumeResult.failure);
|
|
36607
36627
|
}
|
|
36608
36628
|
yield* exports_Effect.tryPromise({
|
|
36609
36629
|
try: () => deps.audit.emit({
|
|
@@ -36732,6 +36752,8 @@ class AgentWorkManager {
|
|
|
36732
36752
|
nativeTaskDeliveryQueue;
|
|
36733
36753
|
deliveryTaskService;
|
|
36734
36754
|
recoveryGates = new Map;
|
|
36755
|
+
respawnStartRetries = new Map;
|
|
36756
|
+
respawnRestartSeq = 0;
|
|
36735
36757
|
disposed = false;
|
|
36736
36758
|
constructor(deps) {
|
|
36737
36759
|
this.deps = deps;
|
|
@@ -36763,7 +36785,10 @@ class AgentWorkManager {
|
|
|
36763
36785
|
releaseTaskAfterTurnFailure: deps.taskService.releaseTaskAfterTurnFailure,
|
|
36764
36786
|
recordDeliveryFailure: deps.taskService.recordDeliveryFailure,
|
|
36765
36787
|
clearDeliveryFailure: deps.taskService.clearDeliveryFailure,
|
|
36766
|
-
|
|
36788
|
+
recordTurnFailure: deps.taskService.recordTurnFailure,
|
|
36789
|
+
decideUncoveredTurnEnd: deps.taskService.decideUncoveredTurnEnd,
|
|
36790
|
+
confirmNudgeSent: deps.taskService.confirmNudgeSent,
|
|
36791
|
+
resetTurnTracking: deps.taskService.resetTurnTracking,
|
|
36767
36792
|
isRedeliveryExhausted: deps.taskService.isRedeliveryExhausted,
|
|
36768
36793
|
clearRedeliveryTracking: deps.taskService.clearRedeliveryTracking,
|
|
36769
36794
|
forgetStaleTask: deps.taskService.forgetStaleTask,
|
|
@@ -36789,6 +36814,7 @@ class AgentWorkManager {
|
|
|
36789
36814
|
this.unsubscribeTaskService = deps.taskService.subscribe((notification) => this.handleTaskServiceNotification(notification));
|
|
36790
36815
|
}
|
|
36791
36816
|
async handleAgentStarted(event) {
|
|
36817
|
+
this.cancelRespawnStart(recoveryKey(event.chatroomId, event.role), `agent_started:${event.reason ?? "unknown"}`);
|
|
36792
36818
|
await this.waitForRoleRecovery(event.chatroomId, event.role);
|
|
36793
36819
|
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
36794
36820
|
if (event.reason === "user.start") {
|
|
@@ -36906,9 +36932,9 @@ class AgentWorkManager {
|
|
|
36906
36932
|
});
|
|
36907
36933
|
}, delay3);
|
|
36908
36934
|
}
|
|
36909
|
-
async
|
|
36935
|
+
async recoverTaskAfterTurnFailure(event, activeTask, phase) {
|
|
36910
36936
|
const { chatroomId, role } = event;
|
|
36911
|
-
const { exceeded } = await this.deps.taskService.
|
|
36937
|
+
const { exceeded } = await this.deps.taskService.recordTurnFailure({
|
|
36912
36938
|
chatroomId,
|
|
36913
36939
|
role,
|
|
36914
36940
|
taskId: activeTask.taskId
|
|
@@ -36937,7 +36963,7 @@ class AgentWorkManager {
|
|
|
36937
36963
|
const errorDetail = completion.error;
|
|
36938
36964
|
console.error(`[NativeDelivery:turn-failed] chatroom=${event.chatroomId} role=${event.role} task=${activeTask2?.taskId ?? "none"} turn=${completion.turnId} status=${completion.status} source=${completion.source} error=${errorDetail ?? "none"}`);
|
|
36939
36965
|
if (activeTask2) {
|
|
36940
|
-
const recovery = await this.
|
|
36966
|
+
const recovery = await this.recoverTaskAfterTurnFailure(event, activeTask2, "turn-failed");
|
|
36941
36967
|
if (recovery === "exceeded") {
|
|
36942
36968
|
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
36943
36969
|
return { kind: "release-slot" };
|
|
@@ -36977,14 +37003,41 @@ class AgentWorkManager {
|
|
|
36977
37003
|
});
|
|
36978
37004
|
if (activeTask) {
|
|
36979
37005
|
const handoff = await this.deps.taskService.getLatestHandoff(event.chatroomId, event.role);
|
|
36980
|
-
|
|
36981
|
-
|
|
36982
|
-
|
|
36983
|
-
|
|
37006
|
+
const covered = handoff?.taskIds.includes(activeTask.taskId) ?? false;
|
|
37007
|
+
if (covered) {
|
|
37008
|
+
this.deps.taskService.resetTurnTracking({
|
|
37009
|
+
chatroomId: event.chatroomId,
|
|
37010
|
+
role: event.role,
|
|
37011
|
+
taskId: activeTask.taskId
|
|
37012
|
+
});
|
|
37013
|
+
} else {
|
|
37014
|
+
const decision = await this.deps.taskService.decideUncoveredTurnEnd({
|
|
37015
|
+
chatroomId: event.chatroomId,
|
|
37016
|
+
role: event.role,
|
|
37017
|
+
taskId: activeTask.taskId,
|
|
37018
|
+
sessionKey: sessionKeyFor(event.pid, event.slot.harnessSessionId)
|
|
37019
|
+
});
|
|
37020
|
+
if (decision.action === "nudge") {
|
|
37021
|
+
await this.enqueueTurnEndedWaitingFact(event);
|
|
37022
|
+
this.scheduleUncoveredAction(event, activeTask, "nudge");
|
|
37023
|
+
return { kind: "release-slot" };
|
|
36984
37024
|
}
|
|
37025
|
+
if (decision.action === "restart-session") {
|
|
37026
|
+
await this.enqueueTurnEndedWaitingFact(event);
|
|
37027
|
+
this.scheduleUncoveredAction(event, activeTask, "restart-session");
|
|
37028
|
+
return { kind: "release-slot" };
|
|
37029
|
+
}
|
|
37030
|
+
await this.enqueueTurnEndedWaitingFact(event);
|
|
37031
|
+
this.applyParkAction(event);
|
|
37032
|
+
return { kind: "release-slot" };
|
|
36985
37033
|
}
|
|
36986
37034
|
}
|
|
36987
37035
|
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
37036
|
+
await this.enqueueTurnEndedWaitingFact(event);
|
|
37037
|
+
this.scheduleRoleDelivery(event.chatroomId, event.role);
|
|
37038
|
+
return { kind: "release-slot" };
|
|
37039
|
+
}
|
|
37040
|
+
async enqueueTurnEndedWaitingFact(event) {
|
|
36988
37041
|
try {
|
|
36989
37042
|
await this.deps.lifecycleOutbox.enqueue(buildAgentStatusFact({
|
|
36990
37043
|
chatroomId: event.chatroomId,
|
|
@@ -36994,8 +37047,244 @@ class AgentWorkManager {
|
|
|
36994
37047
|
} catch (error51) {
|
|
36995
37048
|
console.error(`[NativeDelivery:turn-ended-outbox-error] chatroom=${event.chatroomId} role=${event.role} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
36996
37049
|
}
|
|
37050
|
+
}
|
|
37051
|
+
scheduleUncoveredAction(event, task, action, attempt = 0, delayMs = 0) {
|
|
37052
|
+
setTimeout(() => {
|
|
37053
|
+
if (this.disposed)
|
|
37054
|
+
return;
|
|
37055
|
+
const run = action === "nudge" ? this.sendUncoveredNudge(event, task, attempt) : this.restartSessionForUncoveredTask(event, task);
|
|
37056
|
+
run.catch((error51) => {
|
|
37057
|
+
console.error(`[NativeDelivery:uncovered-action-error] action=${action} chatroom=${event.chatroomId} role=${event.role} task=${task.taskId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
37058
|
+
});
|
|
37059
|
+
}, delayMs);
|
|
37060
|
+
}
|
|
37061
|
+
async sendUncoveredNudge(event, task, attempt = 0) {
|
|
37062
|
+
const { chatroomId, role } = event;
|
|
37063
|
+
const sessionKey = sessionKeyFor(event.pid, event.slot.harnessSessionId);
|
|
37064
|
+
const deliveryState = getRoleDeliveryState();
|
|
37065
|
+
if (!deliveryState.tryAcquireDelivery(chatroomId, role)) {
|
|
37066
|
+
console.log(`[NativeDelivery:uncovered-nudge-skipped] chatroom=${chatroomId} role=${role} task=${task.taskId} reason=delivery_busy`);
|
|
37067
|
+
if (attempt + 1 < MAX_UNCOVERED_NUDGE_SEND_ATTEMPTS) {
|
|
37068
|
+
this.scheduleUncoveredAction(event, task, "nudge", attempt + 1, UNCOVERED_NUDGE_RETRY_MS);
|
|
37069
|
+
return;
|
|
37070
|
+
}
|
|
37071
|
+
await this.escalateUndeliverableNudge(event, task, sessionKey);
|
|
37072
|
+
return;
|
|
37073
|
+
}
|
|
37074
|
+
const generation = deliveryState.getGeneration(chatroomId, role);
|
|
37075
|
+
try {
|
|
37076
|
+
const skipReason = this.uncoveredNudgeSkipReason(event, task);
|
|
37077
|
+
if (skipReason) {
|
|
37078
|
+
console.log(`[NativeDelivery:uncovered-nudge-skipped] chatroom=${chatroomId} role=${role} task=${task.taskId} reason=${skipReason}`);
|
|
37079
|
+
return;
|
|
37080
|
+
}
|
|
37081
|
+
try {
|
|
37082
|
+
await exports_Effect.runPromise(this.deps.agentMgr.resumeTurnForSlot({
|
|
37083
|
+
chatroomId,
|
|
37084
|
+
role,
|
|
37085
|
+
prompt: buildUncoveredTurnNudgePrompt({ taskId: task.taskId })
|
|
37086
|
+
}));
|
|
37087
|
+
} catch (error51) {
|
|
37088
|
+
console.error(`[NativeDelivery:uncovered-nudge] chatroom=${chatroomId} role=${role} task=${task.taskId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
37089
|
+
await this.escalateUndeliverableNudge(event, task, sessionKey);
|
|
37090
|
+
return;
|
|
37091
|
+
}
|
|
37092
|
+
this.deps.taskService.confirmNudgeSent({ chatroomId, role, taskId: task.taskId, sessionKey });
|
|
37093
|
+
} finally {
|
|
37094
|
+
deliveryState.releaseDelivery(chatroomId, role, generation);
|
|
37095
|
+
}
|
|
37096
|
+
}
|
|
37097
|
+
async escalateUndeliverableNudge(event, task, sessionKey) {
|
|
37098
|
+
const decision = await this.deps.taskService.decideUncoveredTurnEnd({
|
|
37099
|
+
chatroomId: event.chatroomId,
|
|
37100
|
+
role: event.role,
|
|
37101
|
+
taskId: task.taskId,
|
|
37102
|
+
sessionKey,
|
|
37103
|
+
nudgeUnavailable: true
|
|
37104
|
+
});
|
|
37105
|
+
if (decision.action === "restart-session") {
|
|
37106
|
+
this.scheduleUncoveredAction(event, task, "restart-session");
|
|
37107
|
+
} else {
|
|
37108
|
+
this.applyParkAction(event);
|
|
37109
|
+
}
|
|
37110
|
+
}
|
|
37111
|
+
async restartSessionForUncoveredTask(event, task) {
|
|
37112
|
+
const { chatroomId, role, pid } = event;
|
|
37113
|
+
const config3 = this.deps.configurationService.get(chatroomId, role);
|
|
37114
|
+
const slot = this.deps.agentMgr.getSlot(chatroomId, role);
|
|
37115
|
+
const harness = config3?.agentHarness ?? slot?.harness;
|
|
37116
|
+
const model = config3?.model ?? slot?.model;
|
|
37117
|
+
const workingDir = config3?.workingDir ?? slot?.workingDir;
|
|
37118
|
+
if (!harness || !model || !workingDir) {
|
|
37119
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} task=${task.taskId} error=missing_agent_config`);
|
|
37120
|
+
const sessionKey = sessionKeyFor(event.pid, event.slot.harnessSessionId);
|
|
37121
|
+
await this.deps.taskService.decideUncoveredTurnEnd({
|
|
37122
|
+
chatroomId,
|
|
37123
|
+
role,
|
|
37124
|
+
taskId: task.taskId,
|
|
37125
|
+
sessionKey,
|
|
37126
|
+
nudgeUnavailable: true
|
|
37127
|
+
});
|
|
37128
|
+
this.applyParkAction(event);
|
|
37129
|
+
return;
|
|
37130
|
+
}
|
|
37131
|
+
const startInput = {
|
|
37132
|
+
chatroomId,
|
|
37133
|
+
role,
|
|
37134
|
+
agentHarness: harness,
|
|
37135
|
+
model,
|
|
37136
|
+
workingDir,
|
|
37137
|
+
reason: DaemonStartReasonCode.RESPAWN,
|
|
37138
|
+
wantResume: false
|
|
37139
|
+
};
|
|
37140
|
+
let outcome;
|
|
37141
|
+
try {
|
|
37142
|
+
outcome = await this.deps.runSerializedForAgent({ chatroomId, role, workingDir }, { timeoutMs: AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS }, async (ops, context3) => {
|
|
37143
|
+
const current = this.deps.agentMgr.getSlot(chatroomId, role);
|
|
37144
|
+
if (this.deps.agentMgr.isStopRequested(chatroomId, role) || current?.pid !== pid || current?.harnessSessionId !== event.slot.harnessSessionId) {
|
|
37145
|
+
console.log(`[NativeDelivery:uncovered-restart-stale] chatroom=${chatroomId} role=${role} task=${task.taskId}`);
|
|
37146
|
+
return { kind: "skipped", reason: "stale_slot" };
|
|
37147
|
+
}
|
|
37148
|
+
await this.prepareRoleRecovery({ chatroomId, role });
|
|
37149
|
+
await ops.stopAgent({ chatroomId, role, reason: AgentStopReasonEnum["daemon.respawn"], pid, workingDir }, context3.signal);
|
|
37150
|
+
await this.recoverStoppedRole({ chatroomId, role, mode: "automatic" });
|
|
37151
|
+
return this.attemptRespawnStart(ops, startInput, context3.signal);
|
|
37152
|
+
});
|
|
37153
|
+
} catch (error51) {
|
|
37154
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
37155
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} task=${task.taskId} error=${message}`);
|
|
37156
|
+
if (this.recoveryGates.has(recoveryKey(chatroomId, role))) {
|
|
37157
|
+
this.scheduleRecoveryRetry(chatroomId, role);
|
|
37158
|
+
}
|
|
37159
|
+
return;
|
|
37160
|
+
}
|
|
37161
|
+
if (outcome.kind === "start-failed") {
|
|
37162
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} task=${task.taskId} attempt=1 of ${MAX_RESPAWN_START_ATTEMPTS} error=${outcome.error}`);
|
|
37163
|
+
this.beginRespawnStartRetries({ chatroomId, role, workingDir, startInput });
|
|
37164
|
+
}
|
|
37165
|
+
}
|
|
37166
|
+
async attemptRespawnStart(ops, startInput, signal) {
|
|
37167
|
+
try {
|
|
37168
|
+
const started = await ops.startAgent(startInput, signal);
|
|
37169
|
+
return started.success ? { kind: "started" } : { kind: "start-failed", error: started.error ?? "start_rejected" };
|
|
37170
|
+
} catch (error51) {
|
|
37171
|
+
return {
|
|
37172
|
+
kind: "start-failed",
|
|
37173
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
37174
|
+
};
|
|
37175
|
+
}
|
|
37176
|
+
}
|
|
37177
|
+
beginRespawnStartRetries(request3) {
|
|
37178
|
+
if (this.disposed)
|
|
37179
|
+
return;
|
|
37180
|
+
const key = recoveryKey(request3.chatroomId, request3.role);
|
|
37181
|
+
this.cancelRespawnStart(key, "superseded_by_new_restart");
|
|
37182
|
+
this.respawnRestartSeq += 1;
|
|
37183
|
+
const pending = {
|
|
37184
|
+
restartId: this.respawnRestartSeq,
|
|
37185
|
+
request: request3,
|
|
37186
|
+
attempts: 1
|
|
37187
|
+
};
|
|
37188
|
+
this.respawnStartRetries.set(key, pending);
|
|
37189
|
+
this.armRespawnStartRetry(key, pending);
|
|
37190
|
+
}
|
|
37191
|
+
armRespawnStartRetry(key, pending) {
|
|
37192
|
+
if (this.disposed)
|
|
37193
|
+
return;
|
|
37194
|
+
const delay3 = Math.min(30000, 1000 * 2 ** (pending.attempts - 1));
|
|
37195
|
+
pending.timer = setTimeout(() => {
|
|
37196
|
+
pending.timer = undefined;
|
|
37197
|
+
this.retryRespawnStart(key, pending.restartId).catch((error51) => {
|
|
37198
|
+
console.error(`[NativeDelivery:uncovered-restart-error] chatroom=${pending.request.chatroomId} role=${pending.request.role} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
37199
|
+
});
|
|
37200
|
+
}, delay3);
|
|
37201
|
+
}
|
|
37202
|
+
async retryRespawnStart(key, restartId) {
|
|
37203
|
+
const pending = this.respawnStartRetries.get(key);
|
|
37204
|
+
if (!pending || pending.restartId !== restartId || this.disposed)
|
|
37205
|
+
return;
|
|
37206
|
+
const { chatroomId, role, workingDir, startInput } = pending.request;
|
|
37207
|
+
if (this.deps.agentMgr.isStopRequested(chatroomId, role)) {
|
|
37208
|
+
this.cancelRespawnStart(key, "stop_requested");
|
|
37209
|
+
return;
|
|
37210
|
+
}
|
|
37211
|
+
const slot = this.deps.agentMgr.getSlot(chatroomId, role);
|
|
37212
|
+
if (slot && isSlotRunning(slot.state)) {
|
|
37213
|
+
this.cancelRespawnStart(key, "agent_already_running");
|
|
37214
|
+
return;
|
|
37215
|
+
}
|
|
37216
|
+
const outcome = await this.deps.runSerializedForAgent({ chatroomId, role, workingDir }, { timeoutMs: AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS }, async (ops, context3) => {
|
|
37217
|
+
if (this.respawnStartRetries.get(key)?.restartId !== restartId) {
|
|
37218
|
+
return { kind: "skipped", reason: "superseded" };
|
|
37219
|
+
}
|
|
37220
|
+
if (this.deps.agentMgr.isStopRequested(chatroomId, role)) {
|
|
37221
|
+
return { kind: "skipped", reason: "stop_requested" };
|
|
37222
|
+
}
|
|
37223
|
+
const current2 = this.deps.agentMgr.getSlot(chatroomId, role);
|
|
37224
|
+
if (current2 && isSlotRunning(current2.state)) {
|
|
37225
|
+
return { kind: "skipped", reason: "agent_already_running" };
|
|
37226
|
+
}
|
|
37227
|
+
return this.attemptRespawnStart(ops, startInput, context3.signal);
|
|
37228
|
+
});
|
|
37229
|
+
if (outcome.kind === "skipped") {
|
|
37230
|
+
this.cancelRespawnStart(key, outcome.reason);
|
|
37231
|
+
return;
|
|
37232
|
+
}
|
|
37233
|
+
const current = this.respawnStartRetries.get(key);
|
|
37234
|
+
if (current?.restartId !== restartId)
|
|
37235
|
+
return;
|
|
37236
|
+
if (outcome.kind === "started") {
|
|
37237
|
+
this.cancelRespawnStart(key, "started");
|
|
37238
|
+
return;
|
|
37239
|
+
}
|
|
37240
|
+
current.attempts += 1;
|
|
37241
|
+
if (current.attempts >= MAX_RESPAWN_START_ATTEMPTS) {
|
|
37242
|
+
this.respawnStartRetries.delete(key);
|
|
37243
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] attempts exhausted chatroom=${chatroomId} role=${role} attempts=${current.attempts} error=${outcome.error}`);
|
|
37244
|
+
return;
|
|
37245
|
+
}
|
|
37246
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} attempt=${current.attempts} of ${MAX_RESPAWN_START_ATTEMPTS} error=${outcome.error}`);
|
|
37247
|
+
this.armRespawnStartRetry(key, current);
|
|
37248
|
+
}
|
|
37249
|
+
cancelRespawnStart(key, reason) {
|
|
37250
|
+
const pending = this.respawnStartRetries.get(key);
|
|
37251
|
+
if (!pending)
|
|
37252
|
+
return;
|
|
37253
|
+
if (pending.timer)
|
|
37254
|
+
clearTimeout(pending.timer);
|
|
37255
|
+
pending.timer = undefined;
|
|
37256
|
+
this.respawnStartRetries.delete(key);
|
|
37257
|
+
if (reason === "started")
|
|
37258
|
+
return;
|
|
37259
|
+
console.log(`[NativeDelivery:uncovered-restart-cancelled] chatroom=${pending.request.chatroomId} role=${pending.request.role} attempts=${pending.attempts} reason=${reason}`);
|
|
37260
|
+
}
|
|
37261
|
+
uncoveredNudgeSkipReason(event, task) {
|
|
37262
|
+
const { chatroomId, role } = event;
|
|
37263
|
+
const slot = this.deps.agentMgr.getSlot(chatroomId, role);
|
|
37264
|
+
if (!slot || !isSlotRunning(slot.state))
|
|
37265
|
+
return "slot_not_running";
|
|
37266
|
+
if (slot.pid !== event.pid)
|
|
37267
|
+
return "pid_changed";
|
|
37268
|
+
if (slot.harnessSessionId !== event.slot.harnessSessionId)
|
|
37269
|
+
return "session_changed";
|
|
37270
|
+
if (!isTurnPhaseIdle(slot.nativeTurnPhase ?? "idle"))
|
|
37271
|
+
return "turn_not_idle";
|
|
37272
|
+
if (this.deps.agentMgr.isStopRequested(chatroomId, role))
|
|
37273
|
+
return "stop_requested";
|
|
37274
|
+
if (this.recoveryGates.has(recoveryKey(chatroomId, role)))
|
|
37275
|
+
return "recovery_in_progress";
|
|
37276
|
+
if (this.deps.agentTaskState.get({ chatroomId, role })?.taskId !== task.taskId) {
|
|
37277
|
+
return "task_not_active";
|
|
37278
|
+
}
|
|
37279
|
+
const current = this.deps.taskInboxState.getForRole(chatroomId, role, task.taskId);
|
|
37280
|
+
if (!current || !ACTIVE_TASK_STATUSES.includes(current.status)) {
|
|
37281
|
+
return "task_not_in_inbox";
|
|
37282
|
+
}
|
|
37283
|
+
return;
|
|
37284
|
+
}
|
|
37285
|
+
applyParkAction(event) {
|
|
37286
|
+
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
36997
37287
|
this.scheduleRoleDelivery(event.chatroomId, event.role);
|
|
36998
|
-
return { kind: "release-slot" };
|
|
36999
37288
|
}
|
|
37000
37289
|
scheduleRoleDelivery(chatroomId, role) {
|
|
37001
37290
|
setTimeout(() => {
|
|
@@ -37006,6 +37295,12 @@ class AgentWorkManager {
|
|
|
37006
37295
|
}
|
|
37007
37296
|
dispose() {
|
|
37008
37297
|
this.disposed = true;
|
|
37298
|
+
for (const pending of this.respawnStartRetries.values()) {
|
|
37299
|
+
if (pending.timer)
|
|
37300
|
+
clearTimeout(pending.timer);
|
|
37301
|
+
pending.timer = undefined;
|
|
37302
|
+
}
|
|
37303
|
+
this.respawnStartRetries.clear();
|
|
37009
37304
|
for (const gate of this.recoveryGates.values()) {
|
|
37010
37305
|
if (gate.retryTimer)
|
|
37011
37306
|
clearTimeout(gate.retryTimer);
|
|
@@ -37217,13 +37512,16 @@ class AgentWorkManager {
|
|
|
37217
37512
|
this.deps.agentTaskState.start(args2);
|
|
37218
37513
|
}
|
|
37219
37514
|
}
|
|
37220
|
-
var recoveryKey = (chatroomId, role) => `${chatroomId}:${role.toLowerCase()}`;
|
|
37515
|
+
var MAX_RESPAWN_START_ATTEMPTS = 3, MAX_UNCOVERED_NUDGE_SEND_ATTEMPTS = 3, UNCOVERED_NUDGE_RETRY_MS = 250, recoveryKey = (chatroomId, role) => `${chatroomId}:${role.toLowerCase()}`, sessionKeyFor = (pid, harnessSessionId) => `${pid}:${harnessSessionId ?? "none"}`;
|
|
37221
37516
|
var init_agent_work_manager = __esm(() => {
|
|
37517
|
+
init_reliability();
|
|
37222
37518
|
init_agent();
|
|
37223
37519
|
init_chatroom_workspace_task_inbox();
|
|
37224
37520
|
init_dist();
|
|
37225
37521
|
init_agent_lifecycle_fact();
|
|
37226
37522
|
init_agent_slot();
|
|
37523
|
+
init_assigned_task();
|
|
37524
|
+
init_check_agent_slot();
|
|
37227
37525
|
init_native_task_injector_logic();
|
|
37228
37526
|
init_convex_native_task_delivery_gateway();
|
|
37229
37527
|
init_daemon_audit_port();
|
|
@@ -40322,6 +40620,27 @@ class TaskInboxState {
|
|
|
40322
40620
|
}
|
|
40323
40621
|
}
|
|
40324
40622
|
|
|
40623
|
+
// src/daemon/services/task-service/domain/usecase/uncovered-turn-ladder.ts
|
|
40624
|
+
function decideUncoveredTurnEnd(entry, sessionKey, opts = {}) {
|
|
40625
|
+
const current = entry ?? FRESH_ENTRY;
|
|
40626
|
+
if (isNudgeDue(current, sessionKey, opts))
|
|
40627
|
+
return { action: "nudge" };
|
|
40628
|
+
return current.sessionRestarts < MAX_SESSION_RESTARTS_PER_TASK ? { action: "restart-session" } : { action: "park" };
|
|
40629
|
+
}
|
|
40630
|
+
function isNudgeDue(entry, sessionKey, opts) {
|
|
40631
|
+
return !opts.nudgeUnavailable && entry.nudgedSessionKey !== sessionKey;
|
|
40632
|
+
}
|
|
40633
|
+
function withNudgeSent(entry, sessionKey) {
|
|
40634
|
+
return { nudgedSessionKey: sessionKey, sessionRestarts: entry?.sessionRestarts ?? 0 };
|
|
40635
|
+
}
|
|
40636
|
+
function withSessionRestart(entry) {
|
|
40637
|
+
return { nudgedSessionKey: undefined, sessionRestarts: (entry?.sessionRestarts ?? 0) + 1 };
|
|
40638
|
+
}
|
|
40639
|
+
var MAX_SESSION_RESTARTS_PER_TASK = 1, FRESH_ENTRY;
|
|
40640
|
+
var init_uncovered_turn_ladder = __esm(() => {
|
|
40641
|
+
FRESH_ENTRY = { sessionRestarts: 0 };
|
|
40642
|
+
});
|
|
40643
|
+
|
|
40325
40644
|
// src/daemon/services/task-service/service/task-service.ts
|
|
40326
40645
|
function createTaskService(deps) {
|
|
40327
40646
|
const gateway = createConvexNativeTaskDeliveryGateway(deps.backend);
|
|
@@ -40339,18 +40658,13 @@ function createTaskService(deps) {
|
|
|
40339
40658
|
let bootstrapRecoveryAttempts = 0;
|
|
40340
40659
|
let bootstrapRecoveryTimer;
|
|
40341
40660
|
let bootstrapRecoveryPromise;
|
|
40342
|
-
const
|
|
40343
|
-
const
|
|
40661
|
+
const TURN_FAILURE_RETRY_CAP = 3;
|
|
40662
|
+
const turnFailureCounts = new Map;
|
|
40663
|
+
const uncoveredLadder = new Map;
|
|
40344
40664
|
const redeliveryExhaustedKeys = new Set;
|
|
40345
40665
|
const redeliveryKey = (chatroomId, role, taskId) => `${chatroomId}:${role.toLowerCase()}:${taskId}`;
|
|
40346
|
-
const
|
|
40666
|
+
const parkTask = async (args2, attempts) => {
|
|
40347
40667
|
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40348
|
-
if (redeliveryExhaustedKeys.has(key))
|
|
40349
|
-
return { exceeded: true };
|
|
40350
|
-
const attempts = (turnEndAttemptCounts.get(key) ?? 0) + 1;
|
|
40351
|
-
turnEndAttemptCounts.set(key, attempts);
|
|
40352
|
-
if (attempts < REDIVERY_ATTEMPT_CAP)
|
|
40353
|
-
return { exceeded: false };
|
|
40354
40668
|
redeliveryExhaustedKeys.add(key);
|
|
40355
40669
|
try {
|
|
40356
40670
|
await gateway.recordDeliveryFailure({
|
|
@@ -40362,13 +40676,50 @@ function createTaskService(deps) {
|
|
|
40362
40676
|
} catch (error51) {
|
|
40363
40677
|
console.warn(`[TaskService] redelivery-exhausted failure record failed chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
40364
40678
|
}
|
|
40365
|
-
|
|
40679
|
+
const attemptsSuffix = attempts === undefined ? "" : ` attempts=${attempts}`;
|
|
40680
|
+
console.log(`[NativeDelivery:redelivery-exhausted] chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}${attemptsSuffix} — release/redelivery stopped; agent restart (user.start) resets`);
|
|
40681
|
+
};
|
|
40682
|
+
const recordTurnFailure = async (args2) => {
|
|
40683
|
+
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40684
|
+
if (redeliveryExhaustedKeys.has(key))
|
|
40685
|
+
return { exceeded: true };
|
|
40686
|
+
const attempts = (turnFailureCounts.get(key) ?? 0) + 1;
|
|
40687
|
+
turnFailureCounts.set(key, attempts);
|
|
40688
|
+
if (attempts < TURN_FAILURE_RETRY_CAP)
|
|
40689
|
+
return { exceeded: false };
|
|
40690
|
+
await parkTask(args2, attempts);
|
|
40366
40691
|
return { exceeded: true };
|
|
40367
40692
|
};
|
|
40693
|
+
const decideUncoveredTurnEnd2 = async (args2) => {
|
|
40694
|
+
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40695
|
+
if (redeliveryExhaustedKeys.has(key))
|
|
40696
|
+
return { action: "park" };
|
|
40697
|
+
turnFailureCounts.delete(key);
|
|
40698
|
+
const entry = uncoveredLadder.get(key);
|
|
40699
|
+
const decision = decideUncoveredTurnEnd(entry, args2.sessionKey, {
|
|
40700
|
+
nudgeUnavailable: args2.nudgeUnavailable ?? false
|
|
40701
|
+
});
|
|
40702
|
+
if (decision.action === "restart-session") {
|
|
40703
|
+
uncoveredLadder.set(key, withSessionRestart(entry));
|
|
40704
|
+
} else if (decision.action === "park") {
|
|
40705
|
+
await parkTask(args2);
|
|
40706
|
+
}
|
|
40707
|
+
return decision;
|
|
40708
|
+
};
|
|
40709
|
+
const confirmNudgeSent = (args2) => {
|
|
40710
|
+
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40711
|
+
uncoveredLadder.set(key, withNudgeSent(uncoveredLadder.get(key), args2.sessionKey));
|
|
40712
|
+
};
|
|
40713
|
+
const resetTurnTracking = (args2) => {
|
|
40714
|
+
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40715
|
+
turnFailureCounts.delete(key);
|
|
40716
|
+
uncoveredLadder.delete(key);
|
|
40717
|
+
};
|
|
40368
40718
|
const isRedeliveryExhausted = (args2) => redeliveryExhaustedKeys.has(redeliveryKey(args2.chatroomId, args2.role, args2.taskId));
|
|
40369
40719
|
const clearRedeliveryTrackingForTask = (chatroomId, role, taskId) => {
|
|
40370
40720
|
const key = redeliveryKey(chatroomId, role, taskId);
|
|
40371
|
-
|
|
40721
|
+
turnFailureCounts.delete(key);
|
|
40722
|
+
uncoveredLadder.delete(key);
|
|
40372
40723
|
redeliveryExhaustedKeys.delete(key);
|
|
40373
40724
|
};
|
|
40374
40725
|
const forgetStaleTask = ({
|
|
@@ -40398,9 +40749,13 @@ function createTaskService(deps) {
|
|
|
40398
40749
|
};
|
|
40399
40750
|
const clearRoleRedeliveryTracking = (chatroomId, role) => {
|
|
40400
40751
|
const prefix = `${chatroomId}:${role.toLowerCase()}:`;
|
|
40401
|
-
for (const key of [...
|
|
40752
|
+
for (const key of [...turnFailureCounts.keys()]) {
|
|
40753
|
+
if (key.startsWith(prefix))
|
|
40754
|
+
turnFailureCounts.delete(key);
|
|
40755
|
+
}
|
|
40756
|
+
for (const key of [...uncoveredLadder.keys()]) {
|
|
40402
40757
|
if (key.startsWith(prefix))
|
|
40403
|
-
|
|
40758
|
+
uncoveredLadder.delete(key);
|
|
40404
40759
|
}
|
|
40405
40760
|
for (const key of [...redeliveryExhaustedKeys]) {
|
|
40406
40761
|
if (key.startsWith(prefix))
|
|
@@ -40802,7 +41157,10 @@ function createTaskService(deps) {
|
|
|
40802
41157
|
taskId,
|
|
40803
41158
|
...expectedReason ? { expectedReason } : {}
|
|
40804
41159
|
}),
|
|
40805
|
-
|
|
41160
|
+
recordTurnFailure,
|
|
41161
|
+
decideUncoveredTurnEnd: decideUncoveredTurnEnd2,
|
|
41162
|
+
confirmNudgeSent,
|
|
41163
|
+
resetTurnTracking,
|
|
40806
41164
|
isRedeliveryExhausted,
|
|
40807
41165
|
clearRedeliveryTracking: ({ chatroomId, role }) => {
|
|
40808
41166
|
clearRoleRedeliveryTracking(chatroomId, role);
|
|
@@ -40817,6 +41175,7 @@ var init_task_service = __esm(() => {
|
|
|
40817
41175
|
init_task_inbox_acknowledgement_retry_watcher();
|
|
40818
41176
|
init_task_inbox_delivery_retry_watcher();
|
|
40819
41177
|
init_api3();
|
|
41178
|
+
init_uncovered_turn_ladder();
|
|
40820
41179
|
init_convex_native_task_delivery_gateway();
|
|
40821
41180
|
});
|
|
40822
41181
|
|
|
@@ -99530,14 +99889,14 @@ var init_team_presets = __esm(() => {
|
|
|
99530
99889
|
duo: {
|
|
99531
99890
|
structureId: TEAM_STRUCTURE_IDS.duo,
|
|
99532
99891
|
name: "Duo",
|
|
99533
|
-
description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral architect, researcher, triage,
|
|
99892
|
+
description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral architect, researcher, triage, uiux-engineer, and verifier roles",
|
|
99534
99893
|
roles: getBuiltinRolesForTeam("duo"),
|
|
99535
99894
|
entryPoint: "planner"
|
|
99536
99895
|
},
|
|
99537
99896
|
solo: {
|
|
99538
99897
|
structureId: TEAM_STRUCTURE_IDS.solo,
|
|
99539
99898
|
name: "Solo",
|
|
99540
|
-
description: "A single agent working independently, with optional ephemeral architect, triage,
|
|
99899
|
+
description: "A single agent working independently, with optional ephemeral architect, triage, uiux-engineer, and verifier roles",
|
|
99541
99900
|
roles: getBuiltinRolesForTeam("solo"),
|
|
99542
99901
|
entryPoint: "solo"
|
|
99543
99902
|
}
|
|
@@ -100621,6 +100980,99 @@ var init_triage_handoff_templates = __esm(() => {
|
|
|
100621
100980
|
init_command2();
|
|
100622
100981
|
});
|
|
100623
100982
|
|
|
100983
|
+
// ../../services/backend/prompts/teams/verifier-handoff-templates.ts
|
|
100984
|
+
function normalizeEntryPointRole5(entryPointRole) {
|
|
100985
|
+
return entryPointRole.trim().toLowerCase() || "planner";
|
|
100986
|
+
}
|
|
100987
|
+
function getEntryPointToVerifierHandoffTemplate(entryPointRole) {
|
|
100988
|
+
const entryPoint = normalizeEntryPointRole5(entryPointRole);
|
|
100989
|
+
return `${getHandoffRecipientVisibilityCallout("verifier")}
|
|
100990
|
+
|
|
100991
|
+
## Handoff Template (${entryPoint} → verifier)
|
|
100992
|
+
|
|
100993
|
+
The verifier judges, from chatroom logs only, whether the user's last message was achieved. It ${VERIFIER_TEST_RULE}; list any user-requested tests under Requested test runs. Treat the claimed outcome as a claim, not as evidence: the verifier does not trust it and checks every requirement against the logs themselves. Request this review only when the task involved code changes, in any conversation mode. The verdict returns to \`${entryPoint}\`.
|
|
100994
|
+
|
|
100995
|
+
## Round
|
|
100996
|
+
<1 or 2; for 2, cite the round-1 verdict message id>
|
|
100997
|
+
|
|
100998
|
+
## Mode
|
|
100999
|
+
<chat | code | code:enhanced>
|
|
101000
|
+
|
|
101001
|
+
## User message id
|
|
101002
|
+
<id of the user's last message: the origin message id of the user task, or from \`chatroom messages anchor\`>
|
|
101003
|
+
|
|
101004
|
+
## Context id
|
|
101005
|
+
<id from \`chatroom context read\`, or \`none\` if no context was read for this task>
|
|
101006
|
+
|
|
101007
|
+
## Requested test runs
|
|
101008
|
+
<the test commands the user explicitly asked for, one per line, or exactly \`None requested.\`>
|
|
101009
|
+
|
|
101010
|
+
## Claimed outcome
|
|
101011
|
+
<requirement-by-requirement what you believe is complete>
|
|
101012
|
+
|
|
101013
|
+
## Evidence pointers
|
|
101014
|
+
<builder handoff message ids, commits, PRs, and test results as quoted in those handoffs>
|
|
101015
|
+
|
|
101016
|
+
## Known gaps
|
|
101017
|
+
<anything incomplete, or exactly \`None.\`>
|
|
101018
|
+
|
|
101019
|
+
## Handoff
|
|
101020
|
+
Send this request to the verifier with the command below, then stop. The verdict returns to \`${entryPoint}\`.
|
|
101021
|
+
|
|
101022
|
+
\`\`\`bash
|
|
101023
|
+
${handoffCommand({
|
|
101024
|
+
chatroomId: "<chatroom-id>",
|
|
101025
|
+
role: entryPoint,
|
|
101026
|
+
nextRole: "verifier",
|
|
101027
|
+
cliEnvPrefix: ""
|
|
101028
|
+
})}
|
|
101029
|
+
\`\`\``;
|
|
101030
|
+
}
|
|
101031
|
+
function getVerifierToEntryPointHandoffTemplate(entryPointRole) {
|
|
101032
|
+
const entryPoint = normalizeEntryPointRole5(entryPointRole);
|
|
101033
|
+
return `${getHandoffRecipientVisibilityCallout(entryPoint)}
|
|
101034
|
+
|
|
101035
|
+
## Handoff Template (Verifier → ${entryPoint})
|
|
101036
|
+
|
|
101037
|
+
Return one advisory verdict for the authoritative request. This review is non-binding: it uses chatroom logs, plus the requested test runs listed in the request, if any, deciding every requirement from the evidence those logs and runs actually contain. It ${VERIFIER_TEST_RULE}. Never accept the claimed outcome as proof. Use the exact sections below, in this order.
|
|
101038
|
+
|
|
101039
|
+
## Verdict
|
|
101040
|
+
<exactly one of ${VERIFIER_VERDICTS.join(", ")} as the value; ${ACHIEVED} only if every requirement is met; ${NOT_ACHIEVED} if any requirement is unmet (logs show it was not done, or contradict it); ${INCONCLUSIVE} if none is unmet but any is unverified (logs are silent)>
|
|
101041
|
+
|
|
101042
|
+
## Objective
|
|
101043
|
+
<user message id, any prior user messages used, objective in 1-2 sentences>
|
|
101044
|
+
|
|
101045
|
+
## Requirements
|
|
101046
|
+
<numbered \`R1 — <requirement> — met | unmet | unverified — <evidence ref>\`>
|
|
101047
|
+
|
|
101048
|
+
## Evidence
|
|
101049
|
+
<bullets: \`<message id or handoff> — "<short quote>" — supports R#\`; for a requested test run, \`<command> — exit <result> — supports R#\`>
|
|
101050
|
+
|
|
101051
|
+
## Missing or unmet
|
|
101052
|
+
<per unmet or unverified requirement: what is missing and what evidence would resolve it; exactly \`None.\` if ${ACHIEVED}>
|
|
101053
|
+
|
|
101054
|
+
## Scope and limits
|
|
101055
|
+
<state the test runs the verifier executed (command and exit result), or "No tests were run; none were requested."; log range read; whether the download was truncated; context id read and whether it matched the request>
|
|
101056
|
+
|
|
101057
|
+
## Handoff
|
|
101058
|
+
Run the normal handoff command to \`${entryPoint}\` with this complete verdict, then stop.
|
|
101059
|
+
|
|
101060
|
+
\`\`\`bash
|
|
101061
|
+
${handoffCommand({
|
|
101062
|
+
chatroomId: "<chatroom-id>",
|
|
101063
|
+
role: "verifier",
|
|
101064
|
+
nextRole: entryPoint,
|
|
101065
|
+
cliEnvPrefix: ""
|
|
101066
|
+
})}
|
|
101067
|
+
\`\`\``;
|
|
101068
|
+
}
|
|
101069
|
+
var VERIFIER_VERDICTS, VERIFIER_TEST_RULE = "runs no test the user did not ask for, and never builds, lints, or edits", ACHIEVED, NOT_ACHIEVED, INCONCLUSIVE;
|
|
101070
|
+
var init_verifier_handoff_templates = __esm(() => {
|
|
101071
|
+
init_command2();
|
|
101072
|
+
VERIFIER_VERDICTS = ["ACHIEVED", "NOT_ACHIEVED", "INCONCLUSIVE"];
|
|
101073
|
+
[ACHIEVED, NOT_ACHIEVED, INCONCLUSIVE] = VERIFIER_VERDICTS;
|
|
101074
|
+
});
|
|
101075
|
+
|
|
100624
101076
|
// ../../services/backend/prompts/teams/duo/handoff-templates/planner.ts
|
|
100625
101077
|
var duoPlannerHandoffContract;
|
|
100626
101078
|
var init_planner = __esm(() => {
|
|
@@ -100631,16 +101083,26 @@ var init_planner = __esm(() => {
|
|
|
100631
101083
|
init_researcher_handoff_templates();
|
|
100632
101084
|
init_triage_handoff_templates();
|
|
100633
101085
|
init_uiux_engineer_handoff_templates();
|
|
101086
|
+
init_verifier_handoff_templates();
|
|
100634
101087
|
duoPlannerHandoffContract = {
|
|
100635
101088
|
role: "planner",
|
|
100636
|
-
receivesFrom: [
|
|
100637
|
-
|
|
101089
|
+
receivesFrom: [
|
|
101090
|
+
"user",
|
|
101091
|
+
"builder",
|
|
101092
|
+
"architect",
|
|
101093
|
+
"uiux-engineer",
|
|
101094
|
+
"triage",
|
|
101095
|
+
"researcher",
|
|
101096
|
+
"verifier"
|
|
101097
|
+
],
|
|
101098
|
+
returnsTo: ["builder", "architect", "uiux-engineer", "triage", "researcher", "verifier", "user"],
|
|
100638
101099
|
outboundTemplates: {
|
|
100639
101100
|
builder: () => getPlannerToBuilderHandoffTemplate(),
|
|
100640
101101
|
architect: () => getEntryPointToArchitectHandoffTemplate("planner"),
|
|
100641
101102
|
"uiux-engineer": () => getEntryPointToUiuxEngineerHandoffTemplate("planner"),
|
|
100642
101103
|
triage: () => getEntryPointToTriageHandoffTemplate("planner"),
|
|
100643
101104
|
researcher: () => getEntryPointToResearcherHandoffTemplate("planner"),
|
|
101105
|
+
verifier: () => getEntryPointToVerifierHandoffTemplate("planner"),
|
|
100644
101106
|
user: (query) => query.conversationMode === "chat" ? getChatToUserHandoffTemplate() : getPlannerToUserReportTemplate({
|
|
100645
101107
|
chatroomId: query.chatroomId,
|
|
100646
101108
|
role: query.role,
|
|
@@ -100692,6 +101154,20 @@ var init_uiux_engineer = __esm(() => {
|
|
|
100692
101154
|
};
|
|
100693
101155
|
});
|
|
100694
101156
|
|
|
101157
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/verifier.ts
|
|
101158
|
+
var duoVerifierHandoffContract;
|
|
101159
|
+
var init_verifier = __esm(() => {
|
|
101160
|
+
init_verifier_handoff_templates();
|
|
101161
|
+
duoVerifierHandoffContract = {
|
|
101162
|
+
role: "verifier",
|
|
101163
|
+
receivesFrom: ["planner"],
|
|
101164
|
+
returnsTo: ["planner"],
|
|
101165
|
+
outboundTemplates: {
|
|
101166
|
+
planner: () => getVerifierToEntryPointHandoffTemplate("planner")
|
|
101167
|
+
}
|
|
101168
|
+
};
|
|
101169
|
+
});
|
|
101170
|
+
|
|
100695
101171
|
// ../shared/src/domain/chatroom-role.ts
|
|
100696
101172
|
var CHATROOM_ROLE_USER = "user";
|
|
100697
101173
|
var init_chatroom_role = __esm(() => {
|
|
@@ -100836,6 +101312,7 @@ var init_handoff_templates = __esm(() => {
|
|
|
100836
101312
|
init_researcher();
|
|
100837
101313
|
init_triage();
|
|
100838
101314
|
init_uiux_engineer();
|
|
101315
|
+
init_verifier();
|
|
100839
101316
|
init_contracts();
|
|
100840
101317
|
DUO_ROLE_HANDOFF_CATALOG = {
|
|
100841
101318
|
planner: duoPlannerHandoffContract,
|
|
@@ -100843,7 +101320,8 @@ var init_handoff_templates = __esm(() => {
|
|
|
100843
101320
|
architect: duoArchitectHandoffContract,
|
|
100844
101321
|
researcher: duoResearcherHandoffContract,
|
|
100845
101322
|
"uiux-engineer": duoUiuxEngineerHandoffContract,
|
|
100846
|
-
triage: duoTriageHandoffContract
|
|
101323
|
+
triage: duoTriageHandoffContract,
|
|
101324
|
+
verifier: duoVerifierHandoffContract
|
|
100847
101325
|
};
|
|
100848
101326
|
DUO_ROLE_HANDOFF_CONTRACTS = Object.values(DUO_ROLE_HANDOFF_CATALOG);
|
|
100849
101327
|
validatedDuoRoleHandoffContracts = (() => {
|
|
@@ -100888,14 +101366,16 @@ var init_solo = __esm(() => {
|
|
|
100888
101366
|
init_architect_handoff_templates();
|
|
100889
101367
|
init_triage_handoff_templates();
|
|
100890
101368
|
init_uiux_engineer_handoff_templates();
|
|
101369
|
+
init_verifier_handoff_templates();
|
|
100891
101370
|
soloHandoffContract = {
|
|
100892
101371
|
role: "solo",
|
|
100893
|
-
receivesFrom: ["user", "architect", "uiux-engineer", "triage"],
|
|
100894
|
-
returnsTo: ["architect", "uiux-engineer", "triage", "user"],
|
|
101372
|
+
receivesFrom: ["user", "architect", "uiux-engineer", "triage", "verifier"],
|
|
101373
|
+
returnsTo: ["architect", "uiux-engineer", "triage", "verifier", "user"],
|
|
100895
101374
|
outboundTemplates: {
|
|
100896
101375
|
architect: () => getEntryPointToArchitectHandoffTemplate("solo"),
|
|
100897
101376
|
"uiux-engineer": () => getEntryPointToUiuxEngineerHandoffTemplate("solo"),
|
|
100898
101377
|
triage: () => getEntryPointToTriageHandoffTemplate("solo"),
|
|
101378
|
+
verifier: () => getEntryPointToVerifierHandoffTemplate("solo"),
|
|
100899
101379
|
user: (query) => query.conversationMode === "chat" ? getChatToUserHandoffTemplate() : getSoloToUserReportTemplate({
|
|
100900
101380
|
chatroomId: query.chatroomId,
|
|
100901
101381
|
role: query.role,
|
|
@@ -100933,6 +101413,20 @@ var init_uiux_engineer2 = __esm(() => {
|
|
|
100933
101413
|
};
|
|
100934
101414
|
});
|
|
100935
101415
|
|
|
101416
|
+
// ../../services/backend/prompts/teams/solo/handoff-templates/verifier.ts
|
|
101417
|
+
var soloVerifierHandoffContract;
|
|
101418
|
+
var init_verifier2 = __esm(() => {
|
|
101419
|
+
init_verifier_handoff_templates();
|
|
101420
|
+
soloVerifierHandoffContract = {
|
|
101421
|
+
role: "verifier",
|
|
101422
|
+
receivesFrom: ["solo"],
|
|
101423
|
+
returnsTo: ["solo"],
|
|
101424
|
+
outboundTemplates: {
|
|
101425
|
+
solo: () => getVerifierToEntryPointHandoffTemplate("solo")
|
|
101426
|
+
}
|
|
101427
|
+
};
|
|
101428
|
+
});
|
|
101429
|
+
|
|
100936
101430
|
// ../../services/backend/prompts/teams/solo/handoff-templates/index.ts
|
|
100937
101431
|
function listSoloRoleHandoffContracts() {
|
|
100938
101432
|
return validatedSoloRoleHandoffContracts;
|
|
@@ -100950,12 +101444,14 @@ var init_handoff_templates2 = __esm(() => {
|
|
|
100950
101444
|
init_solo();
|
|
100951
101445
|
init_triage2();
|
|
100952
101446
|
init_uiux_engineer2();
|
|
101447
|
+
init_verifier2();
|
|
100953
101448
|
init_contracts();
|
|
100954
101449
|
SOLO_ROLE_HANDOFF_CATALOG = {
|
|
100955
101450
|
solo: soloHandoffContract,
|
|
100956
101451
|
architect: soloArchitectHandoffContract,
|
|
100957
101452
|
"uiux-engineer": soloUiuxEngineerHandoffContract,
|
|
100958
|
-
triage: soloTriageHandoffContract
|
|
101453
|
+
triage: soloTriageHandoffContract,
|
|
101454
|
+
verifier: soloVerifierHandoffContract
|
|
100959
101455
|
};
|
|
100960
101456
|
SOLO_ROLE_HANDOFF_CONTRACTS = Object.values(SOLO_ROLE_HANDOFF_CATALOG);
|
|
100961
101457
|
validatedSoloRoleHandoffContracts = (() => {
|
|
@@ -101506,6 +102002,34 @@ chatroom handoff --chatroom-id="<chatroom-id>" --role="uiux-engineer" --next-rol
|
|
|
101506
102002
|
The handoff must contain every required brief section, repository evidence, chosen flow, exact component/prop/state details, explicit states, accessibility details, ownership decisions, concrete classes/tokens, risks, acceptance criteria, tests, and implementation sequence the configured entry point needs to coordinate implementation. It must not contain implementation edits or alternative designs.`;
|
|
101507
102003
|
});
|
|
101508
102004
|
|
|
102005
|
+
// ../../services/backend/prompts/cli/roles/verifier.ts
|
|
102006
|
+
var ACHIEVED2, NOT_ACHIEVED2, INCONCLUSIVE2, VERIFIER_GUIDANCE;
|
|
102007
|
+
var init_verifier3 = __esm(() => {
|
|
102008
|
+
init_verifier_handoff_templates();
|
|
102009
|
+
init_sections();
|
|
102010
|
+
[ACHIEVED2, NOT_ACHIEVED2, INCONCLUSIVE2] = VERIFIER_VERDICTS;
|
|
102011
|
+
VERIFIER_GUIDANCE = `## Verifier Operating Model
|
|
102012
|
+
|
|
102013
|
+
You are the verifier. Your main job is to check whether the user's goal was accomplished, judged from chatroom logs. You are advisory and read-only: do not edit files, implement code, run builds or linters, run \`chatroom context new\`, or act as the planner or builder. The one exception to running nothing is the test rule below: it ${VERIFIER_TEST_RULE}.
|
|
102014
|
+
|
|
102015
|
+
${"{SESSION_CONTINUITY}"}
|
|
102016
|
+
|
|
102017
|
+
${getInterRoleHandoffRule()}
|
|
102018
|
+
|
|
102019
|
+
1. Read the injected verification request: round, mode, user message id, context id, requested test runs, claimed outcome, evidence pointers, and known gaps. The claimed outcome is a claim to be checked, never evidence.
|
|
102020
|
+
2. Recover the objective from the logs. Run \`chatroom messages anchor --chatroom-id="<chatroom-id>" --role="verifier"\`, then \`chatroom messages download --chatroom-id="<chatroom-id>" --role="verifier" --since-message-id=<user message id> --limit=100\`. If the download is truncated, raise \`--limit\` and download again until the range is complete. If the anchor id differs from the request's user message id, use the request's id and report the mismatch. For terse follow-ups, recover intent from the prior user messages the anchor lists.
|
|
102021
|
+
2a. Requested test runs: if the user's last message (or prior user messages for this objective) explicitly asks for tests to be run, run exactly those commands from the repository root, record the command and exit result, and count the output as evidence. Run no test the user did not ask for. If the request is absent, run nothing.
|
|
102022
|
+
3. Run \`chatroom context read --chatroom-id="<chatroom-id>" --role="verifier"\` and note any mismatch with the request's context id.
|
|
102023
|
+
4. Decompose the objective into numbered requirements R1..Rn. Split compound requests and ignore anything the user did not actually ask for.
|
|
102024
|
+
5. Find evidence for every requirement in the logs alone: builder handoff reports, planner delegations, quoted test output, commits, and PRs. \`met\` requires concrete evidence in the logs. The planner's claimed outcome is a claim, not evidence. Silence means \`unverified\`. A contradiction, or a log showing the requirement was not done, means \`unmet\`. Never state that tests passed unless a log reports it, or you ran the requested tests yourself.
|
|
102025
|
+
6. Choose the verdict by the rules in the injected verdict template: exactly one of ${VERIFIER_VERDICTS.join(", ")}. ${ACHIEVED2} only if every requirement is met; ${NOT_ACHIEVED2} if any requirement is unmet; ${INCONCLUSIVE2} if none is unmet but any is unverified.
|
|
102026
|
+
7. Follow the injected \`<handoff-templates>\` verdict template exactly, in its order, and put the complete verdict in the handoff. Then hand off to the configured entry point with the normal command, then stop:
|
|
102027
|
+
|
|
102028
|
+
\`\`\`bash
|
|
102029
|
+
chatroom handoff --chatroom-id="<chatroom-id>" --role="verifier" --next-role="{ENTRY_POINT_ROLE}"
|
|
102030
|
+
\`\`\``;
|
|
102031
|
+
});
|
|
102032
|
+
|
|
101509
102033
|
// ../../services/backend/prompts/cli/roles/planner-guidance-context.ts
|
|
101510
102034
|
var init_planner_guidance_context = __esm(() => {
|
|
101511
102035
|
init_env();
|
|
@@ -101529,6 +102053,7 @@ var init_fromContext2 = __esm(() => {
|
|
|
101529
102053
|
init_planner2();
|
|
101530
102054
|
init_researcher2();
|
|
101531
102055
|
init_uiux_engineer3();
|
|
102056
|
+
init_verifier3();
|
|
101532
102057
|
init_fromContext();
|
|
101533
102058
|
});
|
|
101534
102059
|
|
|
@@ -101618,6 +102143,19 @@ var init_templates = __esm(() => {
|
|
|
101618
102143
|
],
|
|
101619
102144
|
defaultHandoffTarget: "planner"
|
|
101620
102145
|
},
|
|
102146
|
+
verifier: {
|
|
102147
|
+
role: "verifier",
|
|
102148
|
+
title: "Verifier",
|
|
102149
|
+
description: "You are the verifier responsible for judging, from chatroom logs only, whether the user's last message was achieved; you do not implement, test, or inspect code.",
|
|
102150
|
+
responsibilities: [
|
|
102151
|
+
"Recover the user's last message and the pinned context from the chatroom logs",
|
|
102152
|
+
"Break the objective into numbered requirements",
|
|
102153
|
+
"Check each requirement against concrete evidence in the logs, treating the planner claim as unverified",
|
|
102154
|
+
"Return one verdict (ACHIEVED, NOT_ACHIEVED, or INCONCLUSIVE) with evidence and the missing items",
|
|
102155
|
+
"Execute nothing: no tests, builds, or repository inspection"
|
|
102156
|
+
],
|
|
102157
|
+
defaultHandoffTarget: "planner"
|
|
102158
|
+
},
|
|
101621
102159
|
triage: {
|
|
101622
102160
|
role: "triage",
|
|
101623
102161
|
title: "Triage Agent",
|
|
@@ -110754,9 +111292,7 @@ function normalizeDeliveryAttachments(input) {
|
|
|
110754
111292
|
return Object.values(attachments).some((items) => items && items.length > 0) ? attachments : undefined;
|
|
110755
111293
|
}
|
|
110756
111294
|
function normalizeTask(input) {
|
|
110757
|
-
|
|
110758
|
-
return;
|
|
110759
|
-
return { _id: input.taskId, status: input.taskStatus, content: input.taskContent };
|
|
111295
|
+
return input.task ?? undefined;
|
|
110760
111296
|
}
|
|
110761
111297
|
function toSerializableMessage(input) {
|
|
110762
111298
|
const attachments = normalizeDeliveryAttachments(input);
|
|
@@ -111145,10 +111681,6 @@ async function createDefaultDeps17() {
|
|
|
111145
111681
|
}
|
|
111146
111682
|
};
|
|
111147
111683
|
}
|
|
111148
|
-
function formatBadges(message) {
|
|
111149
|
-
const status = message.taskStatus ? ` (${message.taskStatus})` : "";
|
|
111150
|
-
return `${status}`;
|
|
111151
|
-
}
|
|
111152
111684
|
function logMessageContent(content, full) {
|
|
111153
111685
|
if (full) {
|
|
111154
111686
|
console.log(` Content:
|
|
@@ -111220,11 +111752,10 @@ var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(functio
|
|
|
111220
111752
|
console.log("─".repeat(60));
|
|
111221
111753
|
for (const message of messages) {
|
|
111222
111754
|
const timestamp = new Date(message._creationTime).toLocaleString();
|
|
111223
|
-
const badges = formatBadges(message);
|
|
111224
111755
|
console.log(`
|
|
111225
111756
|
\uD83D\uDD39 ID: ${message._id}`);
|
|
111226
111757
|
console.log(` Time: ${timestamp}`);
|
|
111227
|
-
console.log(` Type: ${message.type}
|
|
111758
|
+
console.log(` Type: ${message.type}`);
|
|
111228
111759
|
if (message.targetRole) {
|
|
111229
111760
|
console.log(` Target: ${message.targetRole}`);
|
|
111230
111761
|
}
|
|
@@ -111256,12 +111787,11 @@ var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(functio
|
|
|
111256
111787
|
for (const message of messages) {
|
|
111257
111788
|
const timestamp = new Date(message._creationTime).toLocaleString();
|
|
111258
111789
|
const roleIndicator = message.senderRole.toLowerCase() === "user" ? "\uD83D\uDC64" : "\uD83E\uDD16";
|
|
111259
|
-
const badges = formatBadges(message);
|
|
111260
111790
|
console.log(`
|
|
111261
111791
|
${roleIndicator} ${message.senderRole}${message.targetRole ? ` → ${message.targetRole}` : ""}`);
|
|
111262
111792
|
console.log(` ID: ${message._id}`);
|
|
111263
111793
|
console.log(` Time: ${timestamp}`);
|
|
111264
|
-
console.log(` Type: ${message.type}
|
|
111794
|
+
console.log(` Type: ${message.type}`);
|
|
111265
111795
|
logMessageContent(message.content, options.full ?? false);
|
|
111266
111796
|
}
|
|
111267
111797
|
console.log(`
|
|
@@ -137918,4 +138448,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
137918
138448
|
});
|
|
137919
138449
|
program2.parse();
|
|
137920
138450
|
|
|
137921
|
-
//# debugId=
|
|
138451
|
+
//# debugId=8506C14FCD932D1964756E2164756E21
|