@zhivex-ai/core 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agent.js CHANGED
@@ -416,6 +416,71 @@ const finalizeState = (agent, state, result, newSteps, newToolResults) => {
416
416
  const emitTelemetryEvent = async (agent, event) => {
417
417
  await invokeOperationalHook(agent, "telemetry", event.type, event.runId, agent.onTelemetryEvent ? () => agent.onTelemetryEvent(event) : undefined, undefined);
418
418
  };
419
+ const emitInvocationStartTelemetry = async (agent, runId, startedAt, maxSteps) => {
420
+ try {
421
+ await agent.onTelemetryEvent?.startInvocation?.({
422
+ runId,
423
+ agentId: agent.id,
424
+ agentName: agent.name,
425
+ provider: agent.model.provider,
426
+ modelId: agent.model.modelId,
427
+ maxSteps,
428
+ startedAt
429
+ });
430
+ }
431
+ catch {
432
+ // Invocation telemetry is best-effort and cannot replace setup/business errors.
433
+ }
434
+ };
435
+ const emitInvocationFinishTelemetry = async (agent, runId, status, error) => {
436
+ try {
437
+ await agent.onTelemetryEvent?.finishInvocation?.({
438
+ runId,
439
+ agentId: agent.id,
440
+ agentName: agent.name,
441
+ status,
442
+ error,
443
+ finishedAt: Date.now()
444
+ });
445
+ }
446
+ catch {
447
+ // Invocation telemetry is best-effort and cannot replace setup/business errors.
448
+ }
449
+ };
450
+ const withAgentTelemetryRunContext = (agent, runId, callback) => {
451
+ const telemetryObserver = agent.onTelemetryEvent;
452
+ const wrapper = telemetryObserver?.withRunContext;
453
+ if (!wrapper)
454
+ return Promise.resolve().then(callback);
455
+ let execution;
456
+ let callbackError;
457
+ let callbackFailed = false;
458
+ const executeOnce = () => {
459
+ execution ??= Promise.resolve()
460
+ .then(callback)
461
+ .catch((error) => {
462
+ callbackFailed = true;
463
+ callbackError = error;
464
+ throw error;
465
+ });
466
+ return execution;
467
+ };
468
+ return Promise.resolve()
469
+ .then(async () => {
470
+ await wrapper.call(telemetryObserver, runId, executeOnce);
471
+ return executeOnce();
472
+ })
473
+ .catch(async (wrapperError) => {
474
+ if (callbackFailed)
475
+ throw callbackError;
476
+ try {
477
+ return await executeOnce();
478
+ }
479
+ catch (executionError) {
480
+ throw callbackFailed ? callbackError : executionError ?? wrapperError;
481
+ }
482
+ });
483
+ };
419
484
  const subAgentToolInputSchema = z.object({
420
485
  prompt: z.string().min(1),
421
486
  system: z.string().optional()
@@ -1171,6 +1236,7 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, context,
1171
1236
  context,
1172
1237
  runId: state.runId,
1173
1238
  agentId: state.agentId,
1239
+ agentName: agent.name,
1174
1240
  scope: state.scope,
1175
1241
  metadata: state.metadata,
1176
1242
  executionEnvironment: executionEnvironmentSession
@@ -1206,19 +1272,27 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, context,
1206
1272
  return compacted.messages;
1207
1273
  }
1208
1274
  : undefined,
1209
- onBeforeModelStep: ({ step }) => {
1210
- if (!budget)
1211
- return;
1212
- const trigger = evaluateAgentBudgetPreflight(state, budget, {
1213
- operation: "model",
1214
- requiredSteps: Math.max(1, step - state.currentStep),
1215
- requestedOutputTokens: maxTokens
1216
- });
1217
- if (trigger) {
1218
- throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent model budget preflight failed.", {
1219
- metadata: trigger.metadata
1275
+ onBeforeModelStep: async ({ step }) => {
1276
+ if (budget) {
1277
+ const trigger = evaluateAgentBudgetPreflight(state, budget, {
1278
+ operation: "model",
1279
+ requiredSteps: Math.max(1, step - state.currentStep),
1280
+ requestedOutputTokens: maxTokens
1220
1281
  });
1282
+ if (trigger) {
1283
+ throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent model budget preflight failed.", {
1284
+ metadata: trigger.metadata
1285
+ });
1286
+ }
1221
1287
  }
1288
+ await emitTelemetryEvent(agent, {
1289
+ type: "step-start",
1290
+ runId: state.runId,
1291
+ agentId: state.agentId,
1292
+ agentName: agent.name,
1293
+ stepIndex: step,
1294
+ startedAt: Date.now()
1295
+ });
1222
1296
  },
1223
1297
  onModelStep: async ({ request, response, step, toolCalls, approvalRequests }) => {
1224
1298
  if (!agent.store)
@@ -1285,17 +1359,28 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, context,
1285
1359
  state.revision = checkpointState.revision;
1286
1360
  },
1287
1361
  stepOffset: state.currentStep,
1288
- onBeforeToolExecution: ({ toolCalls }) => {
1289
- if (!budget)
1290
- return;
1291
- reservedToolCalls += toolCalls.length;
1292
- const trigger = evaluateAgentBudgetPreflight(state, budget, {
1293
- operation: "tool",
1294
- requiredToolCalls: reservedToolCalls
1295
- });
1296
- if (trigger) {
1297
- throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent tool budget preflight failed.", {
1298
- metadata: trigger.metadata
1362
+ onBeforeToolExecution: async ({ step, toolCalls }) => {
1363
+ if (budget) {
1364
+ reservedToolCalls += toolCalls.length;
1365
+ const trigger = evaluateAgentBudgetPreflight(state, budget, {
1366
+ operation: "tool",
1367
+ requiredToolCalls: reservedToolCalls
1368
+ });
1369
+ if (trigger) {
1370
+ throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent tool budget preflight failed.", {
1371
+ metadata: trigger.metadata
1372
+ });
1373
+ }
1374
+ }
1375
+ for (const toolCall of toolCalls) {
1376
+ await emitTelemetryEvent(agent, {
1377
+ type: "tool-start",
1378
+ runId: state.runId,
1379
+ agentId: state.agentId,
1380
+ agentName: agent.name,
1381
+ stepIndex: step,
1382
+ toolCall,
1383
+ startedAt: Date.now()
1299
1384
  });
1300
1385
  }
1301
1386
  },
@@ -1500,14 +1585,16 @@ const acquireAgentExecutionLease = async (agent, state, policy) => {
1500
1585
  }
1501
1586
  };
1502
1587
  };
