@jmanuelcorral/openteam 0.27.0 → 0.28.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.
Files changed (76) hide show
  1. package/.opencode/openteam.example.json +7 -1
  2. package/README.es.md +22 -4
  3. package/README.md +28 -4
  4. package/dist/certificates/graph-release-certificate.json +2 -2
  5. package/dist/certificates/graph-shadow-certificate.json +2 -2
  6. package/dist/certificates/opencode-2.0.5/graph-release-certificate.json +2 -2
  7. package/dist/certificates/opencode-2.0.5/graph-shadow-certificate.json +2 -2
  8. package/dist/cli/opencodeVersion.d.ts.map +1 -1
  9. package/dist/cli.js +1641 -258
  10. package/dist/commands/setup.d.ts.map +1 -1
  11. package/dist/config/errors.d.ts.map +1 -1
  12. package/dist/config/graphFeatureGate.d.ts +3 -3
  13. package/dist/config/schema.d.ts +18 -0
  14. package/dist/config/schema.d.ts.map +1 -1
  15. package/dist/console/agentView.d.ts +13 -0
  16. package/dist/console/agentView.d.ts.map +1 -0
  17. package/dist/console/assets.d.ts.map +1 -1
  18. package/dist/console/inspector.d.ts.map +1 -1
  19. package/dist/console/refresh.d.ts +1 -1
  20. package/dist/console/refresh.d.ts.map +1 -1
  21. package/dist/console/render.d.ts +1 -0
  22. package/dist/console/render.d.ts.map +1 -1
  23. package/dist/contract/opencode.d.ts +13 -12
  24. package/dist/contract/opencode.d.ts.map +1 -1
  25. package/dist/graph/certificate.d.ts.map +1 -1
  26. package/dist/graph/schema.d.ts +6 -0
  27. package/dist/graph/schema.d.ts.map +1 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1260 -434
  30. package/dist/lifecycle/contracts.d.ts +2 -2
  31. package/dist/lifecycle/contracts.d.ts.map +1 -1
  32. package/dist/lifecycle/schema.d.ts +6 -0
  33. package/dist/lifecycle/schema.d.ts.map +1 -1
  34. package/dist/local/concurrency.d.ts +1 -1
  35. package/dist/local/concurrency.d.ts.map +1 -1
  36. package/dist/messages/agentView.d.ts +77 -0
  37. package/dist/messages/agentView.d.ts.map +1 -0
  38. package/dist/messages/consoleDetail.d.ts +9 -0
  39. package/dist/messages/consoleDetail.d.ts.map +1 -1
  40. package/dist/messages/nativeHost.d.ts +1 -0
  41. package/dist/messages/nativeHost.d.ts.map +1 -1
  42. package/dist/messages/orchestration.d.ts +26 -0
  43. package/dist/messages/orchestration.d.ts.map +1 -1
  44. package/dist/opencodeArtifacts/orchestratorAgent.d.ts.map +1 -1
  45. package/dist/orchestrator/coordinator.d.ts +13 -1
  46. package/dist/orchestrator/coordinator.d.ts.map +1 -1
  47. package/dist/orchestrator/outputContract.d.ts +3 -0
  48. package/dist/orchestrator/outputContract.d.ts.map +1 -1
  49. package/dist/orchestrator/reportDelivery.d.ts +13 -0
  50. package/dist/orchestrator/reportDelivery.d.ts.map +1 -0
  51. package/dist/orchestrator/subsessions.d.ts +5 -0
  52. package/dist/orchestrator/subsessions.d.ts.map +1 -1
  53. package/dist/orchestrator/taskEffort.d.ts +39 -0
  54. package/dist/orchestrator/taskEffort.d.ts.map +1 -0
  55. package/dist/plugin/capture.d.ts +3 -0
  56. package/dist/plugin/capture.d.ts.map +1 -1
  57. package/dist/plugin/nativeHost.d.ts.map +1 -1
  58. package/dist/plugin/nativeHostContext.d.ts +1 -1
  59. package/dist/plugin/nativeHostContext.d.ts.map +1 -1
  60. package/dist/plugin/nativeSessions.d.ts +9 -0
  61. package/dist/plugin/nativeSessions.d.ts.map +1 -1
  62. package/dist/plugin/orchestrateTool.d.ts +6 -0
  63. package/dist/plugin/orchestrateTool.d.ts.map +1 -1
  64. package/dist/plugin/reportTool.d.ts +4 -0
  65. package/dist/plugin/reportTool.d.ts.map +1 -0
  66. package/dist/plugin/runtimeContext.d.ts +14 -0
  67. package/dist/plugin/runtimeContext.d.ts.map +1 -1
  68. package/dist/plugin/toolcalls.d.ts +9 -1
  69. package/dist/plugin/toolcalls.d.ts.map +1 -1
  70. package/dist/storage/scopedArtifacts.d.ts +7 -0
  71. package/dist/storage/scopedArtifacts.d.ts.map +1 -0
  72. package/dist/telemetry/aggregate.d.ts +7 -0
  73. package/dist/telemetry/aggregate.d.ts.map +1 -1
  74. package/dist/telemetry/events.d.ts +68 -2
  75. package/dist/telemetry/events.d.ts.map +1 -1
  76. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { exec, execFile as execFile4 } from "node:child_process";
7
7
  import { randomUUID as randomUUID5 } from "node:crypto";
8
8
  import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile2 } from "node:fs/promises";
9
9
  import { createRequire as createRequire2 } from "node:module";
10
- import { dirname as dirname11, resolve as resolve17 } from "node:path";
10
+ import { dirname as dirname12, resolve as resolve19 } from "node:path";
11
11
  import { promisify as promisify3 } from "node:util";
