chatroom-cli 1.132.0 → 1.133.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 +805 -47
- package/dist/index.js.map +32 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20412,6 +20412,14 @@ var init_agent_role = __esm(() => {
|
|
|
20412
20412
|
{ teamId: "duo", order: 45 },
|
|
20413
20413
|
{ teamId: "solo", order: 45 }
|
|
20414
20414
|
]
|
|
20415
|
+
},
|
|
20416
|
+
reviewer: {
|
|
20417
|
+
tags: ["ephemeral" /* Ephemeral */],
|
|
20418
|
+
teams: [{ teamId: "duo", order: 35 }]
|
|
20419
|
+
},
|
|
20420
|
+
"game-asset-designer": {
|
|
20421
|
+
tags: ["ephemeral" /* Ephemeral */],
|
|
20422
|
+
teams: [{ teamId: "duo", order: 42 }]
|
|
20415
20423
|
}
|
|
20416
20424
|
};
|
|
20417
20425
|
AGENT_ROLE_DEFINITIONS = Object.fromEntries(Object.entries(roleMetadata).map(([role, metadata]) => [role, { role, ...metadata }]));
|
|
@@ -35270,6 +35278,25 @@ var init_assigned_task = __esm(() => {
|
|
|
35270
35278
|
ACTIVE_TASK_STATUSES = ["pending", "acknowledged", "in_progress"];
|
|
35271
35279
|
});
|
|
35272
35280
|
|
|
35281
|
+
// src/daemon/domain/usecase/compute-backoff-delay.ts
|
|
35282
|
+
function computeBackoffDelay(attempt, opts, random3 = Math.random) {
|
|
35283
|
+
const exp = Math.min(opts.maxMs, opts.baseMs * 2 ** (attempt - 1));
|
|
35284
|
+
const jitterRatio = opts.jitter ?? 0;
|
|
35285
|
+
if (jitterRatio <= 0)
|
|
35286
|
+
return Math.max(0, Math.round(exp));
|
|
35287
|
+
const jitter = exp * jitterRatio * (random3() * 2 - 1);
|
|
35288
|
+
return Math.max(0, Math.round(exp + jitter));
|
|
35289
|
+
}
|
|
35290
|
+
var REDELIVERY_BACKOFF_BASE_MS = 5000, REDELIVERY_BACKOFF_MAX_MS, REDELIVERY_BACKOFF_OPTIONS;
|
|
35291
|
+
var init_compute_backoff_delay = __esm(() => {
|
|
35292
|
+
REDELIVERY_BACKOFF_MAX_MS = 5 * 60000;
|
|
35293
|
+
REDELIVERY_BACKOFF_OPTIONS = {
|
|
35294
|
+
baseMs: REDELIVERY_BACKOFF_BASE_MS,
|
|
35295
|
+
maxMs: REDELIVERY_BACKOFF_MAX_MS,
|
|
35296
|
+
jitter: 0.2
|
|
35297
|
+
};
|
|
35298
|
+
});
|
|
35299
|
+
|
|
35273
35300
|
// src/daemon/infrastructure/local/harness/services/spawn-prompt.ts
|
|
35274
35301
|
function createSpawnPrompt(raw, opts) {
|
|
35275
35302
|
if (opts?.nativeBootstrap) {
|
|
@@ -36877,6 +36904,26 @@ var init_native_task_injector = __esm(() => {
|
|
|
36877
36904
|
};
|
|
36878
36905
|
});
|
|
36879
36906
|
|
|
36907
|
+
// src/daemon/services/agent-process-service/domain/usecase/is-cli-driven-harness.ts
|
|
36908
|
+
function isCliDrivenHarness(harness) {
|
|
36909
|
+
if (harness == null)
|
|
36910
|
+
return false;
|
|
36911
|
+
const capabilities = getHarnessCapabilities(harness);
|
|
36912
|
+
return capabilities?.runtimeKind === "cli";
|
|
36913
|
+
}
|
|
36914
|
+
var init_is_cli_driven_harness = __esm(() => {
|
|
36915
|
+
init_types();
|
|
36916
|
+
});
|
|
36917
|
+
|
|
36918
|
+
// src/daemon/services/agent-process-service/domain/usecase/restart-retry-interval.ts
|
|
36919
|
+
function restartRetryMaxIntervalMs(cliDriven) {
|
|
36920
|
+
return cliDriven ? CLI_RESTART_RETRY_MAX_INTERVAL_MS : AGENT_RESTART_RETRY_MAX_INTERVAL_MS;
|
|
36921
|
+
}
|
|
36922
|
+
var AGENT_RESTART_RETRY_MAX_INTERVAL_MS, CLI_RESTART_RETRY_MAX_INTERVAL_MS = 30000;
|
|
36923
|
+
var init_restart_retry_interval = __esm(() => {
|
|
36924
|
+
AGENT_RESTART_RETRY_MAX_INTERVAL_MS = 10 * 60000;
|
|
36925
|
+
});
|
|
36926
|
+
|
|
36880
36927
|
// src/daemon/services/agent-process-service/service/agent-work-manager.ts
|
|
36881
36928
|
class AgentWorkManager {
|
|
36882
36929
|
deps;
|
|
@@ -36891,6 +36938,8 @@ class AgentWorkManager {
|
|
|
36891
36938
|
recoveryGates = new Map;
|
|
36892
36939
|
respawnStartRetries = new Map;
|
|
36893
36940
|
respawnRestartSeq = 0;
|
|
36941
|
+
turnFailureAccountingFailures = new Map;
|
|
36942
|
+
turnFailureReleaseTimers = new Map;
|
|
36894
36943
|
disposed = false;
|
|
36895
36944
|
constructor(deps) {
|
|
36896
36945
|
this.deps = deps;
|
|
@@ -36923,11 +36972,14 @@ class AgentWorkManager {
|
|
|
36923
36972
|
recordDeliveryFailure: deps.taskService.recordDeliveryFailure,
|
|
36924
36973
|
clearDeliveryFailure: deps.taskService.clearDeliveryFailure,
|
|
36925
36974
|
recordTurnFailure: deps.taskService.recordTurnFailure,
|
|
36975
|
+
parkTurnFailure: deps.taskService.parkTurnFailure,
|
|
36926
36976
|
decideUncoveredTurnEnd: deps.taskService.decideUncoveredTurnEnd,
|
|
36927
36977
|
confirmNudgeSent: deps.taskService.confirmNudgeSent,
|
|
36928
36978
|
resetTurnTracking: deps.taskService.resetTurnTracking,
|
|
36929
36979
|
isRedeliveryExhausted: deps.taskService.isRedeliveryExhausted,
|
|
36930
36980
|
clearRedeliveryTracking: deps.taskService.clearRedeliveryTracking,
|
|
36981
|
+
markTurnFailureReleasePending: deps.taskService.markTurnFailureReleasePending,
|
|
36982
|
+
clearTurnFailureReleasePending: deps.taskService.clearTurnFailureReleasePending,
|
|
36931
36983
|
forgetStaleTask: deps.taskService.forgetStaleTask,
|
|
36932
36984
|
loadAssignedTaskForAction: deps.taskService.loadAssignedTaskForAction,
|
|
36933
36985
|
deliverNativeTask: (task, harnessSessionId, onTaskDelivered, isCurrent) => this.nativeTaskDeliveryQueue.enqueue({
|
|
@@ -36960,6 +37012,9 @@ class AgentWorkManager {
|
|
|
36960
37012
|
role: event.role
|
|
36961
37013
|
});
|
|
36962
37014
|
}
|
|
37015
|
+
if (event.reason === "user.start" || event.reason === "user.restart") {
|
|
37016
|
+
this.cancelTurnFailureReleasesForRole(event.chatroomId, event.role);
|
|
37017
|
+
}
|
|
36963
37018
|
await this.requestReconcile({
|
|
36964
37019
|
chatroomId: event.chatroomId,
|
|
36965
37020
|
role: event.role,
|
|
@@ -37017,14 +37072,29 @@ class AgentWorkManager {
|
|
|
37017
37072
|
}
|
|
37018
37073
|
async recoverStoppedRole(args2) {
|
|
37019
37074
|
const key = recoveryKey(args2.chatroomId, args2.role);
|
|
37020
|
-
|
|
37075
|
+
let gate = this.recoveryGates.get(key);
|
|
37021
37076
|
if (!gate)
|
|
37022
37077
|
throw new Error(`role recovery gate not prepared for ${args2.role}@${args2.chatroomId}`);
|
|
37023
|
-
|
|
37024
|
-
|
|
37078
|
+
while (gate.recoveryPromise) {
|
|
37079
|
+
if (args2.mode !== "explicit")
|
|
37080
|
+
return gate.recoveryPromise;
|
|
37081
|
+
await gate.recoveryPromise.catch(() => {
|
|
37082
|
+
return;
|
|
37083
|
+
});
|
|
37084
|
+
if (!this.recoveryGates.has(key)) {
|
|
37085
|
+
await this.prepareRoleRecovery({ chatroomId: args2.chatroomId, role: args2.role });
|
|
37086
|
+
}
|
|
37087
|
+
const nextGate = this.recoveryGates.get(key);
|
|
37088
|
+
if (!nextGate)
|
|
37089
|
+
throw new Error(`role recovery gate not prepared for ${args2.role}@${args2.chatroomId}`);
|
|
37090
|
+
gate = nextGate;
|
|
37091
|
+
}
|
|
37025
37092
|
gate.recoveryPromise = (async () => {
|
|
37026
37093
|
await gate.drainPromise;
|
|
37027
37094
|
const result3 = await this.deps.taskService.recoverInFlightTasks(args2);
|
|
37095
|
+
if (args2.mode === "explicit") {
|
|
37096
|
+
this.cancelTurnFailureReleasesForRole(args2.chatroomId, args2.role);
|
|
37097
|
+
}
|
|
37028
37098
|
if (this.recoveryGates.get(key) === gate) {
|
|
37029
37099
|
if (gate.retryTimer)
|
|
37030
37100
|
clearTimeout(gate.retryTimer);
|
|
@@ -37060,7 +37130,7 @@ class AgentWorkManager {
|
|
|
37060
37130
|
const gate = this.recoveryGates.get(recoveryKey(chatroomId, role));
|
|
37061
37131
|
if (!gate || gate.retryTimer || this.disposed)
|
|
37062
37132
|
return;
|
|
37063
|
-
const delay3 =
|
|
37133
|
+
const delay3 = computeBackoffDelay(gate.retryAttempts++ + 1, RECOVERY_RETRY_BACKOFF);
|
|
37064
37134
|
gate.retryTimer = setTimeout(() => {
|
|
37065
37135
|
gate.retryTimer = undefined;
|
|
37066
37136
|
this.recoverStoppedRole({ chatroomId, role, mode: "automatic" }).catch((error51) => {
|
|
@@ -37069,26 +37139,115 @@ class AgentWorkManager {
|
|
|
37069
37139
|
});
|
|
37070
37140
|
}, delay3);
|
|
37071
37141
|
}
|
|
37072
|
-
async recoverTaskAfterTurnFailure(event, activeTask
|
|
37142
|
+
async recoverTaskAfterTurnFailure(event, activeTask) {
|
|
37073
37143
|
const { chatroomId, role } = event;
|
|
37074
|
-
const { exceeded } = await this.deps.taskService.recordTurnFailure({
|
|
37144
|
+
const { attempts, exceeded } = await this.deps.taskService.recordTurnFailure({
|
|
37075
37145
|
chatroomId,
|
|
37076
37146
|
role,
|
|
37077
37147
|
taskId: activeTask.taskId
|
|
37078
37148
|
});
|
|
37079
|
-
if (exceeded)
|
|
37080
|
-
|
|
37081
|
-
try {
|
|
37082
|
-
await this.deps.taskService.releaseTaskAfterTurnFailure({
|
|
37149
|
+
if (exceeded) {
|
|
37150
|
+
this.cancelTurnFailureRelease({
|
|
37083
37151
|
chatroomId,
|
|
37084
37152
|
role,
|
|
37085
37153
|
taskId: activeTask.taskId
|
|
37086
37154
|
});
|
|
37155
|
+
return "exceeded";
|
|
37156
|
+
}
|
|
37157
|
+
this.scheduleTurnFailureRelease({ chatroomId, role, taskId: activeTask.taskId }, attempts, event.completion.turnId, "turn-failure");
|
|
37158
|
+
return "scheduled";
|
|
37159
|
+
}
|
|
37160
|
+
scheduleTurnFailureRelease(key, attempt, turnId, source) {
|
|
37161
|
+
if (this.disposed)
|
|
37162
|
+
return;
|
|
37163
|
+
const keyStr = turnFailureReleaseKey(key);
|
|
37164
|
+
if (source === "turn-failure")
|
|
37165
|
+
this.turnFailureAccountingFailures.delete(keyStr);
|
|
37166
|
+
this.cancelTurnFailureRelease(key);
|
|
37167
|
+
const delay3 = computeBackoffDelay(attempt, REDELIVERY_BACKOFF_OPTIONS);
|
|
37168
|
+
this.deps.taskService.markTurnFailureReleasePending(key);
|
|
37169
|
+
const timer = setTimeout(() => {
|
|
37170
|
+
this.turnFailureReleaseTimers.delete(keyStr);
|
|
37171
|
+
this.deps.taskService.clearTurnFailureReleasePending(key);
|
|
37172
|
+
if (this.disposed)
|
|
37173
|
+
return;
|
|
37174
|
+
this.releaseAfterTurnFailureBackoff(key, attempt);
|
|
37175
|
+
}, delay3);
|
|
37176
|
+
this.turnFailureReleaseTimers.set(keyStr, { key, timer });
|
|
37177
|
+
console.log(`[NativeDelivery:turn-failure-backoff] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} turn=${turnId} attempt=${attempt} delayMs=${delay3}`);
|
|
37178
|
+
}
|
|
37179
|
+
async releaseAfterTurnFailureBackoff(key, attempt) {
|
|
37180
|
+
const current = this.deps.taskInboxState.getForRole(key.chatroomId, key.role, key.taskId);
|
|
37181
|
+
if (!current || current.status !== "in_progress") {
|
|
37182
|
+
console.log(`[NativeDelivery:turn-failure-release-skipped] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} reason=task_not_current status=${current?.status ?? "missing"}`);
|
|
37183
|
+
return;
|
|
37184
|
+
}
|
|
37185
|
+
const activeTask = this.deps.agentTaskState.get({
|
|
37186
|
+
chatroomId: key.chatroomId,
|
|
37187
|
+
role: key.role
|
|
37188
|
+
});
|
|
37189
|
+
if (activeTask?.taskId === key.taskId) {
|
|
37190
|
+
console.log(`[NativeDelivery:turn-failure-release-skipped] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} reason=task_active`);
|
|
37191
|
+
return;
|
|
37192
|
+
}
|
|
37193
|
+
try {
|
|
37194
|
+
await this.deps.taskService.releaseTaskAfterTurnFailure({
|
|
37195
|
+
chatroomId: key.chatroomId,
|
|
37196
|
+
role: key.role,
|
|
37197
|
+
taskId: key.taskId
|
|
37198
|
+
});
|
|
37199
|
+
this.turnFailureAccountingFailures.delete(turnFailureReleaseKey(key));
|
|
37087
37200
|
} catch (error51) {
|
|
37088
|
-
console.error(`[NativeDelivery
|
|
37089
|
-
|
|
37201
|
+
console.error(`[NativeDelivery:turn-failed-release-error] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
37202
|
+
await this.retryReleaseAfterFailure(key, attempt);
|
|
37203
|
+
}
|
|
37204
|
+
}
|
|
37205
|
+
async retryReleaseAfterFailure(key, previousAttempt) {
|
|
37206
|
+
const keyStr = turnFailureReleaseKey(key);
|
|
37207
|
+
try {
|
|
37208
|
+
const { attempts, exceeded } = await this.deps.taskService.recordTurnFailure(key);
|
|
37209
|
+
this.turnFailureAccountingFailures.delete(keyStr);
|
|
37210
|
+
if (exceeded)
|
|
37211
|
+
return;
|
|
37212
|
+
this.scheduleTurnFailureRelease(key, attempts, "release-retry", "release-retry");
|
|
37213
|
+
} catch (error51) {
|
|
37214
|
+
console.error(`[NativeDelivery:turn-failed-release-retry-error] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
|
|
37215
|
+
const failures = (this.turnFailureAccountingFailures.get(keyStr) ?? 0) + 1;
|
|
37216
|
+
this.turnFailureAccountingFailures.set(keyStr, failures);
|
|
37217
|
+
if (failures >= MAX_TURN_FAILURE_ACCOUNTING_FAILURES) {
|
|
37218
|
+
this.turnFailureAccountingFailures.delete(keyStr);
|
|
37219
|
+
this.cancelTurnFailureRelease(key);
|
|
37220
|
+
console.error(`[NativeDelivery:turn-failure-accounting-stalled] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} failures=${failures}`);
|
|
37221
|
+
try {
|
|
37222
|
+
await this.deps.taskService.parkTurnFailure(key);
|
|
37223
|
+
} catch (parkError) {
|
|
37224
|
+
console.error(`[NativeDelivery:turn-failure-park-error] chatroom=${key.chatroomId} role=${key.role} task=${key.taskId} error=${parkError instanceof Error ? parkError.stack ?? parkError.message : String(parkError)}`);
|
|
37225
|
+
}
|
|
37226
|
+
return;
|
|
37227
|
+
}
|
|
37228
|
+
this.scheduleTurnFailureRelease(key, previousAttempt, "release-retry", "release-retry");
|
|
37229
|
+
}
|
|
37230
|
+
}
|
|
37231
|
+
cancelTurnFailureRelease(key) {
|
|
37232
|
+
const keyStr = turnFailureReleaseKey(key);
|
|
37233
|
+
const entry = this.turnFailureReleaseTimers.get(keyStr);
|
|
37234
|
+
if (entry === undefined)
|
|
37235
|
+
return;
|
|
37236
|
+
clearTimeout(entry.timer);
|
|
37237
|
+
this.turnFailureReleaseTimers.delete(keyStr);
|
|
37238
|
+
this.turnFailureAccountingFailures.delete(keyStr);
|
|
37239
|
+
this.deps.taskService.clearTurnFailureReleasePending(key);
|
|
37240
|
+
}
|
|
37241
|
+
cancelTurnFailureReleasesForRole(chatroomId, role) {
|
|
37242
|
+
const prefix = `${chatroomId}:${role.toLowerCase()}:`;
|
|
37243
|
+
for (const [keyStr, entry] of [...this.turnFailureReleaseTimers]) {
|
|
37244
|
+
if (!keyStr.startsWith(prefix))
|
|
37245
|
+
continue;
|
|
37246
|
+
clearTimeout(entry.timer);
|
|
37247
|
+
this.turnFailureReleaseTimers.delete(keyStr);
|
|
37248
|
+
this.turnFailureAccountingFailures.delete(keyStr);
|
|
37249
|
+
this.deps.taskService.clearTurnFailureReleasePending(entry.key);
|
|
37090
37250
|
}
|
|
37091
|
-
return "released";
|
|
37092
37251
|
}
|
|
37093
37252
|
async handleAgentTurnEnded(event) {
|
|
37094
37253
|
const completion = event.completion;
|
|
@@ -37100,14 +37259,11 @@ class AgentWorkManager {
|
|
|
37100
37259
|
const errorDetail = completion.error;
|
|
37101
37260
|
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"}`);
|
|
37102
37261
|
if (activeTask2) {
|
|
37103
|
-
const recovery = await this.recoverTaskAfterTurnFailure(event, activeTask2
|
|
37262
|
+
const recovery = await this.recoverTaskAfterTurnFailure(event, activeTask2);
|
|
37104
37263
|
if (recovery === "exceeded") {
|
|
37105
37264
|
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
37106
37265
|
return { kind: "release-slot" };
|
|
37107
37266
|
}
|
|
37108
|
-
if (recovery === "recovery-failed") {
|
|
37109
|
-
return { kind: "hold-slot", reason: "task-recovery-failed" };
|
|
37110
|
-
}
|
|
37111
37267
|
this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
|
|
37112
37268
|
}
|
|
37113
37269
|
const fact = {
|
|
@@ -37296,7 +37452,9 @@ class AgentWorkManager {
|
|
|
37296
37452
|
return;
|
|
37297
37453
|
}
|
|
37298
37454
|
if (outcome.kind === "start-failed") {
|
|
37299
|
-
|
|
37455
|
+
const cliDriven = isCliDrivenHarness(startInput.agentHarness);
|
|
37456
|
+
const attemptLabel = cliDriven ? `1 of ${MAX_RESPAWN_START_ATTEMPTS}` : "1";
|
|
37457
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} task=${task.taskId} attempt=${attemptLabel} error=${outcome.error}`);
|
|
37300
37458
|
this.beginRespawnStartRetries({ chatroomId, role, workingDir, startInput });
|
|
37301
37459
|
}
|
|
37302
37460
|
}
|
|
@@ -37311,9 +37469,13 @@ class AgentWorkManager {
|
|
|
37311
37469
|
};
|
|
37312
37470
|
}
|
|
37313
37471
|
}
|
|
37314
|
-
beginRespawnStartRetries(
|
|
37472
|
+
beginRespawnStartRetries(input) {
|
|
37315
37473
|
if (this.disposed)
|
|
37316
37474
|
return;
|
|
37475
|
+
const request3 = {
|
|
37476
|
+
...input,
|
|
37477
|
+
cliDriven: isCliDrivenHarness(input.startInput.agentHarness)
|
|
37478
|
+
};
|
|
37317
37479
|
const key = recoveryKey(request3.chatroomId, request3.role);
|
|
37318
37480
|
this.cancelRespawnStart(key, "superseded_by_new_restart");
|
|
37319
37481
|
this.respawnRestartSeq += 1;
|
|
@@ -37328,11 +37490,27 @@ class AgentWorkManager {
|
|
|
37328
37490
|
armRespawnStartRetry(key, pending) {
|
|
37329
37491
|
if (this.disposed)
|
|
37330
37492
|
return;
|
|
37331
|
-
const delay3 =
|
|
37493
|
+
const delay3 = computeBackoffDelay(pending.attempts, {
|
|
37494
|
+
baseMs: RESPAWN_RETRY_BASE_MS,
|
|
37495
|
+
maxMs: restartRetryMaxIntervalMs(pending.request.cliDriven),
|
|
37496
|
+
jitter: 0
|
|
37497
|
+
});
|
|
37332
37498
|
pending.timer = setTimeout(() => {
|
|
37333
37499
|
pending.timer = undefined;
|
|
37334
37500
|
this.retryRespawnStart(key, pending.restartId).catch((error51) => {
|
|
37335
|
-
|
|
37501
|
+
const current = this.respawnStartRetries.get(key);
|
|
37502
|
+
const message = error51 instanceof Error ? error51.stack ?? error51.message : String(error51);
|
|
37503
|
+
console.error(`[NativeDelivery:uncovered-restart-error] chatroom=${pending.request.chatroomId} role=${pending.request.role} error=${message}`);
|
|
37504
|
+
if (!current || current.restartId !== pending.restartId || this.disposed)
|
|
37505
|
+
return;
|
|
37506
|
+
current.attempts += 1;
|
|
37507
|
+
if (current.request.cliDriven && current.attempts >= MAX_RESPAWN_START_ATTEMPTS) {
|
|
37508
|
+
this.respawnStartRetries.delete(key);
|
|
37509
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] attempts exhausted chatroom=${current.request.chatroomId} role=${current.request.role} attempts=${current.attempts} error=${message}`);
|
|
37510
|
+
return;
|
|
37511
|
+
}
|
|
37512
|
+
console.error(`[NativeDelivery:uncovered-restart-error-retry] chatroom=${current.request.chatroomId} role=${current.request.role} attempt=${current.attempts} error=${message}`);
|
|
37513
|
+
this.armRespawnStartRetry(key, current);
|
|
37336
37514
|
});
|
|
37337
37515
|
}, delay3);
|
|
37338
37516
|
}
|
|
@@ -37340,7 +37518,7 @@ class AgentWorkManager {
|
|
|
37340
37518
|
const pending = this.respawnStartRetries.get(key);
|
|
37341
37519
|
if (!pending || pending.restartId !== restartId || this.disposed)
|
|
37342
37520
|
return;
|
|
37343
|
-
const { chatroomId, role
|
|
37521
|
+
const { chatroomId, role } = pending.request;
|
|
37344
37522
|
if (this.deps.agentMgr.isStopRequested(chatroomId, role)) {
|
|
37345
37523
|
this.cancelRespawnStart(key, "stop_requested");
|
|
37346
37524
|
return;
|
|
@@ -37350,6 +37528,33 @@ class AgentWorkManager {
|
|
|
37350
37528
|
this.cancelRespawnStart(key, "agent_already_running");
|
|
37351
37529
|
return;
|
|
37352
37530
|
}
|
|
37531
|
+
const config3 = this.deps.configurationService.get(chatroomId, role);
|
|
37532
|
+
if (!config3) {
|
|
37533
|
+
if (this.deps.configurationService.state?.(chatroomId, role) === "syncing") {
|
|
37534
|
+
console.log(`[NativeDelivery:uncovered-restart-config-syncing] chatroom=${chatroomId} role=${role} attempts=${pending.attempts}`);
|
|
37535
|
+
this.armRespawnStartRetry(key, pending);
|
|
37536
|
+
return;
|
|
37537
|
+
}
|
|
37538
|
+
this.cancelRespawnStart(key, "config_removed");
|
|
37539
|
+
return;
|
|
37540
|
+
}
|
|
37541
|
+
const previous = pending.request;
|
|
37542
|
+
if (config3.agentHarness !== previous.startInput.agentHarness || config3.model !== previous.startInput.model || config3.workingDir !== previous.workingDir) {
|
|
37543
|
+
const startInput2 = {
|
|
37544
|
+
...previous.startInput,
|
|
37545
|
+
agentHarness: config3.agentHarness,
|
|
37546
|
+
model: config3.model,
|
|
37547
|
+
workingDir: config3.workingDir
|
|
37548
|
+
};
|
|
37549
|
+
pending.request = {
|
|
37550
|
+
...previous,
|
|
37551
|
+
workingDir: config3.workingDir,
|
|
37552
|
+
startInput: startInput2,
|
|
37553
|
+
cliDriven: isCliDrivenHarness(config3.agentHarness)
|
|
37554
|
+
};
|
|
37555
|
+
console.log(`[NativeDelivery:uncovered-restart-config-changed] chatroom=${chatroomId} role=${role} harness=${config3.agentHarness} model=${config3.model} workingDir=${config3.workingDir}`);
|
|
37556
|
+
}
|
|
37557
|
+
const { workingDir, startInput } = pending.request;
|
|
37353
37558
|
const outcome = await this.deps.runSerializedForAgent({ chatroomId, role, workingDir }, { timeoutMs: AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS }, async (ops, context3) => {
|
|
37354
37559
|
if (this.respawnStartRetries.get(key)?.restartId !== restartId) {
|
|
37355
37560
|
return { kind: "skipped", reason: "superseded" };
|
|
@@ -37375,12 +37580,13 @@ class AgentWorkManager {
|
|
|
37375
37580
|
return;
|
|
37376
37581
|
}
|
|
37377
37582
|
current.attempts += 1;
|
|
37378
|
-
if (current.attempts >= MAX_RESPAWN_START_ATTEMPTS) {
|
|
37583
|
+
if (current.request.cliDriven && current.attempts >= MAX_RESPAWN_START_ATTEMPTS) {
|
|
37379
37584
|
this.respawnStartRetries.delete(key);
|
|
37380
37585
|
console.error(`[NativeDelivery:uncovered-restart-failed] attempts exhausted chatroom=${chatroomId} role=${role} attempts=${current.attempts} error=${outcome.error}`);
|
|
37381
37586
|
return;
|
|
37382
37587
|
}
|
|
37383
|
-
|
|
37588
|
+
const attemptLabel = current.request.cliDriven ? `${current.attempts} of ${MAX_RESPAWN_START_ATTEMPTS}` : `${current.attempts}`;
|
|
37589
|
+
console.error(`[NativeDelivery:uncovered-restart-failed] chatroom=${chatroomId} role=${role} attempt=${attemptLabel} error=${outcome.error}`);
|
|
37384
37590
|
this.armRespawnStartRetry(key, current);
|
|
37385
37591
|
}
|
|
37386
37592
|
cancelRespawnStart(key, reason) {
|
|
@@ -37432,6 +37638,12 @@ class AgentWorkManager {
|
|
|
37432
37638
|
}
|
|
37433
37639
|
dispose() {
|
|
37434
37640
|
this.disposed = true;
|
|
37641
|
+
for (const entry of this.turnFailureReleaseTimers.values()) {
|
|
37642
|
+
clearTimeout(entry.timer);
|
|
37643
|
+
this.deps.taskService.clearTurnFailureReleasePending(entry.key);
|
|
37644
|
+
}
|
|
37645
|
+
this.turnFailureReleaseTimers.clear();
|
|
37646
|
+
this.turnFailureAccountingFailures.clear();
|
|
37435
37647
|
for (const pending of this.respawnStartRetries.values()) {
|
|
37436
37648
|
if (pending.timer)
|
|
37437
37649
|
clearTimeout(pending.timer);
|
|
@@ -37496,6 +37708,20 @@ class AgentWorkManager {
|
|
|
37496
37708
|
if (notification.event.eventType === "task_deleted" /* TaskDeleted */) {
|
|
37497
37709
|
await this.cancelTaskWork(notification.event.chatroomId, notification.event.role, notification.event.taskId);
|
|
37498
37710
|
}
|
|
37711
|
+
if (notification.event.eventType === "task_assigned" /* TaskAssigned */) {
|
|
37712
|
+
this.cancelTurnFailureRelease({
|
|
37713
|
+
chatroomId: notification.event.chatroomId,
|
|
37714
|
+
role: notification.event.role,
|
|
37715
|
+
taskId: notification.event.taskId
|
|
37716
|
+
});
|
|
37717
|
+
}
|
|
37718
|
+
if (notification.event.task?.status === "completed") {
|
|
37719
|
+
this.cancelTurnFailureRelease({
|
|
37720
|
+
chatroomId: notification.event.chatroomId,
|
|
37721
|
+
role: notification.event.role,
|
|
37722
|
+
taskId: notification.event.taskId
|
|
37723
|
+
});
|
|
37724
|
+
}
|
|
37499
37725
|
if (notification.event.eventType === "task_deleted" /* TaskDeleted */) {
|
|
37500
37726
|
return { handledEventIds: [notification.event.eventId] };
|
|
37501
37727
|
}
|
|
@@ -37541,6 +37767,7 @@ class AgentWorkManager {
|
|
|
37541
37767
|
}
|
|
37542
37768
|
}
|
|
37543
37769
|
async cancelTaskWork(chatroomId, role, taskId) {
|
|
37770
|
+
this.cancelTurnFailureRelease({ chatroomId, role, taskId });
|
|
37544
37771
|
const activeTask = this.deps.agentTaskState.get({ chatroomId, role });
|
|
37545
37772
|
if (activeTask?.taskId !== taskId)
|
|
37546
37773
|
return;
|
|
@@ -37649,7 +37876,7 @@ class AgentWorkManager {
|
|
|
37649
37876
|
this.deps.agentTaskState.start(args2);
|
|
37650
37877
|
}
|
|
37651
37878
|
}
|
|
37652
|
-
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"}`;
|
|
37879
|
+
var MAX_RESPAWN_START_ATTEMPTS = 3, MAX_UNCOVERED_NUDGE_SEND_ATTEMPTS = 3, UNCOVERED_NUDGE_RETRY_MS = 250, MAX_TURN_FAILURE_ACCOUNTING_FAILURES = 5, RECOVERY_RETRY_BACKOFF, RESPAWN_RETRY_BASE_MS = 1000, recoveryKey = (chatroomId, role) => `${chatroomId}:${role.toLowerCase()}`, turnFailureReleaseKey = ({ chatroomId, role, taskId }) => `${chatroomId}:${role.toLowerCase()}:${taskId}`, sessionKeyFor = (pid, harnessSessionId) => `${pid}:${harnessSessionId ?? "none"}`;
|
|
37653
37880
|
var init_agent_work_manager = __esm(() => {
|
|
37654
37881
|
init_reliability();
|
|
37655
37882
|
init_agent();
|
|
@@ -37659,11 +37886,15 @@ var init_agent_work_manager = __esm(() => {
|
|
|
37659
37886
|
init_agent_slot();
|
|
37660
37887
|
init_assigned_task();
|
|
37661
37888
|
init_check_agent_slot();
|
|
37889
|
+
init_compute_backoff_delay();
|
|
37662
37890
|
init_native_task_injector_logic();
|
|
37663
37891
|
init_convex_native_task_delivery_gateway();
|
|
37664
37892
|
init_daemon_audit_port();
|
|
37665
37893
|
init_task_delivery_processor();
|
|
37666
37894
|
init_native_task_injector();
|
|
37895
|
+
init_is_cli_driven_harness();
|
|
37896
|
+
init_restart_retry_interval();
|
|
37897
|
+
RECOVERY_RETRY_BACKOFF = { baseMs: 1000, maxMs: 30000, jitter: 0 };
|
|
37667
37898
|
});
|
|
37668
37899
|
|
|
37669
37900
|
// src/daemon/services/agent-process-service/service/index.ts
|
|
@@ -39632,11 +39863,6 @@ class AgentProcessManager {
|
|
|
39632
39863
|
}
|
|
39633
39864
|
};
|
|
39634
39865
|
const dispositions = await Promise.all([...this.agentTurnEndedHandlers].map((handler) => handler(event)));
|
|
39635
|
-
const held = dispositions.find((disposition) => disposition?.kind === "hold-slot");
|
|
39636
|
-
if (held?.kind === "hold-slot") {
|
|
39637
|
-
console.warn(`[AgentProcessManager] Native turn slot held after ${completionStatus}: role=${opts.role} pid=${opts.pid} reason=${held.reason}`);
|
|
39638
|
-
return;
|
|
39639
|
-
}
|
|
39640
39866
|
if (completionStatus !== "completed" && !dispositions.some((disposition) => disposition?.kind === "release-slot")) {
|
|
39641
39867
|
console.warn(`[AgentProcessManager] Native turn slot held: no task-service disposition returned role=${opts.role} pid=${opts.pid} status=${completionStatus}`);
|
|
39642
39868
|
return;
|
|
@@ -40795,14 +41021,80 @@ function createTaskService(deps) {
|
|
|
40795
41021
|
let bootstrapRecoveryAttempts = 0;
|
|
40796
41022
|
let bootstrapRecoveryTimer;
|
|
40797
41023
|
let bootstrapRecoveryPromise;
|
|
40798
|
-
const TURN_FAILURE_RETRY_CAP =
|
|
41024
|
+
const TURN_FAILURE_RETRY_CAP = 8;
|
|
40799
41025
|
const turnFailureCounts = new Map;
|
|
40800
41026
|
const uncoveredLadder = new Map;
|
|
40801
41027
|
const redeliveryExhaustedKeys = new Set;
|
|
41028
|
+
const rearmCycleCounts = new Map;
|
|
41029
|
+
const rearmTimers = new Map;
|
|
41030
|
+
const pendingTurnFailureReleases = new Set;
|
|
40802
41031
|
const redeliveryKey = (chatroomId, role, taskId) => `${chatroomId}:${role.toLowerCase()}:${taskId}`;
|
|
40803
|
-
const
|
|
40804
|
-
const
|
|
40805
|
-
|
|
41032
|
+
const clearRearmTimer = (key) => {
|
|
41033
|
+
const timer = rearmTimers.get(key);
|
|
41034
|
+
if (timer === undefined)
|
|
41035
|
+
return;
|
|
41036
|
+
clearTimeout(timer);
|
|
41037
|
+
rearmTimers.delete(key);
|
|
41038
|
+
};
|
|
41039
|
+
const resetRearmBudget = (key) => {
|
|
41040
|
+
clearRearmTimer(key);
|
|
41041
|
+
rearmCycleCounts.delete(key);
|
|
41042
|
+
};
|
|
41043
|
+
const scheduleRedeliveryRearm = (args2) => {
|
|
41044
|
+
if (inboxStopped)
|
|
41045
|
+
return;
|
|
41046
|
+
const { key } = args2;
|
|
41047
|
+
const cycles = rearmCycleCounts.get(key) ?? 0;
|
|
41048
|
+
if (cycles >= MAX_REARM_CYCLES) {
|
|
41049
|
+
console.log(`[NativeDelivery:redelivery-rearm-exhausted] chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId} cycles=${MAX_REARM_CYCLES}`);
|
|
41050
|
+
return;
|
|
41051
|
+
}
|
|
41052
|
+
clearRearmTimer(key);
|
|
41053
|
+
const delay3 = REARM_DELAYS_MS[cycles];
|
|
41054
|
+
const timer = setTimeout(() => {
|
|
41055
|
+
rearmTimers.delete(key);
|
|
41056
|
+
runRedeliveryRearm({ ...args2, delay: delay3, cycle: cycles + 1 });
|
|
41057
|
+
}, delay3);
|
|
41058
|
+
rearmTimers.set(key, timer);
|
|
41059
|
+
};
|
|
41060
|
+
const runRedeliveryRearm = async (args2) => {
|
|
41061
|
+
const { chatroomId, role, taskId, key } = args2;
|
|
41062
|
+
const current = taskInboxState.getForRole(chatroomId, role, taskId);
|
|
41063
|
+
if (!current || current.status !== "in_progress" || !redeliveryExhaustedKeys.has(key)) {
|
|
41064
|
+
console.log(`[NativeDelivery:redelivery-rearm-skipped] reason=task_not_current chatroom=${chatroomId} role=${role} task=${taskId} status=${current?.status ?? "missing"}`);
|
|
41065
|
+
return;
|
|
41066
|
+
}
|
|
41067
|
+
rearmCycleCounts.set(key, args2.cycle);
|
|
41068
|
+
redeliveryExhaustedKeys.delete(key);
|
|
41069
|
+
turnFailureCounts.delete(key);
|
|
41070
|
+
try {
|
|
41071
|
+
await gateway.clearDeliveryFailure({
|
|
41072
|
+
sessionId: deps.sessionId,
|
|
41073
|
+
machineId: deps.machineId,
|
|
41074
|
+
taskId
|
|
41075
|
+
});
|
|
41076
|
+
} catch (error51) {
|
|
41077
|
+
console.warn(`[TaskService] redelivery-rearm clear failure failed chatroom=${chatroomId} role=${role} task=${taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
41078
|
+
}
|
|
41079
|
+
let released = false;
|
|
41080
|
+
try {
|
|
41081
|
+
const result3 = await releaseTask({ chatroomId, role, taskId });
|
|
41082
|
+
released = result3.released;
|
|
41083
|
+
if (!released) {
|
|
41084
|
+
console.warn(`[TaskService] redelivery-rearm release skipped chatroom=${chatroomId} role=${role} task=${taskId} cycle=${args2.cycle}`);
|
|
41085
|
+
}
|
|
41086
|
+
} catch (error51) {
|
|
41087
|
+
console.error(`[TaskService] redelivery-rearm release failed chatroom=${chatroomId} role=${role} task=${taskId} cycle=${args2.cycle}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
41088
|
+
}
|
|
41089
|
+
if (!released) {
|
|
41090
|
+
redeliveryExhaustedKeys.add(key);
|
|
41091
|
+
await recordExhaustedOnBackend({ chatroomId, role, taskId });
|
|
41092
|
+
scheduleRedeliveryRearm(args2);
|
|
41093
|
+
return;
|
|
41094
|
+
}
|
|
41095
|
+
console.log(`[NativeDelivery:redelivery-rearm] cycle=${args2.cycle}/${MAX_REARM_CYCLES} chatroom=${chatroomId} role=${role} task=${taskId} delayMs=${args2.delay}`);
|
|
41096
|
+
};
|
|
41097
|
+
const recordExhaustedOnBackend = async (args2) => {
|
|
40806
41098
|
try {
|
|
40807
41099
|
await gateway.recordDeliveryFailure({
|
|
40808
41100
|
sessionId: deps.sessionId,
|
|
@@ -40813,19 +41105,31 @@ function createTaskService(deps) {
|
|
|
40813
41105
|
} catch (error51) {
|
|
40814
41106
|
console.warn(`[TaskService] redelivery-exhausted failure record failed chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
40815
41107
|
}
|
|
41108
|
+
};
|
|
41109
|
+
const parkTask = async (args2, reason, attempts) => {
|
|
41110
|
+
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
41111
|
+
redeliveryExhaustedKeys.add(key);
|
|
41112
|
+
await recordExhaustedOnBackend(args2);
|
|
40816
41113
|
const attemptsSuffix = attempts === undefined ? "" : ` attempts=${attempts}`;
|
|
40817
|
-
|
|
41114
|
+
const rearmCycles = rearmCycleCounts.get(key) ?? 0;
|
|
41115
|
+
const rearmDelay = reason === "provider_failure" && rearmCycles < MAX_REARM_CYCLES ? REARM_DELAYS_MS[rearmCycles] : undefined;
|
|
41116
|
+
console.log(`[NativeDelivery:redelivery-exhausted] chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}${attemptsSuffix} reason=${reason} rearm=${rearmDelay ?? "none"} — release/redelivery stopped; user.start or explicit recovery resets`);
|
|
41117
|
+
if (reason === "provider_failure") {
|
|
41118
|
+
scheduleRedeliveryRearm({ ...args2, key });
|
|
41119
|
+
}
|
|
40818
41120
|
};
|
|
40819
41121
|
const recordTurnFailure = async (args2) => {
|
|
40820
41122
|
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
40821
|
-
|
|
40822
|
-
|
|
40823
|
-
|
|
41123
|
+
const recordedAttempts = turnFailureCounts.get(key) ?? 0;
|
|
41124
|
+
if (redeliveryExhaustedKeys.has(key)) {
|
|
41125
|
+
return { attempts: recordedAttempts, exceeded: true };
|
|
41126
|
+
}
|
|
41127
|
+
const attempts = recordedAttempts + 1;
|
|
40824
41128
|
turnFailureCounts.set(key, attempts);
|
|
40825
41129
|
if (attempts < TURN_FAILURE_RETRY_CAP)
|
|
40826
|
-
return { exceeded: false };
|
|
40827
|
-
await parkTask(args2, attempts);
|
|
40828
|
-
return { exceeded: true };
|
|
41130
|
+
return { attempts, exceeded: false };
|
|
41131
|
+
await parkTask(args2, "provider_failure", attempts);
|
|
41132
|
+
return { attempts, exceeded: true };
|
|
40829
41133
|
};
|
|
40830
41134
|
const decideUncoveredTurnEnd2 = async (args2) => {
|
|
40831
41135
|
const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
|
|
@@ -40839,7 +41143,7 @@ function createTaskService(deps) {
|
|
|
40839
41143
|
if (decision.action === "restart-session") {
|
|
40840
41144
|
uncoveredLadder.set(key, withSessionRestart(entry));
|
|
40841
41145
|
} else if (decision.action === "park") {
|
|
40842
|
-
await parkTask(args2);
|
|
41146
|
+
await parkTask(args2, "uncovered_turn");
|
|
40843
41147
|
}
|
|
40844
41148
|
return decision;
|
|
40845
41149
|
};
|
|
@@ -40858,6 +41162,7 @@ function createTaskService(deps) {
|
|
|
40858
41162
|
turnFailureCounts.delete(key);
|
|
40859
41163
|
uncoveredLadder.delete(key);
|
|
40860
41164
|
redeliveryExhaustedKeys.delete(key);
|
|
41165
|
+
resetRearmBudget(key);
|
|
40861
41166
|
};
|
|
40862
41167
|
const forgetStaleTask = ({
|
|
40863
41168
|
chatroomId,
|
|
@@ -40898,11 +41203,19 @@ function createTaskService(deps) {
|
|
|
40898
41203
|
if (key.startsWith(prefix))
|
|
40899
41204
|
redeliveryExhaustedKeys.delete(key);
|
|
40900
41205
|
}
|
|
41206
|
+
for (const key of [...rearmTimers.keys()]) {
|
|
41207
|
+
if (key.startsWith(prefix))
|
|
41208
|
+
resetRearmBudget(key);
|
|
41209
|
+
}
|
|
41210
|
+
for (const key of [...rearmCycleCounts.keys()]) {
|
|
41211
|
+
if (key.startsWith(prefix))
|
|
41212
|
+
rearmCycleCounts.delete(key);
|
|
41213
|
+
}
|
|
40901
41214
|
};
|
|
40902
41215
|
const scheduleBootstrapRecovery = () => {
|
|
40903
41216
|
if (inboxStopped || bootstrapRecoveryTimer || !bootstrapRecoveryPending)
|
|
40904
41217
|
return;
|
|
40905
|
-
const delay3 =
|
|
41218
|
+
const delay3 = computeBackoffDelay(++bootstrapRecoveryAttempts, BOOTSTRAP_RECOVERY_BACKOFF);
|
|
40906
41219
|
bootstrapRecoveryTimer = setTimeout(() => {
|
|
40907
41220
|
bootstrapRecoveryTimer = undefined;
|
|
40908
41221
|
if (!inboxStopped)
|
|
@@ -41119,6 +41432,11 @@ function createTaskService(deps) {
|
|
|
41119
41432
|
},
|
|
41120
41433
|
stopTaskInbox: () => {
|
|
41121
41434
|
inboxStopped = true;
|
|
41435
|
+
for (const timer of rearmTimers.values())
|
|
41436
|
+
clearTimeout(timer);
|
|
41437
|
+
rearmTimers.clear();
|
|
41438
|
+
rearmCycleCounts.clear();
|
|
41439
|
+
pendingTurnFailureReleases.clear();
|
|
41122
41440
|
if (bootstrapRecoveryTimer)
|
|
41123
41441
|
clearTimeout(bootstrapRecoveryTimer);
|
|
41124
41442
|
bootstrapRecoveryTimer = undefined;
|
|
@@ -41209,7 +41527,8 @@ function createTaskService(deps) {
|
|
|
41209
41527
|
}
|
|
41210
41528
|
if (mode === "automatic") {
|
|
41211
41529
|
const tracking = { chatroomId: task.chatroomId, role: taskRole, taskId: task.taskId };
|
|
41212
|
-
|
|
41530
|
+
const key = redeliveryKey(task.chatroomId, taskRole, task.taskId);
|
|
41531
|
+
if (isRedeliveryExhausted(tracking) || rearmTimers.has(key) || pendingTurnFailureReleases.has(key)) {
|
|
41213
41532
|
skipped += 1;
|
|
41214
41533
|
continue;
|
|
41215
41534
|
}
|
|
@@ -41295,6 +41614,7 @@ function createTaskService(deps) {
|
|
|
41295
41614
|
...expectedReason ? { expectedReason } : {}
|
|
41296
41615
|
}),
|
|
41297
41616
|
recordTurnFailure,
|
|
41617
|
+
parkTurnFailure: (args2) => parkTask(args2, "provider_failure"),
|
|
41298
41618
|
decideUncoveredTurnEnd: decideUncoveredTurnEnd2,
|
|
41299
41619
|
confirmNudgeSent,
|
|
41300
41620
|
resetTurnTracking,
|
|
@@ -41302,18 +41622,29 @@ function createTaskService(deps) {
|
|
|
41302
41622
|
clearRedeliveryTracking: ({ chatroomId, role }) => {
|
|
41303
41623
|
clearRoleRedeliveryTracking(chatroomId, role);
|
|
41304
41624
|
},
|
|
41625
|
+
markTurnFailureReleasePending: ({ chatroomId, role, taskId }) => {
|
|
41626
|
+
pendingTurnFailureReleases.add(redeliveryKey(chatroomId, role, taskId));
|
|
41627
|
+
},
|
|
41628
|
+
clearTurnFailureReleasePending: ({ chatroomId, role, taskId }) => {
|
|
41629
|
+
pendingTurnFailureReleases.delete(redeliveryKey(chatroomId, role, taskId));
|
|
41630
|
+
},
|
|
41305
41631
|
forgetStaleTask
|
|
41306
41632
|
};
|
|
41307
41633
|
return service4;
|
|
41308
41634
|
}
|
|
41635
|
+
var BOOTSTRAP_RECOVERY_BACKOFF, REARM_DELAYS_MS, MAX_REARM_CYCLES;
|
|
41309
41636
|
var init_task_service = __esm(() => {
|
|
41310
41637
|
init_chatroom_workspace_task_inbox();
|
|
41311
41638
|
init_pending_task_reconciliation_watcher();
|
|
41312
41639
|
init_task_inbox_acknowledgement_retry_watcher();
|
|
41313
41640
|
init_task_inbox_delivery_retry_watcher();
|
|
41314
41641
|
init_api3();
|
|
41642
|
+
init_compute_backoff_delay();
|
|
41315
41643
|
init_uncovered_turn_ladder();
|
|
41316
41644
|
init_convex_native_task_delivery_gateway();
|
|
41645
|
+
BOOTSTRAP_RECOVERY_BACKOFF = { baseMs: 1000, maxMs: 30000, jitter: 0 };
|
|
41646
|
+
REARM_DELAYS_MS = [15 * 60000, 30 * 60000, 60 * 60000];
|
|
41647
|
+
MAX_REARM_CYCLES = REARM_DELAYS_MS.length;
|
|
41317
41648
|
});
|
|
41318
41649
|
|
|
41319
41650
|
// src/daemon/services/task-service/infrastructure/repository/task-handoff-repository.ts
|
|
@@ -100027,7 +100358,7 @@ var init_team_presets = __esm(() => {
|
|
|
100027
100358
|
duo: {
|
|
100028
100359
|
structureId: TEAM_STRUCTURE_IDS.duo,
|
|
100029
100360
|
name: "Duo",
|
|
100030
|
-
description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral architect, researcher, triage, uiux-engineer, and verifier roles",
|
|
100361
|
+
description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral architect, researcher, triage, reviewer, uiux-engineer, game-asset-designer, and verifier roles",
|
|
100031
100362
|
roles: getBuiltinRolesForTeam("duo"),
|
|
100032
100363
|
entryPoint: "planner"
|
|
100033
100364
|
},
|
|
@@ -100806,6 +101137,166 @@ var init_builder = __esm(() => {
|
|
|
100806
101137
|
};
|
|
100807
101138
|
});
|
|
100808
101139
|
|
|
101140
|
+
// ../../services/backend/prompts/teams/game-asset-designer-evidence.ts
|
|
101141
|
+
var GAME_ASSET_EVIDENCE_LABELS, GAME_ASSET_EVIDENCE_LABEL_LIST, GAME_ASSET_NEVER_CLAIM_RULE = "Never claim a polycount, texel density, loudness, color space, or visual-quality result without `measured` or `inspected` evidence. Numeric results (polycount, texel density, resolution, loudness, true peak, sample rate, bit depth) and color-space slot checks require `measured`; `inspected` covers only visual or audible judgments. A budget or target you write into the spec is a requirement and is labelled `specified`, not a result.", GAME_ASSET_RESULT_RULE = "A checklist item is `pass` or `fail` only with `measured` or `inspected` evidence. Otherwise record it as `not checked` and keep its label (`specified` or `unverified`).", GAME_ASSET_EVIDENCE_DEFINITIONS = `- \`measured\` — a tool or script you ran on the actual file produced the number or read the metadata field; cite the command and its output value (this includes header or JSON values read by command).
|
|
101142
|
+
- \`inspected\` — you opened the actual file or a render and made a qualitative observation (silhouette, seam, palette, style match); describe what you saw and give no numbers.
|
|
101143
|
+
- \`specified\` — a target or requirement from the brief or your own spec; no file was checked. A budget you write into a spec is \`specified\`.
|
|
101144
|
+
- \`unverified\` — a result that should have been checked but could not be (no file access, no tool, unreadable file), or a value someone else stated that you did not check; say why and name the source.`;
|
|
101145
|
+
var init_game_asset_designer_evidence = __esm(() => {
|
|
101146
|
+
GAME_ASSET_EVIDENCE_LABELS = ["measured", "inspected", "specified", "unverified"];
|
|
101147
|
+
GAME_ASSET_EVIDENCE_LABEL_LIST = GAME_ASSET_EVIDENCE_LABELS.map((label) => `\`${label}\``).join(", ");
|
|
101148
|
+
});
|
|
101149
|
+
|
|
101150
|
+
// ../../services/backend/prompts/teams/game-asset-designer-handoff-templates.ts
|
|
101151
|
+
function getEntryPointToGameAssetDesignerHandoffTemplate(query) {
|
|
101152
|
+
const entryPoint = query.fromRole;
|
|
101153
|
+
return `Instructions (not part of the message): Pass the asset request as a brief the designer can act on without guessing. Provide every field below; write \`Not specified.\` for a field you cannot fill and the designer will ask or apply a stated default. Give each asset a stable ID (\`A01\`, \`A02\`…). Use \`spec only\` only when you want a specification with no files; the default is \`spec and generate\`.
|
|
101154
|
+
|
|
101155
|
+
## Asset request
|
|
101156
|
+
<one to three sentences: what the assets are for and the user-visible goal>
|
|
101157
|
+
|
|
101158
|
+
## Target
|
|
101159
|
+
<engine and version; genre and camera; platform tier (mobile, PC, console, VR); render pipeline if known>
|
|
101160
|
+
|
|
101161
|
+
## Assets requested
|
|
101162
|
+
<numbered list with stable IDs \`A01\`, \`A02\`…; each item: asset name, category (3D model, PBR textures, 2D sprite/UI, audio, other), purpose, quantity or variants>
|
|
101163
|
+
|
|
101164
|
+
## Budgets and conventions
|
|
101165
|
+
<triangle budget per LOD, texel density or pixel grid, texture resolution and atlas limits, loudness target, naming convention, folder layout; or \`Not specified.\`>
|
|
101166
|
+
|
|
101167
|
+
## Style reference
|
|
101168
|
+
<repository paths, URLs, or a written style sheet; or \`Not specified.\`>
|
|
101169
|
+
|
|
101170
|
+
## Spec consumer
|
|
101171
|
+
<who produces or integrates these assets from the spec: \`builder\`, \`uiux-engineer\`, an external artist, or \`user\`>
|
|
101172
|
+
|
|
101173
|
+
## Existing assets
|
|
101174
|
+
<repository paths the designer must reuse or must not overwrite; or \`Not specified.\`>
|
|
101175
|
+
|
|
101176
|
+
## Deliverable mode
|
|
101177
|
+
<\`spec and generate\` (default when you name an output location and allowed tools) or \`spec only\`>
|
|
101178
|
+
|
|
101179
|
+
## Allowed tools
|
|
101180
|
+
<the generators the designer may run, or \`Any tool available in the designer's shell.\`; say which>
|
|
101181
|
+
|
|
101182
|
+
## Allowed edit paths
|
|
101183
|
+
<repository-relative paths the designer may edit; \`None.\` for none>
|
|
101184
|
+
|
|
101185
|
+
## Commit
|
|
101186
|
+
<\`None.\` or the commit message and the paths to commit>
|
|
101187
|
+
|
|
101188
|
+
## Output location
|
|
101189
|
+
<the exact repository directory the designer may write to; required for \`spec and generate\`; \`None.\` for \`spec only\`>
|
|
101190
|
+
|
|
101191
|
+
## Acceptance criteria
|
|
101192
|
+
<what must be true for the planner to accept the delivery>
|
|
101193
|
+
|
|
101194
|
+
Instructions after the body (not part of the message): run the normal handoff command to \`game-asset-designer\` with the complete body above, then stop. Use this template only under the conditions below; the body above ends at \`## Acceptance criteria\`.
|
|
101195
|
+
|
|
101196
|
+
### When to use the game asset designer
|
|
101197
|
+
- the request creates or changes game art or audio assets (models, textures, sprites, UI art, sound effects);
|
|
101198
|
+
- assets must meet engine import conventions or budgets before a builder integrates them;
|
|
101199
|
+
- the user asks for an asset spec, style sheet, or asset audit.
|
|
101200
|
+
|
|
101201
|
+
Skip it for code-only work, for a trivial asset rename, and for non-game UI icons or illustrations (use \`uiux-engineer\`).
|
|
101202
|
+
|
|
101203
|
+
\`\`\`bash
|
|
101204
|
+
${handoffCommand({
|
|
101205
|
+
chatroomId: "<chatroom-id>",
|
|
101206
|
+
role: entryPoint,
|
|
101207
|
+
nextRole: "game-asset-designer",
|
|
101208
|
+
cliEnvPrefix: ""
|
|
101209
|
+
})}
|
|
101210
|
+
\`\`\``;
|
|
101211
|
+
}
|
|
101212
|
+
function getGameAssetDesignerToEntryPointHandoffTemplate(query) {
|
|
101213
|
+
const entryPoint = query.toRole;
|
|
101214
|
+
return `Instructions (not part of the message): Return a complete asset delivery report for the request the planner named. State only what you established. Label every checked property with one evidence label: ${GAME_ASSET_EVIDENCE_LABEL_LIST}. Definitions: ${GAME_ASSET_EVIDENCE_DEFINITIONS} ${GAME_ASSET_NEVER_CLAIM_RULE} ${GAME_ASSET_RESULT_RULE} Write only under the named output location and the named edit paths. In \`spec only\` you write no files. Use the exact sections below, in this order. If you are blocked or need a clarification, send the same sections with Summary starting \`Blocked:\` and every section you could not reach written as \`Not started.\`
|
|
101215
|
+
|
|
101216
|
+
## Summary
|
|
101217
|
+
<one to three sentences: what was delivered, whether it is spec only or includes files, and the count of \`fail\` and \`not checked\` items>
|
|
101218
|
+
|
|
101219
|
+
## Target and assumptions
|
|
101220
|
+
<each assumption you made and why; each deviation from the planner's target or budgets; or \`None.\`>
|
|
101221
|
+
|
|
101222
|
+
## Asset manifest
|
|
101223
|
+
<one entry per asset, keyed by the planner's stable ID (\`A01\`…): name, category, file format, path (or \`Specified only.\`), dimensions/units/scale, pivot, naming, LOD list with triangle budgets, texture set with resolution, color space and channel packing, atlas/pixel grid/frame data, audio format and loudness target, import settings, dependencies and variants; for each budget, the acceptance check (exact command and expected value); for each generated file, the generator (tool + version + command or seed)>
|
|
101224
|
+
|
|
101225
|
+
## Quality checklist results
|
|
101226
|
+
<per asset: each applicable checklist item as \`pass\`, \`fail\`, \`n/a\`, or \`not checked\`, with its evidence label and, for \`measured\`, the command and value; write exactly \`None failed.\` after the list if no item failed>
|
|
101227
|
+
|
|
101228
|
+
## Acceptance criteria results
|
|
101229
|
+
<each planner acceptance criterion as \`met\`, \`not met\`, or \`not checked\`, with its evidence label>
|
|
101230
|
+
|
|
101231
|
+
## Style consistency
|
|
101232
|
+
<how the set matches the style reference; deviations; what was compared and how>
|
|
101233
|
+
|
|
101234
|
+
## Created and edited paths
|
|
101235
|
+
<repository-relative paths and a one-line description each; write exactly \`None.\` for spec only>
|
|
101236
|
+
|
|
101237
|
+
## Commits
|
|
101238
|
+
<write exactly \`None.\` if none; otherwise one line per commit: \`<sha> <message>\`>
|
|
101239
|
+
|
|
101240
|
+
## Sources
|
|
101241
|
+
<URLs for engine or platform rules relied on, with version or date checked>
|
|
101242
|
+
|
|
101243
|
+
## Open questions
|
|
101244
|
+
<each clarification needed from the planner with a recommended default; write exactly \`None.\` if none>
|
|
101245
|
+
|
|
101246
|
+
## Not verified
|
|
101247
|
+
<every property you could not measure or inspect; write exactly \`None.\` if none>
|
|
101248
|
+
|
|
101249
|
+
## Recommended next step
|
|
101250
|
+
<exactly one target: \`game-asset-designer\` (re-run with the listed asset IDs), \`builder\` (integrate, or apply the listed import settings), \`uiux-engineer\` (place the listed UI art in a screen design), or \`planner\` (decision needed on an open question); then the affected asset IDs>
|
|
101251
|
+
|
|
101252
|
+
Instructions after the body (not part of the message): run the normal handoff command to \`${entryPoint}\` with the complete body above, then stop.
|
|
101253
|
+
|
|
101254
|
+
\`\`\`bash
|
|
101255
|
+
${handoffCommand({
|
|
101256
|
+
chatroomId: "<chatroom-id>",
|
|
101257
|
+
role: "game-asset-designer",
|
|
101258
|
+
nextRole: entryPoint,
|
|
101259
|
+
cliEnvPrefix: ""
|
|
101260
|
+
})}
|
|
101261
|
+
\`\`\``;
|
|
101262
|
+
}
|
|
101263
|
+
var init_game_asset_designer_handoff_templates = __esm(() => {
|
|
101264
|
+
init_game_asset_designer_evidence();
|
|
101265
|
+
init_command2();
|
|
101266
|
+
});
|
|
101267
|
+
|
|
101268
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/game-asset-designer-planner-code.ts
|
|
101269
|
+
function getGameAssetDesignerPlannerCodeTemplate(query) {
|
|
101270
|
+
return getGameAssetDesignerToEntryPointHandoffTemplate(query);
|
|
101271
|
+
}
|
|
101272
|
+
var init_game_asset_designer_planner_code = __esm(() => {
|
|
101273
|
+
init_game_asset_designer_handoff_templates();
|
|
101274
|
+
});
|
|
101275
|
+
|
|
101276
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/game-asset-designer-planner-chat.ts
|
|
101277
|
+
var init_game_asset_designer_planner_chat = __esm(() => {
|
|
101278
|
+
init_game_asset_designer_planner_code();
|
|
101279
|
+
});
|
|
101280
|
+
|
|
101281
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/game-asset-designer.ts
|
|
101282
|
+
var duoGameAssetDesignerHandoffContract;
|
|
101283
|
+
var init_game_asset_designer = __esm(() => {
|
|
101284
|
+
init_game_asset_designer_planner_chat();
|
|
101285
|
+
init_game_asset_designer_planner_code();
|
|
101286
|
+
init_contracts();
|
|
101287
|
+
duoGameAssetDesignerHandoffContract = {
|
|
101288
|
+
role: "game-asset-designer",
|
|
101289
|
+
receivesFrom: ["planner"],
|
|
101290
|
+
returnsTo: ["planner"],
|
|
101291
|
+
outboundTemplates: {
|
|
101292
|
+
planner: handoffTemplateByMode({
|
|
101293
|
+
chat: getGameAssetDesignerPlannerCodeTemplate,
|
|
101294
|
+
code: getGameAssetDesignerPlannerCodeTemplate
|
|
101295
|
+
})
|
|
101296
|
+
}
|
|
101297
|
+
};
|
|
101298
|
+
});
|
|
101299
|
+
|
|
100809
101300
|
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-architect-code.ts
|
|
100810
101301
|
function getPlannerArchitectCodeTemplate(query) {
|
|
100811
101302
|
return getEntryPointToArchitectHandoffTemplate(query);
|
|
@@ -100907,6 +101398,19 @@ var init_planner_builder_chat = __esm(() => {
|
|
|
100907
101398
|
init_planner_builder_code();
|
|
100908
101399
|
});
|
|
100909
101400
|
|
|
101401
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-game-asset-designer-code.ts
|
|
101402
|
+
function getPlannerGameAssetDesignerCodeTemplate(query) {
|
|
101403
|
+
return getEntryPointToGameAssetDesignerHandoffTemplate(query);
|
|
101404
|
+
}
|
|
101405
|
+
var init_planner_game_asset_designer_code = __esm(() => {
|
|
101406
|
+
init_game_asset_designer_handoff_templates();
|
|
101407
|
+
});
|
|
101408
|
+
|
|
101409
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-game-asset-designer-chat.ts
|
|
101410
|
+
var init_planner_game_asset_designer_chat = __esm(() => {
|
|
101411
|
+
init_planner_game_asset_designer_code();
|
|
101412
|
+
});
|
|
101413
|
+
|
|
100910
101414
|
// ../../services/backend/prompts/teams/researcher-handoff-templates.ts
|
|
100911
101415
|
function getEntryPointToResearcherHandoffTemplate(query) {
|
|
100912
101416
|
const entryPoint = query.fromRole;
|
|
@@ -100992,6 +101496,75 @@ var init_planner_researcher_chat = __esm(() => {
|
|
|
100992
101496
|
init_planner_researcher_code();
|
|
100993
101497
|
});
|
|
100994
101498
|
|
|
101499
|
+
// ../../services/backend/prompts/teams/reviewer-handoff-templates.ts
|
|
101500
|
+
function getEntryPointToReviewerHandoffTemplate(query) {
|
|
101501
|
+
const entryPoint = query.fromRole;
|
|
101502
|
+
return `Pass the review target and the review focus. State the exact commit range, branch, pull request, or file list to inspect, the focus areas (for example security, correctness, or test quality), and any known context or constraints. Ask the reviewer for a report only: do not edit files, run builds or tests, or implement fixes.
|
|
101503
|
+
|
|
101504
|
+
### When to review
|
|
101505
|
+
|
|
101506
|
+
Review before delivery for code-producing work:
|
|
101507
|
+
- a builder slice has just completed and is about to be delivered;
|
|
101508
|
+
- the user asks for a code review;
|
|
101509
|
+
- a change is large, risky, or touches security-sensitive or shared code.
|
|
101510
|
+
|
|
101511
|
+
Skip review for a trivial edit that has already been verified.
|
|
101512
|
+
|
|
101513
|
+
After completing the review, the reviewer must return its issue report to \`${entryPoint}\` with the normal handoff command.
|
|
101514
|
+
|
|
101515
|
+
\`\`\`bash
|
|
101516
|
+
${handoffCommand({
|
|
101517
|
+
chatroomId: "<chatroom-id>",
|
|
101518
|
+
role: entryPoint,
|
|
101519
|
+
nextRole: "reviewer",
|
|
101520
|
+
cliEnvPrefix: ""
|
|
101521
|
+
})}
|
|
101522
|
+
\`\`\``;
|
|
101523
|
+
}
|
|
101524
|
+
function getReviewerToEntryPointHandoffTemplate(query) {
|
|
101525
|
+
const entryPoint = query.toRole;
|
|
101526
|
+
return `Instructions (not part of the message): Return a complete review report for the target the planner named. The reviewer is advisory: do not edit files, implement fixes, run builds or tests, or propose architecture rewrites beyond each issue's suggested fix. Report only what you observed and mark anything unverified as such. Label every issue with a severity: \`blocker\`, \`major\`, \`minor\`, or \`nit\`. Group the issues by severity with blockers first, and cite the repository-relative file and line or line range for each. Use the exact sections below, in this order.
|
|
101527
|
+
|
|
101528
|
+
## Summary
|
|
101529
|
+
<one to three sentences: overall state of the target and the count of blocker/major issues>
|
|
101530
|
+
|
|
101531
|
+
## Review scope
|
|
101532
|
+
<commits, paths, and PR/branch inspected; commands run, if any>
|
|
101533
|
+
|
|
101534
|
+
## Issues
|
|
101535
|
+
<numbered list grouped by severity (blocker, major, minor, nit); each item: \`file:line\` — summary — evidence — suggested fix; write exactly \`None observed.\` if none>
|
|
101536
|
+
|
|
101537
|
+
## Not reviewed
|
|
101538
|
+
<anything out of scope or unreadable; write exactly \`None.\` if none>
|
|
101539
|
+
|
|
101540
|
+
Instructions after the body (not part of the message): run the normal handoff command to \`${entryPoint}\` with the complete body above, then stop.
|
|
101541
|
+
|
|
101542
|
+
\`\`\`bash
|
|
101543
|
+
${handoffCommand({
|
|
101544
|
+
chatroomId: "<chatroom-id>",
|
|
101545
|
+
role: "reviewer",
|
|
101546
|
+
nextRole: entryPoint,
|
|
101547
|
+
cliEnvPrefix: ""
|
|
101548
|
+
})}
|
|
101549
|
+
\`\`\``;
|
|
101550
|
+
}
|
|
101551
|
+
var init_reviewer_handoff_templates = __esm(() => {
|
|
101552
|
+
init_command2();
|
|
101553
|
+
});
|
|
101554
|
+
|
|
101555
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-reviewer-code.ts
|
|
101556
|
+
function getPlannerReviewerCodeTemplate(query) {
|
|
101557
|
+
return getEntryPointToReviewerHandoffTemplate(query);
|
|
101558
|
+
}
|
|
101559
|
+
var init_planner_reviewer_code = __esm(() => {
|
|
101560
|
+
init_reviewer_handoff_templates();
|
|
101561
|
+
});
|
|
101562
|
+
|
|
101563
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/planner-reviewer-chat.ts
|
|
101564
|
+
var init_planner_reviewer_chat = __esm(() => {
|
|
101565
|
+
init_planner_reviewer_code();
|
|
101566
|
+
});
|
|
101567
|
+
|
|
100995
101568
|
// ../../services/backend/prompts/teams/triage-handoff-templates.ts
|
|
100996
101569
|
function getEntryPointToTriageHandoffTemplate(query) {
|
|
100997
101570
|
const entryPoint = query.fromRole;
|
|
@@ -101489,8 +102062,12 @@ var init_planner = __esm(() => {
|
|
|
101489
102062
|
init_planner_architect_code();
|
|
101490
102063
|
init_planner_builder_chat();
|
|
101491
102064
|
init_planner_builder_code();
|
|
102065
|
+
init_planner_game_asset_designer_chat();
|
|
102066
|
+
init_planner_game_asset_designer_code();
|
|
101492
102067
|
init_planner_researcher_chat();
|
|
101493
102068
|
init_planner_researcher_code();
|
|
102069
|
+
init_planner_reviewer_chat();
|
|
102070
|
+
init_planner_reviewer_code();
|
|
101494
102071
|
init_planner_triage_chat();
|
|
101495
102072
|
init_planner_triage_code();
|
|
101496
102073
|
init_planner_uiux_engineer_chat();
|
|
@@ -101509,9 +102086,21 @@ var init_planner = __esm(() => {
|
|
|
101509
102086
|
"uiux-engineer",
|
|
101510
102087
|
"triage",
|
|
101511
102088
|
"researcher",
|
|
102089
|
+
"reviewer",
|
|
102090
|
+
"game-asset-designer",
|
|
102091
|
+
"verifier"
|
|
102092
|
+
],
|
|
102093
|
+
returnsTo: [
|
|
102094
|
+
"user",
|
|
102095
|
+
"builder",
|
|
102096
|
+
"architect",
|
|
102097
|
+
"uiux-engineer",
|
|
102098
|
+
"triage",
|
|
102099
|
+
"researcher",
|
|
102100
|
+
"reviewer",
|
|
102101
|
+
"game-asset-designer",
|
|
101512
102102
|
"verifier"
|
|
101513
102103
|
],
|
|
101514
|
-
returnsTo: ["user", "builder", "architect", "uiux-engineer", "triage", "researcher", "verifier"],
|
|
101515
102104
|
outboundTemplates: {
|
|
101516
102105
|
builder: handoffTemplateByMode({
|
|
101517
102106
|
chat: getPlannerBuilderCodeTemplate,
|
|
@@ -101533,6 +102122,14 @@ var init_planner = __esm(() => {
|
|
|
101533
102122
|
chat: getPlannerResearcherCodeTemplate,
|
|
101534
102123
|
code: getPlannerResearcherCodeTemplate
|
|
101535
102124
|
}),
|
|
102125
|
+
reviewer: handoffTemplateByMode({
|
|
102126
|
+
chat: getPlannerReviewerCodeTemplate,
|
|
102127
|
+
code: getPlannerReviewerCodeTemplate
|
|
102128
|
+
}),
|
|
102129
|
+
"game-asset-designer": handoffTemplateByMode({
|
|
102130
|
+
chat: getPlannerGameAssetDesignerCodeTemplate,
|
|
102131
|
+
code: getPlannerGameAssetDesignerCodeTemplate
|
|
102132
|
+
}),
|
|
101536
102133
|
verifier: handoffTemplateByMode({
|
|
101537
102134
|
chat: getPlannerVerifierCodeTemplate,
|
|
101538
102135
|
code: getPlannerVerifierCodeTemplate
|
|
@@ -101577,6 +102174,38 @@ var init_researcher = __esm(() => {
|
|
|
101577
102174
|
};
|
|
101578
102175
|
});
|
|
101579
102176
|
|
|
102177
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/reviewer-planner-code.ts
|
|
102178
|
+
function getReviewerPlannerCodeTemplate(query) {
|
|
102179
|
+
return getReviewerToEntryPointHandoffTemplate(query);
|
|
102180
|
+
}
|
|
102181
|
+
var init_reviewer_planner_code = __esm(() => {
|
|
102182
|
+
init_reviewer_handoff_templates();
|
|
102183
|
+
});
|
|
102184
|
+
|
|
102185
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/reviewer-planner-chat.ts
|
|
102186
|
+
var init_reviewer_planner_chat = __esm(() => {
|
|
102187
|
+
init_reviewer_planner_code();
|
|
102188
|
+
});
|
|
102189
|
+
|
|
102190
|
+
// ../../services/backend/prompts/teams/duo/handoff-templates/reviewer.ts
|
|
102191
|
+
var duoReviewerHandoffContract;
|
|
102192
|
+
var init_reviewer = __esm(() => {
|
|
102193
|
+
init_reviewer_planner_chat();
|
|
102194
|
+
init_reviewer_planner_code();
|
|
102195
|
+
init_contracts();
|
|
102196
|
+
duoReviewerHandoffContract = {
|
|
102197
|
+
role: "reviewer",
|
|
102198
|
+
receivesFrom: ["planner"],
|
|
102199
|
+
returnsTo: ["planner"],
|
|
102200
|
+
outboundTemplates: {
|
|
102201
|
+
planner: handoffTemplateByMode({
|
|
102202
|
+
chat: getReviewerPlannerCodeTemplate,
|
|
102203
|
+
code: getReviewerPlannerCodeTemplate
|
|
102204
|
+
})
|
|
102205
|
+
}
|
|
102206
|
+
};
|
|
102207
|
+
});
|
|
102208
|
+
|
|
101580
102209
|
// ../../services/backend/prompts/teams/duo/handoff-templates/triage-planner-code.ts
|
|
101581
102210
|
function getTriagePlannerCodeTemplate(query) {
|
|
101582
102211
|
return getTriageToEntryPointHandoffTemplate(query);
|
|
@@ -101681,8 +102310,10 @@ var DUO_ROLE_HANDOFF_CATALOG, DUO_ROLE_HANDOFF_CONTRACTS, validatedDuoRoleHandof
|
|
|
101681
102310
|
var init_handoff_templates = __esm(() => {
|
|
101682
102311
|
init_architect();
|
|
101683
102312
|
init_builder();
|
|
102313
|
+
init_game_asset_designer();
|
|
101684
102314
|
init_planner();
|
|
101685
102315
|
init_researcher();
|
|
102316
|
+
init_reviewer();
|
|
101686
102317
|
init_triage();
|
|
101687
102318
|
init_uiux_engineer();
|
|
101688
102319
|
init_verifier();
|
|
@@ -101692,7 +102323,9 @@ var init_handoff_templates = __esm(() => {
|
|
|
101692
102323
|
builder: duoBuilderHandoffContract,
|
|
101693
102324
|
architect: duoArchitectHandoffContract,
|
|
101694
102325
|
researcher: duoResearcherHandoffContract,
|
|
102326
|
+
reviewer: duoReviewerHandoffContract,
|
|
101695
102327
|
"uiux-engineer": duoUiuxEngineerHandoffContract,
|
|
102328
|
+
"game-asset-designer": duoGameAssetDesignerHandoffContract,
|
|
101696
102329
|
triage: duoTriageHandoffContract,
|
|
101697
102330
|
verifier: duoVerifierHandoffContract
|
|
101698
102331
|
};
|
|
@@ -102505,6 +103138,79 @@ var init_builder2 = __esm(() => {
|
|
|
102505
103138
|
init_sections();
|
|
102506
103139
|
});
|
|
102507
103140
|
|
|
103141
|
+
// ../../services/backend/prompts/cli/roles/game-asset-designer.ts
|
|
103142
|
+
var GAME_ASSET_DESIGNER_GUIDANCE;
|
|
103143
|
+
var init_game_asset_designer2 = __esm(() => {
|
|
103144
|
+
init_game_asset_designer_evidence();
|
|
103145
|
+
init_sections();
|
|
103146
|
+
GAME_ASSET_DESIGNER_GUIDANCE = `## Game Asset Designer Operating Model
|
|
103147
|
+
|
|
103148
|
+
You are the game asset designer responsible for specifying production-quality game assets, and for producing them only when the planner's request and your available tools allow it. You own the asset specification, generate the asset files yourself, and edit only the asset-integration paths the brief allows (engine import or config files and the code that references the asset files). You do not implement application logic outside those paths. In \`spec only\` mode you write no files. You do not act as the builder or planner.
|
|
103149
|
+
|
|
103150
|
+
${"{SESSION_CONTINUITY}"}
|
|
103151
|
+
|
|
103152
|
+
${getInterRoleHandoffRule()}
|
|
103153
|
+
|
|
103154
|
+
1. Recover the authoritative request from the injected task and the \`<handoff-templates>\` brief. Recover relevant chatroom history if the brief refers to earlier assets. Production quality is relative to a target, so confirm these inputs: target engine and version, genre and camera, platform tier (mobile, PC, console, VR), art style reference, asset categories, and budgets (triangles per LOD, texel density, texture resolution, atlas size, loudness). If an input is missing and the repository does not answer it, ask \`{ENTRY_POINT_ROLE}\` through the handoff with a clarification request. Do not guess a target engine or platform. For a non-blocking gap, apply the stated default and list it under assumptions. If you are blocked, send the same report to \`{ENTRY_POINT_ROLE}\` with Summary starting \`Blocked:\` and every section you could not reach written as \`Not started.\`
|
|
103155
|
+
2. Inspect the repository for existing assets, naming and folder conventions, engine and import configuration, and style references. Match what exists unless the planner says otherwise. Cite paths.
|
|
103156
|
+
3. Classify each asset as 3D model, PBR material or texture set, 2D sprite or UI art, audio, or other, and write one manifest entry per asset using the fields in the handoff template.
|
|
103157
|
+
4. Specify against the target engine's documented import conventions. Prefer official engine documentation, the Khronos glTF 2.0 specification, and platform audio guidance. Cite the source for any rule you rely on. Treat community figures (albedo ranges, texel density tiers, polycount tiers) as starting points that the request's declared budgets override, and say so.
|
|
103158
|
+
5. Apply the quality checklist below to every asset before handoff.
|
|
103159
|
+
6. Say exactly how each claim was established, using only these evidence labels (${GAME_ASSET_EVIDENCE_LABEL_LIST}):
|
|
103160
|
+
${GAME_ASSET_EVIDENCE_DEFINITIONS}
|
|
103161
|
+
${GAME_ASSET_NEVER_CLAIM_RULE} ${GAME_ASSET_RESULT_RULE}
|
|
103162
|
+
7. Generate the asset files yourself when the brief names an output location and allowed tools (default mode \`spec and generate\`). Use only the tools the brief allows, and use tools already available in your shell. Record the tool, its version, and the command or seed for every generated file in the manifest. Write only under the named output location. Edit files only at the paths named under \`Allowed edit paths\`. Never overwrite or delete an existing file unless the brief names it. Commit only when the brief names the paths and the commit message; otherwise leave changes uncommitted and say so. If \`Deliverable mode\` is \`spec only\`, write no files. If the output location is missing or ambiguous, deliver \`spec only\`, state that in Summary, and list the gap under Open questions. Describe generated output as production-ready only when every checklist item for it passes with \`measured\` or \`inspected\` evidence. Run each numeric check against the generated file itself, not against your own generator's settings.
|
|
103163
|
+
|
|
103164
|
+
In \`spec only\` the deliverable is a \`Specified only.\` manifest. Each entry has: a stable ID (\`A01\`, \`A02\`…); category; format; units, scale and pivot; name and path; budgets per property; import settings; dependencies and variants; and, for each budget, an acceptance check that gives the exact measuring command and expected value (for example \`ffmpeg -i <file> -af ebur128=peak=true -f null -\`, \`gltf-transform inspect <file>\`, \`ffprobe <file>\`).
|
|
103165
|
+
8. Follow the injected \`<handoff-templates>\` asset delivery report exactly, in its order.
|
|
103166
|
+
9. Hand the report to \`{ENTRY_POINT_ROLE}\` with the normal command, then stop:
|
|
103167
|
+
|
|
103168
|
+
\`\`\`bash
|
|
103169
|
+
chatroom handoff --chatroom-id="<chatroom-id>" --role="game-asset-designer" --next-role="{ENTRY_POINT_ROLE}"
|
|
103170
|
+
\`\`\`
|
|
103171
|
+
|
|
103172
|
+
### Quality checklist (apply to each asset; mark each item pass, fail, or not applicable, with its evidence label)
|
|
103173
|
+
|
|
103174
|
+
**All assets**
|
|
103175
|
+
- Name follows the project convention (type prefix, descriptive name, variant, LOD suffix); ASCII, no spaces; folder matches the asset category.
|
|
103176
|
+
- File format is the one the target engine imports (glTF 2.0/GLB, FBX, PNG, WAV/OGG as named in the request); no unused data (hidden meshes, unused materials, history).
|
|
103177
|
+
- Style matches the named style reference: silhouette, value range, palette, level of detail, and texel density or pixel grid.
|
|
103178
|
+
- Budgets in the request are met; any exceedance is called out with the measured value.
|
|
103179
|
+
|
|
103180
|
+
**3D models**
|
|
103181
|
+
- Units and axes match the target (glTF: meters, +Y up, front +Z); scale is applied (identity scale) and a real-world size is stated.
|
|
103182
|
+
- Pivot/origin follows the declared policy; LODs share the base pivot and alignment.
|
|
103183
|
+
- Clean topology: no n-gons or non-manifold edges unless declared, no flipped normals or inverted winding (glTF: counter-clockwise front faces), consistent normals and tangents (MikkTSpace).
|
|
103184
|
+
- Triangle count per LOD is within budget; LOD naming matches the engine (Unity \`_LOD0\`, \`_LOD1\`; Unreal LODs share the base pivot and alignment and are imported per the FBX import options; Godot generates LODs at import, so declare the Generate LODs setting instead of a suffix).
|
|
103185
|
+
- UVs: non-overlapping unique layout where textures are unique, island padding, consistent texel density, no stretched islands; a second UV set if the engine needs baked lighting.
|
|
103186
|
+
- Collision and sockets use the engine's naming (Unreal \`UBX_\`/\`UCP_\`/\`USP_\`/\`UCX_\` + mesh name and \`SOCKET_\` + mesh name; Godot \`-col\`/\`-convcolonly\` suffixes).
|
|
103187
|
+
- Skinned meshes: bone count within budget, influences per vertex within the declared budget (commonly 4, marked \`unverified\`; glTF allows more via \`JOINTS_1\`), no leftover IK helpers, a named rest pose.
|
|
103188
|
+
|
|
103189
|
+
**PBR materials and textures**
|
|
103190
|
+
- Metallic-roughness workflow with documented channel packing (glTF: roughness in G, metallic in B, occlusion in R).
|
|
103191
|
+
- Color space: base color and emissive sRGB; normal, roughness, metallic, occlusion, masks linear. State the normal map Y convention. In glTF, color space is set by the material slot that references the image, not by embedded PNG chunks. Verify the slot for each map: \`baseColorTexture\` and \`emissiveTexture\` are sRGB; \`metallicRoughnessTexture\`, \`occlusionTexture\` and \`normalTexture\` are linear.
|
|
103192
|
+
- Base color values stay in a physically plausible range (community starting range roughly 30–240 sRGB per channel for non-metals; the request's budget overrides it); metallic is near binary.
|
|
103193
|
+
- Resolution matches the texel-density budget and surface size; dimensions meet the engine/platform rule (power of two where the engine needs mipmaps or streaming, for example Unreal; confirm for the target engine version); compression format named.
|
|
103194
|
+
- Tiling textures wrap without a visible seam; no baked-in lighting or text in base color unless declared.
|
|
103195
|
+
- Maps in one set share texel density, value range, and naming (\`T_Name_BaseColor\`, \`_Normal\`, \`_ORM\`).
|
|
103196
|
+
|
|
103197
|
+
**2D sprites and UI art**
|
|
103198
|
+
- Fixed grid and pixels-per-unit declared; sprite sizes are multiples of the grid; no sub-pixel offsets.
|
|
103199
|
+
- Palette size and colors declared; transparent background with clean alpha edges.
|
|
103200
|
+
- Animation: uniform frame size per clip, frame count, frame rate, loop flag, and pivot per clip.
|
|
103201
|
+
- Atlas: padding of at least the engine default (Unity default 4 px), atlas size within the platform max, filter mode Point for pixel art and no mip maps.
|
|
103202
|
+
- Export PNG (lossless with alpha); name states size and frame count where relevant.
|
|
103203
|
+
|
|
103204
|
+
**Audio**
|
|
103205
|
+
- Loudness measured against the platform target stated in the request (for example console -24 LUFS, mobile -18 LUFS, true peak at or below -1 dBTP); no clipping. Measure with a tool: \`ffmpeg -i <file> -af ebur128=peak=true -f null -\` for integrated loudness and true peak; \`ffprobe <file>\` for sample rate, bit depth and channels; for glTF, \`gltf-transform inspect <file>\` (quote the command's actual output). Cite the platform source. If the request does not state the target, mark the loudness target \`unverified\` and name the source. Mobile targets vary (-18 vs -16 LUFS).
|
|
103206
|
+
- Sample rate, bit depth, channel count, loop points, and format stated; short noisy SFX use PCM or ADPCM, long audio uses compressed or streamed formats, per the engine's guidance.
|
|
103207
|
+
|
|
103208
|
+
### Scope boundary
|
|
103209
|
+
You may specify, name, organize, generate, validate, and integrate assets within the paths the brief allows. You must not claim fidelity you did not establish. If you cannot open or measure a file, write \`unverified\`. Do not present alternative art directions beyond what the request needs; recommend one.
|
|
103210
|
+
|
|
103211
|
+
The handoff must contain every required report section, the manifest with one entry per asset, checklist results with evidence labels, assumptions, created and edited paths (or \`None.\`), and open questions for \`{ENTRY_POINT_ROLE}\`.`;
|
|
103212
|
+
});
|
|
103213
|
+
|
|
102508
103214
|
// ../../services/backend/prompts/cli/roles/planner.ts
|
|
102509
103215
|
var init_planner2 = __esm(() => {
|
|
102510
103216
|
init_env();
|
|
@@ -102536,6 +103242,30 @@ chatroom handoff --chatroom-id="<chatroom-id>" --role="researcher" --next-role="
|
|
|
102536
103242
|
\`\`\``;
|
|
102537
103243
|
});
|
|
102538
103244
|
|
|
103245
|
+
// ../../services/backend/prompts/cli/roles/reviewer.ts
|
|
103246
|
+
var REVIEWER_GUIDANCE;
|
|
103247
|
+
var init_reviewer2 = __esm(() => {
|
|
103248
|
+
init_sections();
|
|
103249
|
+
REVIEWER_GUIDANCE = `## Reviewer Operating Model
|
|
103250
|
+
|
|
103251
|
+
You are the reviewer responsible for inspecting the code artifacts the planner names and reporting the issues you observe. You are advisory only: do not edit files, implement fixes, or act as the builder or planner.
|
|
103252
|
+
|
|
103253
|
+
${"{SESSION_CONTINUITY}"}
|
|
103254
|
+
|
|
103255
|
+
${getInterRoleHandoffRule()}
|
|
103256
|
+
|
|
103257
|
+
1. Recover the review target and focus from the injected task and the \`<handoff-templates>\` brief. If the target is not explicit, ask the planner through the handoff; do not guess a range.
|
|
103258
|
+
2. Inspect the target: the diff or commits, the changed files, and the surrounding code they depend on. Read the actual files; do not review from summaries.
|
|
103259
|
+
3. Look for correctness bugs, security issues, missing or weak tests, unclear ownership or error handling, dead code, duplication, and type drift. Stay within the stated focus when one is given.
|
|
103260
|
+
4. Label every issue with a severity (\`blocker\`, \`major\`, \`minor\`, \`nit\`) and cite the file and line. Only report what you observed; mark anything unverified as such.
|
|
103261
|
+
5. Follow the injected \`<handoff-templates>\` review report exactly, in its order.
|
|
103262
|
+
6. Hand the report to \`{ENTRY_POINT_ROLE}\` with the normal command, then stop:
|
|
103263
|
+
|
|
103264
|
+
\`\`\`bash
|
|
103265
|
+
chatroom handoff --chatroom-id="<chatroom-id>" --role="reviewer" --next-role="{ENTRY_POINT_ROLE}"
|
|
103266
|
+
\`\`\``;
|
|
103267
|
+
});
|
|
103268
|
+
|
|
102539
103269
|
// ../../services/backend/prompts/cli/roles/triage.ts
|
|
102540
103270
|
var TRIAGE_GUIDANCE;
|
|
102541
103271
|
var init_triage3 = __esm(() => {
|
|
@@ -102636,8 +103366,10 @@ var init_fromContext = __esm(() => {
|
|
|
102636
103366
|
var init_fromContext2 = __esm(() => {
|
|
102637
103367
|
init_architect3();
|
|
102638
103368
|
init_builder2();
|
|
103369
|
+
init_game_asset_designer2();
|
|
102639
103370
|
init_planner2();
|
|
102640
103371
|
init_researcher2();
|
|
103372
|
+
init_reviewer2();
|
|
102641
103373
|
init_triage3();
|
|
102642
103374
|
init_uiux_engineer3();
|
|
102643
103375
|
init_verifier3();
|
|
@@ -102731,6 +103463,32 @@ var init_templates = __esm(() => {
|
|
|
102731
103463
|
],
|
|
102732
103464
|
defaultHandoffTarget: "planner"
|
|
102733
103465
|
},
|
|
103466
|
+
reviewer: {
|
|
103467
|
+
role: "reviewer",
|
|
103468
|
+
title: "Reviewer",
|
|
103469
|
+
description: "You are the reviewer responsible for inspecting the code artifacts the planner names and reporting the issues you observe; you are advisory only and do not implement fixes.",
|
|
103470
|
+
responsibilities: [
|
|
103471
|
+
"Recover the review target and focus from the planner brief",
|
|
103472
|
+
"Inspect the diff or commits and the surrounding code they depend on",
|
|
103473
|
+
"Look for correctness, security, test quality, ownership, dead code, duplication, and type drift",
|
|
103474
|
+
"Label each issue with a severity and cite the file and line",
|
|
103475
|
+
"Hand one review report to the planner, separating reviewed scope from what was not reviewed"
|
|
103476
|
+
],
|
|
103477
|
+
defaultHandoffTarget: "planner"
|
|
103478
|
+
},
|
|
103479
|
+
"game-asset-designer": {
|
|
103480
|
+
role: "game-asset-designer",
|
|
103481
|
+
title: "Game Asset Designer",
|
|
103482
|
+
description: "You are the game asset designer responsible for specifying production-quality game assets (and producing them when the planner allows); you do not implement application code.",
|
|
103483
|
+
responsibilities: [
|
|
103484
|
+
"Recover the target, asset request, style reference, and budgets from the planner brief",
|
|
103485
|
+
"Inspect the repository for existing assets, naming and folder conventions, and engine import configuration",
|
|
103486
|
+
"Write one manifest entry per asset covering format, units, pivot, LODs, textures, atlases, and audio",
|
|
103487
|
+
"Self-check each asset against the quality checklist and label every claim with its evidence label",
|
|
103488
|
+
"Hand one asset delivery report to the planner, separating assumptions and unverified properties from established results"
|
|
103489
|
+
],
|
|
103490
|
+
defaultHandoffTarget: "planner"
|
|
103491
|
+
},
|
|
102734
103492
|
verifier: {
|
|
102735
103493
|
role: "verifier",
|
|
102736
103494
|
title: "Verifier",
|
|
@@ -139141,4 +139899,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
139141
139899
|
});
|
|
139142
139900
|
program2.parse();
|
|
139143
139901
|
|
|
139144
|
-
//# debugId=
|
|
139902
|
+
//# debugId=71E0747DD56ECDB664756E2164756E21
|