@vtxmacro/cli 2026.9.18 → 2026.9.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -250,10 +250,22 @@ vtx inference-host agent-next --wait-seconds 50 --json
250
250
  That is the **Provider** loop. `agent-next` returns separate exact
251
251
  `system_prompt`, `user_prompt`,
252
252
  `context_json`, and `output_schema_json` fields. The agent reasons in its own
253
- session, then writes one JSON object to `agent-complete` stdin. At minimum that
254
- object contains a `result` string. If the harness exposes exact token usage,
255
- include the published usage object; otherwise VTX records usage as explicitly
256
- unavailable. For `agent-fail`, choose the truthful dispatch state and set
253
+ session, then writes one JSON object to `agent-complete` stdin. The CLI reports
254
+ its actual release version and negotiates `foreground_v1` Provider freshness.
255
+ Prewarm the harness before requesting work. For Server Mode jobs, the handoff includes
256
+ `provider_dispatch_not_after`, `provider_dispatch_local_not_after`, and
257
+ `provider_dispatch_freshness_remaining_ms`; the local deadline and remaining
258
+ budget account conservatively for RPC time and pending-request replay. Enforce
259
+ both deadlines immediately before the real provider request. If either expires
260
+ before dispatch, do not send stale work: use `agent-fail` with
261
+ `dispatch_outcome: not_dispatched`, `failure_category: freshness`,
262
+ `failure_code: provider_dispatch_freshness_expired`, and `retryable: false`.
263
+ Completion contains a `result` string; negotiated server jobs also require
264
+ `provider_dispatched_at`, the ISO 8601 timestamp captured at provider request
265
+ entry. This is harness-reported evidence, not provider attestation. If the harness
266
+ exposes exact token usage, include the published usage object; otherwise VTX
267
+ records usage as explicitly unavailable. For `agent-fail`, choose the truthful
268
+ dispatch state and set
257
269
  `retryable` true only when a fresh attempt is safe:
258
270
 