1503
- const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals) => {
1588
+ const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals, invocationStartedAt) => {
1504
1589
  await emitTelemetryEvent(agent, {
1505
1590
  type: "run-start",
1506
1591
  runId: state.runId,
1507
1592
  agentId: state.agentId,
1593
+ agentName: agent.name,
1508
1594
  provider: state.provider,
1509
1595
  modelId: state.modelId,
1510
- maxSteps: state.maxSteps
1596
+ maxSteps: state.maxSteps,
1597
+ startedAt: invocationStartedAt
1511
1598
  });
1512
1599
  if (state.handoff) {
1513
1600
  await emitTelemetryEvent(agent, {
@@ -1539,8 +1626,10 @@ const emitRunFinishTelemetry = async (agent, state) => {
1539
1626
  type: "run-finish",
1540
1627
  runId: state.runId,
1541
1628
  agentId: state.agentId,
1629
+ agentName: agent.name,
1542
1630
  status: state.status,
1543
- state: cloneState(state)
1631
+ state: cloneState(state),
1632
+ finishedAt: Date.now()
1544
1633
  });
1545
1634
  };
1546
1635
  export const createAgent = (definition) => ({
@@ -1549,6 +1638,7 @@ export const createAgent = (definition) => ({
1549
1638
  });
1550
1639
  export class Agent {
1551
1640
  id;
1641
+ name;
1552
1642
  model;
1553
1643
  instructions;
1554
1644
  contextSchema;
@@ -1584,6 +1674,7 @@ export class Agent {
1584
1674
  toDefinition() {
1585
1675
  return createAgent({
1586
1676
  id: this.id,
1677
+ name: this.name,
1587
1678
  model: this.model,
1588
1679
  instructions: this.instructions,
1589
1680
  contextSchema: this.contextSchema,
@@ -1788,175 +1879,203 @@ export const cancelAgentRunTree = async (store, runId, options = {}) => {
1788
1879
  };
1789
1880
  };
1790
1881
  export const runAgent = async (agent, input = {}) => {
1791
- const context = await resolveContext(agent, input);
1792
- const currentStatus = normalizeApprovalStatus(context.state.status);
1793
- const policy = resolveRunPolicy(agent, input);
1794
- if (currentStatus === "completed" ||
1795
- currentStatus === "cancelled" ||
1796
- currentStatus === "cancel_requested" ||
1797
- currentStatus === "timed_out") {
1798
- context.state.status = currentStatus;
1799
- return toOutput(context.state);
1800
- }
1801
- if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
1802
- context.state.status = currentStatus;
1803
- return toOutput(context.state);
1804
- }
1805
- const supportsLeases = Boolean(agent.store?.acquireLease && agent.store.renewLease && agent.store.releaseLease);
1806
- if (!context.fresh && currentStatus === "running" && !supportsLeases) {
1807
- return toOutput(context.state);
1808
- }
1809
- const freshRequiresExistingClaim = context.fresh && Boolean(context.state.idempotencyKey);
1810
- if (context.fresh && !freshRequiresExistingClaim) {
1811
- await claimAgentExecution(agent, context.state);
1812
- }
1813
- const executionLease = await acquireAgentExecutionLease(agent, context.state, policy);
1814
- if (!executionLease) {
1815
- if (input.state) {
1816
- throw new ConflictError(`Agent run "${context.state.runId}" is already owned by another worker.`);
1882
+ const invocationStartedAt = Date.now();
1883
+ const telemetryRunId = input.runId ?? input.state?.runId ?? randomId("run");
1884
+ const invocationInput = input.runId || input.state
1885
+ ? input
1886
+ : { ...input, runId: telemetryRunId };
1887
+ let invocationStatus = "completed";
1888
+ let invocationError;
1889
+ const returnInvocationOutput = (output) => {
1890
+ invocationStatus = output.status;
1891
+ if (output.status === "failed" || output.status === "timed_out") {
1892
+ invocationError = new Error(output.error?.message ?? `Agent invocation ${output.status}.`);
1893
+ if (output.status === "timed_out")
1894
+ invocationError.name = "TimeoutError";
1817
1895
  }
1818
- const activeState = await agent.store?.load(context.state.runId, context.state.scope);
1819
- return toOutput(activeState ? normalizeAgentRunState(activeState) : context.state);
1820
- }
1896
+ else {
1897
+ invocationError = undefined;
1898
+ }
1899
+ return output;
1900
+ };
1901
+ await emitInvocationStartTelemetry(agent, telemetryRunId, invocationStartedAt, Math.max(1, input.maxSteps ?? input.state?.maxSteps ?? agent.maxSteps ?? 1));
1821
1902
  try {
1822
- if (!context.fresh || freshRequiresExistingClaim) {
1903
+ const context = await resolveContext(agent, invocationInput);
1904
+ const currentStatus = normalizeApprovalStatus(context.state.status);
1905
+ const policy = resolveRunPolicy(agent, input);
1906
+ if (currentStatus === "completed" ||
1907
+ currentStatus === "cancelled" ||
1908
+ currentStatus === "cancel_requested" ||
1909
+ currentStatus === "timed_out") {
1910
+ context.state.status = currentStatus;
1911
+ invocationStatus = currentStatus;
1912
+ return returnInvocationOutput(toOutput(context.state));
1913
+ }
1914
+ if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
1915
+ context.state.status = currentStatus;
1916
+ invocationStatus = currentStatus;
1917
+ return returnInvocationOutput(toOutput(context.state));
1918
+ }
1919
+ const supportsLeases = Boolean(agent.store?.acquireLease && agent.store.renewLease && agent.store.releaseLease);
1920
+ if (!context.fresh && currentStatus === "running" && !supportsLeases) {
1921
+ invocationStatus = currentStatus;
1922
+ return returnInvocationOutput(toOutput(context.state));
1923
+ }
1924
+ const freshRequiresExistingClaim = context.fresh && Boolean(context.state.idempotencyKey);
1925
+ if (context.fresh && !freshRequiresExistingClaim) {
1823
1926
  await claimAgentExecution(agent, context.state);
1824
1927
  }
1825
- await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
1826
- }
1827
- catch (error) {
1828
- await executionLease.release();
1829
- throw error;
1830
- }
1831
- if (context.remainingSteps === 0) {
1832
- const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
1833
- await persistState(agent, state, policy);
1834
- await emitRunFinishTelemetry(agent, state);
1835
- await executionLease.release();
1836
- return toOutput(state);
1837
- }
1838
- let inputGuardrail;
1839
- try {
1840
- inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
1841
- runId: context.state.runId,
1842
- agentId: context.state.agentId,
1843
- context: context.context,
1844
- state: cloneState(context.state),
1845
- messages: context.messages,
1846
- metadata: context.state.metadata
1847
- }));
1848
- }
1849
- catch (error) {
1850
- await executionLease.release();
1851
- throw error;
1852
- }
1853
- if (inputGuardrail) {
1854
- const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
1855
- await persistState(agent, failedState, policy);
1856
- await emitRunFinishTelemetry(agent, failedState);
1857
- await executionLease.release();
1858
- return toOutput(failedState);
1859
- }
1860
- try {
1861
- await emitTelemetryEvent(agent, {
1862
- type: "step-start",
1863
- runId: context.state.runId,
1864
- agentId: context.state.agentId,
1865
- stepIndex: context.state.currentStep + 1
1866
- });
1867
- }
1868
- catch (error) {
1869
- await executionLease.release();
1870
- throw error;
1871
- }
1872
- const abortContext = createAgentAbortContext(mergeAbortSignals(input.abortSignal, executionLease.signal), policy);
1873
- let executionEnvironmentSession;
1874
- let executionEnvironmentStatus = "failed";
1875
- let executionEnvironmentError;
1876
- try {
1877
- executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
1878
- }
1879
- catch (error) {
1880
- await executionLease.release();
1881
- throw error;
1882
- }
1883
- try {
1884
- const result = await withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal)), abortContext);
1885
- const cancelled = executionLease.cancelledState();
1886
- if (cancelled) {
1887
- executionEnvironmentStatus = cancelled.status;
1888
- await emitRunFinishTelemetry(agent, cancelled);
1889
- return toOutput(cancelled);
1890
- }
1891
- if (executionLease.leaseLost()) {
1892
- throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
1893
- }
1894
- const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
1895
- let output = finalizeState(agent, context.state, result, newSteps, result.toolResults);
1896
- const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
1897
- runId: output.state.runId,
1898
- agentId: output.state.agentId,
1899
- context: context.context,
1900
- state: cloneState(output.state),
1901
- output,
1902
- metadata: output.state.metadata
1903
- }));
1904
- if (outputGuardrail) {
1905
- output = toOutput(applyGuardrailFailure(output.state, "output", outputGuardrail));
1906
- }
1907
- await emitFinalizedStepTelemetry(agent, output.state, newSteps);
1908
- await emitApprovalTelemetry(agent, output.state, [
1909
- ...(result.approvalRequests ?? []),
1910
- ...approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? []))
1911
- ]);
1912
- await persistState(agent, output.state, policy);
1913
- await emitRunFinishTelemetry(agent, output.state);
1914
- executionEnvironmentStatus = output.status;
1915
- return output;
1916
- }
1917
- catch (error) {
1918
- executionEnvironmentError = {
1919
- message: error instanceof Error ? error.message : String(error)
1920
- };
1921
- const cancelled = executionLease.cancelledState();
1922
- if (cancelled) {
1923
- executionEnvironmentStatus = cancelled.status;
1924
- await emitRunFinishTelemetry(agent, cancelled);
1925
- return toOutput(cancelled);
1926
- }
1927
- if (executionLease.leaseLost()) {
1928
- throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
1929
- }
1930
- if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
1931
- const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
1932
- const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
1928
+ const executionLease = await acquireAgentExecutionLease(agent, context.state, policy);
1929
+ if (!executionLease) {
1930
+ if (input.state) {
1931
+ throw new ConflictError(`Agent run "${context.state.runId}" is already owned by another worker.`);
1932
+ }
1933
+ const activeState = await agent.store?.load(context.state.runId, context.state.scope);
1934
+ const outputState = activeState ? normalizeAgentRunState(activeState) : context.state;
1935
+ invocationStatus = outputState.status;
1936
+ return returnInvocationOutput(toOutput(outputState));
1937
+ }
1938
+ try {
1939
+ if (!context.fresh || freshRequiresExistingClaim) {
1940
+ await claimAgentExecution(agent, context.state);
1941
+ }
1942
+ await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals, invocationStartedAt);
1943
+ }
1944
+ catch (error) {
1945
+ await executionLease.release();
1946
+ throw error;
1947
+ }
1948
+ if (context.remainingSteps === 0) {
1949
+ const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
1950
+ await persistState(agent, state, policy);
1951
+ await emitRunFinishTelemetry(agent, state);
1952
+ await executionLease.release();
1953
+ return returnInvocationOutput(toOutput(state));
1954
+ }
1955
+ let inputGuardrail;
1956
+ try {
1957
+ inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
1958
+ runId: context.state.runId,
1959
+ agentId: context.state.agentId,
1960
+ context: context.context,
1961
+ state: cloneState(context.state),
1962
+ messages: context.messages,
1963
+ metadata: context.state.metadata
1964
+ }));
1965
+ }
1966
+ catch (error) {
1967
+ await executionLease.release();
1968
+ throw error;
1969
+ }
1970
+ if (inputGuardrail) {
1971
+ const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
1972
+ await persistState(agent, failedState, policy);
1973
+ await emitRunFinishTelemetry(agent, failedState);
1974
+ await executionLease.release();
1975
+ return returnInvocationOutput(toOutput(failedState));
1976
+ }
1977
+ const abortContext = createAgentAbortContext(mergeAbortSignals(input.abortSignal, executionLease.signal), policy);
1978
+ let executionEnvironmentSession;
1979
+ let executionEnvironmentStatus = "failed";
1980
+ let executionEnvironmentError;
1981
+ try {
1982
+ executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
1983
+ }
1984
+ catch (error) {
1985
+ await executionLease.release();
1986
+ throw error;
1987
+ }
1988
+ try {
1989
+ const result = await withAgentTelemetryRunContext(agent, context.state.runId, () => withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal)), abortContext));
1990
+ const cancelled = executionLease.cancelledState();
1991
+ if (cancelled) {
1992
+ executionEnvironmentStatus = cancelled.status;
1993
+ await emitRunFinishTelemetry(agent, cancelled);
1994
+ return returnInvocationOutput(toOutput(cancelled));
1995
+ }
1996
+ if (executionLease.leaseLost()) {
1997
+ throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
1998
+ }
1999
+ const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
2000
+ let output = finalizeState(agent, context.state, result, newSteps, result.toolResults);
2001
+ const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
2002
+ runId: output.state.runId,
2003
+ agentId: output.state.agentId,
2004
+ context: context.context,
2005
+ state: cloneState(output.state),
2006
+ output,
2007
+ metadata: output.state.metadata
2008
+ }));
2009
+ if (outputGuardrail) {
2010
+ output = toOutput(applyGuardrailFailure(output.state, "output", outputGuardrail));
2011
+ }
2012
+ await emitFinalizedStepTelemetry(agent, output.state, newSteps);
2013
+ await emitApprovalTelemetry(agent, output.state, [
2014
+ ...(result.approvalRequests ?? []),
2015
+ ...approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? []))
2016
+ ]);
2017
+ await persistState(agent, output.state, policy);
2018
+ await emitRunFinishTelemetry(agent, output.state);
2019
+ executionEnvironmentStatus = output.status;
2020
+ return returnInvocationOutput(output);
2021
+ }
2022
+ catch (error) {
2023
+ executionEnvironmentError = {
2024
+ message: error instanceof Error ? error.message : String(error)
2025
+ };
2026
+ const cancelled = executionLease.cancelledState();
2027
+ if (cancelled) {
2028
+ executionEnvironmentStatus = cancelled.status;
2029
+ await emitRunFinishTelemetry(agent, cancelled);
2030
+ return returnInvocationOutput(toOutput(cancelled));
2031
+ }
2032
+ if (executionLease.leaseLost()) {
2033
+ throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
2034
+ }
2035
+ if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
2036
+ const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
2037
+ const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
2038
+ const durableState = agent.store
2039
+ ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
2040
+ : context.state;
2041
+ const timedOutState = createTerminalState(durableState, status, message);
2042
+ await persistState(agent, timedOutState, policy);
2043
+ await emitRunFinishTelemetry(agent, timedOutState);
2044
+ executionEnvironmentStatus = timedOutState.status;
2045
+ return returnInvocationOutput(toOutput(timedOutState));
2046
+ }
1933
2047
  const durableState = agent.store