12
12
  import {
13
13
  confirm as clackConfirm,
@@ -577,6 +577,15 @@ function migrateConfig(raw) {
577
577
  var consoleDetailMessages = {
578
578
  runtimeStateCleanupFailed: "The Console runtime state could not be published and its staging file could not be removed.",
579
579
  requestFailed: "The local opencode request failed.",
580
+ usageUnavailable: "Unavailable / partial",
581
+ usageCaution: "The host did not report trustworthy usage for every message. Counters are not confirmed totals; no token rate or real savings can be inferred.",
582
+ timingUnknown: "Unknown",
583
+ estimatedCost: "Estimated cost",
584
+ estimatedBaseline: "Estimated baseline",
585
+ estimatedSavings: "Estimated savings",
586
+ estimatedInput: "Estimated tokens in",
587
+ estimatedOutput: "Estimated tokens out",
588
+ estimatesNotUsage: "Routing estimates are separate from host-reported usage; they are not measured spend or proven savings.",
580
589
  nativeToolFailed: "The native tool execution failed.",
581
590
  follow: "Follow new activity",
582
591
  metadataOnly: "Lifecycle and telemetry stay metadata-only.",
@@ -832,7 +841,13 @@ var OrchestratorConfigSchema = z2.object({
832
841
  roles: OrchestratorRolesSchema,
833
842
  primary: AgentExecutionPolicySchema.optional(),
834
843
  maxRetries: z2.number().int().min(0).optional(),
835
- maxModelsPerNode: z2.number().int().positive().optional()
844
+ maxModelsPerNode: z2.number().int().positive().optional(),
845
+ taskBudget: z2.object({
846
+ maxBatches: z2.number().int().min(1).max(1000).optional(),
847
+ maxAttempts: z2.number().int().min(1).max(1e4).optional(),
848
+ maxDurationMs: z2.number().int().min(1000).max(86400000).optional(),
849
+ maxDepth: z2.number().int().min(0).max(32).optional()
850
+ }).strict().optional()
836
851
  }).strict();
837
852
  var defaultFrontierModel = {
838
853
  providerID: "google",
@@ -1497,6 +1512,32 @@ function withReportedModelSlots(runtime, reported, aliases) {
1497
1512
  }
1498
1513
 
1499
1514
  // src/messages/orchestration.ts
1515
+ var reportDeliveryMessages = {
1516
+ description: "Submit the active worker's substantive findings to its assigned durable report. No arbitrary paths or other sessions are accepted. This does not replace the coordinator's completion checks.",
1517
+ status: "Whether the assigned work is done or needs another bounded pass.",
1518
+ findings: "Concrete findings, changes, evidence and remaining work; at most 32768 characters.",
1519
+ assignmentConflict: "A report assignment is already active for this session.",
1520
+ noAssignment: "No active coordinator-owned report assignment exists for this session.",
1521
+ invalidReport: "Report delivery requires substantive findings and a valid done/continue status within 32768 characters.",
1522
+ unsafePath: "The private artifact path is unavailable, linked or outside the allowed boundary.",
1523
+ workspaceMismatch: "Orchestration directory must match the trusted host workspace; a model-authored directory cannot change report or session scope.",
1524
+ submitted: (path) => `Report persisted at ${path}. The coordinator will independently validate current, substantive completion.`
1525
+ };
1526
+ var taskEffortMessages = {
1527
+ cancelled: "Task execution was cancelled; no replacement attempt was dispatched.",
1528
+ createDeadline: "task-budget-exhausted: duration. Session creation did not settle before the deadline; no prompt was dispatched and retry is disabled.",
1529
+ ledgerInvalid: "The cumulative task-effort ledger is invalid or unavailable; orchestration is stopped rather than resetting its allowance.",
1530
+ exhausted: (reason) => `task-budget-exhausted: ${reason}. Keep existing work and reports. Another batch or worker cannot reset this task's allowance; a new root user turn starts a new task.`,
1531
+ summary: (batches, attempts, remainingMs, remainingBatches, remainingAttempts) => `Task effort: ${batches} batch(es), ${attempts} attempt(s), ${Math.max(0, Math.floor(remainingMs / 1000))}s remaining; ${remainingBatches} batch(es) and ${remainingAttempts} attempt(s) available.`
1532
+ };
1533
+ var executionPhaseMessages = {
1534
+ "model-request": "Model request",
1535
+ "tool-input": "Preparing tool input",
1536
+ "tool-execution": "Executing a tool",
1537
+ "report-validation": "Validating durable report",
1538
+ idle: "Session idle",
1539
+ unknown: "Session phase unavailable"
1540
+ };
1500
1541
  var HOST_SESSION_REQUIRED = "Orchestration requires a nonempty host ToolContext sessionID.";
1501
1542
  var PARENT_OVERRIDE_IGNORED = "parentSessionID override ignored: the trusted host ToolContext session owns this orchestration and its children. Programmatic callers must supply their parent as the ToolContext sessionID.";
1502
1543
  var PARENT_SESSION_DESCRIPTION = "Legacy parent reference. Omit it: the trusted host ToolContext session is the parent. A differing payload value is ignored with an advisory.";
@@ -2359,15 +2400,29 @@ var MessageEventSchema = EventBaseSchema.extend({
2359
2400
  tokensReasoning: z5.number().int().min(0),
2360
2401
  tokensCacheRead: z5.number().int().min(0),
2361
2402
  tokensCacheWrite: z5.number().int().min(0),
2362
- durationMs: z5.number().int().min(0)
2403
+ durationMs: z5.number().int().min(0),
2404
+ usageStatus: z5.enum(["reported", "missing", "zero-unverified"]).optional(),
2405
+ costStatus: z5.enum(["reported", "unavailable"]).optional(),
2406
+ finish: z5.enum([
2407
+ "stop",
2408
+ "length",
2409
+ "tool-calls",
2410
+ "content-filter",
2411
+ "error",
2412
+ "unknown"
2413
+ ]).optional(),
2414
+ truncatedToolCall: z5.boolean().optional()
2363
2415
  });
2364
2416
  var ToolcallEventSchema = EventBaseSchema.extend({
2365
2417
  type: z5.literal("toolcall"),
2366
2418
  tool: z5.string().min(1),
2367
2419
  callID: z5.string().min(1),
2368
2420
  agent: z5.string().min(1).optional(),
2369
- durationMs: z5.number().int().min(0),
2421
+ durationMs: z5.number().int().min(0).nullable(),
2370
2422
  ok: z5.boolean(),
2423
+ executed: z5.boolean().optional(),
2424
+ providerExecuted: z5.boolean().optional(),
2425
+ failureKind: z5.enum(["input-invalid", "execution", "unknown"]).optional(),
2371
2426
  title: z5.string().optional()
2372
2427
  });
2373
2428
  var MeetingEventSchema = EventBaseSchema.extend({
@@ -2397,7 +2452,15 @@ var ActivityEventSchema = EventBaseSchema.extend({
2397
2452
  type: z5.literal("activity"),
2398
2453
  kind: z5.enum(["route", "agent", "decision", "commit"]),
2399
2454
  agent: z5.string().min(1).optional(),
2400
- summary: z5.string().min(1)
2455
+ summary: z5.string().min(1),
2456
+ phase: z5.enum([
2457
+ "model-request",
2458
+ "tool-input",
2459
+ "tool-execution",
2460
+ "report-validation",
2461
+ "idle",
2462
+ "unknown"
2463
+ ]).optional()
2401
2464
  });
2402
2465
  var SessionEndpointEventSchema = EventBaseSchema.extend({
2403
2466
  type: z5.literal("session-endpoint"),
@@ -2860,11 +2923,17 @@ function reportHasSubstance(text) {
2860
2923
  function roleReportPath(batchID, roleID) {
2861
2924
  return `${ROLE_REPORTS_ROOT}/${reportPathSegment(batchID)}/${reportPathSegment(roleID)}.md`;
2862
2925
  }
2863
- function outputContractInstruction(reportPath) {
2926
+ function outputContractInstruction(reportPath, structuredReport) {
2864
2927
  return [
2865
2928
  "## Output contract (openteam)",
2866
2929
  "",
2867
- "Write your complete findings to the report file below using the `write`",
2930
+ ...structuredReport ? [
2931
+ "Prefer `openteam-submit-report` with substantive `findings` and `status`",
2932
+ "(`done` or `continue`): openteam persists only your assigned report and",
2933
+ "confirms the write. Keep submissions concise; split large work into small",
2934
+ "tool calls. If that tool is unavailable, write your complete findings"
2935
+ ] : ["Write your complete findings"],
2936
+ "to the report file below using the `write`",
2868
2937
  "tool (create parent directories as needed), and keep it updated with",
2869
2938
  "`edit` as you progress. This file is the ONLY channel your work reaches",
2870
2939
  "the coordinator: anything you do not write there is discarded and lost,",
@@ -2907,9 +2976,33 @@ function composeSubsessionPrompt(input) {
2907
2976
  ].join(`
2908
2977
  `) : "";
2909
2978
  return {
2910
- prompt: `${input.prompt}${handoff}
2979
+ prompt: `${input.prompt}${handoff}${input.truncated !== true ? "" : [
2980
+ "",
2981
+ "",
2982
+ "## Truncated tool-call recovery",
2983
+ "",
2984
+ "The previous response ended at the output limit and a tool call was invalid",
2985
+ "before execution. Use smaller, bounded tool calls and concise findings.",
2986
+ "Inspect the existing workspace and assigned report before continuing.",
2987
+ "Never repair and execute the malformed JSON. Produce a fresh, complete",
2988
+ "call only for work that is still necessary. Do not increase model limits",
2989
+ "or change the execution domain. This continuation uses the same pass budget."
2990
+ ].join(`
2991
+ `)}${input.repair === undefined ? "" : [
2992
+ "",
2993
+ "",
2994
+ "## Report delivery repair",
2995
+ "",
2996
+ `Previous pass did not prove completion: ${input.repair}.`,
2997
+ "Continue the SAME goal and existing workspace, not a new orchestration.",
2998
+ "Read the assigned report and inspect the existing work first. Do not redo",
2999
+ "completed implementation merely because its delivery failed. Persist the",
3000
+ input.structuredReport === false ? "missing findings in the assigned report; do not just promise a report." : "missing findings with openteam-submit-report; do not just promise a report.",
3001
+ "This repair consumes the existing node pass allowance."
3002
+ ].join(`
3003
+ `)}
2911
3004
 
2912
- ${outputContractInstruction(reportPath)}`,
3005
+ ${outputContractInstruction(reportPath, input.structuredReport !== false)}`,
2913
3006
  reportPath
2914
3007
  };
2915
3008
  }
@@ -3286,15 +3379,21 @@ function promptArgs(sessionID, req, signal) {
3286
3379
  }
3287
3380
  return args;
3288
3381
  }
3289
- async function withTimeout(promise, timeoutMs) {
3290
- if (timeoutMs === undefined) {
3382
+ async function withTimeout(promise, timeoutMs, signal) {
3383
+ if (timeoutMs === undefined && signal === undefined) {
3291
3384
  return { kind: "settled", value: await promise };
3292
3385
  }
3293
3386
  let timeoutID;
3387
+ let abortListener;
3294
3388
  const timeout = new Promise((resolve) => {
3295
- timeoutID = setTimeout(() => {
3296
- resolve({ kind: "timeout" });
3297
- }, timeoutMs);
3389
+ if (timeoutMs !== undefined)
3390
+ timeoutID = setTimeout(() => {
3391
+ resolve({ kind: "timeout" });
3392
+ }, timeoutMs);
3393
+ abortListener = () => resolve({ kind: "aborted" });
3394
+ signal?.addEventListener("abort", abortListener, { once: true });
3395
+ if (signal?.aborted)
3396
+ abortListener();
3298
3397
  });
3299
3398
  try {
3300
3399
  return await Promise.race([
@@ -3303,6 +3402,8 @@ async function withTimeout(promise, timeoutMs) {
3303
3402
  ]);
3304
3403
  } finally {
3305
3404
  clearTimeout(timeoutID);
3405
+ if (abortListener !== undefined)
3406
+ signal?.removeEventListener("abort", abortListener);
3306
3407
  }
3307
3408
  }
3308
3409
  async function beforeDeadline(request, deadline, deps) {
@@ -3424,7 +3525,35 @@ async function confirmPromptRequestSettled(isSettled, deps, deadline) {
3424
3525
  async function runSubsession(client, req, deps) {
3425
3526
  let sessionID;
3426
3527
  try {
3427
- const created = await client.session.create(createArgs(req));
3528
+ deps.signal?.throwIfAborted();
3529
+ const createController = new AbortController;
3530
+ const createSignal = deps.signal === undefined ? createController.signal : AbortSignal.any([deps.signal, createController.signal]);
3531
+ const remaining = deps.deadlineAt === undefined ? undefined : Math.max(0, deps.deadlineAt - deps.now());
3532
+ if (remaining === 0)
3533
+ return {
3534
+ success: false,
3535
+ roleID: req.roleID,
3536
+ model: req.model,
3537
+ failureStage: "create",
3538
+ failureReason: taskEffortMessages.createDeadline,
3539
+ retrySafe: false
3540
+ };
3541
+ const createOutcome = await withTimeout(req.existingSessionID === undefined ? client.session.create(remaining === undefined && deps.signal === undefined ? createArgs(req) : { ...createArgs(req), signal: createSignal }) : Promise.resolve({
3542
+ data: { id: req.existingSessionID },
3543
+ error: undefined
3544
+ }), remaining, deps.signal);
3545
+ if (createOutcome.kind !== "settled") {
3546
+ createController.abort();
3547
+ return {
3548
+ success: false,
3549
+ roleID: req.roleID,
3550
+ model: req.model,
3551
+ failureStage: "create",
3552
+ failureReason: createOutcome.kind === "timeout" ? taskEffortMessages.createDeadline : taskEffortMessages.cancelled,
3553
+ retrySafe: false
3554
+ };
3555
+ }
3556
+ const created = createOutcome.value;
3428
3557
  if (created.error !== undefined) {
3429
3558
  const normalizedFailure = isNormalizedSessionFailure(created.error) ? created.error : undefined;
3430
3559
  return {
@@ -3451,19 +3580,32 @@ async function runSubsession(client, req, deps) {
3451
3580
  try {
3452
3581
  await deps.onSessionCreated?.(sessionID);
3453
3582
  } catch {}
3583
+ await deps.prepareSession?.(sessionID);
3584
+ deps.signal?.throwIfAborted();
3585
+ if (deps.deadlineAt !== undefined && deps.now() >= deps.deadlineAt)
3586
+ return {
3587
+ success: false,
3588
+ sessionID,
3589
+ roleID: req.roleID,
3590
+ model: req.model,
3591
+ failureStage: "create",
3592
+ failureReason: taskEffortMessages.createDeadline,
3593
+ retrySafe: false
3594
+ };
3454
3595
  } catch (error) {
3455
3596
  return {
3456
3597
  success: false,
3457
3598
  roleID: req.roleID,
3458
3599
  model: req.model,
3459
3600
  failureStage: "create",
3460
- failureReason: errorReason(error),
3461
- retrySafe: true
3601
+ failureReason: deps.signal?.aborted ? taskEffortMessages.cancelled : errorReason(error),
3602
+ retrySafe: deps.signal?.aborted !== true
3462
3603
  };
3463
3604
  }
3464
3605
  const promptController = new AbortController;
3606
+ const promptSignal = deps.signal === undefined ? promptController.signal : AbortSignal.any([deps.signal, promptController.signal]);
3465
3607
  let promptSettled = false;
3466
- const promptPromise = client.session.prompt(promptArgs(sessionID, req, promptController.signal)).then((value) => {
3608
+ const promptPromise = client.session.prompt(promptArgs(sessionID, req, promptSignal)).then((value) => {
3467
3609
  promptSettled = true;
3468
3610
  return value;
3469
3611
  }, (error) => {
@@ -3471,8 +3613,9 @@ async function runSubsession(client, req, deps) {
3471
3613
  throw error;
3472
3614
  });
3473
3615
  try {
3474
- const outcome = await withTimeout(promptPromise, deps.timeoutMs);
3475
- if (outcome.kind === "timeout") {
3616
+ const timeoutMs = deps.deadlineAt === undefined ? deps.timeoutMs : Math.min(deps.timeoutMs ?? Infinity, Math.max(0, deps.deadlineAt - deps.now()));
3617
+ const outcome = await withTimeout(promptPromise, timeoutMs, deps.signal);
3618
+ if (outcome.kind !== "settled") {
3476
3619
  promptController.abort();
3477
3620
  const stopDeadline = deps.now() + Math.max(0, deps.abortGraceMs);
3478
3621
  const stopped = await confirmSessionStopped(client, sessionID, req.directory, deps, stopDeadline);
@@ -3483,8 +3626,8 @@ async function runSubsession(client, req, deps) {
3483
3626
  roleID: req.roleID,
3484
3627
  model: req.model,
3485
3628
  failureStage: "prompt",
3486
- failureReason: !stopped.confirmed ? promptStopUnconfirmed(stopped.cause) : requestSettled ? promptTimedOut(deps.timeoutMs ?? 0) : promptStopUnconfirmed("request-unsettled"),
3487
- retrySafe: stopped.confirmed && requestSettled
3629
+ failureReason: !stopped.confirmed ? promptStopUnconfirmed(stopped.cause) : requestSettled ? deps.deadlineAt !== undefined && deps.now() >= deps.deadlineAt ? taskEffortMessages.exhausted("duration") : outcome.kind === "aborted" ? taskEffortMessages.cancelled : promptTimedOut(deps.timeoutMs ?? 0) : promptStopUnconfirmed("request-unsettled"),
3630
+ retrySafe: stopped.confirmed && requestSettled && outcome.kind !== "aborted" && (deps.deadlineAt === undefined || deps.now() < deps.deadlineAt)
3488
3631
  };
3489
3632
  }
3490
3633
  const prompted = outcome.value;
@@ -3517,8 +3660,8 @@ async function runSubsession(client, req, deps) {
3517
3660
  roleID: req.roleID,
3518
3661
  model: req.model,
3519
3662
  failureStage: "prompt",
3520
- failureReason: stopped.confirmed ? errorReason(error) : promptStopUnconfirmed(stopped.cause),
3521
- retrySafe: stopped.confirmed
3663
+ failureReason: stopped.confirmed ? deps.signal?.aborted ? taskEffortMessages.cancelled : errorReason(error) : promptStopUnconfirmed(stopped.cause),
3664
+ retrySafe: stopped.confirmed && deps.signal?.aborted !== true
3522
3665
  };
3523
3666
  }
3524
3667
  }
@@ -3714,12 +3857,15 @@ var TelemetryFailureClassSchema = z7.enum([
3714
3857
  "prompt-auth",
3715
3858
  "prompt-content-filter",
3716
3859
  "prompt-context-length",
3860
+ "prompt-output-truncated",
3861
+ "budget-exhausted",
3717
3862
  "prompt-validation",
3718
3863
  "prompt-connection",
3719
3864
  "prompt-server",
3720
3865
  "unknown"
3721
3866
  ]);
3722
3867
  var FAILURE_CAUSE_RULES = [
3868
+ [["prompt-output-truncated"], "prompt-output-truncated"],
3723
3869
  [
3724
3870
  [
3725
3871
  "rate limit",
@@ -3822,6 +3968,8 @@ function matchFailureCause(lowerReason) {
3822
3968
  }
3823
3969
  function classifyFailureForTelemetry(failureReason, failureStage) {
3824
3970
  const lower = failureReason.toLowerCase();
3971
+ if (lower.startsWith("task-budget-exhausted:"))
3972
+ return "budget-exhausted";
3825
3973
  if (TIMEOUT_NEEDLES.some((needle) => lower.includes(needle))) {
3826
3974
  return "prompt-timeout";
3827
3975
  }
@@ -3900,6 +4048,8 @@ async function recordLifecycle(lifecycle, event) {
3900
4048
  await lifecycle.recorder.append(event);
3901
4049
  }
3902
4050
  function lifecycleFailureFor(subsession) {
4051
+ if (subsession.failureReason?.startsWith("task-budget-exhausted:"))
4052
+ return { class: "budget-exhausted", retryable: false };
3903
4053
  const normalized = subsession.normalizedFailure;
3904
4054
  if (normalized !== undefined) {
3905
4055
  return {
@@ -3969,7 +4119,7 @@ function buildNodeBudgetExhaustedEvent(roleID, passCount, sessionID, ts) {
3969
4119
  passCount
3970
4120
  };
3971
4121
  }
3972
- async function runRoleTaskWithCorrelation(input, deps, correlation, batch, routed, handoff, reportBatchID, attemptLifecycle) {
4122
+ async function runRoleTaskWithCorrelation(input, deps, correlation, batch, routed, handoff, reportBatchID, attemptLifecycle, repair, truncated, existingSessionID) {
3973
4123
  const role = roleFor(input);
3974
4124
  if (routed.routeKind === "blocked") {
3975
4125
  throw new Error(coordinatorRoutingBlocked(input.roleID, routed.blockReason));
@@ -3980,13 +4130,18 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch, route
3980
4130
  prompt: input.prompt,
3981
4131
  roleID: input.roleID,
3982
4132
  batchID: reportBatchID,
3983
- handoff
4133
+ handoff,
4134
+ structuredReport: deps.reportDelivery !== undefined,
4135
+ ...repair === undefined ? {} : { repair },
4136
+ ...truncated === true ? { truncated: true } : {}
3984
4137
  });
3985
4138
  const request = {
3986
4139
  roleID: input.roleID,
3987
4140
  model: decision2.selected,
3988
4141
  prompt: composed.prompt
3989
4142
  };
4143
+ if (existingSessionID !== undefined)
4144
+ request.existingSessionID = existingSessionID;
3990
4145
  if (input.parentSessionID !== undefined) {
3991
4146
  request.parentSessionID = input.parentSessionID;
3992
4147
  }
@@ -4004,11 +4159,17 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch, route
4004
4159
  if (input.directory !== undefined) {
4005
4160
  request.directory = input.directory;
4006
4161
  }
4162
+ let reportLease;
4007
4163
  const subsessionDeps = {
4008
4164
  abortGraceMs: deps.abortGraceMs,
4009
4165
  statusPollMs: deps.statusPollMs,
4010
4166
  now: deps.now,
4011
4167
  delay: deps.delay,
4168
+ prepareSession: async (sessionID) => {
4169
+ if (deps.taskEffort !== undefined)
4170
+ deps.taskEffort.ledger.bindChild(deps.taskEffort.ref, sessionID);
4171
+ reportLease = deps.reportDelivery?.open(sessionID, reportBatchID, input.roleID);
4172
+ },
4012
4173
  ...deps.timeoutMs === undefined ? {} : { timeoutMs: deps.timeoutMs },
4013
4174
  ...attemptLifecycle === undefined ? {} : {
4014
4175
  onSessionCreated: (sessionID) => recordLifecycle(attemptLifecycle.lifecycle, {
@@ -4053,27 +4214,66 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch, route
4053
4214
  }
4054
4215
  let release;
4055
4216
  let queueObservation;
4056
- if (runtimeId !== undefined && deps.runtimeLimiter !== undefined) {
4057
- const limiter = deps.runtimeLimiter;
4058
- const priority = priorityForRole(role);
4059
- const dispatchRuntime = configuredRuntime(runtimeId, input.config) ?? { id: runtimeId };
4060
- const aliases = localModelAliases(decision2.selected.providerID, input.availableModels);
4061
- const poolKey = slotPoolKey(withReportedModelSlots(dispatchRuntime, deps.reportedModelSlots?.(runtimeId), aliases), aliases.get(decision2.selected.modelID) ?? decision2.selected.modelID);
4062
- release = limiter.tryAcquire(poolKey, priority);
4063
- let waitedMs = 0;
4064
- if (release === undefined) {
4065
- const acquisition = await limiter.acquire(poolKey, priority);
4066
- release = acquisition.release;
4067
- waitedMs = acquisition.waitedMs;
4068
- }
4069
- queueObservation = {
4070
- runtimeId,
4071
- waitedMs,
4072
- activeCount: limiter.activeCount(poolKey),
4073
- queuedCount: limiter.queuedCount(poolKey)
4217
+ const admission = deps.taskEffort?.ledger.check(deps.taskEffort.ref);
4218
+ async function denied(reason) {
4219
+ const subsession = {
4220
+ roleID: input.roleID,
4221
+ model: decision2.selected,
4222
+ success: false,
4223
+ failureStage: "create",
4224
+ retrySafe: false,
4225
+ failureReason: reason
4074
4226
  };
4227
+ const record = buildRecord(input, decision2, subsession, deps, correlation.decisionID, correlation.batchID);
4228
+ await safeEmit(deps.sink, correlatedCostRecordToRouteEvent(record));
4229
+ return { decision: decision2, subsession, record, reportPath: composed.reportPath };
4075
4230
  }
4231
+ if (admission?.ok === false)
4232
+ return denied(taskEffortMessages.exhausted(admission.reason));
4233
+ if (deps.signal?.aborted)
4234
+ return denied(taskEffortMessages.cancelled);
4235
+ const queueController = new AbortController;
4236
+ const queueSignal = deps.signal === undefined ? queueController.signal : AbortSignal.any([deps.signal, queueController.signal]);
4237
+ const queueDeadline = admission?.ok === true ? setTimeout(() => queueController.abort(), admission.remainingMs) : undefined;
4076
4238
  try {
4239
+ if (runtimeId !== undefined && deps.runtimeLimiter !== undefined) {
4240
+ const limiter = deps.runtimeLimiter;
4241
+ const priority = priorityForRole(role);
4242
+ const dispatchRuntime = configuredRuntime(runtimeId, input.config) ?? { id: runtimeId };
4243
+ const aliases = localModelAliases(decision2.selected.providerID, input.availableModels);
4244
+ const poolKey = slotPoolKey(withReportedModelSlots(dispatchRuntime, deps.reportedModelSlots?.(runtimeId), aliases), aliases.get(decision2.selected.modelID) ?? decision2.selected.modelID);
4245
+ release = limiter.tryAcquire(poolKey, priority);
4246
+ let waitedMs = 0;
4247
+ if (release === undefined) {
4248
+ let acquisition;
4249
+ try {
4250
+ acquisition = await limiter.acquire(poolKey, priority, queueSignal);
4251
+ } catch (error) {
4252
+ if (!queueSignal.aborted)
4253
+ throw error;
4254
+ return denied(queueController.signal.aborted ? taskEffortMessages.exhausted("duration") : taskEffortMessages.cancelled);
4255
+ }
4256
+ release = acquisition.release;
4257
+ waitedMs = acquisition.waitedMs;
4258
+ }
4259
+ queueObservation = {
4260
+ runtimeId,
4261
+ waitedMs,
4262
+ activeCount: limiter.activeCount(poolKey),
4263
+ queuedCount: limiter.queuedCount(poolKey)
4264
+ };
4265
+ }
4266
+ clearTimeout(queueDeadline);
4267
+ const effort = deps.taskEffort?.ledger.attempt(deps.taskEffort.ref);
4268
+ if (effort?.ok === false) {
4269
+ return denied(taskEffortMessages.exhausted(effort.reason));
4270
+ }
4271
+ if (effort?.ok === true) {
4272
+ subsessionDeps.timeoutMs = Math.min(subsessionDeps.timeoutMs ?? Infinity, effort.remainingMs);
4273
+ subsessionDeps.deadlineAt = deps.now() + effort.remainingMs;
4274
+ }
4275
+ if (deps.signal !== undefined)
4276
+ subsessionDeps.signal = deps.signal;
4077
4277
  if (attemptLifecycle !== undefined) {
4078
4278
  await recordLifecycle(attemptLifecycle.lifecycle, {
4079
4279
  type: "attempt.started",
@@ -4101,8 +4301,17 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch, route
4101
4301
  await safeEmit(deps.sink, buildQueueEvent(queueObservation, sessionID, deps.now(), queueCorrelation));
4102
4302
  }
4103
4303
  }
4104
- return { decision: decision2, subsession, record, reportPath: composed.reportPath };
4304
+ const reportSubmitted = await reportLease?.close();
4305
+ return {
4306
+ decision: decision2,
4307
+ subsession,
4308
+ record,
4309
+ reportPath: composed.reportPath,
4310
+ ...reportSubmitted === undefined ? {} : { reportSubmitted }
4311
+ };
4105
4312
  } finally {
4313
+ clearTimeout(queueDeadline);
4314
+ await reportLease?.close();
4106
4315
  release?.();
4107
4316
  }
4108
4317
  }
@@ -4214,15 +4423,19 @@ async function assessCurrentAttemptReport(input, before, result, deps) {
4214
4423
  done: false
4215
4424
  };
4216
4425
  }
4217
- if (after === before) {
4426
+ if (after === before && result.reportSubmitted !== true) {
4218
4427
  return {
4219
4428
  result: withFailure(gated, REPORT_STALE_REASON, "report"),
4220
4429
  done: false
4221
4430
  };
4222
4431
  }
4432
+ const done = parseReportStatus(after) === "done" && reportHasSubstance(after);
4223
4433
  return {
4224
4434
  result: gated,
4225
- done: parseReportStatus(after) === "done" && reportHasSubstance(after)
4435
+ done,
4436
+ ...!done ? {
4437
+ repairReason: !reportHasSubstance(after) ? "report-without-substance" : parseReportStatus(after) === "continue" ? "report-continue" : "report-status-missing"
4438
+ } : {}
4226
4439
  };
4227
4440
  }
4228
4441
  function failedPassDisposition(subsession) {
@@ -4233,6 +4446,8 @@ function failedPassDisposition(subsession) {
4233
4446
  if (subsession.failureStage === "report")
4234
4447
  return "retry-incomplete";
4235
4448
  if (subsession.normalizedFailure !== undefined) {
4449
+ if (subsession.normalizedFailure.failureClass === "prompt-output-truncated")
4450
+ return "retry-incomplete";
4236
4451
  return subsession.normalizedFailure.retryable ? "retry-transport" : "terminal";
4237
4452
  }
4238
4453
  const failureReason = subsession.failureReason ?? "";
@@ -4332,6 +4547,7 @@ async function runNodeWithIteration(input, deps, correlation, batch, lifecycle)
4332
4547
  let totalPasses = 0;
4333
4548
  let spend = { usd: 0, frontierTokens: 0 };
4334
4549
  let lastResult;
4550
+ let reportRepairReason;
4335
4551
  let escalationReason;
4336
4552
  const reportBatchID = correlation.batchID ?? correlation.decisionID;
4337
4553
  modelLoop:
@@ -4404,14 +4620,19 @@ async function runNodeWithIteration(input, deps, correlation, batch, lifecycle)
4404
4620
  retryIndex: pass - 1,
4405
4621
  selectionCause: lifecycleSelectionCause(totalPasses, modelIndex, retryReason, escalationReason)
4406
4622
  };
4407
- let result = await runRoleTaskWithCorrelation(input, deps, attemptCorrelation, batch, attemptDecision, modelIndex > 0, reportBatchID, attemptLifecycle);
4623
+ let result = await runRoleTaskWithCorrelation(input, deps, attemptCorrelation, batch, attemptDecision, modelIndex > 0, reportBatchID, attemptLifecycle, reportRepairReason, lastResult?.subsession.normalizedFailure?.failureClass === "prompt-output-truncated", pass > 1 && reportRepairReason !== undefined && (lastResult?.subsession.success || lastResult?.subsession.failureStage === "report") && lastResult?.subsession.retrySafe !== false ? lastResult?.subsession.sessionID : undefined);
4408
4624
  totalPasses += 1;
4409
4625
  spend = {
4410
4626
  usd: attemptDecision.estimatedCostUSD === null ? spend.usd : spend.usd + attemptDecision.estimatedCostUSD,
4411
4627
  frontierTokens: spend.frontierTokens + (attemptDecision.routeKind === "frontier" ? (input.task.estimatedInputTokens ?? 0) + (input.task.estimatedOutputTokens ?? 0) : 0)
4412
4628
  };
4629
+ if (result.subsession.sessionID !== undefined)
4630
+ await deps.sessionPhase?.(result.subsession.sessionID, "report-validation");
4413
4631
  const assessment = await assessCurrentAttemptReport(input, before, result, deps);
4632
+ if (result.subsession.sessionID !== undefined)
4633
+ await deps.sessionPhase?.(result.subsession.sessionID, "idle");
4414
4634
  result = assessment.result;
4635
+ reportRepairReason = assessment.repairReason ?? (result.subsession.failureStage === "report" ? result.subsession.failureReason : undefined);
4415
4636
  lastResult = result;
4416
4637
  if (assessment.done) {
4417
4638
  await recordAttemptTerminal(attemptLifecycle, result, "none", true);
@@ -4497,13 +4718,28 @@ async function runRoleTasks(inputs, deps, options = {}) {
4497
4718
  });
4498
4719
  }
4499
4720
  }
4500
- const waveResults = await Promise.all(runnable.map((input) => {
4501
- const correlation = correlationByRole.get(input.roleID);
4502
- if (correlation === undefined) {
4503
- throw new Error(`missing correlation for ${input.roleID}`);
4721
+ const waveController = deps.taskEffort === undefined ? undefined : new AbortController;
4722
+ const waveDeps = waveController === undefined ? deps : {
4723
+ ...deps,
4724
+ signal: deps.signal === undefined ? waveController.signal : AbortSignal.any([deps.signal, waveController.signal])
4725
+ };
4726
+ const waveTasks = runnable.map(async (input) => {
4727
+ try {
4728
+ const correlation = correlationByRole.get(input.roleID);
4729
+ if (correlation === undefined) {
4730
+ throw new Error(`missing correlation for ${input.roleID}`);
4731
+ }
4732
+ return await runNodeWithIteration(input, waveDeps, correlation, batch, lifecycle);
4733
+ } catch (error) {
4734
+ waveController?.abort();
4735
+ throw error;
4504
4736
  }
4505
- return runNodeWithIteration(input, deps, correlation, batch, lifecycle);
4506
- }));
4737
+ });
4738
+ const waveResults = waveController === undefined ? await Promise.all(waveTasks) : (await Promise.allSettled(waveTasks)).map((outcome) => {
4739
+ if (outcome.status === "rejected")
4740
+ throw outcome.reason;
4741
+ return outcome.value;
4742
+ });
4507
4743
  for (const result of waveResults) {
4508
4744
  allResults.push(result);
4509
4745
  if (result.subsession.success) {
@@ -5720,6 +5956,11 @@ function applyRoute(session2, event) {
5720
5956
  }
5721
5957
  function applyMessage(session2, event) {
5722
5958
  session2.messageCount += 1;
5959
+ const unknownUsage = event.usageStatus === undefined ? event.tokensIn + event.tokensOut + event.tokensReasoning + event.tokensCacheRead + event.tokensCacheWrite === 0 : event.usageStatus !== "reported";
5960
+ if (unknownUsage)
5961
+ session2.unavailableUsageMessages = (session2.unavailableUsageMessages ?? 0) + 1;
5962
+ if (event.costStatus === "unavailable" || event.costStatus === undefined && unknownUsage && event.costUSD === 0)
5963
+ session2.unavailableCostMessages = (session2.unavailableCostMessages ?? 0) + 1;
5723
5964
  session2.realCostUSD += event.costUSD;
5724
5965
  session2.tokensIn += event.tokensIn;
5725
5966
  session2.tokensOut += event.tokensOut;
@@ -5731,7 +5972,9 @@ function applyMessage(session2, event) {
5731
5972
  }
5732
5973
  function applyToolUsage(stats, event) {
5733
5974
  stats.count += 1;
5734
- stats.totalDurationMs += event.durationMs;
5975
+ stats.totalDurationMs += event.durationMs ?? 0;
5976
+ if (event.durationMs === null)
5977
+ stats.unknownDurationCount = (stats.unknownDurationCount ?? 0) + 1;
5735
5978
  if (event.ok) {
5736
5979
  stats.ok += 1;
5737
5980
  } else {
@@ -5774,6 +6017,8 @@ function mergeTool(target, source) {
5774
6017
  target.ok += source.ok;
5775
6018
  target.failed += source.failed;
5776
6019
  target.totalDurationMs += source.totalDurationMs;
6020
+ if (source.unknownDurationCount !== undefined)
6021
+ target.unknownDurationCount = (target.unknownDurationCount ?? 0) + source.unknownDurationCount;
5777
6022
  }
5778
6023
  function accumulateTotals(totals, session2) {
5779
6024
  totals.sessions += 1;
@@ -5784,6 +6029,10 @@ function accumulateTotals(totals, session2) {
5784
6029
  totals.estimatedSavingsUSD = addEstimate(totals.estimatedSavingsUSD, session2.estimatedSavingsUSD);
5785
6030
  totals.realCostUSD += session2.realCostUSD;
5786
6031
  totals.messageCount += session2.messageCount;
6032
+ if (session2.unavailableUsageMessages !== undefined)
6033
+ totals.unavailableUsageMessages = (totals.unavailableUsageMessages ?? 0) + session2.unavailableUsageMessages;
6034
+ if (session2.unavailableCostMessages !== undefined)
6035
+ totals.unavailableCostMessages = (totals.unavailableCostMessages ?? 0) + session2.unavailableCostMessages;
5787
6036
  totals.tokensIn += session2.tokensIn;
5788
6037
  totals.tokensOut += session2.tokensOut;
5789
6038
  totals.tokensReasoning += session2.tokensReasoning;
@@ -5850,6 +6099,7 @@ function aggregateEvents(events) {
5850
6099
  const decisions = [];
5851
6100
  const meetings = [];
5852
6101
  const activity = [];
6102
+ const calls = new Map;
5853
6103
  const sessionFor = (sessionID, ts) => {
5854
6104
  const existing = sessions.get(sessionID);
5855
6105
  if (existing === undefined) {
@@ -5870,7 +6120,13 @@ function aggregateEvents(events) {
5870
6120
  applyMessage(sessionFor(event.sessionID, event.ts), event);
5871
6121
  break;
5872
6122
  case "toolcall":
5873
- applyToolcall(sessionFor(event.sessionID, event.ts), event);
6123
+ {
6124
+ const key = `${event.sessionID}\x00${event.callID}`;
6125
+ const previous = calls.get(key);
6126
+ if (previous === undefined || previous.ok && !event.ok || previous.ok === event.ok && (event.durationMs ?? -1) > (previous.durationMs ?? -1)) {
6127
+ calls.set(key, event);
6128
+ }
6129
+ }
5874
6130
  break;
5875
6131
  case "session-endpoint":
5876
6132
  applyEndpoint(sessionFor(event.sessionID, event.ts), event);
@@ -5883,9 +6139,19 @@ function aggregateEvents(events) {
5883
6139
  break;
5884
6140
  case "activity":
5885
6141
  activity.push(toActivity(event));
6142
+ if (event.phase !== undefined) {
6143
+ const session2 = sessionFor(event.sessionID, event.ts);
6144
+ if (event.ts >= (session2.phaseAt ?? -Infinity)) {
6145
+ session2.phase = event.phase;
6146
+ session2.phaseAt = event.ts;
6147
+ }
6148
+ }
5886
6149
  break;
5887
6150
  }
5888
6151
  }
6152
+ for (const event of calls.values()) {
6153
+ applyToolcall(sessionFor(event.sessionID, event.ts), event);
6154
+ }
5889
6155
  const totals = {
5890
6156
  sessions: 0,
5891
6157
  routeCount: 0,
@@ -6176,8 +6442,15 @@ var KNOWN_CONFIG_KEYS_BY_PATH = new Map([
6176
6442
  ["graph.worktrees", ["enabled"]],
6177
6443
  ["lifecycle", ["root", "retention"]],
6178
6444
  ["lifecycle.retention", ["maxTerminalRuns", "maxAgeDays", "maxBytes"]],
6179
- ["orchestrator", ["roles", "primary", "maxRetries", "maxModelsPerNode"]],
6445
+ [
6446
+ "orchestrator",
6447
+ ["roles", "primary", "maxRetries", "maxModelsPerNode", "taskBudget"]
6448
+ ],
6180
6449
  ["orchestrator.primary", ["executionMode", "model", "fallbacks"]],
6450
+ [
6451
+ "orchestrator.taskBudget",
6452
+ ["maxBatches", "maxAttempts", "maxDurationMs", "maxDepth"]
6453
+ ],
6181
6454
  ["orchestrator.primary.fallbacks[]", MODEL_REF_KEYS]
6182
6455
  ]);
6183
6456
  function isUnrecognizedKeysIssue(issue) {
@@ -7466,6 +7739,488 @@ async function handleLifecycleHistoryRoute(request, response, url, deps) {
7466
7739
  return true;
7467
7740
  }
7468
7741
 
7742
+ // src/messages/agentView.ts
7743
+ var agentViewMessages = {
7744
+ title: "Agent workspace",
7745
+ graph: "Graph",
7746
+ chronology: "Chronology",
7747
+ filters: "Filters",
7748
+ search: "Search operations",
7749
+ searchHint: "Alias, role, model or operation ID",
7750
+ allRoles: "All roles",
7751
+ allStates: "All states",
7752
+ role: "Role",
7753
+ state: "State",
7754
+ matches: "Matching operations",
7755
+ noMatches: "No operations match these filters.",
7756
+ filterNote: "Filtered operations stay dimmed to preserve context.",
7757
+ hierarchy: "Hierarchy",
7758
+ dependency: "Prerequisite",
7759
+ legend: "Solid: parent/child. Dashed: prerequisite. Neither implies model activity.",
7760
+ inspector: "Operation inspector",
7761
+ root: "Select coordinator",
7762
+ isolate: "Isolate hierarchy branch",
7763
+ clearIsolation: "Show all branches",
7764
+ selectedFiltered: "The selected operation is outside the current filters.",
7765
+ zoomIn: "Zoom in",
7766
+ zoomOut: "Zoom out",
7767
+ fit: "Fit graph",
7768
+ reset: "Reset view",
7769
+ canvas: "Agent graph. Select an operation with Enter or Space. Arrow keys pan the canvas; plus and minus zoom.",
7770
+ play: "Play recorded events",
7771
+ pause: "Pause replay",
7772
+ previous: "Previous event",
7773
+ next: "Next event",
7774
+ speed: "Events per second",
7775
+ replayNote: "Replay uses recorded event prefixes. Current conversations and telemetry are never shown at historical cursors.",
7776
+ noEvents: "No recorded events at this cursor.",
7777
+ eventWindow: "Showing the most recent 1,000 events in this prefix. Use the cursor to inspect earlier history.",
7778
+ attempts: "Attempts",
7779
+ failures: "Failed attempts",
7780
+ noModel: "Model not recorded",
7781
+ noSession: "No session created",
7782
+ phase: "Observed phase",
7783
+ phaseUnknown: "Session phase unavailable",
7784
+ historicalMetrics: "Live phase and usage are unavailable in historical replay.",
7785
+ usage: "Reported input / output tokens",
7786
+ usageUnavailable: "Usage unavailable / partial",
7787
+ cost: "Reported cost",
7788
+ costUnavailable: "Cost unavailable / partial",
7789
+ tools: "Tool calls / failures",
7790
+ noTools: "No tool outcomes observed",
7791
+ related: "Related batches",
7792
+ relatedNote: "Same task, or a batch whose root session is this operation's child session. Outcomes remain independent.",
7793
+ relatedFailed: "Related failed batches",
7794
+ missing: "Unresolved references",
7795
+ states: {
7796
+ waiting: "Waiting",
7797
+ selected: "Selected",
7798
+ queued: "Queued",
7799
+ running: "Running",
7800
+ succeeded: "Succeeded",
7801
+ failed: "Failed",
7802
+ blocked: "Blocked",
7803
+ cancelled: "Cancelled",
7804
+ interrupted: "Interrupted",
7805
+ unknown: "Unknown"
7806
+ },
7807
+ events: {
7808
+ "run.started": "Batch started",
7809
+ "operation.queued": "Operation queued",
7810
+ "attempt.model-selected": "Model selected",
7811
+ "attempt.queued": "Attempt queued",
7812
+ "attempt.started": "Attempt started",
7813
+ "session.created": "Session created",
7814
+ "attempt.terminal": "Attempt finished",
7815
+ "operation.terminal": "Operation finished",
7816
+ "run.terminal": "Batch finished"
7817
+ }
7818
+ };
7819
+
7820
+ // src/console/agentView.ts
7821
+ function layoutAgentOperations(operations) {
7822
+ const byID = new Map(operations.map((operation) => [operation.operationID, operation]));
7823
+ const rows = new Map;
7824
+ const nodes = [];
7825
+ let width = 256;
7826
+ let height = 152;
7827
+ for (const operation of byID.values()) {
7828
+ const seen = new Set([operation.operationID]);
7829
+ let parent = operation.parentOperationID;
7830
+ let depth = 0;
7831
+ while (parent !== undefined && byID.has(parent) && !seen.has(parent)) {
7832
+ seen.add(parent);
7833
+ depth++;
7834
+ parent = byID.get(parent)?.parentOperationID;
7835
+ }
7836
+ const row = rows.get(depth) ?? 0;
7837
+ rows.set(depth, row + 1);
7838
+ const x = 28 + depth * 268;
7839
+ const y = 28 + row * 148;
7840
+ nodes.push({ id: operation.operationID, x, y });
7841
+ width = Math.max(width, x + 256);
7842
+ height = Math.max(height, y + 144);
7843
+ }
7844
+ return { nodes, width, height };
7845
+ }
7846
+ var AGENT_VIEW_CLIENT_JS = `
7847
+ var layoutAgentOperations=${layoutAgentOperations.toString()};
7848
+ function createAgentViewer(host,options){
7849
+ var msg=${JSON.stringify(agentViewMessages)},phases=${JSON.stringify(executionPhaseMessages)};
7850
+ var source=null,selected=null,isolated=null,run=null,mode='graph',playing=false,playTimer=null,pendingCursor=null;
7851
+ var scale=1,offsetX=24,offsetY=24,positioned=false,drag=null,summaries=new Map((document.__consoleTelemetry||[]).map(function(item){return [item.sessionID,item]})),nodes=new Map(),layout=null;
7852
+ var metricsKey='',relatedKey='';
7853
+ var namespace='http://www.w3.org/2000/svg',byID=new Map(),tree=null,filtered=new Set(),openDetails=new Map();
7854
+ function element(tag,text,className){var node=document.createElement(tag);if(text!==undefined){node.textContent=text}if(className){node.className=className}return node}
7855
+ function button(label,action,marker){
7856
+ var node=element('button',label);node.type='button';node.setAttribute('aria-label',label);
7857
+ if(marker){node.setAttribute('data-agent-'+marker,'')}
7858
+ node.addEventListener('click',action);return node;
7859
+ }
7860
+ function svgElement(tag,attributes,text){
7861
+ var node=document.createElementNS(namespace,tag);
7862
+ Object.keys(attributes||{}).forEach(function(key){node.setAttribute(key,String(attributes[key]))});
7863
+ if(text!==undefined){node.textContent=text}return node;
7864
+ }
7865
+ function label(text,input){var node=element('label',text);node.appendChild(input);return node}
7866
+ function option(select,value,text){var node=element('option',text);node.value=value;select.appendChild(node)}
7867
+ var shell=element('section',undefined,'av-workspace');shell.setAttribute('aria-label',msg.title);
7868
+ var tools=element('div',undefined,'av-toolbar');
7869
+ var graphButton=button(msg.graph,function(){setMode('graph')},'graph');
7870
+ var chronologyButton=button(msg.chronology,function(){setMode('chronology')},'chronology');
7871
+ tools.append(graphButton,chronologyButton);
7872
+ tools.appendChild(button(msg.previous,function(){pause();seek(Math.max(0,source.cursor-1),true)},'previous'));
7873
+ var playButton=button(msg.play,togglePlayback,'play');tools.appendChild(playButton);
7874
+ tools.appendChild(button(msg.next,function(){pause();seek(Math.min(source.head,source.cursor+1),true)},'next'));
7875
+ var speed=element('select');speed.setAttribute('aria-label',msg.speed);
7876
+ [1,2,5].forEach(function(value){option(speed,String(value),String(value))});
7877
+ tools.appendChild(label(msg.speed,speed));
7878
+ var count=element('span',undefined,'av-count');count.setAttribute('role','status');tools.appendChild(count);
7879
+ shell.appendChild(tools);
7880
+ var body=element('div',undefined,'av-body'),filters=element('aside',undefined,'av-filters');
7881
+ filters.appendChild(element('h3',msg.filters));
7882
+ var search=element('input');search.type='search';search.placeholder=msg.searchHint;search.setAttribute('data-agent-search','');
7883
+ filters.appendChild(label(msg.search,search));
7884
+ var role=element('select');role.setAttribute('data-agent-role','');option(role,'',msg.allRoles);
7885
+ filters.appendChild(label(msg.role,role));
7886
+ var status=element('select');status.setAttribute('data-agent-state','');option(status,'',msg.allStates);
7887
+ Object.keys(msg.states).forEach(function(key){option(status,key,msg.states[key])});
7888
+ filters.appendChild(label(msg.state,status));
7889
+ var isolate=button(msg.isolate,function(){isolated=isolated?null:selected;applyFilters()},'isolate');
7890
+ filters.appendChild(isolate);
7891
+ filters.appendChild(button(msg.root,function(){select(source.rootOperationID)},'root'));
7892
+ filters.appendChild(element('p',msg.filterNote));
7893
+ var legend=element('div',undefined,'av-legend');legend.appendChild(element('p',msg.hierarchy,'av-solid'));
7894
+ legend.appendChild(element('p',msg.dependency,'av-dashed'));legend.appendChild(element('p',msg.legend));filters.appendChild(legend);
7895
+ var unresolved=element('p');unresolved.setAttribute('data-agent-unresolved','');filters.appendChild(unresolved);
7896
+ body.appendChild(filters);
7897
+ var center=element('div',undefined,'av-center');
7898
+ var canvas=element('div',undefined,'av-canvas');
7899
+ var zoomTools=element('div',undefined,'av-zoom');
7900
+ zoomTools.append(button(msg.zoomIn,function(){zoom(1.2)},'zoom-in'),button(msg.zoomOut,function(){zoom(1/1.2)},'zoom-out'),
7901
+ button(msg.fit,fit,'fit'),button(msg.reset,function(){scale=1;offsetX=24;offsetY=24;transform()},'reset'));
7902
+ canvas.appendChild(zoomTools);
7903
+ var svg=svgElement('svg',{'class':'av-graph','role':'group','aria-label':msg.canvas,tabindex:0});
7904
+ var viewport=svgElement('g',{'class':'av-viewport'});svg.appendChild(viewport);canvas.appendChild(svg);
7905
+ center.appendChild(canvas);
7906
+ var chronology=element('ol',undefined,'av-chronology');chronology.hidden=true;chronology.setAttribute('aria-label',msg.chronology);
7907
+ center.appendChild(chronology);
7908
+ var empty=element('p',msg.noMatches,'av-empty');empty.hidden=true;center.appendChild(empty);
7909
+ body.appendChild(center);
7910
+ var inspector=element('aside',undefined,'av-inspector');
7911
+ inspector.setAttribute('aria-label',msg.inspector);inspector.appendChild(element('h3',msg.inspector));
7912
+ var notice=element('p',msg.selectedFiltered);notice.hidden=true;inspector.appendChild(notice);
7913
+ var metrics=element('div',undefined,'av-metrics');metrics.setAttribute('data-agent-metrics','');inspector.appendChild(metrics);
7914
+ var inspectorTree=element('div');inspectorTree.setAttribute('data-agent-inspector-tree','');inspector.appendChild(inspectorTree);
7915
+ var related=element('section',undefined,'av-related');inspector.appendChild(related);
7916
+ body.appendChild(inspector);shell.appendChild(body);
7917
+ var replayNote=element('p',msg.replayNote,'av-replay-note');shell.appendChild(replayNote);
7918
+ host.replaceChildren(shell);
7919
+ host.classList.add('av-host');
7920
+ function setMode(value){
7921
+ mode=value;canvas.hidden=value!=='graph';chronology.hidden=value!=='chronology';
7922
+ graphButton.setAttribute('aria-pressed',String(value==='graph'));chronologyButton.setAttribute('aria-pressed',String(value==='chronology'));
7923
+ if(value==='graph'&&!positioned){fit()}
7924
+ }
7925
+ function transform(){
7926
+ viewport.setAttribute('transform','translate('+offsetX+' '+offsetY+') scale('+scale+')');
7927
+ svg.setAttribute('data-agent-scale',String(scale));
7928
+ }
7929
+ function fit(){
7930
+ if(!layout){return}
7931
+ var bounds=svg.getBoundingClientRect();if(!bounds.width||!bounds.height){return}
7932
+ scale=Math.max(.06,Math.min(1.25,(bounds.width-48)/layout.width,(bounds.height-92)/layout.height));
7933
+ offsetX=(bounds.width-layout.width*scale)/2;offsetY=60;positioned=true;transform();
7934
+ }
7935
+ function zoom(factor,x,y){
7936
+ var bounds=svg.getBoundingClientRect(),cx=x===undefined?bounds.width/2:x,cy=y===undefined?bounds.height/2:y;
7937
+ var next=Math.max(.06,Math.min(3,scale*factor));
7938
+ offsetX=cx-(cx-offsetX)*next/scale;offsetY=cy-(cy-offsetY)*next/scale;scale=next;positioned=true;transform();
7939
+ }
7940
+ svg.addEventListener('wheel',function(event){
7941
+ if(!event.ctrlKey&&!event.metaKey){return}
7942
+ event.preventDefault();var bounds=svg.getBoundingClientRect();zoom(event.deltaY>0?1/1.15:1.15,event.clientX-bounds.left,event.clientY-bounds.top);
7943
+ },{passive:false});
7944
+ svg.addEventListener('pointerdown',function(event){
7945
+ if(event.button!==0||event.target.closest('[data-agent-operation]')){return}
7946
+ drag={x:event.clientX,y:event.clientY,left:offsetX,top:offsetY,id:event.pointerId};
7947
+ svg.setPointerCapture(event.pointerId);
7948
+ });
7949
+ svg.addEventListener('pointermove',function(event){
7950
+ if(!drag||event.pointerId!==drag.id){return}
7951
+ offsetX=drag.left+event.clientX-drag.x;offsetY=drag.top+event.clientY-drag.y;positioned=true;transform();
7952
+ });
7953
+ function endDrag(){drag=null}
7954
+ svg.addEventListener('pointerup',endDrag);svg.addEventListener('pointercancel',endDrag);svg.addEventListener('lostpointercapture',endDrag);
7955
+ svg.addEventListener('keydown',function(event){
7956
+ if(event.key==='+'||event.key==='='){event.preventDefault();zoom(1.2)}
7957
+ else if(event.key==='-'){event.preventDefault();zoom(1/1.2)}
7958
+ else if(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(event.key)){
7959
+ event.preventDefault();offsetX+=event.key==='ArrowLeft'?40:event.key==='ArrowRight'?-40:0;
7960
+ offsetY+=event.key==='ArrowUp'?40:event.key==='ArrowDown'?-40:0;positioned=true;transform();
7961
+ }
7962
+ });
7963
+ function lastAttempt(operation){var attempts=operation.attempts||[];return attempts[attempts.length-1]}
7964
+ function phaseFor(operation){
7965
+ if(operation.state!=='running'){return msg.states[operation.state]||msg.states.unknown}
7966
+ if(!options.isLive()){return msg.phaseUnknown}
7967
+ var attempt=lastAttempt(operation),summary=summaries.get(attempt&&attempt.sessionID||options.rootSessionID(operation.operationID));
7968
+ return summary&&phases[summary.phase]||msg.phaseUnknown;
7969
+ }
7970
+ function appendMetric(name,value){var row=element('p');row.append(element('span',name),element('strong',String(value)));metrics.appendChild(row)}
7971
+ function updateMetrics(){
7972
+ if(!source){return}
7973
+ nodes.forEach(function(node,id){var phase=phaseFor(byID.get(id));if(node.phase.textContent!==phase){node.phase.textContent=phase}});
7974
+ var operation=byID.get(selected),attempt=operation&&lastAttempt(operation);
7975
+ var session=attempt&&attempt.sessionID||options.rootSessionID(operation&&operation.operationID),summary=summaries.get(session);
7976
+ var key=JSON.stringify([source.runID,selected,options.isLive(),operation&&phaseFor(operation),summary]);
7977
+ if(metricsKey===key){return}metricsKey=key;
7978
+ metrics.replaceChildren();
7979
+ if(!options.isLive()){metrics.appendChild(element('p',msg.historicalMetrics));return}
7980
+ if(!operation){return}
7981
+ appendMetric(msg.phase,phaseFor(operation));
7982
+ var hasUsage=summary&&summary.messageCount>0&&!(summary.unavailableUsageMessages>0);
7983
+ appendMetric(msg.usage,hasUsage?summary.tokensIn+' / '+summary.tokensOut:msg.usageUnavailable);
7984
+ appendMetric(msg.cost,summary&&summary.messageCount>0&&!(summary.unavailableCostMessages>0)?'$'+summary.realCostUSD.toFixed(5):msg.costUnavailable);
7985
+ appendMetric(msg.tools,summary&&summary.toolcalls.count?summary.toolcalls.count+' / '+summary.toolcalls.failed:msg.noTools);
7986
+ }
7987
+ function updateRelated(){
7988
+ if(!source){return}
7989
+ var current=(options.runs()||[]).find(function(item){return item.runID===source.runID});
7990
+ var operation=byID.get(selected),sessions=new Set((operation&&operation.attempts||[]).map(function(attempt){return attempt.sessionID}).filter(Boolean));
7991
+ var others=(options.isLive()?options.runs():[]).filter(function(item){return item.runID!==source.runID&&
7992
+ (current&&item.rootTaskID===current.rootTaskID||item.rootSessionID&&sessions.has(item.rootSessionID))});
7993
+ var key=JSON.stringify([source.runID,selected,others]);if(relatedKey===key){return}relatedKey=key;
7994
+ related.replaceChildren();
7995
+ if(!others.length){return}
7996
+ related.appendChild(element('h4',msg.related));related.appendChild(element('p',msg.relatedNote));
7997
+ var failed=others.filter(function(item){return item.terminalOutcome==='failed'}).length;
7998
+ if(failed){related.appendChild(element('p',msg.relatedFailed+': '+failed,'av-warning'))}
7999
+ others.forEach(function(item){
8000
+ var link=element('a',item.runID+' · '+(msg.states[item.terminalOutcome]||msg.states.unknown));
8001
+ var url=new URL(window.location.href);url.searchParams.set('run',item.runID);url.searchParams.delete('cursor');link.href=url.href;
8002
+ link.setAttribute('data-agent-related-run',item.runID);link.setAttribute('data-state',item.terminalOutcome||'unknown');related.appendChild(link);
8003
+ });
8004
+ }
8005
+ function select(id,quiet){
8006
+ if(!byID.has(id)){selected=null;shell.removeAttribute('data-agent-selected');updateMetrics();updateRelated();return}
8007
+ selected=id;shell.setAttribute('data-agent-selected',id);
8008
+ nodes.forEach(function(node,key){node.group.setAttribute('aria-pressed',String(key===id))});
8009
+ inspectorTree.querySelectorAll('.lct-operation-card').forEach(function(card){
8010
+ card.hidden=card.parentElement.getAttribute('data-lifecycle-operation-id')!==id;
8011
+ });
8012
+ notice.hidden=!filtered.has(id);
8013
+ inspector.scrollTop=quiet?inspector.scrollTop:0;
8014
+ updateMetrics();updateRelated();
8015
+ if(!quiet){document.dispatchEvent(new CustomEvent('console-conversation-selection'))}
8016
+ }
8017
+ function applyFilters(){
8018
+ if(!source){return}
8019
+ var term=search.value.trim().toLowerCase(),branch=null;
8020
+ if(isolated&&byID.has(isolated)){
8021
+ branch=new Set([isolated]);var parent=byID.get(isolated).parentOperationID;
8022
+ while(parent&&byID.has(parent)&&!branch.has(parent)){branch.add(parent);parent=byID.get(parent).parentOperationID}
8023
+ var descend=new Set([isolated]),changed=true;
8024
+ while(changed){changed=false;byID.forEach(function(op,id){if(descend.has(op.parentOperationID)&&!descend.has(id)){descend.add(id);changed=true}})}
8025
+ descend.forEach(function(id){branch.add(id)});
8026
+ }
8027
+ filtered=new Set();
8028
+ byID.forEach(function(op,id){
8029
+ var models=(op.attempts||[]).map(function(a){return a.model?a.model.providerID+'/'+a.model.modelID:''}).join(' ');
8030
+ var match=(!term||[id,op.agentID||'',op.roleID,models].join(' ').toLowerCase().includes(term))&&
8031
+ (!role.value||op.roleID===role.value)&&(!status.value||op.state===status.value)&&(!branch||branch.has(id));
8032
+ if(!match){filtered.add(id)}
8033
+ var node=nodes.get(id);if(node){node.group.setAttribute('data-filtered',String(!match));node.group.setAttribute('tabindex',match?'0':'-1')}
8034
+ });
8035
+ viewport.querySelectorAll('[data-edge-from]').forEach(function(edge){
8036
+ edge.setAttribute('data-filtered',String(filtered.has(edge.getAttribute('data-edge-from'))||filtered.has(edge.getAttribute('data-edge-to'))));
8037
+ });
8038
+ chronology.querySelectorAll('[data-event-operation]').forEach(function(row){row.hidden=filtered.has(row.getAttribute('data-event-operation'))});
8039
+ count.textContent=msg.matches+': '+(byID.size-filtered.size)+' / '+byID.size;
8040
+ empty.hidden=filtered.size!==byID.size||byID.size===0;
8041
+ notice.hidden=!filtered.has(selected);isolate.textContent=isolated?msg.clearIsolation:msg.isolate;
8042
+ isolate.setAttribute('aria-pressed',String(!!isolated));
8043
+ }
8044
+ search.addEventListener('input',applyFilters);role.addEventListener('change',applyFilters);status.addEventListener('change',applyFilters);
8045
+ function buildGraph(){
8046
+ var focus=document.activeElement&&document.activeElement.getAttribute('data-agent-operation');
8047
+ viewport.replaceChildren();nodes.clear();layout=layoutAgentOperations(Array.from(byID.values()));
8048
+ var positions=new Map(layout.nodes.map(function(node){return [node.id,node]}));
8049
+ var defs=svgElement('defs'),marker=svgElement('marker',{id:'agent-dependency-arrow',viewBox:'0 0 8 8',refX:7,refY:4,markerWidth:6,markerHeight:6,orient:'auto'});
8050
+ marker.appendChild(svgElement('path',{d:'M0 0 L8 4 L0 8 Z',fill:'#8a777e'}));defs.appendChild(marker);viewport.appendChild(defs);
8051
+ var missing=0;
8052
+ function edge(from,to,kind){
8053
+ var a=positions.get(from),b=positions.get(to);if(!a||!b){missing++;return}
8054
+ var x1=a.x+228,y1=a.y+56,x2=b.x,y2=b.y+56,middle=(x1+x2)/2;
8055
+ var path=svgElement('path',{'class':'av-edge','data-kind':kind,'data-edge-from':from,'data-edge-to':to,
8056
+ d:'M'+x1+' '+y1+' C'+middle+' '+y1+' '+middle+' '+y2+' '+x2+' '+y2});
8057
+ if(kind==='dependency'){path.setAttribute('marker-end','url(#agent-dependency-arrow)')}
8058
+ path.appendChild(svgElement('title',{},(kind==='hierarchy'?msg.hierarchy:msg.dependency)+': '+from+' -> '+to));viewport.appendChild(path);
8059
+ }
8060
+ byID.forEach(function(operation){
8061
+ if(operation.parentOperationID){edge(operation.parentOperationID,operation.operationID,'hierarchy')}
8062
+ (operation.dependsOnOperationIDs||[]).forEach(function(id){edge(id,operation.operationID,'dependency')});
8063
+ });
8064
+ unresolved.textContent=msg.missing+': '+missing;unresolved.hidden=missing===0;
8065
+ layout.nodes.forEach(function(position){
8066
+ var operation=byID.get(position.id),attempt=lastAttempt(operation),identity=operation.agentID||operation.roleID;
8067
+ var group=svgElement('g',{'class':'av-node',transform:'translate('+position.x+' '+position.y+')','data-agent-operation':position.id,
8068
+ 'data-state':operation.state,role:'button',tabindex:0,'aria-label':identity+' · '+operation.roleID+' · '+(msg.states[operation.state]||msg.states.unknown)});
8069
+ group.appendChild(svgElement('rect',{width:228,height:120,rx:12}));
8070
+ group.appendChild(svgElement('title',{},identity+' · '+position.id));
8071
+ group.appendChild(svgElement('text',{x:14,y:23,'class':'av-role'},operation.roleID.slice(0,31)));
8072
+ group.appendChild(svgElement('text',{x:14,y:45,'class':'av-name'},identity.slice(0,28)));
8073
+ var model=attempt&&attempt.model?attempt.model.providerID+'/'+attempt.model.modelID:msg.noModel;
8074
+ group.appendChild(svgElement('text',{x:14,y:64,'class':'av-model'},model.slice(0,32)));
8075
+ var phase=svgElement('text',{x:14,y:84,'class':'av-phase'},phaseFor(operation));group.appendChild(phase);
8076
+ var failures=(operation.attempts||[]).filter(function(a){return ['failed','interrupted','cancelled'].includes(a.state)}).length;
8077
+ group.appendChild(svgElement('text',{x:14,y:105,'class':'av-attempts'},msg.attempts+': '+operation.attempts.length+' · '+msg.failures+': '+failures));
8078
+ group.addEventListener('click',function(){select(position.id)});
8079
+ group.addEventListener('keydown',function(event){if(event.key==='Enter'||event.key===' '){event.preventDefault();select(position.id)}});
8080
+ nodes.set(position.id,{group:group,phase:phase});viewport.appendChild(group);
8081
+ });
8082
+ transform();if(!positioned){fit()}
8083
+ if(focus&&nodes.has(focus)){nodes.get(focus).group.focus({preventScroll:true})}
8084
+ }
8085
+ function buildChronology(){
8086
+ var top=chronology.scrollTop;chronology.replaceChildren();var events=source.timeline||[];
8087
+ if(!events.length){chronology.appendChild(element('li',msg.noEvents))}
8088
+ if(events.length>1000){chronology.appendChild(element('li',msg.eventWindow))}
8089
+ events.slice(-1000).forEach(function(event){
8090
+ var id=event.operationID||event.root&&event.root.operationID||source.rootOperationID,op=byID.get(id);
8091
+ var row=element('li');row.setAttribute('data-event-operation',id);row.setAttribute('data-event-seq',String(event.seq));
8092
+ var item=button('',function(){pause();select(id);seek(event.seq+1,true)});
8093
+ item.appendChild(element('time',new Date(event.at).toISOString()));
8094
+ item.appendChild(element('strong',msg.events[event.type]||event.type));
8095
+ item.appendChild(element('span',(op&&(op.agentID||op.roleID)||id)+(event.outcome?' · '+(msg.states[event.outcome]||event.outcome):'')));
8096
+ row.appendChild(item);chronology.appendChild(row);
8097
+ });chronology.scrollTop=top;
8098
+ }
8099
+ function seek(cursor,push){pendingCursor=cursor;options.seek(cursor,push)}
8100
+ function pause(){playing=false;clearTimeout(playTimer);playTimer=null;playButton.textContent=msg.play;playButton.setAttribute('aria-label',msg.play);playButton.setAttribute('aria-pressed','false')}
8101
+ function tick(){
8102
+ if(!playing||!source){return}
8103
+ if(document.hidden||!shell.isConnected){pause();return}
8104
+ if(pendingCursor===null||source.cursor===pendingCursor){
8105
+ if(source.cursor>=source.head){pause();return}
8106
+ seek(source.cursor+1,false);
8107
+ }
8108
+ playTimer=setTimeout(tick,1000/Number(speed.value));
8109
+ }
8110
+ function togglePlayback(){
8111
+ if(playing){pause();return}if(!source||source.head===0){return}
8112
+ playing=true;playButton.textContent=msg.pause;playButton.setAttribute('aria-label',msg.pause);playButton.setAttribute('aria-pressed','true');
8113
+ seek(source.cursor>=source.head?0:source.cursor,true);playTimer=setTimeout(tick,1000/Number(speed.value));
8114
+ }
8115
+ function rememberDetails(){
8116
+ if(!tree){return}
8117
+ tree.querySelectorAll('[data-lifecycle-operation-id]').forEach(function(article){
8118
+ var card=article.querySelector('.lct-operation-card');if(!card){return}
8119
+ card.querySelectorAll('details:not(.lc-conversation details)').forEach(function(detail,index){
8120
+ openDetails.set(article.getAttribute('data-lifecycle-operation-id')+':'+index,detail.open);
8121
+ });
8122
+ });
8123
+ }
8124
+ function update(projection,nextTree){
8125
+ var changedRun=run!==projection.runID;
8126
+ if(changedRun){pause();selected=projection.rootOperationID;isolated=null;positioned=false;openDetails.clear()}
8127
+ else{rememberDetails()}
8128
+ run=projection.runID;source=projection;
8129
+ byID=new Map((projection.operations||[]).map(function(op){return [op.operationID,op]}));
8130
+ if(!byID.has(selected)){selected=byID.has(projection.rootOperationID)?projection.rootOperationID:byID.keys().next().value}
8131
+ var oldRole=role.value;role.replaceChildren();option(role,'',msg.allRoles);
8132
+ Array.from(new Set(Array.from(byID.values()).map(function(op){return op.roleID}))).sort().forEach(function(value){option(role,value,value)});
8133
+ if(Array.from(role.options).some(function(item){return item.value===oldRole})){role.value=oldRole}
8134
+ var top=inspector.scrollTop;tree=nextTree;inspectorTree.replaceChildren(tree);
8135
+ tree.querySelectorAll('[data-lifecycle-operation-id]').forEach(function(article){
8136
+ var card=article.querySelector('.lct-operation-card');if(!card){return}
8137
+ card.querySelectorAll('details').forEach(function(detail,index){
8138
+ var saved=openDetails.get(article.getAttribute('data-lifecycle-operation-id')+':'+index);if(saved!==undefined){detail.open=saved}
8139
+ });
8140
+ });
8141
+ isolate.disabled=byID.size===0;
8142
+ buildGraph();buildChronology();applyFilters();select(selected,true);setMode(mode);inspector.scrollTop=changedRun?0:top;
8143
+ }
8144
+ function onTelemetry(event){
8145
+ summaries=new Map((event.detail&&event.detail.sessions||[]).map(function(item){return [item.sessionID,item]}));
8146
+ if(shell.isConnected){updateMetrics()}
8147
+ }
8148
+ function onVisibility(){if(document.hidden){pause()}}
8149
+ document.addEventListener('console-telemetry',onTelemetry);
8150
+ document.addEventListener('visibilitychange',onVisibility);
8151
+ window.addEventListener('pagehide',pause);
8152
+ return {update:update,pause:pause,connected:function(){return shell.isConnected},refresh:function(){updateMetrics();updateRelated();if(!positioned&&mode==='graph'){fit()}},
8153
+ dispose:function(){pause();document.removeEventListener('console-telemetry',onTelemetry);document.removeEventListener('visibilitychange',onVisibility);
8154
+ window.removeEventListener('pagehide',pause);host.classList.remove('av-host')}};
8155
+ }
8156
+ `;
8157
+ var AGENT_VIEW_STYLE = `
8158
+ #screen-tree:has(.av-workspace){max-width:none}
8159
+ .av-host{max-height:none;padding:0;overflow:visible}
8160
+ .av-workspace{--av-bg:#121012;--av-text:#f3edef;--av-muted:#b7aab0;--av-line:#3b3036;--av-accent:#c9184c;--cp-bg:#121012;--cp-surface:#1d181c;--cp-surface-soft:#262026;--cp-text:#f3edef;--cp-text-muted:#b7aab0;--cp-border:#3b3036;--cp-border-strong:#79646d;--cp-accent:#f47b9e;--cp-success:#77c491;--cp-danger:#ff8699;color:var(--av-text);background:var(--av-bg);border:1px solid var(--av-line);border-radius:14px;overflow:hidden}
8161
+ .av-workspace [hidden]{display:none!important}
8162
+ .av-workspace button,.av-workspace input,.av-workspace select{font:inherit;color:inherit;background:#211b20;border:1px solid var(--av-line);border-radius:7px;padding:6px 9px}
8163
+ .av-workspace button:hover,.av-workspace button[aria-pressed="true"]{border-color:#ee7399;color:#ffa2be}
8164
+ .av-workspace :focus-visible{outline:2px solid #ec5282;outline-offset:3px}
8165
+ .av-toolbar{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:12px;border-bottom:1px solid var(--av-line);font-size:12px}
8166
+ .av-toolbar label{display:flex;align-items:center;gap:6px}.av-count{margin-left:auto;color:var(--av-muted)}
8167
+ .av-body{display:grid;grid-template-columns:180px minmax(300px,1fr) minmax(280px,360px);height:min(78vh,860px);min-height:520px}
8168
+ .av-filters{padding:16px 12px;border-right:1px solid var(--av-line);overflow:auto;font-size:12px}
8169
+ .av-filters h3,.av-inspector>h3{color:var(--av-text);font-size:12px;letter-spacing:.06em;text-transform:uppercase;margin-bottom:14px}
8170
+ .av-filters label{display:grid;gap:6px;margin-bottom:14px;color:var(--av-muted)}
8171
+ .av-filters input,.av-filters select{width:100%;min-width:0;color:var(--av-text)}
8172
+ .av-filters>button{display:block;width:100%;margin:9px 0;font-size:12px;text-align:left}
8173
+ .av-filters p,.av-related p{margin:10px 0;color:var(--av-muted);line-height:1.5;font-size:11px}
8174
+ .av-legend{margin-top:24px;border-top:1px solid var(--av-line);padding-top:8px}
8175
+ .av-solid:before,.av-dashed:before{content:"";display:inline-block;width:24px;border-top:2px solid #b7aab0;margin-right:7px;vertical-align:middle}.av-dashed:before{border-top-style:dashed}
8176
+ .av-center{position:relative;min-width:0;background:#fdfbf6;color:#30272d}
8177
+ .av-canvas{position:relative;height:100%;min-height:380px;overflow:hidden;background-image:radial-gradient(#e3d9db .8px,transparent .8px);background-size:20px 20px}
8178
+ .av-zoom{position:absolute;display:flex;gap:5px;top:12px;left:12px;z-index:2;flex-wrap:wrap}
8179
+ .av-zoom button{background:#fdfbf6;color:#342c30;border-color:#d6c5cb;font-size:11px;box-shadow:0 1px 3px #0001}
8180
+ .av-zoom button:hover{color:#a50e3c}
8181
+ .av-graph{width:100%;height:100%;display:block;cursor:grab;touch-action:none}
8182
+ .av-node{cursor:pointer;outline:none}.av-node rect{fill:#fffdf9;stroke:#d2c5ca;stroke-width:1.5}
8183
+ .av-node:hover rect,.av-node:focus-visible rect{stroke:#69434f;stroke-width:2}
8184
+ .av-node[aria-pressed="true"] rect{stroke:#c9184c;stroke-width:2.5}
8185
+ .av-node[data-state="failed"] rect,.av-node[data-state="interrupted"] rect{fill:#fff0f1;stroke:#bf3150}
8186
+ .av-node[data-state="succeeded"] rect{fill:#f4faf3;stroke:#79a186}
8187
+ .av-node text{fill:#362b31;font:11px var(--font-sans);pointer-events:none}
8188
+ .av-node .av-role{fill:#755f69;font-size:10px;letter-spacing:.04em}
8189
+ .av-node .av-name{font-size:16px;font-weight:650}
8190
+ .av-node .av-model,.av-node .av-attempts{fill:#6d5a63;font-size:10px;font-family:var(--font-mono)}
8191
+ .av-node .av-phase{fill:#9d234b;font-size:11px}
8192
+ .av-node[data-filtered="true"]{opacity:.22}
8193
+ .av-edge{fill:none;stroke:#a6969c;stroke-width:1.5}
8194
+ .av-edge[data-kind="dependency"]{stroke:#bd5776;stroke-dasharray:5 5}
8195
+ .av-edge[data-filtered="true"]{opacity:.12}
8196
+ .av-empty{position:absolute;bottom:18px;left:18px;right:18px;background:#fdfbf6;padding:10px;border:1px solid #d6c5cb;border-radius:7px;font-size:12px}
8197
+ .av-inspector{overflow:auto;min-width:0;padding:16px 14px;border-left:1px solid var(--av-line);overflow-anchor:none}
8198
+ .av-inspector .lct-tree,.av-inspector .lct-operation{min-width:0;margin:0;padding:0;max-width:100%}
8199
+ .av-inspector .lct-operation-children{margin:0;padding:0;border:0}
8200
+ .av-inspector .lct-operation-card{border-left-width:3px;box-shadow:none;border-radius:8px;padding:12px}
8201
+ .av-inspector .lct-operation-meta,.av-inspector .lct-attempt-meta{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
8202
+ .av-inspector .lct-operation-head,.av-inspector .lct-attempt-head{flex-wrap:wrap}
8203
+ .av-inspector .lct-session{display:block}.av-inspector code,.av-inspector dd{overflow-wrap:anywhere;word-break:break-word}
8204
+ .av-inspector .lc-conversation{padding:9px}.av-inspector .lc-conversation pre{padding:8px;font-size:11px}
8205
+ .av-inspector .lct-attempt{padding:10px;min-width:0}
8206
+ .av-inspector .lct-times{display:block;font-size:10px}
8207
+ .av-inspector .lct-unlinked>h4,.av-inspector .lct-unlinked>p,.av-inspector .lct-no-attempts{font-size:11px}
8208
+ .av-metrics{margin-bottom:14px;font-size:11px;border-bottom:1px solid var(--av-line);padding-bottom:10px}
8209
+ .av-metrics p{display:flex;flex-wrap:wrap;gap:3px 8px;justify-content:space-between;margin:5px 0}.av-metrics span{color:var(--av-muted)}
8210
+ .av-related{margin-top:18px}.av-related a{display:block;color:#f59fba;overflow-wrap:anywhere;font-size:11px;margin:8px 0}
8211
+ .av-related a[data-state="failed"],.av-warning{color:#ff8699!important}
8212
+ .av-chronology{list-style:none;margin:0;padding:18px;height:100%;overflow:auto}
8213
+ .av-chronology li{border-left:2px solid #d2c5ca;padding:0 0 12px 16px}
8214
+ .av-chronology button{display:grid;gap:7px;text-align:left;width:100%;background:#fffdf9;color:#30272d;border-color:#d2c5ca;padding:12px}
8215
+ .av-chronology button:hover{border-color:#c9184c;color:#9d234b}
8216
+ .av-chronology time{font:10px var(--font-mono);color:#725e68}.av-chronology span{font-size:11px;overflow-wrap:anywhere}
8217
+ .av-replay-note{padding:10px 14px;margin:0;border-top:1px solid var(--av-line);color:var(--av-muted);font-size:11px}
8218
+ @media(max-width:1280px){.av-body{grid-template-columns:145px minmax(250px,1fr) 300px}.av-toolbar{gap:5px}}
8219
+ @media(max-width:1050px){.av-body{grid-template-columns:minmax(250px,1fr) 300px}.av-filters{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:10px;align-items:center;border-bottom:1px solid var(--av-line);max-height:150px}.av-filters label{width:150px;margin:0}.av-filters>button{width:auto}.av-filters h3,.av-filters>p,.av-legend{display:none}.av-body{height:auto}.av-center,.av-inspector{height:620px}}
8220
+ @media(max-width:740px){.av-body{display:block}.av-center{height:430px}.av-inspector{height:auto;max-height:650px;border-left:0;border-top:1px solid var(--av-line)}.av-count{width:100%;margin:0}.av-filters{max-height:none}.av-toolbar{font-size:11px}}
8221
+ @media(prefers-reduced-motion:reduce){.av-workspace *{animation:none!important;transition:none!important}}
8222
+ `;
8223
+
7469
8224
  // src/console/inspector.ts
7470
8225
  var CONVERSATION_CLIENT_JS = `
7471
8226
  function wireConversationInspector(){
@@ -7708,7 +8463,9 @@ function wireConversationInspector(){
7708
8463
  });
7709
8464
  document.addEventListener('conversation-tree-replacing',function(){
7710
8465
  var scroll=document.querySelector('.scroll'),tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
7711
- retained={focus:document.activeElement,scroll:scroll,scrollTop:scroll&&scroll.scrollTop,treeTop:tree&&tree.scrollTop,positions:[]};
8466
+ var inspector=mount&&mount.querySelector('.av-inspector');
8467
+ retained={focus:document.activeElement,scroll:scroll,scrollTop:scroll&&scroll.scrollTop,treeTop:tree&&tree.scrollTop,
8468
+ inspector:inspector,inspectorTop:inspector&&inspector.scrollTop,positions:[]};
7712
8469
  records.forEach(function(record){
7713
8470
  [record.body].concat(Array.from(record.panel.querySelectorAll('pre'))).forEach(function(node){
7714
8471
  retained.positions.push([node,node.scrollTop,node.scrollLeft]);
@@ -7723,6 +8480,7 @@ function wireConversationInspector(){
7723
8480
  var tree=mount&&mount.querySelector('[data-lifecycle-tree-mount]');
7724
8481
  if(tree&&retained.treeTop!==null){tree.scrollTop=retained.treeTop}
7725
8482
  if(retained.scroll){retained.scroll.scrollTop=retained.scrollTop}
8483
+ if(retained.inspector&&retained.inspector.isConnected){retained.inspector.scrollTop=retained.inspectorTop}
7726
8484
  if(retained.focus&&retained.focus.isConnected&&retained.focus!==document.activeElement&&
7727
8485
  (!document.activeElement||document.activeElement===document.body||document.activeElement===document.documentElement)){
7728
8486
  retained.focus.focus({preventScroll:true});
@@ -7877,6 +8635,8 @@ var CONSOLE_REFRESH_JS = `
7877
8635
  if(viewStopped||viewRequest!==controller||!result){return}
7878
8636
  if(typeof result.data.routing!=='string'||typeof result.data.logs!=='string'){return}
7879
8637
  latestView=result.data;viewRevision=result.revision;
8638
+ document.__consoleTelemetry=latestView.sessionSummaries||[];
8639
+ document.dispatchEvent(new CustomEvent('console-telemetry',{detail:{sessions:latestView.sessionSummaries||[]}}));
7880
8640
  var updated=document.querySelector('.token-chip b');
7881
8641
  if(updated&&typeof latestView.updatedAt==='string'){updated.textContent=latestView.updatedAt}
7882
8642
  if(Number.isInteger(latestView.refreshMs)&&latestView.refreshMs>=250&&latestView.refreshMs!==ms){
@@ -8515,6 +9275,7 @@ var LIFECYCLE_THEME_BOOT = `
8515
9275
  })();
8516
9276
  `;
8517
9277
  var LIFECYCLE_CLIENT_JS = `
9278
+ ${AGENT_VIEW_CLIENT_JS}
8518
9279
  function wireLifecycleTimeline(){
8519
9280
  var mount=document.getElementById('lifecycle-timeline-mount');
8520
9281
  if(!mount){return}
@@ -8553,6 +9314,11 @@ function wireLifecycleTimeline(){
8553
9314
  lastSseHead:new Map()
8554
9315
  };
8555
9316
  var projectionKey='';
9317
+ var agentViewer=null,agentRuns=[],agentRootOperationID=null;
9318
+ function clearAgentViewer(){
9319
+ if(agentViewer){agentViewer.dispose();agentViewer=null}
9320
+ var note=mount.querySelector('[data-agent-degraded]');if(note){note.remove()}
9321
+ }
8556
9322
  var committedRun=mount.getAttribute('data-lifecycle-run')||null,committedCursor=urlCursor();
8557
9323
 
8558
9324
  function invalidateConversation(){
@@ -8687,6 +9453,7 @@ function wireLifecycleTimeline(){
8687
9453
  if(sliderWired||!slider){return}
8688
9454
  sliderWired=true;
8689
9455
  slider.addEventListener('input',function(){
9456
+ if(agentViewer){agentViewer.pause()}
8690
9457
  var c=parseInt(slider.value,10);
8691
9458
  if(!scrubInProgress){
8692
9459
  history.pushState(null,'',window.location.href);
@@ -8829,6 +9596,7 @@ function wireLifecycleTimeline(){
8829
9596
  function renderView(d,requestedCursor){
8830
9597
  ensureViewControls();
8831
9598
  state.runID=d.selection.runID;
9599
+ agentRuns=d.runs||[];
8832
9600
  state.head=d.selection.head;
8833
9601
  state.cursor=d.selection.cursor;
8834
9602
  state.followLive=requestedCursor===null;
@@ -8905,6 +9673,7 @@ function wireLifecycleTimeline(){
8905
9673
  }
8906
9674
 
8907
9675
  function renderLegacy(d){
9676
+ clearAgentViewer();
8908
9677
  ensureViewControls();
8909
9678
  state.runID=d.selection.runID;
8910
9679
  state.head=0;
@@ -8943,19 +9712,37 @@ function wireLifecycleTimeline(){
8943
9712
 
8944
9713
  function renderProjection(proj,recording){
8945
9714
  if(!proj||!treeMountEl){return}
9715
+ agentRootOperationID=proj.rootOperationID;
8946
9716
  var degraded=recording&&recording.state==='degraded';
8947
9717
  var option=runSelect&&Array.from(runSelect.options).find(function(o){return o.value===proj.runID});
8948
9718
  var key=JSON.stringify([proj.runID,proj.cursor,proj.rootOperationID,proj.operations,degraded,option&&option.getAttribute('data-run-session')]);
8949
- if(key===projectionKey){return}
9719
+ if(key===projectionKey){if(agentViewer){agentViewer.refresh()}return}
8950
9720
  projectionKey=key;
8951
9721
  document.dispatchEvent(new CustomEvent('conversation-tree-replacing'));
8952
- treeMountEl.textContent='';
9722
+ if(!agentViewer||!agentViewer.connected()){
9723
+ if(agentViewer){agentViewer.dispose()}
9724
+ agentViewer=createAgentViewer(treeMountEl,{
9725
+ isLive:function(){return state.followLive},
9726
+ runs:function(){return agentRuns},
9727
+ rootSessionID:function(id){
9728
+ if(id!==agentRootOperationID){return undefined}
9729
+ var option=runSelect&&Array.from(runSelect.options).find(function(o){return o.value===state.runID});
9730
+ return option&&option.getAttribute('data-run-session')||undefined;
9731
+ },
9732
+ seek:function(cursor,push){
9733
+ if(push){pushUrl(state.runID,cursor)}else{replaceUrl(state.runID,cursor)}
9734
+ navigateFromUrl(false);
9735
+ }
9736
+ });
9737
+ }
9738
+ var priorNote=mount.querySelector('[data-agent-degraded]');if(priorNote){priorNote.remove()}
8953
9739
  if(degraded){
8954
9740
  var note=makeElement('div','lc-partial-note',message('partial-tail'));
9741
+ note.setAttribute('data-agent-degraded','');
8955
9742
  note.setAttribute('role','status');
8956
- treeMountEl.appendChild(note);
9743
+ treeMountEl.before(note);
8957
9744
  }
8958
- treeMountEl.appendChild(buildOperationTree(proj));
9745
+ agentViewer.update(proj,buildOperationTree(proj));
8959
9746
  }
8960
9747
 
8961
9748
  function makeElement(tag,className,text){
@@ -9288,6 +10075,7 @@ function wireLifecycleTimeline(){
9288
10075
  }
9289
10076
 
9290
10077
  function showError(){
10078
+ clearAgentViewer();
9291
10079
  invalidateConversation();
9292
10080
  projectionKey='';
9293
10081
  mount.setAttribute('data-lifecycle-state','error');
@@ -9304,6 +10092,7 @@ function wireLifecycleTimeline(){
9304
10092
  }
9305
10093
 
9306
10094
  function showApiError(err){
10095
+ clearAgentViewer();
9307
10096
  projectionKey='';
9308
10097
  mount.setAttribute('data-lifecycle-state','error');
9309
10098
  updateScreenCopy('unavailable',false,null);
@@ -9320,6 +10109,7 @@ function wireLifecycleTimeline(){
9320
10109
  }
9321
10110
 
9322
10111
  function showDisabled(d){
10112
+ clearAgentViewer();
9323
10113
  projectionKey='';
9324
10114
  mount.setAttribute('data-lifecycle-state','disabled');
9325
10115
  updateScreenCopy('unavailable',false,null);
@@ -9337,6 +10127,7 @@ function wireLifecycleTimeline(){
9337
10127
  }
9338
10128
 
9339
10129
  function showEmpty(){
10130
+ clearAgentViewer();
9340
10131
  projectionKey='';
9341
10132
  mount.setAttribute('data-lifecycle-state','empty');
9342
10133
  updateScreenCopy('empty',false,null);
@@ -9355,6 +10146,7 @@ function wireLifecycleTimeline(){
9355
10146
  // --- Run selector change ---
9356
10147
  if(runSelect){
9357
10148
  runSelect.addEventListener('change',function(){
10149
+ if(agentViewer){agentViewer.pause()}
9358
10150
  var newRun=runSelect.value;
9359
10151
  pushUrl(newRun,null); // committed selection → traversable entry
9360
10152
  navigateFromUrl(false);
@@ -9369,6 +10161,7 @@ function wireLifecycleTimeline(){
9369
10161
  // --- Live / Return to live ---
9370
10162
  if(liveBtn){
9371
10163
  liveBtn.addEventListener('click',function(){
10164
+ if(agentViewer){agentViewer.pause()}
9372
10165
  pushUrl(state.explicitRun,null); // return-to-live preserves automatic run selection
9373
10166
  navigateFromUrl(false);
9374
10167
  });
@@ -9393,6 +10186,7 @@ function wireLifecycleTimeline(){
9393
10186
 
9394
10187
  // --- Browser back/forward ---
9395
10188
  window.addEventListener('popstate',function(){
10189
+ if(agentViewer){agentViewer.pause()}
9396
10190
  navigateFromUrl(false);
9397
10191
  });
9398
10192
 
@@ -9447,7 +10241,11 @@ function ms(value) {
9447
10241
  return value >= 1000 ? `${(value / 1000).toFixed(2)}s` : `${Math.round(value)}ms`;
9448
10242
  }
9449
10243
  function avgMs(stats) {
9450
- return stats.count === 0 ? "—" : ms(stats.totalDurationMs / stats.count);
10244
+ const known = stats.count - (stats.unknownDurationCount ?? 0);
10245
+ return stats.count === 0 ? "—" : known === 0 ? consoleDetailMessages.timingUnknown : ms(stats.totalDurationMs / known);
10246
+ }
10247
+ function measured(value, unavailable) {
10248
+ return (unavailable ?? 0) === 0 ? String(value) : `<span title="${escapeHtml(consoleDetailMessages.usageCaution)}">${escapeHtml(consoleDetailMessages.usageUnavailable)}</span>`;
9451
10249
  }
9452
10250
  function agentModel(agent) {
9453
10251
  return agent.model === undefined ? "inherits default" : `${agent.model.providerID}/${agent.model.modelID}`;
@@ -9462,15 +10260,16 @@ function summaryPanel(cost) {
9462
10260
  return [
9463
10261
  '<section class="panel" id="panel-summary">',
9464
10262
  "<h2>Routing summary</h2>",
10263
+ `<p class="muted">${escapeHtml(consoleDetailMessages.estimatesNotUsage)}</p>`,
9465
10264
  '<div class="grid">',
9466
10265
  `<div class="stat"><span class="k">Decisions</span><span class="v">${cost.count}</span></div>`,
9467
10266
  `<div class="stat"><span class="k">Local</span><span class="v">${cost.localCount}</span></div>`,
9468
10267
  `<div class="stat"><span class="k">Frontier</span><span class="v">${cost.frontierCount}</span></div>`,
9469
- `<div class="stat"><span class="k">Real cost</span><span class="v">${usd(cost.totalEstimatedUSD)}</span></div>`,
9470
- `<div class="stat"><span class="k">Baseline</span><span class="v">${usd(cost.totalBaselineUSD)}</span></div>`,
9471
- `<div class="stat"><span class="k">Savings</span><span class="v good">${usd(cost.totalSavingsUSD)} (${pct(cost.savingsPct)})</span></div>`,
9472
- `<div class="stat"><span class="k">Tokens in</span><span class="v">${cost.tokensIn}</span></div>`,
9473
- `<div class="stat"><span class="k">Tokens out</span><span class="v">${cost.tokensOut}</span></div>`,
10268
+ `<div class="stat"><span class="k">${escapeHtml(consoleDetailMessages.estimatedCost)}</span><span class="v">${usd(cost.totalEstimatedUSD)}</span></div>`,
10269
+ `<div class="stat"><span class="k">${escapeHtml(consoleDetailMessages.estimatedBaseline)}</span><span class="v">${usd(cost.totalBaselineUSD)}</span></div>`,
10270
+ `<div class="stat"><span class="k">${escapeHtml(consoleDetailMessages.estimatedSavings)}</span><span class="v good">${usd(cost.totalSavingsUSD)} (${pct(cost.savingsPct)})</span></div>`,
10271
+ `<div class="stat"><span class="k">${escapeHtml(consoleDetailMessages.estimatedInput)}</span><span class="v">${cost.tokensIn}</span></div>`,
10272
+ `<div class="stat"><span class="k">${escapeHtml(consoleDetailMessages.estimatedOutput)}</span><span class="v">${cost.tokensOut}</span></div>`,
9474
10273
  "</div>",
9475
10274
  "</section>"
9476
10275
  ].join("");
@@ -9585,12 +10384,12 @@ function totalsPanel(totals) {
9585
10384
  '<div class="grid">',
9586
10385
  `<div class="stat"><span class="k">Sessions</span><span class="v">${totals.sessions}</span></div>`,
9587
10386
  `<div class="stat"><span class="k">Messages</span><span class="v">${totals.messageCount}</span></div>`,
9588
- `<div class="stat"><span class="k">Real cost</span><span class="v">${usd(totals.realCostUSD)}</span></div>`,
10387
+ `<div class="stat"><span class="k">Real cost</span><span class="v">${measured(usd(totals.realCostUSD), totals.unavailableCostMessages)}</span></div>`,
9589
10388
  `<div class="stat"><span class="k">Est. savings</span><span class="v good">${usd(totals.estimatedSavingsUSD)}</span></div>`,
9590
- `<div class="stat"><span class="k">Tokens in</span><span class="v">${totals.tokensIn}</span></div>`,
9591
- `<div class="stat"><span class="k">Tokens out</span><span class="v">${totals.tokensOut}</span></div>`,
9592
- `<div class="stat"><span class="k">Reasoning</span><span class="v">${totals.tokensReasoning}</span></div>`,
9593
- `<div class="stat"><span class="k">Cache r/w</span><span class="v">${totals.tokensCacheRead}/${totals.tokensCacheWrite}</span></div>`,
10389
+ `<div class="stat"><span class="k">Tokens in</span><span class="v">${measured(totals.tokensIn, totals.unavailableUsageMessages)}</span></div>`,
10390
+ `<div class="stat"><span class="k">Tokens out</span><span class="v">${measured(totals.tokensOut, totals.unavailableUsageMessages)}</span></div>`,
10391
+ `<div class="stat"><span class="k">Reasoning</span><span class="v">${measured(totals.tokensReasoning, totals.unavailableUsageMessages)}</span></div>`,
10392
+ `<div class="stat"><span class="k">Cache r/w</span><span class="v">${measured(`${totals.tokensCacheRead}/${totals.tokensCacheWrite}`, totals.unavailableUsageMessages)}</span></div>`,
9594
10393
  `<div class="stat"><span class="k">Toolcalls</span><span class="v">${t.count} <span class="good">${t.ok}✓</span> <span class="frontier">${t.failed}✗</span></span></div>`,
9595
10394
  `<div class="stat"><span class="k">Avg tool dur.</span><span class="v">${avgMs(t)}</span></div>`,
9596
10395
  "</div>",
@@ -9613,8 +10412,8 @@ function sessionRow(session2) {
9613
10412
  `<td>${sessionNameHtml(session2.sessionID, sessionName(session2))}<br>${observedTimeHtml(session2)}<br><code>${escapeHtml(session2.sessionID)}</code></td>`,
9614
10413
  `<td>${models}</td>`,
9615
10414
  `<td><span class="local">${session2.localCount}</span>/<span class="frontier">${session2.frontierCount}</span></td>`,
9616
- `<td>${usd(session2.realCostUSD)}</td>`,
9617
- `<td>${session2.tokensIn}/${session2.tokensOut}</td>`,
10415
+ `<td>${measured(usd(session2.realCostUSD), session2.unavailableCostMessages)}</td>`,
10416
+ `<td>${measured(`${session2.tokensIn}/${session2.tokensOut}`, session2.unavailableUsageMessages)}</td>`,
9618
10417
  `<td>${session2.toolcalls.count} (${avgMs(session2.toolcalls)})</td>`,
9619
10418
  "</tr>"
9620
10419
  ].join("");
@@ -9751,8 +10550,8 @@ function sessionHeaderPanel(tab) {
9751
10550
  `<div class="stat"><span class="k">Endpoint</span><span class="v">${endpoint}</span></div>`,
9752
10551
  dir,
9753
10552
  title,
9754
- `<div class="stat"><span class="k">Real cost</span><span class="v">${usd(s.realCostUSD)}</span></div>`,
9755
- `<div class="stat"><span class="k">Tokens in/out</span><span class="v">${s.tokensIn}/${s.tokensOut}</span></div>`,
10553
+ `<div class="stat"><span class="k">Real cost</span><span class="v">${measured(usd(s.realCostUSD), s.unavailableCostMessages)}</span></div>`,
10554
+ `<div class="stat"><span class="k">Tokens in/out</span><span class="v">${measured(`${s.tokensIn}/${s.tokensOut}`, s.unavailableUsageMessages)}</span></div>`,
9756
10555
  `<div class="stat"><span class="k">Local/Frontier</span><span class="v"><span class="local">${s.localCount}</span>/<span class="frontier">${s.frontierCount}</span></span></div>`,
9757
10556
  `<div class="stat"><span class="k">Toolcalls</span><span class="v">${s.toolcalls.count} (${avgMs(s.toolcalls)})</span></div>`,
9758
10557
  "</div>",
@@ -10653,7 +11452,7 @@ function renderConsoleHtml(state, options = {}) {
10653
11452
  '<meta name="viewport" content="width=device-width,initial-scale=1">',
10654
11453
  "<title>openteam console</title>",
10655
11454
  lifecycleEnabled ? `<script>${LIFECYCLE_THEME_BOOT}</script>` : "",
10656
- `<style>${CONSOLE_STYLE}${CONVERSATION_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE : ""}</style>`,
11455
+ `<style>${CONSOLE_STYLE}${CONVERSATION_STYLE}${graphOptionProvided ? GRAPH_STYLE : ""}${lifecycleEnabled ? LIFECYCLE_STYLE + AGENT_VIEW_STYLE : ""}</style>`,
10657
11456
  "</head>",
10658
11457
  "<body>",
10659
11458
  '<div class="app">',
@@ -10683,6 +11482,7 @@ function renderConsoleUpdate(state, options = {}) {
10683
11482
  routing: routingScreenBody(state),
10684
11483
  logs: logsScreenBody(state, options.terminalEnabled === true, options.ptyEnabled === true),
10685
11484
  sessionCount: state.sessionsDetail.length,
11485
+ sessionSummaries: state.sessions,
10686
11486
  ...options.graphView ? {
10687
11487
  tree: treeGraphPanel(options.graphView.projection, options.drilldownEnabled === true, false, options.graphView.mode ?? "active")
10688
11488
  } : {}
@@ -12618,6 +13418,7 @@ var lifecycleFailureSchema = z13.object({
12618
13418
  "prompt-auth",
12619
13419
  "prompt-content-filter",
12620
13420
  "prompt-context-length",
13421
+ "prompt-output-truncated",
12621
13422
  "prompt-validation",
12622
13423
  "prompt-connection",
12623
13424
  "prompt-server",
@@ -13662,6 +14463,8 @@ var NodeFailureClassSchema = z17.enum([
13662
14463
  "prompt-auth",
13663
14464
  "prompt-content-filter",
13664
14465
  "prompt-context-length",
14466
+ "prompt-output-truncated",
14467
+ "budget-exhausted",
13665
14468
  "prompt-validation",
13666
14469
  "prompt-connection",
13667
14470
  "prompt-server",
@@ -17776,7 +18579,7 @@ function createMigrateAdapter(root) {
17776
18579
  }
17777
18580
 
17778
18581
  // src/cli/opencodeVersion.ts
17779
- var SEMVER_PATTERN = /(\d+\.\d+\.\d+)/;
18582
+ var SEMVER_PATTERN = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)/;
17780
18583
  function normalizeVersion(raw) {
17781
18584
  if (raw === undefined) {
17782
18585
  return;
@@ -18170,7 +18973,15 @@ function buildOrchestratorAgent(model, options = {}) {
18170
18973
  " `title` is optional and only for readability.",
18171
18974
  "- `parentSessionID` (optional): the current session, so that the subsessions",
18172
18975
  " link as children.",
18173
- "- `directory` (optional): working directory if it differs from the current one.",
18976
+ "- `directory` (optional): omit it to use the trusted host workspace. A different",
18977
+ " directory is rejected; this argument cannot authorize another workspace.",
18978
+ "",
18979
+ "Related and nested batches share a cumulative task budget, not a fresh allowance",
18980
+ "per call. On `task-budget-exhausted`, stop delegation and summarize preserved",
18981
+ "work and remaining tasks. Never create another batch/session or edit effort",
18982
+ "state to reset that allowance. Only a genuinely new root user turn starts a new task.",
18983
+ "Workers should use `openteam-submit-report` for their assigned report, retaining",
18984
+ "operator permission checks. Do not broaden file or shell permissions to deliver it.",
18174
18985
  "",
18175
18986
  "The tool returns a per-role summary: selected model, success or",
18176
18987
  "failure, the created session, and — for each role — a `report` file path",
@@ -21728,7 +22539,8 @@ var nativeHostMessages = {
21728
22539
  setupPolicyQuestion: (paths, providers) => `Remove exact project-local provider.use deny rules for ${providers.join(", ")} from ${paths.join(", ")}? Older setup added these rules, but they may also be operator limits. Confirm only if you intend to enable the guarded native transport.`,
21729
22540
  setupPolicyPreserved: "Setup stopped. Provider-denial rules were preserved and no files were written.",
21730
22541
  setupPolicyNotice: "Only the explicitly reviewed project rules were removed. Wildcard, scoped, unrelated and global provider denials remain authoritative.",
21731
- unsupportedVersion: (version) => `openteam has not verified the native plugin contract for opencode ${version}. The supported native host is 2.0.5.`,
22542
+ unsupportedVersion: (version) => `openteam does not support native opencode ${version}. The supported stable native range is >=2.0.5 <3.0.0.`,
22543
+ certificateVersionMismatch: (certified, live) => `certified for "${certified}", running "${live}" — evidence is outside the supported host compatibility range`,
21732
22544
  missingBindings: "The native opencode execution context is unavailable. No child session or privileged operation was started.",
21733
22545
  missingVersion: "The opencode host did not provide an authoritative version or server endpoint.",
21734
22546
  workspaceMismatch: "The native opencode session does not belong to this workspace.",
@@ -21746,7 +22558,7 @@ var nativeHostMessages = {
21746
22558
  // package.json
21747
22559
  var package_default = {
21748
22560
  name: "@jmanuelcorral/openteam",
21749
- version: "0.27.0",
22561
+ version: "0.28.0",
21750
22562
  packageManager: "bun@1.3.14",
21751
22563
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
21752
22564
  license: "MIT",
@@ -22041,10 +22853,31 @@ function parseSemver(version) {
22041
22853
  patch: Number(match[3])
22042
22854
  };
22043
22855
  }
22856
+ function compareSemver2(a, b) {
22857
+ if (a.major !== b.major) {
22858
+ return a.major < b.major ? -1 : 1;
22859
+ }
22860
+ if (a.minor !== b.minor) {
22861
+ return a.minor < b.minor ? -1 : 1;
22862
+ }
22863
+ if (a.patch !== b.patch) {
22864
+ return a.patch < b.patch ? -1 : 1;
22865
+ }
22866
+ return 0;
22867
+ }
22868
+ function isSupportedNativeOpencodeVersion(version) {
22869
+ if (version === undefined || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version))
22870
+ return false;
22871
+ const live = parseSemver(version);
22872
+ const minimum = parseSemver(NATIVE_OPENCODE_VERSION);
22873
+ return Object.values(live).every(Number.isSafeInteger) && live.major === minimum.major && compareSemver2(live, minimum) >= 0;
22874
+ }
22044
22875
  function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
22045
22876
  if (certifiedVersion === liveVersion) {
22046
22877
  return true;
22047
22878
  }
22879
+ if (isSupportedNativeOpencodeVersion(certifiedVersion) && isSupportedNativeOpencodeVersion(liveVersion))
22880
+ return compareSemver2(parseSemver(liveVersion), parseSemver(certifiedVersion)) >= 0;
22048
22881
  let certified;
22049
22882
  let live;
22050
22883
  try {
@@ -22609,7 +23442,7 @@ function orchestratorAgentModel(answers) {
22609
23442
  }
22610
23443
  function buildOpencodeConfig(answers) {
22611
23444
  assertSetupResidency(answers);
22612
- const policies = answers.opencodeVersion === NATIVE_OPENCODE_VERSION ? [] : residentV2Policies(answers.runtimes.filter((choice) => choice.enabled && runtimeRequiresModelResidency(localRuntimeKind(choice))).map(choiceProviderID));
23445
+ const policies = isSupportedNativeOpencodeVersion(answers.opencodeVersion) ? [] : residentV2Policies(answers.runtimes.filter((choice) => choice.enabled && runtimeRequiresModelResidency(localRuntimeKind(choice))).map(choiceProviderID));
22613
23446
  const provider = {};
22614
23447
  const providerOwner = new Map;
22615
23448
  for (const choice of answers.runtimes) {
@@ -24153,7 +24986,7 @@ async function runSetup(deps) {
24153
24986
  try {
24154
24987
  existingOpenTeamConfig = await readExistingOpenTeamConfig(openTeamPath, deps);
24155
24988
  existingOpencodeFiles = await readExistingOpencodeConfigFiles(opencodeConfigPaths, deps);
24156
- if (opencodeVersion === NATIVE_OPENCODE_VERSION) {
24989
+ if (isSupportedNativeOpencodeVersion(opencodeVersion)) {
24157
24990
  existingOpencodeFiles = existingOpencodeFiles.map((file) => {
24158
24991
  nativeDocuments.set(file.path, file.raw);
24159
24992
  return { ...file, raw: nativeSetupView(file.raw) };
@@ -24198,7 +25031,7 @@ async function runSetup(deps) {
24198
25031
  throw new Error(finalOpenTeamConfig.local.runtimes.some((runtime) => runtime.defaultModel.providerID === providerID && localRuntimeKind(runtime) === "lemonade") ? executionSetupMessages.lemonadeRetainedProviderUnprotected(providerID) : executionSetupMessages.retainedResidencyProviderUnprotected(providerID));
24199
25032
  }
24200
25033
  const nativePolicyMigrations = new Set;
24201
- if (opencodeVersion === NATIVE_OPENCODE_VERSION) {
25034
+ if (isSupportedNativeOpencodeVersion(opencodeVersion)) {
24202
25035
  const providers = new Set(effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtime.enabled && runtimeRequiresModelResidency(localRuntimeKind(runtime))).map((runtime) => runtime.defaultModel.providerID));
24203
25036
  const planned = existingOpencodeFiles.map((file) => {
24204
25037
  const raw = withoutReviewedNativeDenials(file.raw, providers);
@@ -24217,7 +25050,7 @@ async function runSetup(deps) {
24217
25050
  prompt.note(nativeHostMessages.setupPolicyNotice);
24218
25051
  }
24219
25052
  }
24220
- const v2Denials = opencodeVersion === NATIVE_OPENCODE_VERSION ? [] : residentV2Policies([
25053
+ const v2Denials = isSupportedNativeOpencodeVersion(opencodeVersion) ? [] : residentV2Policies([
24221
25054
  ...opencodeConfig.experimental?.policies.map((policy) => policy.resource) ?? [],
24222
25055
  ...effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtimeRequiresModelResidency(localRuntimeKind(runtime))).map((runtime) => runtime.defaultModel.providerID)
24223
25056
  ]);
@@ -24360,7 +25193,7 @@ async function runSetup(deps) {
24360
25193
  await verifyResidencyBeforeWrite(deps, answers, effectiveOpenTeamConfig, effectivePlannedOpencode);
24361
25194
  const serializedOpencodePlan = [...opencodeWritePlan].map(([path2, contents]) => [
24362
25195
  path2,
24363
- serializeOpencodeConfig(opencodeVersion === NATIVE_OPENCODE_VERSION ? restoreNativeSetupConfig(contents, nativeDocuments.get(path2)) : contents)
25196
+ serializeOpencodeConfig(isSupportedNativeOpencodeVersion(opencodeVersion) ? restoreNativeSetupConfig(contents, nativeDocuments.get(path2)) : contents)
24364
25197
  ]);
24365
25198
  await deps.writeFile(openTeamPath, serializeOpenTeamConfig(finalOpenTeamConfig));
24366
25199
  for (const [path2, contents] of serializedOpencodePlan) {
@@ -27850,7 +28683,7 @@ function planStartupHygieneMigration(exists) {
27850
28683
 
27851
28684
  // src/index.ts
27852
28685
  import { readFile as readFile5 } from "node:fs/promises";
27853
- import { basename as basename4, dirname as dirname10 } from "node:path";
28686
+ import { basename as basename4, dirname as dirname11, resolve as resolve18 } from "node:path";
27854
28687
 
27855
28688
  // src/graph/certificate.ts
27856
28689
  import { z as z29 } from "zod";
@@ -28017,7 +28850,7 @@ function parseShadowCertificate(value, opencodeVersion) {
28017
28850
  throw new ShadowCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
28018
28851
  }
28019
28852
  if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
28020
- throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
28853
+ throw new ShadowCertificateError("opencode-mismatch", nativeHostMessages.certificateVersionMismatch(certificate.opencodeVersion, opencodeVersion));
28021
28854
  }
28022
28855
  return certificate;
28023
28856
  }
@@ -28202,7 +29035,7 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
28202
29035
  throw new ReleaseCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
28203
29036
  }
28204
29037
  if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
28205
- throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
29038
+ throw new ReleaseCertificateError("opencode-mismatch", nativeHostMessages.certificateVersionMismatch(certificate.opencodeVersion, opencodeVersion));
28206
29039
  }
28207
29040
  return certificate;
28208
29041
  }
@@ -29020,17 +29853,31 @@ function createRuntimeConcurrencyLimiter(options) {
29020
29853
  }
29021
29854
  return {
29022
29855
  tryAcquire,
29023
- acquire(runtimeId, priority = "worker") {
29856
+ async acquire(runtimeId, priority = "worker", signal) {
29857
+ signal?.throwIfAborted();
29024
29858
  const immediate = tryAcquire(runtimeId, priority);
29025
29859
  if (immediate !== undefined) {
29026
29860
  return Promise.resolve({ release: immediate, waitedMs: 0 });
29027
29861
  }
29028
29862
  const state = stateFor(runtimeId);
29029
29863
  const queuedAt = options.now();
29030
- return new Promise((resolve13) => {
29031
- state.queues[priority].push((release) => {
29864
+ return new Promise((resolve13, reject) => {
29865
+ const waiter = (release) => {
29866
+ signal?.removeEventListener("abort", cancel2);
29032
29867
  resolve13({ release, waitedMs: options.now() - queuedAt });
29033
- });
29868
+ };
29869
+ function cancel2() {
29870
+ const index = state.queues[priority].indexOf(waiter);
29871
+ if (index < 0)
29872
+ return;
29873
+ state.queues[priority].splice(index, 1);
29874
+ signal?.removeEventListener("abort", cancel2);
29875
+ reject(signal?.reason);
29876
+ }
29877
+ state.queues[priority].push(waiter);
29878
+ signal?.addEventListener("abort", cancel2, { once: true });
29879
+ if (signal?.aborted)
29880
+ cancel2();
29034
29881
  });
29035
29882
  },
29036
29883
  activeCount(runtimeId) {
@@ -29071,6 +29918,192 @@ var readReportArtifact = async (reportPath) => {
29071
29918
  }
29072
29919
  };
29073
29920
 
29921
+ // src/orchestrator/reportDelivery.ts
29922
+ import { z as z30 } from "zod";
29923
+ var Submission = z30.object({
29924
+ status: z30.enum(["done", "continue"]),
29925
+ findings: z30.string().min(1).max(32768)
29926
+ }).strict();
29927
+ function createReportDelivery(deps) {
29928
+ const active = new Map;
29929
+ return {
29930
+ open(sessionID, batchID, roleID) {
29931
+ if (active.has(sessionID))
29932
+ throw new Error(reportDeliveryMessages.assignmentConflict);
29933
+ const lease = {
29934
+ path: roleReportPath(batchID, roleID),
29935
+ open: true,
29936
+ submitted: false,
29937
+ pending: Promise.resolve()
29938
+ };
29939
+ active.set(sessionID, lease);
29940
+ return {
29941
+ async close() {
29942
+ lease.open = false;
29943
+ await lease.pending;
29944
+ if (active.get(sessionID) === lease)
29945
+ active.delete(sessionID);
29946
+ return lease.submitted;
29947
+ }
29948
+ };
29949
+ },
29950
+ path(sessionID) {
29951
+ const lease = active.get(sessionID);
29952
+ if (lease?.open !== true)
29953
+ throw new Error(reportDeliveryMessages.noAssignment);
29954
+ return lease.path;
29955
+ },
29956
+ async submit(sessionID, input) {
29957
+ const payload = Submission.safeParse(input);
29958
+ if (!payload.success || !reportHasSubstance(payload.data.findings))
29959
+ throw new Error(reportDeliveryMessages.invalidReport);
29960
+ const lease = active.get(sessionID);
29961
+ if (lease?.open !== true)
29962
+ throw new Error(reportDeliveryMessages.noAssignment);
29963
+ const content = `${payload.data.findings.trim()}
29964
+
29965
+ openteam-status: ${payload.data.status}
29966
+ `;
29967
+ if (parseReportStatus(content) !== payload.data.status)
29968
+ throw new Error(reportDeliveryMessages.invalidReport);
29969
+ const write = lease.pending.then(async () => {
29970
+ if (!lease.open)
29971
+ throw new Error(reportDeliveryMessages.noAssignment);
29972
+ await deps.write(lease.path, content);
29973
+ lease.submitted = true;
29974
+ });
29975
+ lease.pending = write.then(() => {}, () => {});
29976
+ await write;
29977
+ return lease.path;
29978
+ }
29979
+ };
29980
+ }
29981
+
29982
+ // src/orchestrator/taskEffort.ts
29983
+ import { z as z31 } from "zod";
29984
+ var Binding = z31.object({
29985
+ taskID: z31.string().regex(/^[a-f0-9]{64}$/),
29986
+ depth: z31.number().int().min(0).max(128),
29987
+ worker: z31.boolean()
29988
+ }).strict();
29989
+ var Record = z31.object({
29990
+ v: z31.literal(1),
29991
+ startedAt: z31.number().finite(),
29992
+ batches: z31.number().int().min(0),
29993
+ attempts: z31.number().int().min(0),
29994
+ maxBatches: z31.number().int().positive(),
29995
+ maxAttempts: z31.number().int().positive(),
29996
+ maxDurationMs: z31.number().int().positive(),
29997
+ maxDepth: z31.number().int().min(0)
29998
+ }).strict();
29999
+ function decode(schema, raw) {
30000
+ let value;
30001
+ try {
30002
+ value = JSON.parse(raw ?? "null");
30003
+ } catch {
30004
+ throw new Error(taskEffortMessages.ledgerInvalid);
30005
+ }
30006
+ const parsed = schema.safeParse(value);
30007
+ if (!parsed.success)
30008
+ throw new Error(taskEffortMessages.ledgerInvalid);
30009
+ return parsed.data;
30010
+ }
30011
+ function createTaskEffort(deps) {
30012
+ const limits = {
30013
+ maxBatches: deps.limits?.maxBatches ?? 8,
30014
+ maxAttempts: deps.limits?.maxAttempts ?? 32,
30015
+ maxDurationMs: deps.limits?.maxDurationMs ?? 7200000,
30016
+ maxDepth: deps.limits?.maxDepth ?? 3
30017
+ };
30018
+ const bindingPath = (sessionID) => `sessions/${sha256Hex(sessionID)}.json`;
30019
+ const taskPath = (taskID) => `tasks/${taskID}.json`;
30020
+ function initialize(sessionID, messageID) {
30021
+ const taskID = sha256Hex(`${sessionID}\x00${messageID}`);
30022
+ deps.store.update(taskPath(taskID), (previous) => {
30023
+ if (previous !== undefined) {
30024
+ decode(Record, previous);
30025
+ return previous;
30026
+ }
30027
+ return JSON.stringify({
30028
+ v: 1,
30029
+ startedAt: deps.now(),
30030
+ batches: 0,
30031
+ attempts: 0,
30032
+ ...limits
30033
+ });
30034
+ });
30035
+ const binding = { taskID, depth: 0, worker: false };
30036
+ deps.store.write(bindingPath(sessionID), JSON.stringify(binding));
30037
+ return binding;
30038
+ }
30039
+ function bindChild(parent, sessionID) {
30040
+ if (parent.depth >= 128)
30041
+ throw new Error(taskEffortMessages.ledgerInvalid);
30042
+ deps.store.write(bindingPath(sessionID), JSON.stringify({
30043
+ taskID: parent.taskID,
30044
+ depth: parent.depth + 1,
30045
+ worker: true
30046
+ }));
30047
+ }
30048
+ async function forSession(sessionID, ancestors = new Set) {
30049
+ if (ancestors.has(sessionID) || ancestors.size >= 128)
30050
+ throw new Error(taskEffortMessages.ledgerInvalid);
30051
+ const stored = deps.store.read(bindingPath(sessionID));
30052
+ if (stored !== undefined)
30053
+ return decode(Binding, stored);
30054
+ ancestors.add(sessionID);
30055
+ const parentID = await deps.parentSession?.(sessionID);
30056
+ if (parentID !== undefined) {
30057
+ const parent = await forSession(parentID, ancestors);
30058
+ bindChild(parent, sessionID);
30059
+ return { taskID: parent.taskID, depth: parent.depth + 1 };
30060
+ }
30061
+ return initialize(sessionID, "restored-session");
30062
+ }
30063
+ function consume(ref, kind) {
30064
+ let result = { ok: false, reason: "duration" };
30065
+ deps.store.update(taskPath(ref.taskID), (raw) => {
30066
+ const record3 = decode(Record, raw);
30067
+ const remainingMs = Math.min(record3.maxDurationMs, limits.maxDurationMs) - Math.max(0, deps.now() - record3.startedAt);
30068
+ const reason = ref.depth > Math.min(record3.maxDepth, limits.maxDepth) ? "depth" : remainingMs <= 0 ? "duration" : kind === "batches" && record3.batches >= Math.min(record3.maxBatches, limits.maxBatches) ? "batches" : kind === "attempts" && record3.attempts >= Math.min(record3.maxAttempts, limits.maxAttempts) ? "attempts" : undefined;
30069
+ if (reason !== undefined)
30070
+ result = { ok: false, reason };
30071
+ else {
30072
+ if (kind !== "check")
30073
+ record3[kind] += 1;
30074
+ result = {
30075
+ ok: true,
30076
+ remainingMs,
30077
+ batches: record3.batches,
30078
+ attempts: record3.attempts,
30079
+ remainingBatches: Math.max(0, Math.min(record3.maxBatches, limits.maxBatches) - record3.batches),
30080
+ remainingAttempts: Math.max(0, Math.min(record3.maxAttempts, limits.maxAttempts) - record3.attempts)
30081
+ };
30082
+ }
30083
+ return JSON.stringify(record3);
30084
+ });
30085
+ return result;
30086
+ }
30087
+ return {
30088
+ async userTurn(sessionID, messageID) {
30089
+ const stored = deps.store.read(bindingPath(sessionID));
30090
+ if (stored !== undefined && decode(Binding, stored).worker)
30091
+ return;
30092
+ const parentID = await deps.parentSession?.(sessionID);
30093
+ if (parentID !== undefined) {
30094
+ bindChild(await forSession(parentID), sessionID);
30095
+ return;
30096
+ }
30097
+ initialize(sessionID, messageID);
30098
+ },
30099
+ forSession,
30100
+ bindChild,
30101
+ batch: (ref) => consume(ref, "batches"),
30102
+ attempt: (ref) => consume(ref, "attempts"),
30103
+ check: (ref) => consume(ref, "check")
30104
+ };
30105
+ }
30106
+
29074
30107
  // src/plugin/availability.ts
29075
30108
  function modelKey4(model) {
29076
30109
  return `${model.providerID}/${model.modelID}`;
@@ -29316,7 +30349,7 @@ function createAvailabilityCache(options) {
29316
30349
 
29317
30350
  // src/plugin/capture.ts
29318
30351
  function nonNeg(value) {
29319
- return typeof value === "number" && value >= 0 ? Math.trunc(value) : 0;
30352
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : 0;
29320
30353
  }
29321
30354
  function messageEventFrom(info, now) {
29322
30355
  if (info.role !== "assistant") {
@@ -29330,6 +30363,17 @@ function messageEventFrom(info, now) {
29330
30363
  }
29331
30364
  const created = info.time.created ?? info.time.completed;
29332
30365
  const durationMs = Math.max(0, Math.trunc(info.time.completed - created));
30366
+ const counters = [info.tokens?.input, info.tokens?.output];
30367
+ const hasUsage = counters.every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0);
30368
+ const zeroUsage = [
30369
+ info.tokens?.input,
30370
+ info.tokens?.output,
30371
+ info.tokens?.reasoning,
30372
+ info.tokens?.cache?.read,
30373
+ info.tokens?.cache?.write
30374
+ ].every((value) => value === undefined || value === 0);
30375
+ const usageStatus = !hasUsage ? "missing" : zeroUsage && info.hasGeneratedOutput !== false ? "zero-unverified" : "reported";
30376
+ const hasCost = typeof info.cost === "number" && Number.isFinite(info.cost) && info.cost >= 0;
29333
30377
  const event = {
29334
30378
  v: EVENT_SCHEMA_VERSION,
29335
30379
  type: "message",
@@ -29337,7 +30381,11 @@ function messageEventFrom(info, now) {
29337
30381
  sessionID: info.sessionID,
29338
30382
  providerID: info.providerID,
29339
30383
  modelID: info.modelID,
29340
- costUSD: typeof info.cost === "number" && info.cost >= 0 ? info.cost : 0,
30384
+ costUSD: hasCost ? info.cost ?? 0 : 0,
30385
+ usageStatus,
30386
+ costStatus: hasCost && (info.cost !== 0 || usageStatus === "reported") ? "reported" : "unavailable",
30387
+ ...info.finish === undefined ? {} : { finish: finishReason(info.finish) },
30388
+ ...info.truncatedToolCall === true ? { truncatedToolCall: true } : {},
29341
30389
  tokensIn: nonNeg(info.tokens?.input),
29342
30390
  tokensOut: nonNeg(info.tokens?.output),
29343
30391
  tokensReasoning: nonNeg(info.tokens?.reasoning),
@@ -29348,6 +30396,18 @@ function messageEventFrom(info, now) {
29348
30396
  if (info.mode !== undefined) {
29349
30397
  event.mode = info.mode;
29350
30398
  }
30399
+ function finishReason(value) {
30400
+ switch (value) {
30401
+ case "stop":
30402
+ case "length":
30403
+ case "tool-calls":
30404
+ case "content-filter":
30405
+ case "error":
30406
+ return value;
30407
+ default:
30408
+ return "unknown";
30409
+ }
30410
+ }
29351
30411
  if (info.id !== undefined) {
29352
30412
  event.messageID = info.id;
29353
30413
  }
@@ -30077,36 +31137,36 @@ function formatResponse(response) {
30077
31137
  }
30078
31138
 
30079
31139
  // src/plugin/reportRunSchema.ts
30080
- import { z as z31 } from "zod";
31140
+ import { z as z33 } from "zod";
30081
31141
 
30082
31142
  // src/plugin/graphShadowIngress.ts
30083
- import { z as z30 } from "zod";
31143
+ import { z as z32 } from "zod";
30084
31144
  var LEGACY_EXECUTION_TRACE_VERSION = 1;
30085
- var LegacyExecutionStatusSchema = z30.enum([
31145
+ var LegacyExecutionStatusSchema = z32.enum([
30086
31146
  "completed",
30087
31147
  "failed",
30088
31148
  "cancelled"
30089
31149
  ]);
30090
- var LegacyReviewOutcomeSchema = z30.enum([
31150
+ var LegacyReviewOutcomeSchema = z32.enum([
30091
31151
  "approved",
30092
31152
  "rejected",
30093
31153
  "inconclusive"
30094
31154
  ]);
30095
- var LegacyTraceNodeV1Schema = z30.object({
31155
+ var LegacyTraceNodeV1Schema = z32.object({
30096
31156
  id: NodeIDSchema,
30097
31157
  role: NodeRoleSchema,
30098
- model: z30.string().min(1),
30099
- sessionRef: z30.string().min(1).optional(),
31158
+ model: z32.string().min(1),
31159
+ sessionRef: z32.string().min(1).optional(),
30100
31160
  errorClass: ErrorClassSchema.optional()
30101
31161
  }).strict();
30102
- var LegacyExecutionTraceV1Schema = z30.object({
30103
- version: z30.literal(LEGACY_EXECUTION_TRACE_VERSION),
31162
+ var LegacyExecutionTraceV1Schema = z32.object({
31163
+ version: z32.literal(LEGACY_EXECUTION_TRACE_VERSION),
30104
31164
  runID: NodeIDSchema,
30105
31165
  status: LegacyExecutionStatusSchema,
30106
31166
  outcome: LegacyReviewOutcomeSchema,
30107
- fixes: z30.number().int().nonnegative(),
30108
- nodes: z30.array(LegacyTraceNodeV1Schema).min(1),
30109
- parentSessionRef: z30.string().min(1).optional()
31167
+ fixes: z32.number().int().nonnegative(),
31168
+ nodes: z32.array(LegacyTraceNodeV1Schema).min(1),
31169
+ parentSessionRef: z32.string().min(1).optional()
30110
31170
  }).strict();
30111
31171
  function gateReason2(gate) {
30112
31172
  if (gate === undefined || gate.mode === "off") {
@@ -30177,20 +31237,20 @@ function parseLegacyExecutionTrace(input) {
30177
31237
  }
30178
31238
 
30179
31239
  // src/plugin/reportRunSchema.ts
30180
- var ReportRunNodeSchema = z31.object({
31240
+ var ReportRunNodeSchema = z33.object({
30181
31241
  id: NodeIDSchema,
30182
31242
  role: NodeRoleSchema,
30183
- model: z31.string().min(1),
30184
- ok: z31.boolean(),
30185
- sessionRef: z31.string().min(1).optional(),
31243
+ model: z33.string().min(1),
31244
+ ok: z33.boolean(),
31245
+ sessionRef: z33.string().min(1).optional(),
30186
31246
  errorClass: ErrorClassSchema.optional()
30187
31247
  }).strict();
30188
- var ReportRunPayloadSchema = z31.object({
31248
+ var ReportRunPayloadSchema = z33.object({
30189
31249
  runID: NodeIDSchema,
30190
31250
  status: LegacyExecutionStatusSchema,
30191
- fixes: z31.number().int().nonnegative(),
30192
- nodes: z31.array(ReportRunNodeSchema).min(1),
30193
- parentSessionRef: z31.string().min(1).optional()
31251
+ fixes: z33.number().int().nonnegative(),
31252
+ nodes: z33.array(ReportRunNodeSchema).min(1),
31253
+ parentSessionRef: z33.string().min(1).optional()
30194
31254
  }).strict();
30195
31255
 
30196
31256
  // src/orchestrator/graphShadow.ts
@@ -30692,7 +31752,7 @@ async function handleReportRun(args, observe, tracker) {
30692
31752
  }
30693
31753
 
30694
31754
  // src/plugin/modelResidency.ts
30695
- import { z as z32 } from "zod";
31755
+ import { z as z34 } from "zod";
30696
31756
 
30697
31757
  // src/local/lemonadeRequestGuard.ts
30698
31758
  var inferencePaths = new Set([
@@ -31063,7 +32123,7 @@ function createModelResidencyGuard(config, options) {
31063
32123
  throw failure2("identity", model.providerID);
31064
32124
  fetch2 = installed.fetch;
31065
32125
  }
31066
- const configuredHeaders = z32.record(z32.string(), z32.string()).safeParse(provider?.options?.headers ?? {});
32126
+ const configuredHeaders = z34.record(z34.string(), z34.string()).safeParse(provider?.options?.headers ?? {});
31067
32127
  if (!configuredHeaders.success)
31068
32128
  throw failure2("configuration", model.providerID);
31069
32129
  const headers = new Headers(configuredHeaders.data);
@@ -31291,62 +32351,62 @@ ${lines.join(`
31291
32351
  }
31292
32352
 
31293
32353
  // src/memory/types.ts
31294
- import { z as z33 } from "zod";
32354
+ import { z as z35 } from "zod";
31295
32355
  var SHARED_OWNER_KEY = "*";
31296
- var OwnerKeySchema = z33.string().min(1);
31297
- var MemoryKindSchema = z33.enum(["fact", "preference", "entity"]);
31298
- var MemoryBaseSchema = z33.object({
31299
- id: z33.string().min(1),
32356
+ var OwnerKeySchema = z35.string().min(1);
32357
+ var MemoryKindSchema = z35.enum(["fact", "preference", "entity"]);
32358
+ var MemoryBaseSchema = z35.object({
32359
+ id: z35.string().min(1),
31300
32360
  ownerKey: OwnerKeySchema,
31301
- confidence: z33.number().min(0).max(1),
31302
- validFrom: z33.number().finite(),
31303
- validUntil: z33.number().finite().nullable().default(null),
31304
- createdAt: z33.number().finite(),
31305
- invalidatedAt: z33.number().finite().nullable().default(null),
31306
- sourceHash: z33.string().min(1),
31307
- supersededBy: z33.string().min(1).nullable().default(null)
32361
+ confidence: z35.number().min(0).max(1),
32362
+ validFrom: z35.number().finite(),
32363
+ validUntil: z35.number().finite().nullable().default(null),
32364
+ createdAt: z35.number().finite(),
32365
+ invalidatedAt: z35.number().finite().nullable().default(null),
32366
+ sourceHash: z35.string().min(1),
32367
+ supersededBy: z35.string().min(1).nullable().default(null)
31308
32368
  }).strict();
31309
32369
  var FactSchema = MemoryBaseSchema.extend({
31310
- kind: z33.literal("fact"),
31311
- subject: z33.string().min(1),
31312
- predicate: z33.string().min(1),
31313
- object: z33.string().min(1),
31314
- category: z33.string().min(1).nullable().default(null)
32370
+ kind: z35.literal("fact"),
32371
+ subject: z35.string().min(1),
32372
+ predicate: z35.string().min(1),
32373
+ object: z35.string().min(1),
32374
+ category: z35.string().min(1).nullable().default(null)
31315
32375
  });
31316
32376
  var PreferenceSchema = MemoryBaseSchema.extend({
31317
- kind: z33.literal("preference"),
31318
- category: z33.string().min(1),
31319
- preference: z33.string().min(1),
31320
- context: z33.string().min(1).nullable().default(null),
31321
- lastAccessedAt: z33.number().finite().nullable().default(null),
31322
- accessCount: z33.number().int().min(0).default(0)
32377
+ kind: z35.literal("preference"),
32378
+ category: z35.string().min(1),
32379
+ preference: z35.string().min(1),
32380
+ context: z35.string().min(1).nullable().default(null),
32381
+ lastAccessedAt: z35.number().finite().nullable().default(null),
32382
+ accessCount: z35.number().int().min(0).default(0)
31323
32383
  });
31324
32384
  var EntitySchema = MemoryBaseSchema.extend({
31325
- kind: z33.literal("entity"),
31326
- canonicalName: z33.string().min(1),
31327
- type: z33.string().min(1),
31328
- aliases: z33.array(z33.string().min(1)).default([])
32385
+ kind: z35.literal("entity"),
32386
+ canonicalName: z35.string().min(1),
32387
+ type: z35.string().min(1),
32388
+ aliases: z35.array(z35.string().min(1)).default([])
31329
32389
  });
31330
32390
  var RelationSchema = MemoryBaseSchema.extend({
31331
- kind: z33.literal("relation"),
31332
- from: z33.string().min(1),
31333
- to: z33.string().min(1),
31334
- predicate: z33.string().min(1),
31335
- annotation: z33.string().min(1).nullable().default(null)
32391
+ kind: z35.literal("relation"),
32392
+ from: z35.string().min(1),
32393
+ to: z35.string().min(1),
32394
+ predicate: z35.string().min(1),
32395
+ annotation: z35.string().min(1).nullable().default(null)
31336
32396
  });
31337
- var MemoryRecordSchema = z33.discriminatedUnion("kind", [
32397
+ var MemoryRecordSchema = z35.discriminatedUnion("kind", [
31338
32398
  FactSchema,
31339
32399
  PreferenceSchema,
31340
32400
  EntitySchema,
31341
32401
  RelationSchema
31342
32402
  ]);
31343
- var RecallQuerySchema = z33.object({
32403
+ var RecallQuerySchema = z35.object({
31344
32404
  ownerKey: OwnerKeySchema,
31345
- kinds: z33.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
31346
- asOf: z33.number().finite().nullable().default(null),
31347
- limit: z33.number().int().positive().default(8),
31348
- minSimilarity: z33.number().min(0).max(1).default(0.2),
31349
- includeShared: z33.boolean().default(true)
32405
+ kinds: z35.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
32406
+ asOf: z35.number().finite().nullable().default(null),
32407
+ limit: z35.number().int().positive().default(8),
32408
+ minSimilarity: z35.number().min(0).max(1).default(0.2),
32409
+ includeShared: z35.boolean().default(true)
31350
32410
  }).strict();
31351
32411
 
31352
32412
  // src/memory/rank.ts
@@ -31917,42 +32977,42 @@ function buildMemoryInjector(deps, policy) {
31917
32977
 
31918
32978
  // src/plugin/memoryTool.ts
31919
32979
  import { tool as tool5 } from "@opencode-ai/plugin";
31920
- import { z as z35 } from "zod";
32980
+ import { z as z37 } from "zod";
31921
32981
 
31922
32982
  // src/memory/extract.ts
31923
- import { z as z34 } from "zod";
32983
+ import { z as z36 } from "zod";
31924
32984
  var REDACTED = "[redacted]";
31925
- var RawFactSchema = z34.object({
31926
- subject: z34.string().min(1),
31927
- predicate: z34.string().min(1),
31928
- object: z34.string().min(1),
31929
- category: z34.string().min(1).nullable().optional(),
31930
- confidence: z34.number().min(0).max(1).optional()
32985
+ var RawFactSchema = z36.object({
32986
+ subject: z36.string().min(1),
32987
+ predicate: z36.string().min(1),
32988
+ object: z36.string().min(1),
32989
+ category: z36.string().min(1).nullable().optional(),
32990
+ confidence: z36.number().min(0).max(1).optional()
31931
32991
  });
31932
- var RawPreferenceSchema = z34.object({
31933
- category: z34.string().min(1),
31934
- preference: z34.string().min(1),
31935
- context: z34.string().min(1).nullable().optional(),
31936
- confidence: z34.number().min(0).max(1).optional()
32992
+ var RawPreferenceSchema = z36.object({
32993
+ category: z36.string().min(1),
32994
+ preference: z36.string().min(1),
32995
+ context: z36.string().min(1).nullable().optional(),
32996
+ confidence: z36.number().min(0).max(1).optional()
31937
32997
  });
31938
- var RawEntitySchema = z34.object({
31939
- canonicalName: z34.string().min(1),
31940
- type: z34.string().min(1),
31941
- aliases: z34.array(z34.string().min(1)).optional(),
31942
- confidence: z34.number().min(0).max(1).optional()
32998
+ var RawEntitySchema = z36.object({
32999
+ canonicalName: z36.string().min(1),
33000
+ type: z36.string().min(1),
33001
+ aliases: z36.array(z36.string().min(1)).optional(),
33002
+ confidence: z36.number().min(0).max(1).optional()
31943
33003
  });
31944
- var RawRelationSchema = z34.object({
31945
- from: z34.string().min(1),
31946
- to: z34.string().min(1),
31947
- predicate: z34.string().min(1),
31948
- annotation: z34.string().min(1).nullable().optional(),
31949
- confidence: z34.number().min(0).max(1).optional()
33004
+ var RawRelationSchema = z36.object({
33005
+ from: z36.string().min(1),
33006
+ to: z36.string().min(1),
33007
+ predicate: z36.string().min(1),
33008
+ annotation: z36.string().min(1).nullable().optional(),
33009
+ confidence: z36.number().min(0).max(1).optional()
31950
33010
  }).strict();
31951
- var RawExtractionSchema = z34.object({
31952
- facts: z34.array(RawFactSchema).default([]),
31953
- preferences: z34.array(RawPreferenceSchema).default([]),
31954
- entities: z34.array(RawEntitySchema).default([]),
31955
- relations: z34.array(RawRelationSchema).default([])
33011
+ var RawExtractionSchema = z36.object({
33012
+ facts: z36.array(RawFactSchema).default([]),
33013
+ preferences: z36.array(RawPreferenceSchema).default([]),
33014
+ entities: z36.array(RawEntitySchema).default([]),
33015
+ relations: z36.array(RawRelationSchema).default([])
31956
33016
  });
31957
33017
  var DEFAULT_CONFIDENCE = 0.6;
31958
33018
  var SYSTEM_PROMPT = [
@@ -32206,13 +33266,13 @@ var memoryToolMessages = {
32206
33266
 
32207
33267
  // src/plugin/memoryTool.ts
32208
33268
  var SCRIBE_ROLE_ID = "scribe";
32209
- var MemoryMessageSchema = z35.object({
32210
- role: z35.enum(["system", "user", "assistant"]).describe(memoryToolMessages.messageRoleDescription),
32211
- content: z35.string().min(1).describe(memoryToolMessages.messageContentDescription)
33269
+ var MemoryMessageSchema = z37.object({
33270
+ role: z37.enum(["system", "user", "assistant"]).describe(memoryToolMessages.messageRoleDescription),
33271
+ content: z37.string().min(1).describe(memoryToolMessages.messageContentDescription)
32212
33272
  }).strict();
32213
- var MemoryWritePayloadSchema = z35.object({
32214
- messages: z35.array(MemoryMessageSchema).min(1).describe(memoryToolMessages.messagesDescription),
32215
- roleID: z35.string().min(1).default(SCRIBE_ROLE_ID).describe(memoryToolMessages.roleIDDescription)
33273
+ var MemoryWritePayloadSchema = z37.object({
33274
+ messages: z37.array(MemoryMessageSchema).min(1).describe(memoryToolMessages.messagesDescription),
33275
+ roleID: z37.string().min(1).default(SCRIBE_ROLE_ID).describe(memoryToolMessages.roleIDDescription)
32216
33276
  }).strict();
32217
33277
  function createMemoryTool(deps) {
32218
33278
  return tool5({
@@ -32396,7 +33456,7 @@ import { Session as Session2 } from "@opencode/schema/session";
32396
33456
  import { Tool as Tool2 } from "@opencode/schema/tool";
32397
33457
  import { tool as legacyTool } from "@opencode-ai/plugin/tool";
32398
33458
  import { Clock, Effect, Option, Stream } from "effect";
32399
- import { z as z37 } from "zod";
33459
+ import { z as z39 } from "zod";
32400
33460
 
32401
33461
  // src/plugin/nativeHostContext.ts
32402
33462
  import {
@@ -32458,7 +33518,7 @@ function nativeConfiguredInventory(models, config) {
32458
33518
 
32459
33519
  // src/plugin/nativeResidency.ts
32460
33520
  import { randomUUID as randomUUID4 } from "node:crypto";
32461
- import { z as z36 } from "zod";
33521
+ import { z as z38 } from "zod";
32462
33522
  var NATIVE_RESIDENCY_PROVIDER_PACKAGE = "aisdk:@ai-sdk/openai-compatible@2.0.75";
32463
33523
  var NATIVE_ADMISSION_HEADER = "x-openteam-local-admission";
32464
33524
  var MAX_ADMISSIONS = 2048;
@@ -32536,7 +33596,7 @@ function createNativeModelResidencyGuard(config, options) {
32536
33596
  });
32537
33597
  }
32538
33598
  function target(model, send, baseURL) {
32539
- const headers = new Headers(z36.record(z36.string(), z36.string()).parse(model.headers ?? {}));
33599
+ const headers = new Headers(z38.record(z38.string(), z38.string()).parse(model.headers ?? {}));
32540
33600
  if (typeof model.settings?.apiKey === "string" && !headers.has("authorization"))
32541
33601
  headers.set("authorization", `Bearer ${model.settings.apiKey}`);
32542
33602
  return {
@@ -32574,7 +33634,7 @@ function createNativeModelResidencyGuard(config, options) {
32574
33634
  localTargets.set(key(ref), target(model, guardedFetch(binding, options.fetch), binding.baseURL));
32575
33635
  return model;
32576
33636
  } catch (error) {
32577
- if (!(error instanceof ModelResidencyError) && !(error instanceof z36.ZodError))
33637
+ if (!(error instanceof ModelResidencyError) && !(error instanceof z38.ZodError))
32578
33638
  throw error;
32579
33639
  (options.warn ?? console.warn)(modelResidencyMessages.providerDisabled(model.providerID, residentRuntimes.find((runtime) => runtime.defaultModel.providerID === model.providerID)?.kind === "llama-swap" ? "llama-swap" : "Lemonade"));
32580
33640
  return { ...model, enabled: false };
@@ -32731,6 +33791,10 @@ function nativeAssistantResponse(sessionID, message2) {
32731
33791
  ...message2.cost === undefined ? {} : { cost: message2.cost },
32732
33792
  ...message2.tokens === undefined ? {} : { tokens: message2.tokens },
32733
33793
  ...message2.error === undefined ? {} : { error: message2.error },
33794
+ ...message2.finish === undefined ? {} : { finish: message2.finish },
33795
+ ...message2.rawFinish === undefined ? {} : { rawFinish: message2.rawFinish },
33796
+ hasGeneratedOutput: message2.content.some((part) => part.type === "tool" || part.text.length > 0),
33797
+ truncatedToolCall: message2.finish === "length" && message2.content.some((part) => part.type === "tool" && part.executed === false && part.state.status === "error" && part.state.error.type === "tool.input-json"),
32734
33798
  time: {
32735
33799
  created: DateTime.toEpochMillis(message2.time.created),
32736
33800
  ...message2.time.completed === undefined ? {} : { completed: DateTime.toEpochMillis(message2.time.completed) }
@@ -32757,7 +33821,8 @@ function createNativeSessionClient(deps) {
32757
33821
  }
32758
33822
  return {
32759
33823
  async create(args) {
32760
- const signal = deps.signal?.();
33824
+ const parentSignal = deps.signal?.();
33825
+ const signal = parentSignal === undefined ? args.signal : args.signal === undefined ? parentSignal : AbortSignal.any([parentSignal, args.signal]);
32761
33826
  signal?.throwIfAborted();
32762
33827
  assertDirectory(args.query?.directory);
32763
33828
  if (args.body?.parentID === undefined)
@@ -32767,7 +33832,7 @@ function createNativeSessionClient(deps) {
32767
33832
  const child = await deps.run(deps.sessions().create({
32768
33833
  parentID,
32769
33834
  ...args.body.title === undefined ? {} : { title: args.body.title }
32770
- }));
33835
+ }), signal === undefined ? undefined : { signal });
32771
33836
  if (!sameNativeWorkspace(child.location, location))
32772
33837
  throw new Error(nativeHostMessages.workspaceMismatch);
32773
33838
  if (child.parentID !== parentID)
@@ -32806,6 +33871,11 @@ function createNativeSessionClient(deps) {
32806
33871
  const assistant = [...messages].reverse().find((message2) => message2.type === "assistant");
32807
33872
  if (final.outcome === "interrupted")
32808
33873
  throw new Error(nativeHostMessages.executionInterrupted);
33874
+ if (assistant?.time.completed !== undefined) {
33875
+ const response = nativeAssistantResponse(sessionID, assistant);
33876
+ if (response.info.truncatedToolCall)
33877
+ return { data: response, error: undefined };
33878
+ }
32809
33879
  if (final.outcome !== "succeeded" || assistant?.error !== undefined)
32810
33880
  throw new Error(nativeHostMessages.executionFailed);
32811
33881
  if (assistant?.time.completed === undefined)
@@ -32845,8 +33915,8 @@ function createNativeSessionClient(deps) {
32845
33915
  }
32846
33916
 
32847
33917
  // src/plugin/nativeHost.ts
32848
- var executionEvent = z37.preprocess(only(["type", "location", "data"]), z37.object({
32849
- type: z37.enum([
33918
+ var executionEvent = z39.preprocess(only(["type", "location", "data"]), z39.object({
33919
+ type: z39.enum([
32850
33920
  "session.created",
32851
33921
  "session.deleted",
32852
33922
  "session.execution.started",
@@ -32854,15 +33924,30 @@ var executionEvent = z37.preprocess(only(["type", "location", "data"]), z37.obje
32854
33924
  "session.execution.failed",
32855
33925
  "session.execution.interrupted",
32856
33926
  "session.step.ended",
32857
- "session.step.failed"
33927
+ "session.step.started",
33928
+ "session.step.failed",
33929
+ "session.tool.input.started",
33930
+ "session.tool.success",
33931
+ "session.tool.failed"
32858
33932
  ]),
32859
- location: z37.preprocess(only(["directory", "workspaceID"]), z37.object({
32860
- directory: z37.string(),
32861
- workspaceID: z37.string().optional()
33933
+ location: z39.preprocess(only(["directory", "workspaceID"]), z39.object({
33934
+ directory: z39.string(),
33935
+ workspaceID: z39.string().optional()
32862
33936
  }).strict()).optional(),
32863
- data: z37.preprocess(only(["sessionID", "assistantMessageID"]), z37.object({
32864
- sessionID: z37.string(),
32865
- assistantMessageID: z37.string().optional()
33937
+ data: z39.preprocess(only([
33938
+ "sessionID",
33939
+ "assistantMessageID",
33940
+ "id",
33941
+ "name",
33942
+ "executed",
33943
+ "error"
33944
+ ]), z39.object({
33945
+ sessionID: z39.string(),
33946
+ assistantMessageID: z39.string().optional(),
33947
+ id: z39.string().optional(),
33948
+ name: z39.string().optional(),
33949
+ executed: z39.boolean().optional(),
33950
+ error: z39.preprocess(only(["type"]), z39.object({ type: z39.string() }).strict()).optional()
32866
33951
  }).strict())
32867
33952
  }).strict());
32868
33953
  var nativeBindings = Effect.fn("openteam.nativeBindings")(function* () {
@@ -32903,7 +33988,7 @@ function legacyModel(ref) {
32903
33988
  }
32904
33989
  function createNativePluginEffect(factory) {
32905
33990
  return Effect.fn("openteam.native")(function* (ctx) {
32906
- if (ctx.app.version !== NATIVE_OPENCODE_VERSION)
33991
+ if (!isSupportedNativeOpencodeVersion(ctx.app.version))
32907
33992
  return yield* Effect.die(new Error(nativeHostMessages.unsupportedVersion(ctx.app.version)));
32908
33993
  const version = ctx.app.version;
32909
33994
  const effectContext = yield* Effect.context();
@@ -32917,6 +34002,15 @@ function createNativePluginEffect(factory) {
32917
34002
  let config;
32918
34003
  let residency;
32919
34004
  const systemPrompts = new Map;
34005
+ const toolNames = new Map;
34006
+ const rememberTool = (sessionID, callID, name) => {
34007
+ toolNames.set(`${sessionID}\x00${callID}`, name);
34008
+ if (toolNames.size > 8192) {
34009
+ const oldest = toolNames.keys().next().value;
34010
+ if (oldest !== undefined)
34011
+ toolNames.delete(oldest);
34012
+ }
34013
+ };
32920
34014
  const toolSignals = new AsyncLocalStorage;
32921
34015
  const remember = (next) => {
32922
34016
  bindings = { ...bindings, ...next };
@@ -32971,6 +34065,12 @@ function createNativePluginEffect(factory) {
32971
34065
  directory,
32972
34066
  worktree: ctx.location.project.directory,
32973
34067
  client,
34068
+ toolOutcomesFromEvents: true,
34069
+ parentSession: async (sessionID) => {
34070
+ const info = await run(requiredBindings().sessions.get(Session2.ID.make(sessionID)));
34071
+ assertWorkspace(info.location);
34072
+ return info.parentID;
34073
+ },
32974
34074
  exec: nativeExec(directory),
32975
34075
  serverUrl: () => {
32976
34076
  const url = bindings?.server?.urls().find((value) => {
@@ -33015,6 +34115,7 @@ function createNativePluginEffect(factory) {
33015
34115
  yield* Effect.addFinalizer(() => Effect.promise(async () => {
33016
34116
  guard.dispose();
33017
34117
  systemPrompts.clear();
34118
+ toolNames.clear();
33018
34119
  await hooks.dispose?.();
33019
34120
  }));
33020
34121
  yield* ctx.provider.transform(guard.providers);
@@ -33147,7 +34248,10 @@ function createNativePluginEffect(factory) {
33147
34248
  });
33148
34249
  }
33149
34250
  });
33150
- yield* ctx.tool.hook("execute.before", (event) => Effect.promise(() => hooks["tool.execute.before"]?.({ tool: event.tool, sessionID: event.sessionID, callID: event.id }, { args: event.input }) ?? Promise.resolve()));
34251
+ yield* ctx.tool.hook("execute.before", (event) => Effect.promise(() => {
34252
+ rememberTool(event.sessionID, event.id, event.tool);
34253
+ return hooks["tool.execute.before"]?.({ tool: event.tool, sessionID: event.sessionID, callID: event.id }, { args: event.input }) ?? Promise.resolve();
34254
+ }));
33151
34255
  yield* ctx.tool.hook("execute.after", (event) => Effect.promise(() => hooks["tool.execute.after"]?.({
33152
34256
  tool: event.tool,
33153
34257
  sessionID: event.sessionID,
@@ -33174,9 +34278,62 @@ function createNativePluginEffect(factory) {
33174
34278
  if (value.type === "session.deleted") {
33175
34279
  guard.finished(sessionID);
33176
34280
  systemPrompts.delete(sessionID);
34281
+ for (const key2 of toolNames.keys()) {
34282
+ if (key2.startsWith(`${sessionID}\x00`))
34283
+ toolNames.delete(key2);
34284
+ }
33177
34285
  }
33178
34286
  let mapped;
33179
- if (value.type === "session.created" || value.type === "session.deleted") {
34287
+ if (value.type === "session.tool.input.started") {
34288
+ if (value.data.id !== undefined && value.data.name !== undefined)
34289
+ rememberTool(sessionID, value.data.id, value.data.name);
34290
+ await hooks.event?.({
34291
+ event: {
34292
+ type: "openteam.session.phase",
34293
+ properties: { sessionID, phase: "tool-input" }
34294
+ }
34295
+ });
34296
+ return;
34297
+ } else if (value.type === "session.step.started") {
34298
+ mapped = {
34299
+ type: "openteam.session.phase",
34300
+ properties: { sessionID, phase: "model-request" }
34301
+ };
34302
+ } else if (value.type === "session.tool.success" || value.type === "session.tool.failed") {
34303
+ if (value.data.id === undefined)
34304
+ return;
34305
+ const key2 = `${sessionID}\x00${value.data.id}`;
34306
+ let name = toolNames.get(key2);
34307
+ if (name === undefined && bindings !== undefined) {
34308
+ const messages = await run(bindings.sessions.messages({
34309
+ sessionID: Session2.ID.make(sessionID),
34310
+ limit: 100
34311
+ }));
34312
+ const message2 = messages.find((candidate) => candidate.id === value.data.assistantMessageID);
34313
+ if (message2?.type === "assistant") {
34314
+ const part = message2.content.find((candidate) => candidate.type === "tool" && candidate.id === value.data.id);
34315
+ if (part?.type === "tool")
34316
+ name = part.name;
34317
+ }
34318
+ }
34319
+ const ok2 = value.type === "session.tool.success";
34320
+ const inputFailure = value.data.error?.type === "tool.input-json" || value.data.error?.type === "tool.input-schema";
34321
+ const executionReached = ok2 || value.data.error?.type === "tool.execution";
34322
+ mapped = {
34323
+ type: "openteam.tool.completed",
34324
+ properties: {
34325
+ sessionID,
34326
+ callID: value.data.id,
34327
+ tool: name ?? "unknown",
34328
+ ok: ok2,
34329
+ ...inputFailure ? { executed: false } : executionReached ? { executed: true } : {},
34330
+ ...value.data.executed === undefined ? {} : { providerExecuted: value.data.executed },
34331
+ ...!ok2 ? {
34332
+ failureKind: inputFailure ? "input-invalid" : executionReached ? "execution" : "unknown"
34333
+ } : {}
34334
+ }
34335
+ };
34336
+ } else if (value.type === "session.created" || value.type === "session.deleted") {
33180
34337
  mapped = {
33181
34338
  type: value.type,
33182
34339
  properties: { info: { id: sessionID } }
@@ -33217,12 +34374,13 @@ function createNativePluginEffect(factory) {
33217
34374
  }
33218
34375
 
33219
34376
  // src/plugin/orchestrateTool.ts
34377
+ import { resolve as resolve16 } from "node:path";
33220
34378
  import { tool as tool6 } from "@opencode-ai/plugin";
33221
- import { z as z38 } from "zod";
34379
+ import { z as z40 } from "zod";
33222
34380
  var _pluginWarnedRoles = new Set;
33223
34381
  var EMPTY_ROLE_ID_SET = new Set;
33224
34382
  var SAFE_ROLE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
33225
- var RoleIDSchema = z38.string().min(1).superRefine((roleID, ctx) => {
34383
+ var RoleIDSchema = z40.string().min(1).superRefine((roleID, ctx) => {
33226
34384
  if (!SAFE_ROLE_ID.test(roleID)) {
33227
34385
  ctx.addIssue({
33228
34386
  code: "custom",
@@ -33230,16 +34388,16 @@ var RoleIDSchema = z38.string().min(1).superRefine((roleID, ctx) => {
33230
34388
  });
33231
34389
  }
33232
34390
  });
33233
- var RoleAssignmentSchema = z38.object({
34391
+ var RoleAssignmentSchema = z40.object({
33234
34392
  roleID: RoleIDSchema.describe("Role identifier from the roster"),
33235
- prompt: z38.string().min(1).describe("Work prompt for this role"),
33236
- title: z38.string().min(1).optional().describe("Human-readable title"),
33237
- dependsOn: z38.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
34393
+ prompt: z40.string().min(1).describe("Work prompt for this role"),
34394
+ title: z40.string().min(1).optional().describe("Human-readable title"),
34395
+ dependsOn: z40.array(RoleIDSchema).optional().describe("Role IDs this assignment depends on (DAG edges)")
33238
34396
  }).strict();
33239
- var OrchestratePayloadSchema = z38.object({
33240
- assignments: z38.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
33241
- parentSessionID: z38.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
33242
- directory: z38.string().min(1).optional().describe("Working directory override")
34397
+ var OrchestratePayloadSchema = z40.object({
34398
+ assignments: z40.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
34399
+ parentSessionID: z40.string().min(1).optional().describe(PARENT_SESSION_DESCRIPTION),
34400
+ directory: z40.string().min(1).optional().describe("Working directory override")
33243
34401
  }).strict();
33244
34402
  function validateAssignmentDependencies(assignments) {
33245
34403
  const ids = new Set;
@@ -33424,8 +34582,15 @@ function adaptSdkClient(sdk, orchestratedModelOwnership) {
33424
34582
  if (result.error !== undefined) {
33425
34583
  out.error = normalizeSdkSessionFailure(result.error, "prompt");
33426
34584
  } else {
34585
+ const info = isRecord8(result.data) ? nestedRecord2(result.data, "info") : undefined;
33427
34586
  const nestedFailure = nestedAssistantFailure(result.data);
33428
- if (nestedFailure !== undefined) {
34587
+ if (info?.finish === "length" && info.truncatedToolCall === true) {
34588
+ out.error = {
34589
+ kind: "normalized-session-failure",
34590
+ failureClass: "prompt-output-truncated",
34591
+ retryable: true
34592
+ };
34593
+ } else if (nestedFailure !== undefined) {
33429
34594
  out.error = normalizeSdkSessionFailure(nestedFailure, "prompt");
33430
34595
  } else if (result.data !== undefined) {
33431
34596
  out.data = result.data;
@@ -33585,7 +34750,10 @@ function mapTelemetryFailureClassToErrorClass(cls) {
33585
34750
  case "prompt-rate-limited":
33586
34751
  case "prompt-connection":
33587
34752
  case "prompt-server":
34753
+ case "prompt-output-truncated":
33588
34754
  return "transient";
34755
+ case "budget-exhausted":
34756
+ return "permanent";
33589
34757
  case "create-rejected":
33590
34758
  case "create-no-id":
33591
34759
  case "prompt-rejected":
@@ -33868,8 +35036,16 @@ function createOrchestrateTool(deps) {
33868
35036
  }
33869
35037
  const parentOverrideIgnored = parsed.data.parentSessionID !== undefined && parsed.data.parentSessionID !== context.sessionID;
33870
35038
  parsed.data.parentSessionID = context.sessionID;
33871
- if (parsed.data.directory === undefined && context.directory !== undefined)
33872
- parsed.data.directory = context.directory;
35039
+ if (deps.workspaceDirectory !== undefined) {
35040
+ const workspace = resolve16(deps.workspaceDirectory);
35041
+ if (resolve16(workspace, parsed.data.directory ?? ".") !== workspace)
35042
+ return reportDeliveryMessages.workspaceMismatch;
35043
+ parsed.data.directory = workspace;
35044
+ } else if (parsed.data.directory === undefined) {
35045
+ const directory = context.directory;
35046
+ if (directory !== undefined)
35047
+ parsed.data.directory = directory;
35048
+ }
33873
35049
  const depError = validateAssignmentDependencies(parsed.data.assignments);
33874
35050
  if (depError !== undefined) {
33875
35051
  return dependencyValidationError(depError.code, depError.detail);
@@ -33892,6 +35068,14 @@ function createOrchestrateTool(deps) {
33892
35068
  return missingAgentDefinition(agentName, input.roleID);
33893
35069
  }
33894
35070
  }
35071
+ const taskRef = await deps.taskEffort?.forSession(context.sessionID);
35072
+ if (taskRef !== undefined && deps.taskEffort !== undefined) {
35073
+ const effort2 = deps.taskEffort.batch(taskRef);
35074
+ if (!effort2.ok)
35075
+ return taskEffortMessages.exhausted(effort2.reason);
35076
+ for (const input of inputs)
35077
+ input.taskID = taskRef.taskID;
35078
+ }
33895
35079
  const batchID = deps.newDecisionID();
33896
35080
  if (deps.lifecycleRecorder !== undefined) {
33897
35081
  const rootOperationID = operationID(batchID, "openteam");
@@ -33901,7 +35085,7 @@ function createOrchestrateTool(deps) {
33901
35085
  executionAuthority: "coordinator",
33902
35086
  root: {
33903
35087
  operationID: rootOperationID,
33904
- taskID: batchID,
35088
+ taskID: taskRef?.taskID ?? batchID,
33905
35089
  kind: "coordinator",
33906
35090
  roleID: "openteam",
33907
35091
  agentID: context.agent,
@@ -33926,6 +35110,12 @@ function createOrchestrateTool(deps) {
33926
35110
  reportedModelSlots: deps.reportedModelSlots,
33927
35111
  readReportArtifact: deps.readReportArtifact,
33928
35112
  reportArtifactExists: deps.reportArtifactExists,
35113
+ ...deps.reportDelivery === undefined ? {} : { reportDelivery: deps.reportDelivery },
35114
+ ...deps.sessionPhase === undefined ? {} : { sessionPhase: deps.sessionPhase },
35115
+ signal: context.abort,
35116
+ ...deps.taskEffort === undefined || taskRef === undefined ? {} : {
35117
+ taskEffort: { ledger: deps.taskEffort, ref: taskRef }
35118
+ },
33929
35119
  abortGraceMs: deps.abortGraceMs,
33930
35120
  statusPollMs: deps.statusPollMs,
33931
35121
  delay: deps.delay,
@@ -33943,10 +35133,15 @@ function createOrchestrateTool(deps) {
33943
35133
  lifecycleStatus = activeLifecycleRecorder?.status() ?? lifecycleStatus;
33944
35134
  const projected = projectResults(batch.results, batch.cancelled);
33945
35135
  const shadowResult = await runShadowProducer(deps, parsed.data, batch);
35136
+ const effort = taskRef === undefined ? undefined : deps.taskEffort?.check(taskRef);
35137
+ const effortSummary = effort === undefined ? "" : `
35138
+
35139
+ ${effort.ok ? taskEffortMessages.summary(effort.batches, effort.attempts, effort.remainingMs, effort.remainingBatches, effort.remainingAttempts) : taskEffortMessages.exhausted(effort.reason)}`;
33946
35140
  const base = formatResult(projected) + (parentOverrideIgnored ? `
33947
35141
 
33948
- ${PARENT_OVERRIDE_IGNORED}` : "");
33949
- const advisory = singleAssignmentAdvisory(parsed.data.assignments.length, assignableRoleIDs(deps.config).length);
35142
+ ${PARENT_OVERRIDE_IGNORED}` : "") + effortSummary;
35143
+ const exhausted = effort?.ok === false || effort?.ok === true && (effort.remainingBatches === 0 || effort.remainingAttempts === 0);
35144
+ const advisory = exhausted ? undefined : singleAssignmentAdvisory(parsed.data.assignments.length, assignableRoleIDs(deps.config).length);
33950
35145
  const withAdvisory = advisory === undefined ? base : `${base}
33951
35146
 
33952
35147
  ${advisory}`;
@@ -33977,7 +35172,7 @@ ${advisory}`;
33977
35172
 
33978
35173
  // src/plugin/registerCastTool.ts
33979
35174
  import { tool as tool7 } from "@opencode-ai/plugin";
33980
- import { z as z39 } from "zod";
35175
+ import { z as z41 } from "zod";
33981
35176
 
33982
35177
  // src/messages/rosterSummary.ts
33983
35178
  var ROSTER_SUMMARY_HEADING = "Registered team model policies";
@@ -34093,12 +35288,12 @@ function createTeamPreparation(deps) {
34093
35288
  OpenTeamConfigSchema.parse(migrateConfig(baseRaw));
34094
35289
  const overlay = overlayBefore === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayBefore);
34095
35290
  const options = configOptionsFromPluginOptions(deps.options);
34096
- const resolve16 = (raw) => {
35291
+ const resolve17 = (raw) => {
34097
35292
  const config = validateConfigInput(TEAM_PREPARATION_CONFIG, mergeConfigInputs(mergeConfigInputs(raw, options), overlay));
34098
35293
  assertEffectiveConfigModelDomains(config);
34099
35294
  return config;
34100
35295
  };
34101
- const current = resolve16(baseRaw);
35296
+ const current = resolve17(baseRaw);
34102
35297
  const live = liveConfig ?? current;
34103
35298
  if (resolveExecutionMode({
34104
35299
  routerMode: current.router.executionMode,
@@ -34120,7 +35315,7 @@ function createTeamPreparation(deps) {
34120
35315
  ]))
34121
35316
  }
34122
35317
  });
34123
- const effective = resolve16(candidate);
35318
+ const effective = resolve17(candidate);
34124
35319
  const requestedProfiles = new Set(roster.entries.filter((entry) => !isKnownNonWorkerRole(entry.roleID) && getRoleProfile(entry.roleID, live.orchestrator?.roles) === undefined).map((entry) => getRoleProfile(entry.roleID, effective.orchestrator?.roles)));
34125
35320
  const additions = Object.fromEntries(Object.entries(effective.orchestrator?.roles ?? {}).filter(([key2, profile]) => requestedProfiles.has(profile) && !Object.hasOwn(live.orchestrator?.roles ?? {}, key2)));
34126
35321
  const hasAdditions = Object.keys(additions).length > 0;
@@ -34206,13 +35401,13 @@ async function persistPreparedTeam(storage, files) {
34206
35401
  }
34207
35402
 
34208
35403
  // src/plugin/registerCastTool.ts
34209
- var CastEntrySchema = z39.object({
34210
- roleID: z39.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
34211
- agentName: z39.string().min(1).describe("Themed character name assigned to the role")
35404
+ var CastEntrySchema = z41.object({
35405
+ roleID: z41.string().min(1).describe("Stable role identifier, e.g. scribe or guardian"),
35406
+ agentName: z41.string().min(1).describe("Themed character name assigned to the role")
34212
35407
  }).strict();
34213
- var RegisterCastPayloadSchema = z39.object({
34214
- universe: z39.string().min(1).describe("Casting universe for this roster"),
34215
- entries: z39.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
35408
+ var RegisterCastPayloadSchema = z41.object({
35409
+ universe: z41.string().min(1).describe("Casting universe for this roster"),
35410
+ entries: z41.array(CastEntrySchema).min(1).describe("Role-to-name mappings")
34216
35411
  }).strict();
34217
35412
  function offendingFields(error) {
34218
35413
  const fields = error.issues.flatMap((issue) => {
@@ -34332,7 +35527,7 @@ function createRegisterCastTool(deps) {
34332
35527
  }
34333
35528
 
34334
35529
  // src/plugin/reloadTool.ts
34335
- import { resolve as resolve16 } from "node:path";
35530
+ import { resolve as resolve17 } from "node:path";
34336
35531
  import { tool as tool8 } from "@opencode-ai/plugin/tool";
34337
35532
  var WAIT_MS = 30000;
34338
35533
  var SDK_TIMEOUT_MS = 5000;
@@ -34366,7 +35561,7 @@ function createReloadController(deps) {
34366
35561
  return () => clearTimeout(timer);
34367
35562
  }
34368
35563
  };
34369
- const directory = resolve16(deps.directory);
35564
+ const directory = resolve17(deps.directory);
34370
35565
  let pending;
34371
35566
  let authorizing;
34372
35567
  let closed = false;
@@ -34514,7 +35709,7 @@ function createReloadController(deps) {
34514
35709
  const parsed = schema.safeParse(args);
34515
35710
  if (!parsed.success)
34516
35711
  return reloadMessages.invalid;
34517
- if (context.agent !== "openteam" || resolve16(context.directory) !== directory || context.sessionID.trim().length === 0)
35712
+ if (context.agent !== "openteam" || resolve17(context.directory) !== directory || context.sessionID.trim().length === 0)
34518
35713
  return reloadMessages.denied;
34519
35714
  if (parsed.data.action === "cancel") {
34520
35715
  if ((pending?.sessionID ?? authorizing?.sessionID) !== context.sessionID)
@@ -34632,6 +35827,31 @@ function createReloadController(deps) {
34632
35827
  };
34633
35828
  }
34634
35829
 
35830
+ // src/plugin/reportTool.ts
35831
+ import { tool as tool9 } from "@opencode-ai/plugin";
35832
+ function createReportTool(delivery) {
35833
+ return tool9({
35834
+ description: reportDeliveryMessages.description,
35835
+ args: {
35836
+ status: tool9.schema.enum(["done", "continue"]).describe(reportDeliveryMessages.status),
35837
+ findings: tool9.schema.string().min(1).max(32768).describe(reportDeliveryMessages.findings)
35838
+ },
35839
+ async execute(args, context) {
35840
+ context.abort.throwIfAborted();
35841
+ const path4 = delivery.path(context.sessionID);
35842
+ await context.ask({
35843
+ permission: "openteam-submit-report",
35844
+ patterns: [path4],
35845
+ always: [],
35846
+ metadata: { reportPath: path4 }
35847
+ });
35848
+ context.abort.throwIfAborted();
35849
+ await delivery.submit(context.sessionID, args);
35850
+ return reportDeliveryMessages.submitted(path4);
35851
+ }
35852
+ });
35853
+ }
35854
+
34635
35855
  // src/plugin/sessionTracker.ts
34636
35856
  function createSessionTracker() {
34637
35857
  const knownSessions = new Set;
@@ -34660,22 +35880,40 @@ function createSessionTracker() {
34660
35880
  }
34661
35881
 
34662
35882
  // src/plugin/toolcalls.ts
35883
+ var MAX_TRACKED_CALLS = 8192;
34663
35884
  function key2(sessionID, callID) {
34664
35885
  return `${sessionID}\x00${callID}`;
34665
35886
  }
34666
35887
  function createToolcallTracker(deps) {
34667
35888
  const now = deps.now ?? Date.now;
34668
35889
  const started = new Map;
35890
+ const completed = new Set;
34669
35891
  return {
34670
35892
  before(input) {
34671
- started.set(key2(input.sessionID, input.callID), now());
35893
+ const mapKey = key2(input.sessionID, input.callID);
35894
+ if (completed.has(mapKey) || started.has(mapKey))
35895
+ return;
35896
+ started.set(mapKey, now());
35897
+ if (started.size > MAX_TRACKED_CALLS) {
35898
+ const oldest = started.keys().next().value;
35899
+ if (oldest !== undefined)
35900
+ started.delete(oldest);
35901
+ }
34672
35902
  },
34673
35903
  after(input) {
34674
35904
  const mapKey = key2(input.sessionID, input.callID);
35905
+ if (completed.has(mapKey))
35906
+ return;
35907
+ completed.add(mapKey);
35908
+ if (completed.size > MAX_TRACKED_CALLS) {
35909
+ const oldest = completed.values().next().value;
35910
+ if (oldest !== undefined)
35911
+ completed.delete(oldest);
35912
+ }
34675
35913
  const start = started.get(mapKey);
34676
35914
  started.delete(mapKey);
34677
35915
  const end = now();
34678
- const durationMs = start === undefined ? 0 : Math.max(0, end - start);
35916
+ const durationMs = input.durationMs !== undefined ? input.durationMs : start === undefined ? null : Math.max(0, Math.trunc(end - start));
34679
35917
  const event = {
34680
35918
  v: EVENT_SCHEMA_VERSION,
34681
35919
  type: "toolcall",
@@ -34684,9 +35922,23 @@ function createToolcallTracker(deps) {
34684
35922
  tool: input.tool,
34685
35923
  callID: input.callID,
34686
35924
  durationMs,
34687
- ok: true
35925
+ ok: input.ok ?? true,
35926
+ ...input.executed === undefined ? {} : { executed: input.executed },
35927
+ ...input.providerExecuted === undefined ? {} : { providerExecuted: input.providerExecuted },
35928
+ ...input.failureKind === undefined ? {} : { failureKind: input.failureKind }
34688
35929
  };
34689
35930
  return deps.sink.emit(event);
35931
+ },
35932
+ forgetSession(sessionID) {
35933
+ const prefix = `${sessionID}\x00`;
35934
+ for (const mapKey of started.keys()) {
35935
+ if (mapKey.startsWith(prefix))
35936
+ started.delete(mapKey);
35937
+ }
35938
+ for (const mapKey of completed) {
35939
+ if (mapKey.startsWith(prefix))
35940
+ completed.delete(mapKey);
35941
+ }
34690
35942
  }
34691
35943
  };
34692
35944
  }
@@ -34874,6 +36126,61 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
34874
36126
  }
34875
36127
  }
34876
36128
 
36129
+ // src/storage/scopedArtifacts.ts
36130
+ import { lstatSync as lstatSync4, mkdirSync as mkdirSync4, realpathSync as realpathSync3 } from "node:fs";
36131
+ import { dirname as dirname10, join as join22 } from "node:path";
36132
+ function createScopedArtifactStore(directory, root) {
36133
+ function pathFor(name) {
36134
+ const workspace = realpathSync3(directory);
36135
+ const segments = [...splitVirtualPath(root), ...splitVirtualPath(name)];
36136
+ const filename = segments.pop();
36137
+ if (!filename)
36138
+ throw new Error(reportDeliveryMessages.unsafePath);
36139
+ let path4 = workspace;
36140
+ for (const segment of segments) {
36141
+ path4 = join22(path4, segment);
36142
+ try {
36143
+ mkdirSync4(path4, { mode: 448 });
36144
+ } catch (error) {
36145
+ if (!(typeof error === "object" && error !== null && ("code" in error) && error.code === "EEXIST"))
36146
+ throw error;
36147
+ }
36148
+ const stat4 = lstatSync4(path4);
36149
+ if (!stat4.isDirectory() || stat4.isSymbolicLink())
36150
+ throw new Error(reportDeliveryMessages.unsafePath);
36151
+ }
36152
+ const target = join22(path4, filename);
36153
+ try {
36154
+ const stat4 = lstatSync4(target);
36155
+ if (!stat4.isFile() || stat4.isSymbolicLink() || stat4.size > 1048576)
36156
+ throw new Error(reportDeliveryMessages.unsafePath);
36157
+ } catch (error) {
36158
+ if (!isMissingFsError(error))
36159
+ throw error;
36160
+ }
36161
+ return target;
36162
+ }
36163
+ return {
36164
+ read(name) {
36165
+ return readUtf8(pathFor(name));
36166
+ },
36167
+ write(name, content) {
36168
+ if (Buffer.byteLength(content, "utf8") > 1048576)
36169
+ throw new Error(reportDeliveryMessages.unsafePath);
36170
+ writeDurableAtomically(pathFor(name), content);
36171
+ },
36172
+ update(name, change) {
36173
+ const path4 = pathFor(name);
36174
+ withRunMutationLock(dirname10(path4), () => {
36175
+ const content = change(readUtf8(path4));
36176
+ if (Buffer.byteLength(content, "utf8") > 1048576)
36177
+ throw new Error(reportDeliveryMessages.unsafePath);
36178
+ writeDurableAtomically(path4, content);
36179
+ });
36180
+ }
36181
+ };
36182
+ }
36183
+
34877
36184
  // src/telemetry/otelExporter.ts
34878
36185
  var DEFAULT_SERVICE_NAME = "openteam";
34879
36186
  function tracesUrl(ingestionEndpoint) {
@@ -35018,7 +36325,7 @@ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
35018
36325
  var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
35019
36326
  async function readPackagedCertificate(path4) {
35020
36327
  try {
35021
- const hostDirectory = basename4(dirname10(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
36328
+ const hostDirectory = basename4(dirname11(path4)) === NATIVE_CERTIFICATE_DIRECTORY ? `${NATIVE_CERTIFICATE_DIRECTORY}/` : "";
35022
36329
  return await readFile5(new URL(`./certificates/${hostDirectory}${basename4(path4)}`, import.meta.url), "utf8");
35023
36330
  } catch {
35024
36331
  return;
@@ -35143,8 +36450,8 @@ async function tryReadOpencodeVersion(serverUrl, fetchFn, client) {
35143
36450
  }
35144
36451
  var DEFAULT_OPENCODE_VERSION_BACKOFF_MS = [50, 250];
35145
36452
  function defaultSleep(ms2) {
35146
- return new Promise((resolve17) => {
35147
- setTimeout(resolve17, ms2);
36453
+ return new Promise((resolve19) => {
36454
+ setTimeout(resolve19, ms2);
35148
36455
  });
35149
36456
  }
35150
36457
  function createLazyOpencodeVersionReader(serverUrl, options = {}) {
@@ -35187,7 +36494,7 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
35187
36494
  };
35188
36495
  }
35189
36496
  async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
35190
- const hostPath = (path4) => opencodeVersion === NATIVE_OPENCODE_VERSION ? `${dirname10(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
36497
+ const hostPath = (path4) => isSupportedNativeOpencodeVersion(opencodeVersion) ? `${dirname11(path4)}/${NATIVE_CERTIFICATE_DIRECTORY}/${basename4(path4)}` : path4;
35191
36498
  const shadow = await readAndParseCertificate(storage, hostPath(SHADOW_CERT_PATH), (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
35192
36499
  const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
35193
36500
  const release = await readAndParseCertificate(storage, hostPath(RELEASE_CERT_PATH), (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
@@ -35396,7 +36703,7 @@ function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SE
35396
36703
  telemetryPath,
35397
36704
  opencodeConfigPaths: OPENCODE_CONFIG_CANDIDATES,
35398
36705
  orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
35399
- agentDir: dirname10(ORCHESTRATOR_AGENT_PATH),
36706
+ agentDir: dirname11(ORCHESTRATOR_AGENT_PATH),
35400
36707
  cachePort: realCacheAdapter,
35401
36708
  purge: createPluginPurgeRuntime(sessionsDir),
35402
36709
  loadRosterForDoctor: () => readStoredRosterForDoctor(storage),
@@ -35547,6 +36854,24 @@ async function createPluginRuntime(ctx, rawOptions) {
35547
36854
  return version === "unknown" ? undefined : version;
35548
36855
  }, workspaceRoot);
35549
36856
  const toolcalls = createToolcallTracker({ sink });
36857
+ const sessionPhase = async (sessionID, phase) => {
36858
+ try {
36859
+ await sink.emit({
36860
+ v: 1,
36861
+ type: "activity",
36862
+ ts: now(),
36863
+ sessionID,
36864
+ kind: "agent",
36865
+ summary: executionPhaseMessages[phase],
36866
+ phase
36867
+ });
36868
+ } catch (error) {
36869
+ logTelemetryError(error, diagnostics.warn("telemetry-write-failed", {
36870
+ persist: false,
36871
+ toast: false
36872
+ }));
36873
+ }
36874
+ };
35550
36875
  const announcedSessions = new Set;
35551
36876
  const announceEndpoint = (sessionID) => {
35552
36877
  if (sessionID === undefined || announcedSessions.has(sessionID)) {
@@ -35667,6 +36992,18 @@ async function createPluginRuntime(ctx, rawOptions) {
35667
36992
  },
35668
36993
  now
35669
36994
  });
36995
+ const reportStore = createScopedArtifactStore(ctx.directory, ROLE_REPORTS_ROOT);
36996
+ const taskEffort = createTaskEffort({
36997
+ store: createScopedArtifactStore(ctx.directory, ".opencode/openteam-local/effort"),
36998
+ now,
36999
+ ...config.orchestrator?.taskBudget === undefined ? {} : { limits: config.orchestrator.taskBudget },
37000
+ ...ctx.parentSession === undefined ? {} : { parentSession: ctx.parentSession }
37001
+ });
37002
+ const reportDelivery = createReportDelivery({
37003
+ write: async (path4, content) => {
37004
+ reportStore.write(path4.slice(ROLE_REPORTS_ROOT.length + 1), content);
37005
+ }
37006
+ });
35670
37007
  const orchestrateTool = createOrchestrateTool({
35671
37008
  client: orchestrateClient,
35672
37009
  sink,
@@ -35687,11 +37024,15 @@ async function createPluginRuntime(ctx, rawOptions) {
35687
37024
  capabilityProfiles,
35688
37025
  reportArtifactExists,
35689
37026
  readReportArtifact,
37027
+ reportDelivery,
37028
+ taskEffort,
37029
+ sessionPhase,
37030
+ workspaceDirectory: ctx.directory,
35690
37031
  lifecycleRecorder,
35691
37032
  abortGraceMs: DEFAULT_ABORT_GRACE_MS,
35692
37033
  statusPollMs: DEFAULT_STATUS_POLL_MS,
35693
- delay: (ms2) => new Promise((resolve17) => {
35694
- setTimeout(resolve17, ms2);
37034
+ delay: (ms2) => new Promise((resolve19) => {
37035
+ setTimeout(resolve19, ms2);
35695
37036
  }),
35696
37037
  warn: diagnostics.warn("unknown-role")
35697
37038
  });
@@ -35757,7 +37098,9 @@ async function createPluginRuntime(ctx, rawOptions) {
35757
37098
  ...hooks,
35758
37099
  "chat.message": async (input, output) => {
35759
37100
  reload.userTurn(input.sessionID);
37101
+ await taskEffort.userTurn(input.sessionID, input.messageID ?? output.message.id);
35760
37102
  await hooks["chat.message"]?.(input, output);
37103
+ await sessionPhase(input.sessionID, "model-request");
35761
37104
  announceEndpoint(input.sessionID);
35762
37105
  },
35763
37106
  dispose: async () => {
@@ -35771,18 +37114,44 @@ async function createPluginRuntime(ctx, rawOptions) {
35771
37114
  "openteam-orchestrate": orchestrateTool,
35772
37115
  "openteam-memory": memoryTool,
35773
37116
  "openteam-register-cast": registerCastTool,
35774
- "openteam-reload": reload.tool
37117
+ "openteam-reload": reload.tool,
37118
+ "openteam-submit-report": createReportTool(reportDelivery)
35775
37119
  },
35776
37120
  "tool.execute.before": async (input) => {
35777
37121
  toolcalls.before(input);
37122
+ await sessionPhase(input.sessionID, "tool-execution");
35778
37123
  },
35779
37124
  "tool.execute.after": async (input) => {
35780
- await toolcalls.after(input);
37125
+ if (ctx.toolOutcomesFromEvents !== true)
37126
+ await toolcalls.after(input);
35781
37127
  if (input.tool === "openteam-reload")
35782
37128
  reload.toolCompleted(input.sessionID);
35783
37129
  },
35784
37130
  event: async ({ event }) => {
35785
37131
  reload.event(event);
37132
+ if (event.type === "openteam.tool.completed") {
37133
+ await toolcalls.after(event.properties);
37134
+ await sessionPhase(event.properties.sessionID, "unknown");
37135
+ } else if (event.type === "openteam.session.phase") {
37136
+ await sessionPhase(event.properties.sessionID, event.properties.phase);
37137
+ } else if (event.type === "session.idle") {
37138
+ if (typeof event.properties?.sessionID === "string")
37139
+ await sessionPhase(event.properties.sessionID, "idle");
37140
+ } else if (event.type === "message.part.updated") {
37141
+ const part = event.properties.part;
37142
+ if (part.type === "tool" && (part.state.status === "completed" || part.state.status === "error")) {
37143
+ await toolcalls.after({
37144
+ tool: part.tool,
37145
+ sessionID: part.sessionID,
37146
+ callID: part.callID,
37147
+ ok: part.state.status === "completed",
37148
+ durationMs: Math.max(0, Math.trunc(part.state.time.end - part.state.time.start)),
37149
+ ...part.state.status === "error" ? { failureKind: "execution" } : {}
37150
+ });
37151
+ }
37152
+ } else if (event.type === "session.deleted") {
37153
+ toolcalls.forgetSession(event.properties.info.id);
37154
+ }
35786
37155
  if (event.type === "session.created" || event.type === "session.idle") {
35787
37156
  cache.refresh();
35788
37157
  }
@@ -35799,6 +37168,7 @@ async function createPluginRuntime(ctx, rawOptions) {
35799
37168
  announceEndpoint(message2.sessionID);
35800
37169
  sessionTracker.trackSession(message2.sessionID);
35801
37170
  sink.emit(message2);
37171
+ await sessionPhase(message2.sessionID, "unknown");
35802
37172
  }
35803
37173
  }
35804
37174
  }
@@ -35808,6 +37178,19 @@ var server = (ctx, rawOptions) => createPluginRuntime({
35808
37178
  directory: ctx.directory,
35809
37179
  worktree: ctx.worktree,
35810
37180
  client: ctx.client,
37181
+ ...typeof ctx.client.session?.get !== "function" ? {} : {
37182
+ parentSession: async (sessionID) => {
37183
+ const response = await ctx.client.session.get({
37184
+ path: { id: sessionID },
37185
+ query: { directory: ctx.directory }
37186
+ });
37187
+ if (response.error !== undefined || response.data === undefined)
37188
+ throw new Error(nativeHostMessages.missingBindings);
37189
+ if (resolve18(response.data.directory) !== resolve18(ctx.directory))
37190
+ throw new Error(nativeHostMessages.workspaceMismatch);
37191
+ return response.data.parentID;
37192
+ }
37193
+ },
35811
37194
  exec: createShellExec(ctx.$),
35812
37195
  serverUrl: () => ctx.serverUrl,
35813
37196
  ...ctx.client.config?.providers === undefined ? {} : {
@@ -36434,7 +37817,7 @@ function createGitLastCommit(exec) {
36434
37817
  // src/cli.ts
36435
37818
  var execFileAsync3 = promisify3(execFile4);
36436
37819
  var execAsync = promisify3(exec);
36437
- var AGENT_DIR = dirname11(ORCHESTRATOR_AGENT_PATH);
37820
+ var AGENT_DIR = dirname12(ORCHESTRATOR_AGENT_PATH);
36438
37821
  var storage = createFsStorageProvider(process.cwd());
36439
37822
  var CLI_LIFECYCLE_WRITER_IDENTITY = {
36440
37823
  processID: process.pid,
@@ -36521,7 +37904,7 @@ async function readConfigFile(path4, resolution) {
36521
37904
  return storage.read(path4);
36522
37905
  }
36523
37906
  try {
36524
- return await readFile6(resolve17(path4), "utf8");
37907
+ return await readFile6(resolve19(path4), "utf8");
36525
37908
  } catch (error) {
36526
37909
  throw new Error(`[openteam] config file named by --config could not be read: ${path4} (${describeFileReadError(error)})`);
36527
37910
  }
@@ -36544,8 +37927,8 @@ async function saveConfig(config, path4, resolution = { explicit: false }) {
36544
37927
  return;
36545
37928
  }
36546
37929
  const contents = serializeOpenTeamConfig(config);
36547
- const diskPath = resolve17(path4);
36548
- await mkdir5(dirname11(diskPath), { recursive: true });
37930
+ const diskPath = resolve19(path4);
37931
+ await mkdir5(dirname12(diskPath), { recursive: true });
36549
37932
  await writeFile2(diskPath, contents, "utf8");
36550
37933
  }
36551
37934
  function parseConfigSelection(argv) {
@@ -36590,8 +37973,8 @@ async function openBrowser(url) {
36590
37973
  }
36591
37974
  }
36592
37975
  function waitForSignal() {
36593
- return new Promise((resolve18) => {
36594
- const done = () => resolve18();
37976
+ return new Promise((resolve20) => {
37977
+ const done = () => resolve20();
36595
37978
  process.once("SIGINT", done);
36596
37979
  process.once("SIGTERM", done);
36597
37980
  });
@@ -36792,7 +38175,7 @@ async function main() {
36792
38175
  workspaceRoot,
36793
38176
  configuredRoot: initialConsoleConfig.lifecycle?.root ?? DEFAULT_LIFECYCLE_ROOT
36794
38177
  });
36795
- const legacyGraphRoot = resolve17(workspaceRoot, initialConsoleConfig.graph?.journalRoot ?? DEFAULT_GRAPH_JOURNAL_ROOT);
38178
+ const legacyGraphRoot = resolve19(workspaceRoot, initialConsoleConfig.graph?.journalRoot ?? DEFAULT_GRAPH_JOURNAL_ROOT);
36796
38179
  const lifecycleAvailability = initialConsoleConfig.console.graphView?.enabled === true ? "enabled" : "history-disabled";
36797
38180
  const lifecycleHistoryReader = createLifecycleHistoryProvider({
36798
38181
  lifecycleRoot,