@sema-agent/core 1.280.0 → 1.282.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.
@@ -300,6 +300,8 @@ export class Runner {
300
300
  for (const w of manualCompactRef.waiters.splice(0))
301
301
  w(outcome);
302
302
  };
303
+ const runStartedAt = Date.now();
304
+ const taskIdRef = { current: spec.taskId, sessionId: spec.sessionId };
303
305
  const run = (async () => {
304
306
  const releaseLock = spec.sessionId
305
307
  ? await this.acquireSessionLock(spec.sessionId)
@@ -316,7 +318,7 @@ export class Runner {
316
318
  publishReady(h);
317
319
  }, (s) => {
318
320
  reapHandle = s;
319
- }, manualCompactRef, resume, { ...(internals ?? {}), detachHub });
321
+ }, manualCompactRef, taskIdRef, resume, { ...(internals ?? {}), detachHub });
320
322
  }
321
323
  finally {
322
324
  releaseLock?.();
@@ -346,15 +348,35 @@ export class Runner {
346
348
  catch {
347
349
  }
348
350
  }
351
+ if (resultValue !== undefined) {
352
+ try {
353
+ this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: resultValue.sessionId });
354
+ }
355
+ catch {
356
+ }
357
+ queue.close();
358
+ return;
359
+ }
349
360
  resultValue = {
350
- taskId: spec.taskId ?? "unknown",
351
- sessionId: spec.sessionId ?? "unknown",
361
+ taskId: taskIdRef.current ?? "unknown",
362
+ sessionId: taskIdRef.sessionId ?? "unknown",
352
363
  status: "failed",
353
364
  result: "",
354
365
  errorMessage: err instanceof Error ? err.message : String(err),
355
366
  errorCode: code,
356
367
  stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costUsd: 0 },
357
368
  };
369
+ emitTrace(spec.tracer ?? this.deps.tracer, () => ({
370
+ kind: "task.end",
371
+ version: 1,
372
+ taskId: taskIdRef.current ?? "unknown",
373
+ status: "failed",
374
+ errorCode: code,
375
+ turns: 0,
376
+ tokens: 0,
377
+ durationMs: Date.now() - runStartedAt,
378
+ ts: Date.now(),
379
+ }));
358
380
  queue.push({ type: "done", result: resultValue });
359
381
  queue.close();
360
382
  });
@@ -460,7 +482,7 @@ export class Runner {
460
482
  })()),
461
483
  };
462
484
  }