259
271
  ```json
@@ -16,7 +16,7 @@ import { fileURLToPath } from "node:url";
16
16
  // agent-cli-release.json
17
17
  var agent_cli_release_default = {
18
18
  package_name: "@vtxmacro/cli",
19
- package_version: "2026.9.18",
19
+ package_version: "2026.9.20",
20
20
  codex_package_name: "@openai/codex",
21
21
  codex_version: "0.153.3",
22
22
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -144,6 +144,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
144
144
  if($mode -eq 'harden') {
145
145
  $failureStage='acl_read'
146
146
  $acl=Get-Acl -LiteralPath $path
147
+ $originalAccess=$acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)
147
148
  $failureStage='owner_validation'
148
149
  $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
149
150
  if($existingOwner -ne $current.Value -and ($existingOwner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
@@ -164,7 +165,9 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
164
165
  [void]$acl.AddAccessRule($rule)
165
166
  }
166
167
  $failureStage='acl_write'
167
- (Get-Item -LiteralPath $path).SetAccessControl($acl)
168
+ if($originalAccess -ne $acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)) {
169
+ (Get-Item -LiteralPath $path).SetAccessControl($acl)
170
+ }
168
171
  }
169
172
  $failureStage='acl_readback'
170
173
  $actual=Get-Acl -LiteralPath $path
package/bin/vtx.js CHANGED
@@ -69,7 +69,7 @@ var init_agent_cli_release = __esm({
69
69
  "agent-cli-release.json"() {
70
70
  agent_cli_release_default = {
71
71
  package_name: "@vtxmacro/cli",
72
- package_version: "2026.9.18",
72
+ package_version: "2026.9.20",
73
73
  codex_package_name: "@openai/codex",
74
74
  codex_version: "0.153.3",
75
75
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -15014,12 +15014,16 @@ var init_external_inference_contract = __esm({
15014
15014
  authenticated_account_plan: safeCodeSchema.nullable().optional(),
15015
15015
  protocol_version: protocolVersionSchema,
15016
15016
  host_runtime_version: protocolVersionSchema.optional(),
15017
+ provider_dispatch_freshness_contract: external_exports.literal("foreground_v1").optional(),
15017
15018
  envelope_public_key: base64Url32BytesSchema,
15018
15019
  health: external_exports.enum(["healthy", "degraded", "draining"]),
15019
15020
  advertised_at: timestampSchema,
15020
15021
  expires_at: timestampSchema,
15021
15022
  models: external_exports.array(advertisedModelSchema).min(1).max(64)
15022
15023
  }).superRefine((value, context) => {
15024
+ if (value.provider_dispatch_freshness_contract !== void 0 && !/^\d+\.\d+\.\d+$/.test(value.host_runtime_version ?? "")) {
15025
+ context.addIssue({ code: "custom", message: "Foreground freshness contract requires a numeric runtime version." });
15026
+ }
15023
15027
  const modelIds = value.models.map((model) => model.model_id);
15024
15028
  if (new Set(modelIds).size !== modelIds.length) {
15025
15029
  context.addIssue({
@@ -15420,12 +15424,17 @@ var init_external_inference_contract = __esm({
15420
15424
  model_id: identifierSchema,
15421
15425
  model_label: displayNameSchema,
15422
15426
  reasoning_effort: reasoningEffortSchema,
15423
- requested_at: timestampSchema
15427
+ requested_at: timestampSchema,
15428
+ host_runtime_version: external_exports.string().regex(/^\d+\.\d+\.\d+$/).optional(),
15429
+ provider_dispatch_freshness_contract: external_exports.literal("foreground_v1").optional()
15430
+ }).refine((value) => value.host_runtime_version === void 0 === (value.provider_dispatch_freshness_contract === void 0), {
15431
+ message: "Foreground freshness negotiation requires the actual CLI version."
15424
15432
  });
15425
15433
  agentNextRequestSchema = external_exports.strictObject({
15426
15434
  operation_id: operationIdentifierSchema,
15427
15435
  host_id: identifierSchema,
15428
- requested_at: timestampSchema
15436
+ requested_at: timestampSchema,
15437
+ provider_dispatch_freshness_contract: external_exports.literal("foreground_v1").optional()
15429
15438
  });
15430
15439
  agentHeartbeatRequestSchema = external_exports.strictObject({
15431
15440
  operation_id: operationIdentifierSchema,
@@ -15449,6 +15458,9 @@ var init_external_inference_contract = __esm({
15449
15458
  requested_reasoning_effort: reasoningEffortSchema,
15450
15459
  output_schema_version: protocolVersionSchema,
15451
15460
  deadline_at: timestampSchema,
15461
+ provider_dispatch_freshness_contract: external_exports.literal("foreground_v1").optional(),
15462
+ provider_dispatch_not_after: timestampSchema.optional(),
15463
+ provider_dispatch_freshness_remaining_ms: nonNegativeSafeIntegerSchema.optional(),
15452
15464
  system_prompt: boundedPromptTextSchema,
15453
15465
  system_prompt_sha256: sha256HexSchema,
15454
15466
  user_prompt: boundedPromptTextSchema,
@@ -15458,6 +15470,14 @@ var init_external_inference_contract = __esm({
15458
15470
  output_schema_json: boundedJsonTextSchema,
15459
15471
  output_schema_sha256: sha256HexSchema
15460
15472
  }).superRefine((value, context) => {
15473
+ const freshnessFields = [
15474
+ value.provider_dispatch_freshness_contract,
15475
+ value.provider_dispatch_not_after,
15476
+ value.provider_dispatch_freshness_remaining_ms
15477
+ ];
15478
+ if (freshnessFields.some((field) => field !== void 0) && (freshnessFields.some((field) => field === void 0) || value.execution_mode !== "server")) {
15479
+ context.addIssue({ code: "custom", message: "Foreground freshness handoff is incomplete." });
15480
+ }
15461
15481
  for (const [field, exact, digest] of [
15462
15482
  ["system_prompt", value.system_prompt, value.system_prompt_sha256],
15463
15483
  ["user_prompt", value.user_prompt, value.user_prompt_sha256],
@@ -15489,7 +15509,8 @@ var init_external_inference_contract = __esm({
15489
15509
  time_to_first_token_ms: nonNegativeSafeIntegerSchema.nullable().optional(),
15490
15510
  finish_reason: safeCodeSchema.nullable().optional(),
15491
15511
  refusal_status: external_exports.enum(["none", "refused", "blocked", "unknown"]),
15492
- completed_at: timestampSchema
15512
+ completed_at: timestampSchema,
15513
+ provider_dispatched_at: timestampSchema.nullable().optional()
15493
15514
  }).superRefine((value, context) => {
15494
15515
  if (value.time_to_first_token_ms !== void 0 && value.time_to_first_token_ms !== null && value.time_to_first_token_ms > value.latency_ms) {
15495
15516
  context.addIssue({
@@ -17994,6 +18015,7 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
17994
18015
  if($mode -eq 'harden') {
17995
18016
  $failureStage='acl_read'
17996
18017
  $acl=Get-Acl -LiteralPath $path
18018
+ $originalAccess=$acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)
17997
18019
  $failureStage='owner_validation'
17998
18020
  $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
17999
18021
  if($existingOwner -ne $current.Value -and ($existingOwner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
@@ -18014,7 +18036,9 @@ while(($line=[Console]::In.ReadLine()) -ne $null) {
18014
18036
  [void]$acl.AddAccessRule($rule)
18015
18037
  }
18016
18038
  $failureStage='acl_write'
18017
- (Get-Item -LiteralPath $path).SetAccessControl($acl)
18039
+ if($originalAccess -ne $acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)) {
18040
+ (Get-Item -LiteralPath $path).SetAccessControl($acl)
18041
+ }
18018
18042
  }
18019
18043
  $failureStage='acl_readback'
18020
18044
  $actual=Get-Acl -LiteralPath $path
@@ -19847,7 +19871,7 @@ var init_mcp_client = __esm({
19847
19871
  const hasPublicToolErrorMarker = hasStructuredPublicToolErrorMarker || errorText.includes(PUBLIC_TOOL_ERROR_PREFIX);
19848
19872
  const completionEvidenceExpired = name === "inference.job.complete" && (publicToolError ? publicToolError.failure_code === "completion_evidence_expired" && publicToolError.phase === "validation" && publicToolError.retryable === false && publicToolError.terminal_before_execution === true : !hasPublicToolErrorMarker && errorText.includes("External inference completion evidence window expired"));
19849
19873
  const hostNotClaimable = name === "inference.job.claim" && publicToolError?.failure_code === "host_not_claimable" && publicToolError.phase === "admission" && publicToolError.retryable === true && publicToolError.terminal_before_execution === true;
19850
- const definitivelyNotApplied = (name === "inference.host.register" || name === "inference.host.advertise") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.")) || (name === "inference.agent.next" || name === "inference.agent.heartbeat") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.") || errorText.includes("Heartbeat time is outside the allowed clock skew.")) || name === "inference.host.heartbeat" && errorText.includes("Heartbeat time is outside the allowed clock skew.") || name === "inference.job.claim" && (errorText.includes(
19874
+ const definitivelyNotApplied = name === "inference.agent.decision.submit" && publicToolError?.phase === "validation" && publicToolError.retryable === false && publicToolError.terminal_before_execution === true && (publicToolError.failure_code === "agent_decision_schema_invalid" || publicToolError.failure_code === "invalid_arguments" && publicToolError.reason_code === "schema_validation") || (name === "inference.host.register" || name === "inference.host.advertise") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.")) || (name === "inference.agent.next" || name === "inference.agent.heartbeat") && (errorText.includes("Advertisement time is outside the allowed clock skew.") || errorText.includes("Advertisement is already expired.") || errorText.includes("Heartbeat time is outside the allowed clock skew.")) || name === "inference.host.heartbeat" && errorText.includes("Heartbeat time is outside the allowed clock skew.") || name === "inference.job.claim" && (errorText.includes(
19851
19875
  "External inference claim request was not applied before its generation became stale"
19852
19876
  ) || errorText.includes("External inference host advertisement is expired or superseded")) || name === "inference.job.complete" && completionEvidenceExpired;
19853
19877
  const retryableInfrastructureRejection = EXTERNAL_INFERENCE_AUTOMATED_HOST_TOOLS.includes(name) && (errorText.includes("External inference polling is at its configured concurrency limit") || errorText.includes("The Insights action reached the response timeout") || errorText.includes("Insights capability failed without exposing private runtime details"));
@@ -21219,6 +21243,19 @@ var init_agent_state = __esm({
21219
21243
  "received_at",
21220
21244
  "pending_terminal"
21221
21245
  ];
21246
+ const freshnessKeys = ["provider_dispatch_not_after", "provider_dispatch_local_not_after"];
21247
+ if ("execution_mode" in record3) {
21248
+ if (!["server", "client"].includes(String(record3.execution_mode))) {
21249
+ throw new Error("Agent-driven inference execution mode is invalid.");
21250
+ }
21251
+ exactKeys.push("execution_mode");
21252
+ }
21253
+ if (freshnessKeys.some((key) => key in record3)) {
21254
+ if (freshnessKeys.some((key) => typeof record3[key] !== "string" || !Number.isFinite(Date.parse(record3[key])))) {
21255
+ throw new Error("Agent-driven inference freshness recovery state is invalid.");
21256
+ }
21257
+ exactKeys.push(...freshnessKeys);
21258
+ }
21222
21259
  if (Object.keys(record3).sort().join("\0") !== exactKeys.slice().sort().join("\0") || record3.schema_version !== "vtx_inference_agent_attempt_v1" || typeof record3.host_id !== "string" || !record3.host_id || typeof record3.job_id !== "string" || !record3.job_id || typeof record3.attempt_id !== "string" || !record3.attempt_id || typeof record3.claim_handle !== "string" || !record3.claim_handle || typeof record3.completion_operation_id !== "string" || !record3.completion_operation_id || typeof record3.failure_operation_id !== "string" || !record3.failure_operation_id || typeof record3.requested_model !== "string" || !record3.requested_model || typeof record3.requested_reasoning_effort !== "string" || !record3.requested_reasoning_effort || !["provider_response", "decision_candidate"].includes(String(record3.response_mode)) || typeof record3.deadline_at !== "string" || !Number.isFinite(Date.parse(record3.deadline_at)) || typeof record3.received_at !== "string" || !Number.isFinite(Date.parse(record3.received_at))) {
21223
21260
  throw new Error("Agent-driven inference attempt state is invalid.");
21224
21261
  }
@@ -42397,7 +42434,9 @@ Waiting for approval...
42397
42434
  model_id: parsed.modelId,
42398
42435
  model_label: parsed.modelLabel ?? parsed.modelId,
42399
42436
  reasoning_effort: parsed.reasoningEffort,
42400
- requested_at: now.toISOString()
42437
+ requested_at: now.toISOString(),
42438
+ host_runtime_version: INFERENCE_HOST_CLI_VERSION,
42439
+ provider_dispatch_freshness_contract: "foreground_v1"
42401
42440
  });
42402
42441
  return {
42403
42442
  exitCode: 0,
@@ -42768,6 +42807,14 @@ Waiting for approval...
42768
42807
  ` : ""