1934
2048
  ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
1935
2049
  : context.state;
1936
- const timedOutState = createTerminalState(durableState, status, message);
1937
- await persistState(agent, timedOutState, policy);
1938
- await emitRunFinishTelemetry(agent, timedOutState);
1939
- executionEnvironmentStatus = timedOutState.status;
1940
- return toOutput(timedOutState);
1941
- }
1942
- const durableState = agent.store
1943
- ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
1944
- : context.state;
1945
- const failedState = createFailedState(durableState, error instanceof Error ? error.message : String(error));
1946
- await persistState(agent, failedState, policy);
1947
- await emitRunFinishTelemetry(agent, failedState);
1948
- executionEnvironmentStatus = failedState.status;
2050
+ const failedState = createFailedState(durableState, error instanceof Error ? error.message : String(error));
2051
+ await persistState(agent, failedState, policy);
2052
+ await emitRunFinishTelemetry(agent, failedState);
2053
+ executionEnvironmentStatus = failedState.status;
2054
+ throw error;
2055
+ }
2056
+ finally {
2057
+ await executionEnvironmentSession?.release?.({
2058
+ status: executionEnvironmentStatus,
2059
+ error: executionEnvironmentError
2060
+ });
2061
+ await executionLease.release();
2062
+ }
2063
+ }
2064
+ catch (error) {
2065
+ invocationStatus = "failed";
2066
+ invocationError = error instanceof Error ? error : new Error(String(error));
1949
2067
  throw error;
1950
2068
  }
