titan-agent 6.2.2 → 6.3.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/agent/goalDriver.js +154 -4
- package/dist/agent/goalDriver.js.map +1 -1
- package/dist/agent/orchestrator.js +9 -10
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/planner.js +42 -10
- package/dist/agent/planner.js.map +1 -1
- package/dist/agent/systemPromptParts.js +13 -16
- package/dist/agent/systemPromptParts.js.map +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/sw.js +1 -1
package/dist/agent/goalDriver.js
CHANGED
|
@@ -200,7 +200,149 @@ async function tickPlanning(goal, state) {
|
|
|
200
200
|
state.phase = "delegating";
|
|
201
201
|
appendHistory(state, "delegating", `Planned: ${Object.keys(state.subtaskStates).length} subtasks classified`);
|
|
202
202
|
}
|
|
203
|
+
const MAX_BURST_PARALLEL = 4;
|
|
204
|
+
async function tryBurstDispatch(goal, state) {
|
|
205
|
+
const ready = await pickAllReadySubtasks(goal, state, MAX_BURST_PARALLEL);
|
|
206
|
+
if (ready.length < 2) return "fallthrough";
|
|
207
|
+
const validated = [];
|
|
208
|
+
for (const sub of ready) {
|
|
209
|
+
const subState = state.subtaskStates[sub.id];
|
|
210
|
+
if (!subState) continue;
|
|
211
|
+
const cap = subState.maxAttempts ?? 5;
|
|
212
|
+
if (subState.attempts + 1 > cap) continue;
|
|
213
|
+
const strategy = nextFallback(subState.kind, subState.attempts, subState.lastError, cap);
|
|
214
|
+
if (!strategy) continue;
|
|
215
|
+
validated.push({ subtask: sub, subState, strategy });
|
|
216
|
+
}
|
|
217
|
+
if (validated.length < 2) return "fallthrough";
|
|
218
|
+
const batchStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
219
|
+
state.parallelBatch = {
|
|
220
|
+
ids: validated.map((v) => v.subtask.id),
|
|
221
|
+
startedAt: batchStartedAt
|
|
222
|
+
};
|
|
223
|
+
for (const v of validated) {
|
|
224
|
+
v.subState.attempts += 1;
|
|
225
|
+
v.subState.specialist = v.strategy.specialist;
|
|
226
|
+
v.subState.pendingSpawn = {
|
|
227
|
+
attemptedAt: batchStartedAt,
|
|
228
|
+
specialist: v.strategy.specialist
|
|
229
|
+
};
|
|
230
|
+
try {
|
|
231
|
+
emitAgentEvent({
|
|
232
|
+
type: "agent_spawn",
|
|
233
|
+
agentId: v.strategy.specialist,
|
|
234
|
+
agentName: v.strategy.specialist,
|
|
235
|
+
timestamp: Date.now(),
|
|
236
|
+
data: {
|
|
237
|
+
goalId: goal.id,
|
|
238
|
+
subtaskId: v.subtask.id,
|
|
239
|
+
subtaskTitle: v.subtask.title,
|
|
240
|
+
subtaskKind: v.subState.kind,
|
|
241
|
+
attempt: v.subState.attempts,
|
|
242
|
+
burst: true,
|
|
243
|
+
burstSize: validated.length
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
appendHistory(
|
|
250
|
+
state,
|
|
251
|
+
"observing",
|
|
252
|
+
`Burst dispatch: ${validated.length} subtasks in parallel (${validated.map((v) => v.subtask.title.slice(0, 32)).join(" | ")})`
|
|
253
|
+
);
|
|
254
|
+
state.phase = "observing";
|
|
255
|
+
const burstStartMs = Date.now();
|
|
256
|
+
const settled = await Promise.allSettled(
|
|
257
|
+
validated.map((v) => structuredSpawn({
|
|
258
|
+
specialistId: v.strategy.specialist,
|
|
259
|
+
task: `${v.subtask.title}
|
|
260
|
+
|
|
261
|
+
${v.subtask.description}${v.strategy.promptAdjustment ?? ""}`,
|
|
262
|
+
modelOverride: v.strategy.modelOverride,
|
|
263
|
+
toolAllowlist: routeForKind(v.subState.kind).toolAllowlist,
|
|
264
|
+
maxRounds: v.strategy.maxRounds,
|
|
265
|
+
goalId: goal.id
|
|
266
|
+
}))
|
|
267
|
+
);
|
|
268
|
+
const burstDurationMs = Date.now() - burstStartMs;
|
|
269
|
+
let happyCount = 0;
|
|
270
|
+
for (let i = 0; i < validated.length; i++) {
|
|
271
|
+
const v = validated[i];
|
|
272
|
+
const outcome = settled[i];
|
|
273
|
+
v.subState.pendingSpawn = void 0;
|
|
274
|
+
if (outcome.status === "rejected") {
|
|
275
|
+
v.subState.lastError = outcome.reason?.message ?? String(outcome.reason);
|
|
276
|
+
appendHistory(state, "iterating", `Burst spawn threw for ${v.subtask.id}: ${v.subState.lastError.slice(0, 100)}`);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const result = outcome.value;
|
|
280
|
+
recordSpend(state, {
|
|
281
|
+
elapsedMs: burstDurationMs / validated.length,
|
|
282
|
+
// amortized; the wall-clock saving is the point
|
|
283
|
+
tokens: result.tokensUsed ?? 0,
|
|
284
|
+
costUsd: result.costUsd ?? 0
|
|
285
|
+
});
|
|
286
|
+
v.subState.artifacts = [.../* @__PURE__ */ new Set([
|
|
287
|
+
...v.subState.artifacts,
|
|
288
|
+
...result.artifacts.map((a) => a.ref)
|
|
289
|
+
])];
|
|
290
|
+
try {
|
|
291
|
+
emitAgentEvent({
|
|
292
|
+
type: "agent_done",
|
|
293
|
+
agentId: v.strategy.specialist,
|
|
294
|
+
agentName: v.strategy.specialist,
|
|
295
|
+
timestamp: Date.now(),
|
|
296
|
+
data: {
|
|
297
|
+
goalId: goal.id,
|
|
298
|
+
subtaskId: v.subtask.id,
|
|
299
|
+
subtaskTitle: v.subtask.title,
|
|
300
|
+
status: result.status,
|
|
301
|
+
reasoning: result.reasoning,
|
|
302
|
+
artifactCount: result.artifacts.length,
|
|
303
|
+
artifacts: result.artifacts.map((a) => ({ ref: a.ref, type: a.type })),
|
|
304
|
+
toolsUsed: result.toolsUsed ?? [],
|
|
305
|
+
tokensUsed: result.tokensUsed ?? 0,
|
|
306
|
+
costUsd: result.costUsd ?? 0,
|
|
307
|
+
durationMs: burstDurationMs,
|
|
308
|
+
model: v.strategy.modelOverride ?? v.strategy.specialist,
|
|
309
|
+
burst: true
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
} catch {
|
|
313
|
+
}
|
|
314
|
+
if (result.status === "done") {
|
|
315
|
+
v.subState.consecutiveNeedsInfoCount = 0;
|
|
316
|
+
v.subState.lastSpawnResult = result;
|
|
317
|
+
try {
|
|
318
|
+
const { completeSubtask } = await import("./goals.js");
|
|
319
|
+
completeSubtask(goal.id, v.subtask.id, result.reasoning || "completed via burst");
|
|
320
|
+
} catch (err) {
|
|
321
|
+
v.subState.lastError = `Burst completion persist failed: ${err.message}`;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
happyCount++;
|
|
325
|
+
} else if (result.status === "failed") {
|
|
326
|
+
v.subState.lastError = result.reasoning || "failed";
|
|
327
|
+
v.subState.consecutiveNeedsInfoCount = 0;
|
|
328
|
+
} else {
|
|
329
|
+
v.subState.lastError = result.reasoning || `Burst returned ${result.status}; deferring to single-path retry.`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
state.parallelBatch = void 0;
|
|
333
|
+
appendHistory(
|
|
334
|
+
state,
|
|
335
|
+
"delegating",
|
|
336
|
+
`Burst complete: ${happyCount}/${validated.length} succeeded in ${burstDurationMs}ms (parallel)`
|
|
337
|
+
);
|
|
338
|
+
state.phase = "delegating";
|
|
339
|
+
return "handled";
|
|
340
|
+
}
|
|
203
341
|
async function tickDelegating(goal, state) {
|
|
342
|
+
if (!state.parallelBatch) {
|
|
343
|
+
const burst = await tryBurstDispatch(goal, state);
|
|
344
|
+
if (burst === "handled") return;
|
|
345
|
+
}
|
|
204
346
|
const next = await pickNextReadySubtask(goal, state);
|
|
205
347
|
if (!next) {
|
|
206
348
|
let freshGoal = goal;
|
|
@@ -900,12 +1042,19 @@ async function tickCancelled(goal, state) {
|
|
|
900
1042
|
appendHistory(state, "cancelled", "Cancelled by user");
|
|
901
1043
|
}
|
|
902
1044
|
async function pickNextReadySubtask(goal, state) {
|
|
903
|
-
const
|
|
1045
|
+
const ready = await pickAllReadySubtasks(goal, state, 1);
|
|
1046
|
+
return ready[0] ?? null;
|
|
1047
|
+
}
|
|
1048
|
+
async function pickAllReadySubtasks(goal, state, cap) {
|
|
1049
|
+
if (cap < 1) return [];
|
|
1050
|
+
const attemptCap = (id) => state.subtaskStates[id]?.maxAttempts ?? 5;
|
|
1051
|
+
const ready = [];
|
|
904
1052
|
for (const sub of goal.subtasks || []) {
|
|
1053
|
+
if (ready.length >= cap) break;
|
|
905
1054
|
if (sub.status !== "pending") continue;
|
|
906
1055
|
const subState = state.subtaskStates[sub.id];
|
|
907
1056
|
if (!subState) continue;
|
|
908
|
-
const exhaustedAttempts = subState.attempts >=
|
|
1057
|
+
const exhaustedAttempts = subState.attempts >= attemptCap(sub.id) || subState.attempts >= state.budgetCaps.maxRetries;
|
|
909
1058
|
if (subState.verificationResult?.passed === false && exhaustedAttempts) {
|
|
910
1059
|
try {
|
|
911
1060
|
const { failSubtask } = await import("./goals.js");
|
|
@@ -920,9 +1069,9 @@ async function pickNextReadySubtask(goal, state) {
|
|
|
920
1069
|
return dep?.status === "done";
|
|
921
1070
|
});
|
|
922
1071
|
if (!depsSatisfied) continue;
|
|
923
|
-
|
|
1072
|
+
ready.push(sub);
|
|
924
1073
|
}
|
|
925
|
-
return
|
|
1074
|
+
return ready;
|
|
926
1075
|
}
|
|
927
1076
|
function slugForGoal(title) {
|
|
928
1077
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "report";
|
|
@@ -1229,6 +1378,7 @@ export {
|
|
|
1229
1378
|
listActiveDrivers,
|
|
1230
1379
|
listAllDrivers,
|
|
1231
1380
|
pauseDriver,
|
|
1381
|
+
pickAllReadySubtasks,
|
|
1232
1382
|
reprioritizeDriver,
|
|
1233
1383
|
resumeDriverControl,
|
|
1234
1384
|
shouldAutoCancelOnRecurringBlock,
|