463
- async runLocked(spec, queue, setResult, onConsolidation, onSuggestions, onReady, onSuspend, manualCompactRef, resume, internals) {
485
+ async runLocked(spec, queue, setResult, onConsolidation, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals) {
464
486
  const prepareResume = resume
465
487
  ? {
466
488
  leafId: resume.cp.leafId,
@@ -569,6 +591,10 @@ export class Runner {
569
591
  const effectiveMaxTurns = resolveMaxTurns(spec.limits);
570
592
  const tracer = spec.tracer ?? this.deps.tracer;
571
593
  const taskId = spec.taskId ?? prepared.sessionId;
594
+ if (taskIdRef) {
595
+ taskIdRef.current = taskId;
596
+ taskIdRef.sessionId = prepared.sessionId;
597
+ }
572
598
  const taskStart = Date.now();
573
599
  void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, taskId)).catch(() => { });
574
600
  const recordDegraded = (info, toModel) => {
@@ -783,6 +809,14 @@ export class Runner {
783
809
  }
784
810
  }
785
811
  let nudgeIdx = 0;
812
+ const buildFinalizeText = () => `<system-reminder>[deadline] the wall-clock budget is exhausted — this is the FINAL turn. ` +
813
+ `Write your best current work to the deliverable path NOW, even if incomplete — persist to disk FIRST; everything else comes after. ` +
814
+ `If a better version exists anywhere else (a draft, scratch or temp file), copy it to the deliverable path now. ` +
815
+ `Do NOT open new work, new investigations, or long commands; write out and stop.` +
816
+ (finalVerificationOn
817
+ ? ` After writing, if seconds permit, confirm the deliverable file actually EXISTS at the required path with your latest content.`
818
+ : "") +
819
+ `</system-reminder>`;
786
820
  const timeout = startTimeout(prepared.harness, prepared.abortController, spec.limits?.timeoutSec, prepared.suspendForResource !== undefined);
787
821
  const parentToolCallId = internals?.parentToolCallId;
788
822
  const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: taskId } : { eventId: uuidv7() };
@@ -975,6 +1009,15 @@ export class Runner {
975
1009
  }
976
1010
  if (m.role === "assistant" && m.errorMessage === WALLTIME_CUTOFF_MESSAGE) {
977
1011
  callCutoffs += 1;
1012
+ if (prepared.callCapRef)
1013
+ prepared.callCapRef.finalizeMode = true;
1014
+ if (!finalizeInjected) {
1015
+ const finalizeText = buildFinalizeText();
1016
+ void prepared.harness.steer(finalizeText).catch(() => { });
1017
+ finalizeInjected = true;
1018
+ nudgeIdx = nudgeSchedule.length;
1019
+ queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
1020
+ }
978
1021
  }
979
1022
  if (m.role === "assistant") {
980
1023
  const rep = m.repetition;
@@ -1430,14 +1473,7 @@ export class Runner {
1430
1473
  finalizeInjected = true;
1431
1474
  prepared.callCapRef.finalizeMode = true;
1432
1475
  nudgeIdx = nudgeSchedule.length;
1433
- const finalizeText = `<system-reminder>[deadline] the wall-clock budget is exhausted — this is the FINAL turn. ` +
1434
- `Write your best current work to the deliverable path NOW, even if incomplete — persist to disk FIRST; everything else comes after. ` +
1435
- `If a better version exists anywhere else (a draft, scratch or temp file), copy it to the deliverable path now. ` +
1436
- `Do NOT open new work, new investigations, or long commands; write out and stop.` +
1437
- (finalVerificationOn
1438
- ? ` After writing, if seconds permit, confirm the deliverable file actually EXISTS at the required path with your latest content.`
1439
- : "") +
1440
- `</system-reminder>`;
1476
+ const finalizeText = buildFinalizeText();
1441
1477
  void prepared.harness.steer(finalizeText).catch(() => { });
1442
1478
  boundarySteered = true;
1443
1479
  queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
@@ -1990,13 +2026,17 @@ export class Runner {
1990
2026
  }
1991
2027
  const capShrinks = prepared.callCapRef?.shrinks ?? 0;
1992
2028
  const toolClamps = prepared.callCapRef?.toolClamps ?? 0;
1993
- if (finalizeInjected || nudgesSent > 0 || capShrinks > 0 || callCutoffs > 0 || toolClamps > 0 || finalVerifyInjections > 0 || attachmentsInjected > 0 || repetitionCuts > 0 || repetitionSpared > 0) {
2029
+ const toolCuts = prepared.callCapRef?.toolCuts ?? 0;
2030
+ const pendingCutTools = prepared.callCapRef?.pendingCutTools ?? 0;
2031
+ if (finalizeInjected || nudgesSent > 0 || capShrinks > 0 || callCutoffs > 0 || toolClamps > 0 || toolCuts > 0 || pendingCutTools > 0 || finalVerifyInjections > 0 || attachmentsInjected > 0 || repetitionCuts > 0 || repetitionSpared > 0) {
1994
2032
  stats.mechanisms = {
1995
2033
  ...(finalizeInjected ? { finalizeInjected: true } : {}),
1996
2034
  ...(nudgesSent > 0 ? { nudgesSent } : {}),
1997
2035
  ...(capShrinks > 0 ? { capShrinks } : {}),
1998
2036
  ...(callCutoffs > 0 ? { callCutoffs } : {}),
1999
2037
  ...(toolClamps > 0 ? { toolClamps } : {}),
2038
+ ...(toolCuts > 0 ? { toolCuts } : {}),
2039
+ ...(pendingCutTools > 0 ? { pendingCutTools } : {}),
2000
2040
  ...(finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
2001
2041
  ...(finalVerifyInjections > 0 ? { finalVerifyInjections } : {}),
2002
2042
  ...(attachmentsInjected > 0 ? { attachmentsInjected } : {}),