1951
2069
  finally {
1952
- await executionEnvironmentSession?.release?.({
1953
- status: executionEnvironmentStatus,
1954
- error: executionEnvironmentError
1955
- });
1956
- await executionLease.release();
2070
+ await emitInvocationFinishTelemetry(agent, telemetryRunId, invocationStatus, invocationError);
1957
2071
  }
1958
2072
  };
1959
2073
  export const streamAgent = (agent, input = {}) => {
2074
+ const invocationStartedAt = Date.now();
2075
+ const telemetryRunId = input.runId ?? input.state?.runId ?? randomId("run");
2076
+ const invocationInput = input.runId || input.state
2077
+ ? input
2078
+ : { ...input, runId: telemetryRunId };
1960
2079
  const policy = resolveRunPolicy(agent, input);
1961
2080
  const broadcast = new BoundedReplayBroadcast({
1962
2081
  maxHistory: policy?.maxStreamEvents ?? 4096
@@ -1964,12 +2083,19 @@ export const streamAgent = (agent, input = {}) => {
1964
2083
  const publish = (event, terminal = false) => broadcast.publish(event, { terminal });
1965
2084
  let activeLease;
1966
2085
  let activeExecutionEnvironment;
2086
+ let invocationFinishPromise;
2087
+ const finishInvocation = (status, error) => {
2088
+ invocationFinishPromise ??= emitInvocationFinishTelemetry(agent, telemetryRunId, status, error);
2089
+ return invocationFinishPromise;
2090
+ };
1967
2091
  const runner = (async () => {
1968
- const context = await resolveContext(agent, input);
2092
+ await emitInvocationStartTelemetry(agent, telemetryRunId, invocationStartedAt, Math.max(1, input.maxSteps ?? input.state?.maxSteps ?? agent.maxSteps ?? 1));
2093
+ const context = await resolveContext(agent, invocationInput);
1969
2094
  const currentStatus = normalizeApprovalStatus(context.state.status);
1970
2095
  const supportsLeases = Boolean(agent.store?.acquireLease && agent.store.renewLease && agent.store.releaseLease);
1971
2096
  if (!context.fresh && currentStatus === "running" && !supportsLeases) {
1972
2097
  broadcast.close();
2098
+ await finishInvocation(currentStatus);
1973
2099
  return {
1974
2100
  output: toOutput(context.state),
1975
2101
  textStream: emptyAsyncIterable()
@@ -1981,6 +2107,7 @@ export const streamAgent = (agent, input = {}) => {
1981
2107
  currentStatus === "timed_out") {
1982
2108
  context.state.status = currentStatus;
1983
2109
  broadcast.close();
2110
+ await finishInvocation(currentStatus);
1984
2111
  return {
1985
2112
  output: toOutput(context.state),
1986
2113
  textStream: emptyAsyncIterable()
@@ -1989,6 +2116,7 @@ export const streamAgent = (agent, input = {}) => {
1989
2116
  if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
1990
2117
  context.state.status = currentStatus;
1991
2118
  broadcast.close();
2119
+ await finishInvocation(currentStatus);
1992
2120
  return {
1993
2121
  output: toOutput(context.state),
1994
2122
  textStream: emptyAsyncIterable()
@@ -2005,8 +2133,10 @@ export const streamAgent = (agent, input = {}) => {
2005
2133
  }
2006
2134
  const activeState = await agent.store?.load(context.state.runId, context.state.scope);
2007
2135
  broadcast.close();
2136
+ const outputState = activeState ? normalizeAgentRunState(activeState) : context.state;
2137
+ await finishInvocation(outputState.status);
2008
2138
  return {
2009
- output: toOutput(activeState ? normalizeAgentRunState(activeState) : context.state),
2139
+ output: toOutput(outputState),
2010
2140
  textStream: emptyAsyncIterable()
2011
2141
  };
2012
2142
  }
@@ -2015,7 +2145,7 @@ export const streamAgent = (agent, input = {}) => {
2015
2145
  if (!context.fresh || freshRequiresExistingClaim) {
2016
2146
  await claimAgentExecution(agent, context.state);
2017
2147
  }
2018
- await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
2148
+ await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals, invocationStartedAt);
2019
2149
  }
2020
2150
  catch (error) {
2021
2151
  await executionLease.release();
@@ -2027,6 +2157,7 @@ export const streamAgent = (agent, input = {}) => {
2027
2157
  await emitRunFinishTelemetry(agent, state);
2028
2158
  await executionLease.release();
2029
2159
  broadcast.close();
2160
+ await finishInvocation(state.status);
2030
2161
  return {
2031
2162
  output: toOutput(state),
2032
2163
  textStream: emptyAsyncIterable()
@@ -2064,6 +2195,7 @@ export const streamAgent = (agent, input = {}) => {
2064
2195
  }, true);
2065
2196
  broadcast.close();
2066
2197
  await executionLease.release();
2198
+ await finishInvocation(failedState.status);
2067
2199
  return {
2068
2200
  output: toOutput(failedState),
2069
2201
  textStream: emptyAsyncIterable()
@@ -2084,12 +2216,6 @@ export const streamAgent = (agent, input = {}) => {
2084
2216
  type: "agent-step-start",
2085
2217
  stepIndex: context.state.currentStep + 1
2086
2218
  });
2087
- await emitTelemetryEvent(agent, {
2088
- type: "step-start",
2089
- runId: context.state.runId,
2090
- agentId: context.state.agentId,
2091
- stepIndex: context.state.currentStep + 1
2092
- });
2093
2219
  const abortContext = createAgentAbortContext(mergeAbortSignals(input.abortSignal, executionLease.signal), policy);
2094
2220
  const executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
2095
2221
  activeExecutionEnvironment = executionEnvironmentSession;
@@ -2117,7 +2243,7 @@ export const streamAgent = (agent, input = {}) => {
2117
2243
  throw error;
2118
2244
  }
2119
2245
  const approvalRequests = [];
2120
- const eventRelay = (async () => {
2246
+ const eventRelay = withAgentTelemetryRunContext(agent, context.state.runId, async () => {
2121
2247
  for await (const event of streamResult.eventStream) {
2122
2248
  await publish(event);
2123
2249
  if (event.type === "tool-approval-request") {
@@ -2162,7 +2288,7 @@ export const streamAgent = (agent, input = {}) => {
2162
2288
  });
2163
2289
  }
2164
2290
  }
2165
- })();
2291
+ });
2166
2292
  const output = (async () => {
2167
2293
  try {
2168
2294
  const final = await withAgentPolicyTimeout(eventRelay.then(() => streamResult.collect()), abortContext);
@@ -2291,12 +2417,13 @@ export const streamAgent = (agent, input = {}) => {
2291
2417
  activeExecutionEnvironment = undefined;
2292
2418
  await executionLease.release();
2293
2419
  }
2294
- })();
2420
+ })().finally(() => finishInvocation(executionEnvironmentStatus, executionEnvironmentError ? new Error(executionEnvironmentError.message) : undefined));
2295
2421
  return {
2296
2422
  output,
2297
2423
  textStream: streamResult.textStream
2298
2424
  };
2299
2425
  })().catch(async (error) => {
2426
+ await finishInvocation("failed", error instanceof Error ? error : new Error(String(error)));
2300
2427
  await activeExecutionEnvironment?.release?.({
2301
2428
  status: "failed",
2302
2429
  error: { message: error instanceof Error ? error.message : String(error) }