42769
42808
  };
42770
42809
  } catch (error48) {
42810
+ if (error48 instanceof ExternalInferenceMcpError && error48.definitivelyNotApplied) {
42811
+ await writeInferenceForegroundAgentControlState(config2.statePath, {
42812
+ ...state,
42813
+ pending_decision: null,
42814
+ updated_at: now().toISOString()
42815
+ });
42816
+ throw error48;
42817
+ }
42771
42818
  const checkedAt = now().toISOString();
42772
42819
  state = {
42773
42820
  ...state,
@@ -43028,11 +43075,14 @@ Waiting for approval...
43028
43075
  await writeInferenceAgentNextState(config2.statePath, pending);
43029
43076
  }
43030
43077
  let result2;
43078
+ const callStartedAtMs = now().getTime();
43079
+ const callStartedMonotonicMs = performance.now();
43031
43080
  try {
43032
43081
  result2 = await session.client.callTool("inference.agent.next", {
43033
43082
  operation_id: pending.operation_id,
43034
43083
  host_id: session.localState.host_id,
43035
- requested_at: pending.requested_at
43084
+ requested_at: pending.requested_at,
43085
+ provider_dispatch_freshness_contract: "foreground_v1"
43036
43086
  });
43037
43087
  } catch (error48) {
43038
43088
  const definitivelyNotApplied = Boolean(
@@ -43045,10 +43095,31 @@ Waiting for approval...
43045
43095
  }
43046
43096
  if (result2.claim_state === "claimed") {
43047
43097
  const receivedAt = now().toISOString();
43098
+ const receivedAtMs = Date.parse(receivedAt);
43099
+ const roundTripMs = Math.max(0, dependencies.now ? receivedAtMs - callStartedAtMs : performance.now() - callStartedMonotonicMs);
43100
+ let localDispatchDeadline;
43101
+ const validFreshness = result2.provider_dispatch_freshness_contract === "foreground_v1" && typeof result2.provider_dispatch_not_after === "string" && Number.isFinite(Date.parse(result2.provider_dispatch_not_after)) && Number.isSafeInteger(result2.provider_dispatch_freshness_remaining_ms) && Number(result2.provider_dispatch_freshness_remaining_ms) >= 0;
43102
+ if (result2.execution_mode === "server" && validFreshness) {
43103
+ const conservativeDeadline = Math.min(
43104
+ Date.parse(result2.provider_dispatch_not_after),
43105
+ receivedAtMs + Number(result2.provider_dispatch_freshness_remaining_ms) - roundTripMs,
43106
+ Date.parse(pending.requested_at) + Number(result2.provider_dispatch_freshness_remaining_ms),
43107
+ active?.provider_dispatch_local_not_after ? Date.parse(active.provider_dispatch_local_not_after) : Number.POSITIVE_INFINITY
43108
+ );
43109
+ localDispatchDeadline = new Date(conservativeDeadline).toISOString();
43110
+ }
43048
43111
  if (active) {
43049
- if (result2.job_id !== active.job_id || result2.attempt_id !== active.attempt_id || result2.claim_handle !== active.claim_handle || result2.requested_model !== active.requested_model || result2.requested_reasoning_effort !== active.requested_reasoning_effort || result2.response_mode !== active.response_mode || result2.deadline_at !== active.deadline_at) {
43112
+ if (result2.job_id !== active.job_id || result2.attempt_id !== active.attempt_id || result2.claim_handle !== active.claim_handle || result2.requested_model !== active.requested_model || result2.requested_reasoning_effort !== active.requested_reasoning_effort || result2.response_mode !== active.response_mode || result2.deadline_at !== active.deadline_at || active.provider_dispatch_not_after !== void 0 && result2.provider_dispatch_not_after !== active.provider_dispatch_not_after) {
43050
43113
  throw new Error("Recovered agent job does not match the active private attempt state.");
43051
43114
  }
43115
+ if (localDispatchDeadline !== void 0) {
43116
+ await writeInferenceAgentAttemptState(config2.statePath, {
43117
+ ...active,
43118
+ execution_mode: result2.execution_mode,
43119
+ provider_dispatch_not_after: result2.provider_dispatch_not_after,
43120
+ provider_dispatch_local_not_after: localDispatchDeadline
43121
+ });
43122
+ }
43052
43123
  } else {
43053
43124
  await writeInferenceAgentAttemptState(config2.statePath, {
43054
43125
  schema_version: "vtx_inference_agent_attempt_v1",
@@ -43061,17 +43132,35 @@ Waiting for approval...
43061
43132
  requested_model: result2.requested_model,
43062
43133
  requested_reasoning_effort: result2.requested_reasoning_effort,
43063
43134
  response_mode: result2.response_mode,
43135
+ execution_mode: result2.execution_mode,
43064
43136
  deadline_at: result2.deadline_at,
43065
43137
  received_at: receivedAt,
43138
+ ...localDispatchDeadline === void 0 ? {} : {
43139
+ provider_dispatch_not_after: result2.provider_dispatch_not_after,
43140
+ provider_dispatch_local_not_after: localDispatchDeadline
43141
+ },
43066
43142
  pending_terminal: null
43067
43143
  });
43068
43144
  }
43145
+ if (result2.execution_mode === "server" && localDispatchDeadline === void 0) {
43146
+ throw new Error("The Server foreground Provider freshness receipt is unavailable. Fail this attempt without dispatch.");
43147
+ }
43148
+ if (localDispatchDeadline !== void 0 && Date.parse(localDispatchDeadline) <= now().getTime()) {
43149
+ throw new Error("The foreground Provider freshness budget expired before handoff. Use agent-fail with not_dispatched.");
43150
+ }
43069
43151
  const { claim_handle: _claimHandle, ...publicJob } = result2;
43070
43152
  return {
43071
43153
  exitCode: 0,
43072
43154
  stdout: render({
43073
43155
  ...publicJob,
43074
- instructions: "Treat system_prompt and user_prompt as separate VTX-delivered roles. Use context_json and satisfy output_schema_json exactly. Send only the final result through agent-complete stdin; do not execute trading tools yourself.",
43156
+ ...localDispatchDeadline === void 0 ? {} : {
43157
+ provider_dispatch_local_not_after: localDispatchDeadline,
43158
+ provider_dispatch_freshness_remaining_ms: Math.max(
43159
+ 0,
43160
+ Math.floor(Date.parse(localDispatchDeadline) - now().getTime())
43161
+ )
43162
+ },
43163
+ instructions: "Treat system_prompt and user_prompt as separate VTX-delivered roles. Use context_json and satisfy output_schema_json exactly. Send only the final result through agent-complete stdin; do not execute trading tools yourself. For Server Provider jobs enforce both provider dispatch deadlines at actual provider entry and report the self-attested provider_dispatched_at timestamp. If the budget expires before dispatch, use agent-fail with not_dispatched.",
43075
43164
  complete_command: "vtx inference-host agent-complete --json",
43076
43165
  fail_command: "vtx inference-host agent-fail --json"
43077
43166
  }, parsed.json),
@@ -43116,12 +43205,19 @@ Waiting for approval...
43116
43205
  "latency_ms",
43117
43206
  "time_to_first_token_ms",
43118
43207
  "finish_reason",
43119
- "refusal_status"
43208
+ "refusal_status",
43209
+ "provider_dispatched_at"
43120
43210
  ]);
43121
43211
  if (typeof input.result !== "string" || !input.result) {
43122
43212
  throw new Error("agent-complete stdin requires a non-empty result string.");
43123
43213
  }
43124
43214
  const now = (dependencies.now ?? (() => /* @__PURE__ */ new Date()))();
43215
+ if (state.execution_mode === "server" || state.provider_dispatch_not_after !== void 0) {
43216
+ const dispatchedAt = typeof input.provider_dispatched_at === "string" ? Date.parse(input.provider_dispatched_at) : Number.NaN;
43217
+ if (!Number.isFinite(dispatchedAt) || state.provider_dispatch_local_not_after === void 0 || state.provider_dispatch_not_after === void 0 || dispatchedAt < Date.parse(state.received_at) || dispatchedAt > Date.parse(state.provider_dispatch_local_not_after) || dispatchedAt > Date.parse(state.provider_dispatch_not_after) || dispatchedAt > now.getTime()) {
43218
+ throw new Error("agent-complete requires actual provider_dispatched_at within the foreground freshness handoff.");
43219
+ }
43220
+ }
43125
43221
  const measuredLatency = Math.max(0, now.getTime() - Date.parse(state.received_at));
43126
43222
  request = agentCompleteRequestSchema.parse({
43127
43223
  operation_id: state.completion_operation_id,
@@ -43145,7 +43241,10 @@ Waiting for approval...
43145
43241
  time_to_first_token_ms: input.time_to_first_token_ms == null ? null : Number(input.time_to_first_token_ms),
43146
43242
  finish_reason: input.finish_reason == null ? null : String(input.finish_reason),
43147
43243
  refusal_status: String(input.refusal_status ?? "none"),
43148
- completed_at: now.toISOString()
43244
+ completed_at: now.toISOString(),
43245
+ ...input.provider_dispatched_at == null ? {} : {
43246
+ provider_dispatched_at: String(input.provider_dispatched_at)
43247
+ }
43149
43248
  });
43150
43249
  await writeInferenceAgentAttemptState(config2.statePath, {
43151
43250
  ...state,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.9.18",
3
+ "version": "2026.9.20",
4
4
  "description": "VTX Macro CLI, MCP server, and durable external inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",