eve 0.29.1 → 0.29.3

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 (182) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/src/channel/types.d.ts +15 -0
  3. package/dist/src/cli/commands/integration-setup.d.ts +3 -7
  4. package/dist/src/cli/commands/integration-setup.js +1 -1
  5. package/dist/src/cli/commands/registry.d.ts +1 -0
  6. package/dist/src/cli/commands/registry.js +1 -1
  7. package/dist/src/cli/commands/trace.d.ts +7 -24
  8. package/dist/src/cli/commands/trace.js +7 -5
  9. package/dist/src/cli/dev/tui/blocks.js +1 -1
  10. package/dist/src/cli/dev/tui/clipboard.d.ts +17 -0
  11. package/dist/src/cli/dev/tui/clipboard.js +1 -0
  12. package/dist/src/cli/dev/tui/command-typeahead.js +1 -1
  13. package/dist/src/cli/dev/tui/prompt-commands.d.ts +3 -0
  14. package/dist/src/cli/dev/tui/prompt-commands.js +1 -1
  15. package/dist/src/cli/dev/tui/remote-auth.js +1 -1
  16. package/dist/src/cli/dev/tui/runner.d.ts +6 -0
  17. package/dist/src/cli/dev/tui/runner.js +1 -1
  18. package/dist/src/cli/dev/tui/stream-format.d.ts +8 -0
  19. package/dist/src/cli/dev/tui/stream-format.js +1 -1
  20. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +3 -0
  21. package/dist/src/cli/dev/tui/terminal-renderer.js +12 -12
  22. package/dist/src/cli/dev/tui/test/mock-terminal.js +3 -3
  23. package/dist/src/cli/dev/tui/traces/trace-content.d.ts +28 -0
  24. package/dist/src/cli/dev/tui/traces/trace-content.js +2 -0
  25. package/dist/src/cli/dev/tui/traces/trace-conversation.d.ts +58 -0
  26. package/dist/src/cli/dev/tui/traces/trace-conversation.js +2 -0
  27. package/dist/src/cli/dev/tui/traces/trace-store.d.ts +27 -0
  28. package/dist/src/cli/dev/tui/traces/trace-store.js +1 -0
  29. package/dist/src/cli/dev/tui/traces/trace-view.d.ts +48 -0
  30. package/dist/src/cli/dev/tui/traces/trace-view.js +2 -0
  31. package/dist/src/cli/dev/tui/traces/trace-viewer-session.d.ts +41 -0
  32. package/dist/src/cli/dev/tui/traces/trace-viewer-session.js +1 -0
  33. package/dist/src/cli/dev/tui/traces/trace-viewer-state.d.ts +110 -0
  34. package/dist/src/cli/dev/tui/traces/trace-viewer-state.js +1 -0
  35. package/dist/src/cli/ui/alt-screen.d.ts +42 -0
  36. package/dist/src/cli/ui/alt-screen.js +1 -0
  37. package/dist/src/cli/ui/terminal-text.d.ts +4 -0
  38. package/dist/src/cli/ui/terminal-text.js +1 -1
  39. package/dist/src/compiled/.vendor-stamp.json +2 -1
  40. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/LICENSE +21 -0
  41. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/_spectrum-core.d.ts +3 -0
  42. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/_spectrum-imessage.d.ts +2 -0
  43. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.d.ts +496 -0
  44. package/dist/src/compiled/@photon-ai/chat-adapter-imessage/index.js +137 -0
  45. package/dist/src/compiled/@vercel/sandbox/index.js +13 -107
  46. package/dist/src/compiled/_chunks/node/auth-BS3UoyvT.js +2 -0
  47. package/dist/src/compiled/_chunks/node/compat-Dm2n85Mx.js +1 -0
  48. package/dist/src/compiled/_chunks/node/schemas-DtQwuPdM.js +65 -0
  49. package/dist/src/compiled/_chunks/node/undici-DWL_MYmT.js +95 -0
  50. package/dist/src/compiled/_chunks/node/version-Cztrk7z6.js +1 -0
  51. package/dist/src/compiled/shadcn-registry/index.js +461 -893
  52. package/dist/src/context/keys.d.ts +3 -1
  53. package/dist/src/context/keys.js +1 -1
  54. package/dist/src/evals/runner/execute-task.js +1 -1
  55. package/dist/src/evals/runner/run-evals.js +1 -1
  56. package/dist/src/execution/delegated-parent-notification.js +1 -1
  57. package/dist/src/execution/delegated-parent-result.js +1 -1
  58. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  59. package/dist/src/execution/runtime-context.js +1 -1
  60. package/dist/src/execution/sandbox/glob-tool.js +3 -3
  61. package/dist/src/execution/sandbox/grep-tool.js +1 -1
  62. package/dist/src/execution/sandbox/read-file-tool.js +3 -3
  63. package/dist/src/execution/sandbox/require-sandbox.d.ts +3 -3
  64. package/dist/src/execution/sandbox/require-sandbox.js +1 -1
  65. package/dist/src/execution/sandbox/write-file-tool.js +1 -1
  66. package/dist/src/execution/subagent-adapter-state.d.ts +39 -0
  67. package/dist/src/execution/subagent-adapter-state.js +1 -0
  68. package/dist/src/execution/subagent-adapter.d.ts +0 -34
  69. package/dist/src/execution/subagent-adapter.js +1 -1
  70. package/dist/src/execution/subagent-tool.d.ts +2 -1
  71. package/dist/src/execution/subagent-tool.js +1 -1
  72. package/dist/src/execution/workflow-steps.js +1 -1
  73. package/dist/src/harness/emission.js +1 -1
  74. package/dist/src/harness/instrumentation-lifecycle.d.ts +18 -0
  75. package/dist/src/harness/instrumentation-native-events.d.ts +3 -1
  76. package/dist/src/harness/instrumentation-native-events.js +1 -1
  77. package/dist/src/harness/parent-lineage.d.ts +11 -0
  78. package/dist/src/harness/parent-lineage.js +1 -0
  79. package/dist/src/harness/runtime-actions.js +1 -1
  80. package/dist/src/harness/tool-loop.js +2 -2
  81. package/dist/src/internal/application/package.js +1 -1
  82. package/dist/src/internal/nitro/host/local-tracing-runtime-plugin.js +1 -1
  83. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  84. package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +7 -0
  85. package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
  86. package/dist/src/public/channels/photon/inboundContent.d.ts +4 -0
  87. package/dist/src/public/channels/photon/inboundContent.js +1 -0
  88. package/dist/src/public/channels/photon/index.d.ts +1 -0
  89. package/dist/src/public/channels/photon/index.js +1 -0
  90. package/dist/src/public/channels/photon/photonIMessageChannel.d.ts +54 -0
  91. package/dist/src/public/channels/photon/photonIMessageChannel.js +1 -0
  92. package/dist/src/runtime/channels/registry.js +1 -1
  93. package/dist/src/runtime/framework-tools/glob.js +1 -1
  94. package/dist/src/runtime/framework-tools/grep.js +1 -1
  95. package/dist/src/runtime/framework-tools/read-file.js +1 -1
  96. package/dist/src/runtime/framework-tools/write-file.js +1 -1
  97. package/dist/src/setup/boxes/resolve-provisioning.js +1 -1
  98. package/dist/src/setup/connect-provisioning.d.ts +32 -0
  99. package/dist/src/setup/connect-provisioning.js +1 -0
  100. package/dist/src/setup/flows/login.js +1 -1
  101. package/dist/src/setup/flows/registry.js +1 -1
  102. package/dist/src/setup/integrations/photon/connect.d.ts +40 -0
  103. package/dist/src/setup/integrations/photon/connect.js +1 -0
  104. package/dist/src/setup/integrations/photon/management.d.ts +34 -0
  105. package/dist/src/setup/integrations/photon/management.js +1 -0
  106. package/dist/src/setup/integrations/photon/setup-flow.d.ts +39 -0
  107. package/dist/src/setup/integrations/photon/setup-flow.js +20 -0
  108. package/dist/src/setup/integrations/photon/setup.d.ts +3 -0
  109. package/dist/src/setup/integrations/photon/setup.js +1 -0
  110. package/dist/src/setup/integrations/registry.d.ts +6 -0
  111. package/dist/src/setup/integrations/registry.js +1 -0
  112. package/dist/src/setup/integrations/runner.d.ts +25 -0
  113. package/dist/src/setup/integrations/runner.js +1 -0
  114. package/dist/src/setup/integrations/{channels → shared}/environment.d.ts +3 -3
  115. package/dist/src/setup/integrations/shared/environment.js +1 -0
  116. package/dist/src/setup/integrations/shared/scaffold.d.ts +19 -0
  117. package/dist/src/setup/integrations/shared/scaffold.js +1 -0
  118. package/dist/src/setup/integrations/{channels → shared}/ui.d.ts +3 -3
  119. package/dist/src/setup/integrations/shared/ui.js +2 -0
  120. package/dist/src/setup/integrations/slack/setup.d.ts +16 -0
  121. package/dist/src/setup/integrations/slack/setup.js +1 -0
  122. package/dist/src/setup/integrations/types.d.ts +26 -0
  123. package/dist/src/setup/integrations/web/setup.d.ts +3 -0
  124. package/dist/src/setup/integrations/web/setup.js +1 -0
  125. package/dist/src/setup/primitives/run-vercel.d.ts +2 -7
  126. package/dist/src/setup/primitives/run-vercel.js +1 -1
  127. package/dist/src/setup/scaffold/create/add-to-project.js +1 -1
  128. package/dist/src/setup/scaffold/create/project.js +2 -2
  129. package/dist/src/setup/scaffold/update/channels.js +1 -1
  130. package/dist/src/setup/scaffold/update/connections.js +1 -1
  131. package/dist/src/setup/slack-connect-lifecycle.d.ts +2 -8
  132. package/dist/src/setup/slack-connect-lifecycle.js +1 -1
  133. package/dist/src/setup/state.d.ts +0 -12
  134. package/dist/src/setup/state.js +1 -1
  135. package/dist/src/shared/skill-paths.d.ts +10 -0
  136. package/dist/src/shared/skill-paths.js +2 -2
  137. package/dist/src/tracing/agent-otel-content.d.ts +32 -0
  138. package/dist/src/tracing/agent-otel-content.js +4 -0
  139. package/dist/src/{harness → tracing}/agent-otel-provider.d.ts +10 -1
  140. package/dist/src/tracing/agent-otel-provider.js +2 -0
  141. package/dist/src/{harness → tracing}/agent-trace-context-store.d.ts +8 -1
  142. package/dist/src/tracing/agent-trace-context-store.js +1 -0
  143. package/dist/src/{harness → tracing}/agent-trace-span-processor.d.ts +5 -1
  144. package/dist/src/tracing/agent-trace-span-processor.js +1 -0
  145. package/dist/src/tracing/local-instrumentation-runtime.js +1 -0
  146. package/dist/src/tracing/local-trace-reader.d.ts +57 -0
  147. package/dist/src/tracing/local-trace-reader.js +1 -0
  148. package/dist/src/{harness → tracing}/local-trace-retention.js +1 -1
  149. package/dist/src/{harness → tracing}/local-trace-span-processor.js +1 -1
  150. package/docs/channels/chat-sdk.mdx +18 -1
  151. package/docs/channels/meta.json +1 -0
  152. package/docs/channels/overview.mdx +1 -1
  153. package/docs/channels/photon.mdx +78 -0
  154. package/docs/concepts/default-harness.md +2 -0
  155. package/docs/guides/dev-tui.md +20 -1
  156. package/docs/guides/instrumentation.md +8 -7
  157. package/docs/reference/cli.md +9 -1
  158. package/docs/sandbox.mdx +1 -1
  159. package/package.json +7 -1
  160. package/dist/src/compiled/_chunks/node/auth-DMsLOrh7.js +0 -2
  161. package/dist/src/compiled/_chunks/node/version-BQBAL95V.js +0 -64
  162. package/dist/src/harness/agent-otel-provider.js +0 -1
  163. package/dist/src/harness/agent-trace-context-store.js +0 -1
  164. package/dist/src/harness/agent-trace-span-processor.js +0 -1
  165. package/dist/src/harness/local-instrumentation-runtime.js +0 -1
  166. package/dist/src/setup/integrations/channels/environment.js +0 -1
  167. package/dist/src/setup/integrations/channels/index.d.ts +0 -7
  168. package/dist/src/setup/integrations/channels/index.js +0 -1
  169. package/dist/src/setup/integrations/channels/runner.d.ts +0 -7
  170. package/dist/src/setup/integrations/channels/runner.js +0 -1
  171. package/dist/src/setup/integrations/channels/setup.d.ts +0 -130
  172. package/dist/src/setup/integrations/channels/setup.js +0 -2
  173. package/dist/src/setup/integrations/channels/slack.d.ts +0 -3
  174. package/dist/src/setup/integrations/channels/slack.js +0 -1
  175. package/dist/src/setup/integrations/channels/types.d.ts +0 -49
  176. package/dist/src/setup/integrations/channels/ui.js +0 -2
  177. package/dist/src/setup/integrations/channels/web.d.ts +0 -3
  178. package/dist/src/setup/integrations/channels/web.js +0 -1
  179. /package/dist/src/setup/integrations/{channels/types.js → types.js} +0 -0
  180. /package/dist/src/{harness → tracing}/local-instrumentation-runtime.d.ts +0 -0
  181. /package/dist/src/{harness → tracing}/local-trace-retention.d.ts +0 -0
  182. /package/dist/src/{harness → tracing}/local-trace-span-processor.d.ts +0 -0
@@ -147,8 +147,6 @@ export interface SetupState {
147
147
  setupMode: SetupMode;
148
148
  modelId: string;
149
149
  modelWiring: WiringMode;
150
- /** Channels chosen in the interview phase; scaffolded later by the channels box. */
151
- channelSelection: ChannelKind[];
152
150
  /** Connections planned in the interview phase; scaffolded later by the connections box. */
153
151
  connectionSelection: ConnectionPlan[];
154
152
  /** Decided once by the resolve-provisioning box; executed later by the link box. */
@@ -158,20 +156,10 @@ export interface SetupState {
158
156
  projectPath: ResolvedProjectPath;
159
157
  aiGatewayCredentials: ResolvedAiGatewayCredentials;
160
158
  chat: ChatPreference | null;
161
- /** Channels scaffolded so far in this run. */
162
- channels: ChannelKind[];
163
- webScaffolded: boolean;
164
- slackScaffolded: boolean;
165
159
  deploymentDependenciesInstalled: boolean;
166
160
  /** The linked Vercel project facts, from the link box or the on-disk `.vercel` link. */
167
161
  project: ProjectResolution;
168
162
  deploymentPending: boolean;
169
- slackbotCreated: boolean;
170
- slackbotAttached: boolean;
171
- slackConnectorUid: string | undefined;
172
- /** Deep link that opens a DM compose with the bot ("chat with your agent"). */
173
- slackChatUrl: string | undefined;
174
- slackWorkspaceName: string | undefined;
175
163
  }
176
164
  export declare function createDefaultSetupState(): SetupState;
177
165
  /**
@@ -1 +1 @@
1
- import{isProjectResolved}from"./project-resolution.js";import{CURRENT_DIRECTORY_PROJECT_NAME}from"#setup/scaffold/index.js";function createDefaultSetupState(){return{channels:[],webScaffolded:!1,slackScaffolded:!1,deploymentDependenciesInstalled:!1,project:{kind:`unresolved`},deploymentPending:!1,slackbotCreated:!1,slackbotAttached:!1,slackConnectorUid:void 0,slackChatUrl:void 0,slackWorkspaceName:void 0,agentName:``,setupMode:`complete`,modelId:``,modelWiring:`gateway`,channelSelection:[],connectionSelection:[],vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},projectPath:{kind:`unresolved`,inPlace:!1},aiGatewayCredentials:{kind:`unresolved`},chat:null}}function hasVercelProject(t){return t.vercelProject.kind!==`none`||isProjectResolved(t.project)}function requireProjectPath(e){if(e.projectPath.kind===`resolved`)return e.projectPath.path;throw Error(`Project path has not been resolved.`)}function snapshotSetupState(e){return Object.freeze({...e,aiGatewayCredentials:Object.freeze({...e.aiGatewayCredentials}),aiGateway:Object.freeze({...e.aiGateway}),channelSelection:Object.freeze([...e.channelSelection]),connectionSelection:Object.freeze([...e.connectionSelection]),channels:Object.freeze([...e.channels]),project:Object.freeze({...e.project}),projectPath:Object.freeze({...e.projectPath}),vercelProject:Object.freeze({...e.vercelProject})})}export{CURRENT_DIRECTORY_PROJECT_NAME,createDefaultSetupState,hasVercelProject,requireProjectPath,snapshotSetupState};
1
+ import{isProjectResolved}from"./project-resolution.js";import{CURRENT_DIRECTORY_PROJECT_NAME}from"#setup/scaffold/index.js";function createDefaultSetupState(){return{deploymentDependenciesInstalled:!1,project:{kind:`unresolved`},deploymentPending:!1,agentName:``,setupMode:`complete`,modelId:``,modelWiring:`gateway`,connectionSelection:[],vercelProject:{kind:`none`},aiGateway:{kind:`inherit`},projectPath:{kind:`unresolved`,inPlace:!1},aiGatewayCredentials:{kind:`unresolved`},chat:null}}function hasVercelProject(t){return t.vercelProject.kind!==`none`||isProjectResolved(t.project)}function requireProjectPath(e){if(e.projectPath.kind===`resolved`)return e.projectPath.path;throw Error(`Project path has not been resolved.`)}function snapshotSetupState(e){return Object.freeze({...e,aiGatewayCredentials:Object.freeze({...e.aiGatewayCredentials}),aiGateway:Object.freeze({...e.aiGateway}),connectionSelection:Object.freeze([...e.connectionSelection]),project:Object.freeze({...e.project}),projectPath:Object.freeze({...e.projectPath}),vercelProject:Object.freeze({...e.vercelProject})})}export{CURRENT_DIRECTORY_PROJECT_NAME,createDefaultSetupState,hasVercelProject,requireProjectPath,snapshotSetupState};
@@ -12,6 +12,16 @@ export declare function formatFallbackSkillPath(input: {
12
12
  export declare function resolveSandboxSkillRoot(input: {
13
13
  readonly sandbox: SandboxSession;
14
14
  }): Promise<string>;
15
+ /**
16
+ * Resolves a leading `$HOME` in a model-supplied sandbox path without
17
+ * evaluating any other shell syntax. Skill paths retain their documented
18
+ * `/workspace/skills` fallback when the sandbox does not expose a usable
19
+ * home directory.
20
+ */
21
+ export declare function resolveSandboxModelPath(input: {
22
+ readonly path: string;
23
+ readonly sandbox: SandboxSession;
24
+ }): Promise<string>;
15
25
  export declare function resolveSandboxSkillReadPaths(input: {
16
26
  readonly name: string;
17
27
  readonly relativePath: string;
@@ -1,2 +1,2 @@
1
- const MODEL_SKILL_ROOT=`$HOME/.agents/skills`,FALLBACK_SKILL_ROOT=`/workspace/skills`,skillRootCache=new WeakMap;function formatSkillModelPath(t){return formatSkillPath({name:t.name,relativePath:t.relativePath,root:MODEL_SKILL_ROOT})}function formatFallbackSkillPath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:FALLBACK_SKILL_ROOT})}async function resolveSandboxSkillRoot(e){let t=skillRootCache.get(e.sandbox);if(t!==void 0)return await t;let r=probeSandboxSkillRoot(e.sandbox);return skillRootCache.set(e.sandbox,r),await r}async function resolveSandboxSkillReadPaths(e){return[formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})]}async function resolveSandboxSkillWritePath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})}async function resolveSandboxSeedFilePath(e){return e.path.startsWith(`$HOME/.agents/skills/`)?`${await resolveSandboxSkillRoot({sandbox:e.sandbox})}${e.path.slice(20)}`:e.path}function formatSkillPath(e){return`${e.root}/${e.name}/${e.relativePath}`}async function probeSandboxSkillRoot(e){try{let n=await e.run({command:`printf '%s\\n' "$HOME"`});if(n.exitCode!==0)return FALLBACK_SKILL_ROOT;let r=n.stdout.trim();return isUsableSandboxHome(r)?joinHomeSkillRoot(r):FALLBACK_SKILL_ROOT}catch{return FALLBACK_SKILL_ROOT}}function isUsableSandboxHome(e){return e.length>0&&e.startsWith(`/`)&&!e.includes(`\0`)&&!e.includes(`
2
- `)&&!e.includes(`\r`)}function joinHomeSkillRoot(e){return`${e===`/`?``:e.replace(/\/+$/,``)}/.agents/skills`}export{FALLBACK_SKILL_ROOT,MODEL_SKILL_ROOT,formatFallbackSkillPath,formatSkillModelPath,resolveSandboxSeedFilePath,resolveSandboxSkillReadPaths,resolveSandboxSkillRoot,resolveSandboxSkillWritePath};
1
+ const MODEL_SKILL_ROOT=`$HOME/.agents/skills`,FALLBACK_SKILL_ROOT=`/workspace/skills`,MODEL_HOME_ROOT=`$HOME`,sandboxHomeCache=new WeakMap;function formatSkillModelPath(t){return formatSkillPath({name:t.name,relativePath:t.relativePath,root:MODEL_SKILL_ROOT})}function formatFallbackSkillPath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:FALLBACK_SKILL_ROOT})}async function resolveSandboxSkillRoot(e){let n=await resolveSandboxHome(e.sandbox);return n===null?FALLBACK_SKILL_ROOT:joinHomeSkillRoot(n)}async function resolveSandboxModelPath(e){if(!isModelHomePath(e.path))return e.path;let n=await resolveSandboxHome(e.sandbox);if(n!==null){let t=e.path.slice(5);return t.length===0?n:`${n===`/`?``:n}${t}`}return isModelSkillPath(e.path)?`${FALLBACK_SKILL_ROOT}${e.path.slice(20)}`:e.path}async function resolveSandboxSkillReadPaths(e){return[formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})]}async function resolveSandboxSkillWritePath(e){return formatSkillPath({name:e.name,relativePath:e.relativePath,root:await resolveSandboxSkillRoot({sandbox:e.sandbox})})}async function resolveSandboxSeedFilePath(e){return isModelSkillPath(e.path)?await resolveSandboxModelPath(e):e.path}function formatSkillPath(e){return`${e.root}/${e.name}/${e.relativePath}`}async function resolveSandboxHome(e){let t=sandboxHomeCache.get(e);if(t!==void 0)return await t;let n=probeSandboxHome(e);return sandboxHomeCache.set(e,n),await n}async function probeSandboxHome(e){try{let t=await e.run({command:`printf '%s\\n' "$HOME"`});if(t.exitCode!==0)return null;let n=t.stdout.trim();return isUsableSandboxHome(n)?normalizeSandboxHome(n):null}catch{return null}}function isModelHomePath(e){return e===MODEL_HOME_ROOT||e.startsWith(`${MODEL_HOME_ROOT}/`)}function isModelSkillPath(e){return e===`$HOME/.agents/skills`||e.startsWith(`$HOME/.agents/skills/`)}function isUsableSandboxHome(e){return e.length>0&&e.startsWith(`/`)&&!e.includes(`\0`)&&!e.includes(`
2
+ `)&&!e.includes(`\r`)}function joinHomeSkillRoot(e){return`${e===`/`?``:e}/.agents/skills`}function normalizeSandboxHome(e){return e===`/`?e:e.replace(/\/+$/,``)}export{FALLBACK_SKILL_ROOT,MODEL_SKILL_ROOT,formatFallbackSkillPath,formatSkillModelPath,resolveSandboxModelPath,resolveSandboxSeedFilePath,resolveSandboxSkillReadPaths,resolveSandboxSkillRoot,resolveSandboxSkillWritePath};
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Serializes model and tool payloads into span content attributes for the
3
+ * local trace viewer: prompt messages, the system prompt, responses, and
4
+ * tool arguments/results. Everything is capped so a giant payload cannot
5
+ * bloat a span segment past the reader's per-file limit, and message
6
+ * payloads stay valid JSON through every truncation path.
7
+ */
8
+ /** Content attributes are capped so a giant payload cannot bloat a span segment. */
9
+ export declare const CONTENT_ATTRIBUTE_LIMIT: number;
10
+ /**
11
+ * Serializes one payload value. `strip` removes {@link CONTENT_NOISE_KEYS};
12
+ * pass `false` for tool data, where those keys may be legitimate domain
13
+ * fields the user authored or the tool returned.
14
+ */
15
+ export declare function contentAttribute(value: unknown, strip?: boolean): string | undefined;
16
+ /**
17
+ * Serializes the prompt messages, keeping the result parseable: oldest
18
+ * messages drop first behind an omission marker, and a single message over
19
+ * the cap is truncated at the text level rather than the JSON level.
20
+ */
21
+ export declare function messagesContentAttribute(messages: unknown): string | undefined;
22
+ /**
23
+ * Serializes provider-executed tool results, keeping the attribute valid
24
+ * JSON under the cap: when the full payload is too big, each entry's input
25
+ * and output collapse to capped text at progressively smaller budgets
26
+ * instead of cutting the JSON mid-string.
27
+ */
28
+ export declare function toolResultsContentAttribute(results: readonly Record<string, unknown>[]): string | undefined;
29
+ /** Normalizes the AI SDK's `instructions` prompt to plain text for `ai.prompt.system`. */
30
+ export declare function systemPromptAttribute(instructions: unknown): string | undefined;
31
+ /** Caps plain text, marking the cut. */
32
+ export declare function textContentAttribute(text: string): string | undefined;
@@ -0,0 +1,4 @@
1
+ const CONTENT_ATTRIBUTE_LIMIT=32*1024,CONTENT_NOISE_KEYS=new Set([`providerOptions`,`providerMetadata`]),TRUNCATED_MESSAGES_KEY=`eve.truncated`;function contentAttribute(e,t=!0){if(e===void 0)return;let n=t?stripContentNoise(e,0):e,r;try{r=JSON.stringify(n)}catch{return}if(r!==void 0)return textContentAttribute(r)}function messagesContentAttribute(e){if(!Array.isArray(e))return contentAttribute(e);let t=stripContentNoise(e,0),r=stringifyContent(t);if(r!==void 0&&r.length<=32768)return r;for(let e=1;e<t.length;e+=1){let r=stringifyContent([{[TRUNCATED_MESSAGES_KEY]:{omittedMessages:e}},...t.slice(e)]);if(r!==void 0&&r.length<=32768)return r}return truncateSingleMessage(t)}function toolResultsContentAttribute(t){if(t.length===0)return;let n=stringifyContent(t);if(n!==void 0&&n.length<=32768)return n;for(let n=CONTENT_ATTRIBUTE_LIMIT;n>=0;n=n>=256?Math.floor(n/2):-1){let e=stringifyContent(t.map(e=>cappedToolResult(e,n)));if(e!==void 0&&e.length<=32768)return e}}function cappedToolResult(e,t){let n={toolName:e.toolName};for(let r of[`input`,`output`,`error`]){if(!(r in e))continue;let i=e[r],a=typeof i==`string`?i:stringifyContent(i)??``;n[r]=a.length<=t?a:`${a.slice(0,t)}… [truncated]`}return n}function systemPromptAttribute(e){if(typeof e==`string`)return textContentAttribute(e);if(!isRecord(e)&&!Array.isArray(e))return;let t=Array.isArray(e)?e:[e],n=[];for(let e of t)if(isRecord(e)){if(typeof e.content==`string`)n.push(e.content);else if(Array.isArray(e.content))for(let t of e.content)isRecord(t)&&t.type===`text`&&typeof t.text==`string`&&n.push(t.text)}let r=n.join(`
2
+
3
+ `).trim();return r.length===0?void 0:textContentAttribute(r)}function textContentAttribute(t){if(t.length!==0)return t.length<=32768?t:`${t.slice(0,CONTENT_ATTRIBUTE_LIMIT)}… [truncated]`}function stripContentNoise(e,n){if(n>32||typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(e=>stripContentNoise(e,n+1));let r={};for(let[i,a]of Object.entries(e))CONTENT_NOISE_KEYS.has(i)||(r[i]=stripContentNoise(a,n+1));return r}function truncateSingleMessage(t){if(t.length===0)return;let r=t[t.length-1];if(!isRecord(r))return;let i={[TRUNCATED_MESSAGES_KEY]:{omittedMessages:t.length-1}},a=typeof r.role==`string`?r.role:`user`,o=``;if(typeof r.content==`string`)o=r.content;else if(Array.isArray(r.content))for(let e of r.content)isRecord(e)&&e.type===`text`&&typeof e.text==`string`&&(o+=(o?`
4
+ `:``)+e.text);for(let t=Math.min(o.length,CONTENT_ATTRIBUTE_LIMIT);t>0;t-=256){let e=stringifyContent([i,{role:a,content:t>=o.length?o:`${o.slice(0,t)}… [truncated]`}]);if(e!==void 0&&e.length<=32768)return e}return stringifyContent([i,{role:a,content:``}])}function stringifyContent(e){try{return JSON.stringify(e)}catch{return}}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{CONTENT_ATTRIBUTE_LIMIT,contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute};
@@ -1,11 +1,14 @@
1
1
  import { type SpanContext, type Tracer } from "#compiled/@opentelemetry/api/index.js";
2
2
  import type { InstrumentationContextRunner, InstrumentationProviderDefinition, InstrumentationTurnTerminalEvent } from "#harness/instrumentation-lifecycle.js";
3
+ /** Sized so an ordinary session stays one trace and only an outsized one rolls. */
4
+ export declare const SESSION_WINDOW_TURN_LIMIT = 200;
3
5
  export interface AgentSessionTraceState {
4
6
  readonly agentName?: string;
5
7
  readonly channelKind?: string;
6
8
  readonly context: SpanContext;
7
- readonly pendingStarted: boolean;
8
9
  readonly rootSessionId: string;
10
+ readonly turnsInWindow: number;
11
+ readonly window: number;
9
12
  }
10
13
  export interface AgentTurnTraceState {
11
14
  readonly context: SpanContext;
@@ -36,6 +39,12 @@ export declare class InMemoryAgentTraceStateStore implements AgentTraceStateStor
36
39
  setTurn(sessionId: string, turnId: string, state: AgentTurnTraceState): void;
37
40
  }
38
41
  export interface AgentOtelInstrumentationInput {
42
+ /**
43
+ * Capture model prompts/responses and tool call inputs/outputs as span
44
+ * attributes. Content stays on the local machine — this provider only
45
+ * wires the dev-time local spool — but can be turned off per project.
46
+ */
47
+ readonly captureContent?: boolean;
39
48
  readonly frameworkVersion: string;
40
49
  readonly stateStore: AgentTraceStateStore;
41
50
  readonly tracer: Tracer;
@@ -0,0 +1,2 @@
1
+ import{ROOT_CONTEXT,SpanStatusCode,context,trace}from"#compiled/@opentelemetry/api/index.js";import{contentAttribute,messagesContentAttribute,systemPromptAttribute,textContentAttribute,toolResultsContentAttribute}from"#tracing/agent-otel-content.js";const SESSION_WINDOW_TURN_LIMIT=200;var InMemoryAgentTraceStateStore=class{#e=new Map;#t=new Map;deleteSession(e){this.#e.delete(e)}deleteTurn(e,t){this.#t.delete(turnKey(e,t))}getSession(e){return this.#e.get(e)}getTurn(e,t){return this.#t.get(turnKey(e,t))}setSession(e,t){this.#e.set(e,t)}setTurn(e,t,n){this.#t.set(turnKey(e,t),n)}};function createAgentOtelInstrumentation(e){let t=e.captureContent??!0,l=new WeakMap,u=new WeakMap,onSessionStarted=async e=>{await ensureSessionContext(e)},onTurnStarted=async t=>{let n=advanceSessionWindow(t.sessionId,await ensureSessionContext({agentName:void 0,channelKind:void 0,parentTraceContext:t.parentTraceContext,rootSessionId:t.rootSessionId,sessionId:t.sessionId,type:`session.started`})),r=e.tracer.startSpan(`agent.turn`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":n.agentName,...parentLineageAttributes(t.parentLineage),"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":n.window,"agent.turn.id":t.turnId,"agent.turn.sequence":t.sequence}},contextFromSpanContext(n.context));r.addEvent(`turn.started`),await e.stateStore.setSession(t.sessionId,{...n,turnsInWindow:n.turnsInWindow+1}),await e.stateStore.setTurn(t.sessionId,t.turnId,{context:r.spanContext(),rootSessionId:t.rootSessionId,sequence:t.sequence}),r.end()},onAttemptStarted=async t=>{let n=await e.stateStore.getTurn(t.scope.sessionId,t.scope.turnId);if(n===void 0)return;let i=contextFromSpanContext(n.context),a=e.tracer.startSpan(`agent.step`,{attributes:{"agent.session.id":t.scope.sessionId,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":t.scope.rootSessionId??t.scope.sessionId,"agent.step.attempt":t.scope.attemptIndex,"agent.step.index":t.scope.stepIndex,"agent.turn.id":t.scope.turnId,"agent.name":t.scope.functionId}},i);a.addEvent(`step.started`);let o=trace.setSpan(i,a),s=t.operation.operationId,c=e.tracer.startSpan(s,{attributes:{"gen_ai.operation.name":s,"gen_ai.provider.name":t.operation.provider,"gen_ai.request.model":t.operation.modelId}},o);u.set(t.scope,{operation:{context:trace.setSpan(o,c),name:s,span:c},step:{context:o,span:a}})},onAttemptTerminal=e=>{l.delete(e.scope);let t=u.get(e.scope);t!==void 0&&(t.step.span.addEvent(e.type===`attempt.completed`?`step.completed`:`step.failed`),e.type===`attempt.failed`&&(recordError(t.operation.span,e.error),recordError(t.step.span,e.error)),t.operation.span.end(),t.step.span.end(),u.delete(e.scope))},onTurnTerminal=async t=>{let n=await e.stateStore.getTurn(t.sessionId,t.turnId);n!==void 0&&await e.stateStore.setTurn(t.sessionId,t.turnId,{...n,terminal:{error:t.error,type:t.type}})},onSessionTransition=async t=>{if(t.turnId!==void 0){let n=await e.stateStore.getTurn(t.sessionId,t.turnId);if(n!==void 0){let r=e.tracer.startSpan(`agent.turn.terminal`,{attributes:{"agent.root.session.id":n.rootSessionId,"agent.session.id":t.sessionId,"agent.turn.id":t.turnId,"agent.turn.sequence":n.sequence}},contextFromSpanContext(n.context));n.terminal!==void 0&&(r.addEvent(n.terminal.type),n.terminal.type===`turn.failed`&&recordError(r,n.terminal.error)),r.addEvent(t.type),r.end(),await e.stateStore.deleteTurn(t.sessionId,t.turnId)}}(t.type===`session.completed`||t.type===`session.failed`)&&await e.stateStore.deleteSession(t.sessionId)},beforeModelCall=n=>{let i=u.get(n.scope);if(i===void 0)return;i.step.span.setAttribute(`agent.model.id`,n.source.modelId),i.step.span.setAttribute(`agent.model.provider`,n.source.provider);let s=e.tracer.startSpan(modelSpanName(i.operation.name),{attributes:{"gen_ai.operation.name":i.operation.name,"gen_ai.provider.name":n.source.provider,"gen_ai.request.model":n.source.modelId}},i.operation.context);if(t){let e=messagesContentAttribute(n.source.messages);e!==void 0&&s.setAttribute(`ai.prompt.messages`,e);let t=systemPromptAttribute(n.source.instructions);t!==void 0&&s.setAttribute(`ai.prompt.system`,t)}let c={context:trace.setSpan(i.operation.context,s),span:s};return getExecutionContexts(n.scope).models.set(n.id,c.context),c},afterModelCall=(e,n)=>{if(l.get(e.scope)?.models.delete(e.id),isSpanState(n)){if(e.type===`model.call.failed`)recordError(n.span,e.error);else{setUsage(n.span,e.source.usage);let r=u.get(e.scope);if(r!==void 0&&setUsage(r.step.span,e.source.usage),t){n.span.setAttribute(`ai.response.finish_reason`,e.source.finishReason);let t=textContentAttribute(e.source.content.filter(e=>e.type===`reasoning`).map(e=>e.text).filter(e=>e.trim().length>0).join(`
2
+ `));t!==void 0&&n.span.setAttribute(`ai.response.reasoning`,t);let r=textContentAttribute(e.source.content.filter(e=>e.type===`text`).map(e=>e.text).join(``));r!==void 0&&n.span.setAttribute(`ai.response.text`,r);let a=e.source.content.filter(e=>e.type===`tool-call`).map(e=>({input:e.input,toolName:e.toolName}));if(a.length>0){let e=contentAttribute(a,!1);e!==void 0&&n.span.setAttribute(`ai.response.tool_calls`,e)}let o=e.source.content.filter(e=>e.type===`tool-result`||e.type===`tool-error`).map(e=>e.type===`tool-result`?{input:e.input,output:e.output,toolName:e.toolName}:{error:errorText(e.error),input:e.input,toolName:e.toolName});if(o.length>0){let e=toolResultsContentAttribute(o);e!==void 0&&n.span.setAttribute(`ai.response.tool_results`,e)}}}n.span.end()}},beforeToolCall=n=>{let a=u.get(n.scope);if(a===void 0)return;let o=e.tracer.startSpan(`agent.action`,{attributes:{"agent.action.call_id":n.source.toolCall.toolCallId,"agent.action.kind":`tool`,"agent.action.name":n.source.toolCall.toolName,"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.root.session.id":n.scope.rootSessionId??n.scope.sessionId,"agent.session.id":n.scope.sessionId,"agent.step.attempt":n.scope.attemptIndex,"agent.step.index":n.scope.stepIndex,"agent.turn.id":n.scope.turnId}},a.step.context),s=trace.setSpan(a.step.context,o),c=e.tracer.startSpan(`ai.toolCall`,{attributes:{"gen_ai.operation.name":`execute_tool`,"gen_ai.tool.call.id":n.source.toolCall.toolCallId,"gen_ai.tool.name":n.source.toolCall.toolName}},s);if(t){let e=contentAttribute(n.source.toolCall.input,!1);e!==void 0&&c.setAttribute(`gen_ai.tool.call.arguments`,e)}let l={context:trace.setSpan(s,c),span:o,toolSpan:c};return getExecutionContexts(n.scope).tools.set(n.id,l.context),l},afterToolCall=(e,n)=>{if(l.get(e.scope)?.tools.delete(e.id),isToolSpanState(n)){if(e.type===`tool.call.failed`)recordError(n.toolSpan,e.error),recordError(n.span,e.error);else if(e.source.toolOutput.type!==`tool-result`)recordError(n.toolSpan,e.source.toolOutput.error),recordError(n.span,e.source.toolOutput.error);else if(t){let t=contentAttribute(e.source.toolOutput.output,!1);t!==void 0&&n.toolSpan.setAttribute(`gen_ai.tool.call.result`,t)}n.toolSpan.end(),n.span.end()}},openSessionWindow=t=>{let n=e.tracer.startSpan(`agent.session`,{attributes:{"agent.framework.name":`eve`,"agent.framework.version":e.frameworkVersion,"agent.name":t.agentName,"agent.root.session.id":t.rootSessionId,"agent.session.id":t.sessionId,"agent.session.window":t.index,...t.previousTraceId===void 0?{}:{"agent.session.window.previous.trace.id":t.previousTraceId}},root:!0});return n.addEvent(t.index===0?`session.started`:`session.window.opened`),n.end(),n.spanContext()},ensureSessionContext=async t=>{let n=await e.stateStore.getSession(t.sessionId);return n===void 0&&(n={agentName:t.agentName,channelKind:t.channelKind,context:t.parentTraceContext===void 0?openSessionWindow({agentName:t.agentName,index:0,rootSessionId:t.rootSessionId,sessionId:t.sessionId}):adoptedSpanContext(t.parentTraceContext),rootSessionId:t.rootSessionId,turnsInWindow:0,window:0},await e.stateStore.setSession(t.sessionId,n)),n},advanceSessionWindow=(e,t)=>{if(t.turnsInWindow<200)return t;let n=t.window+1;return{...t,context:openSessionWindow({agentName:t.agentName,index:n,previousTraceId:t.context.traceId,rootSessionId:t.rootSessionId,sessionId:e}),turnsInWindow:0,window:n}};return{hook:{events:{"attempt.completed":onAttemptTerminal,"attempt.failed":onAttemptTerminal,"attempt.metadata":e=>{let t=u.get(e.scope);if(t===void 0)return;let n=readGatewayCost(e.providerMetadata);if(n!==void 0)for(let[e,r]of Object.entries(n))t.step.span.setAttribute(e,r)},"attempt.started":onAttemptStarted,"model.call":{after:afterModelCall,before:beforeModelCall},"session.completed":onSessionTransition,"session.failed":onSessionTransition,"session.started":onSessionStarted,"session.waiting":onSessionTransition,"tool.call":{after:afterToolCall,before:beforeToolCall},"turn.cancelled":onTurnTerminal,"turn.completed":onTurnTerminal,"turn.failed":onTurnTerminal,"turn.started":onTurnStarted}},runInContext(e,t){let r=l.get(e.scope),i=e.type===`model.call`?r?.models.get(e.id):r?.tools.get(e.id);return i===void 0?t():context.with(i,t)}};function getExecutionContexts(e){let t=l.get(e);return t===void 0&&(t={models:new Map,tools:new Map},l.set(e,t)),t}}function turnKey(e,t){return`${e}:${t}`}function parentLineageAttributes(e){if(e===void 0)return{};let t={"agent.parent.call_id":e.callId,"agent.parent.session.id":e.sessionId,"agent.parent.turn.id":e.turnId};return e.subagentName!==void 0&&(t[`agent.subagent.name`]=e.subagentName),t}function adoptedSpanContext(e){return{isRemote:!1,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function contextFromSpanContext(t){return trace.setSpan(ROOT_CONTEXT,trace.wrapSpanContext(t))}function readGatewayCost(e){let t=e.gateway;if(!isRecord(t))return;let n={},r=readUsd(t.cost);r!==void 0&&(n[`gen_ai.usage.cost`]=r);let i=readUsd(t.gatewayCost);i!==void 0&&(n[`gen_ai.usage.gateway_cost`]=i);let a=readUsd(t.inputInferenceCost);a!==void 0&&(n[`gen_ai.usage.input_cost`]=a);let o=readUsd(t.outputInferenceCost);return o!==void 0&&(n[`gen_ai.usage.output_cost`]=o),typeof t.generationId==`string`&&t.generationId.length>0&&(n[`gen_ai.generation.id`]=t.generationId),Object.keys(n).length===0?void 0:n}function readUsd(e){if(typeof e!=`string`)return;let t=Number(e);return Number.isFinite(t)?t:void 0}function isSpanState(e){return typeof e==`object`&&!!e&&`context`in e&&`span`in e}function isToolSpanState(e){return isSpanState(e)&&`toolSpan`in e}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function modelSpanName(e){return e===`ai.generateText`?`ai.generateText.doGenerate`:`ai.streamText.doStream`}function setUsage(e,t){t.inputTokens!==void 0&&e.setAttribute(`agent.usage.input_tokens`,t.inputTokens),t.outputTokens!==void 0&&e.setAttribute(`agent.usage.output_tokens`,t.outputTokens);let n=t.inputTokenDetails;n?.cacheReadTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_read.input_tokens`,n.cacheReadTokens),n?.cacheWriteTokens!==void 0&&e.setAttribute(`gen_ai.usage.cache_creation.input_tokens`,n.cacheWriteTokens)}function errorText(e){return e instanceof Error?e.message:e}function recordError(e,n){n instanceof Error?(e.recordException(n),e.setStatus({code:SpanStatusCode.ERROR,message:n.message})):e.setStatus({code:SpanStatusCode.ERROR})}export{InMemoryAgentTraceStateStore,SESSION_WINDOW_TURN_LIMIT,createAgentOtelInstrumentation};
@@ -1,6 +1,13 @@
1
- import type { AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#harness/agent-otel-provider.js";
1
+ import type { SpanContext } from "#compiled/@opentelemetry/api/index.js";
2
+ import type { AgentSessionTraceState, AgentTraceStateStore, AgentTurnTraceState } from "#tracing/agent-otel-provider.js";
2
3
  /** Keeps only framework trace state from an interrupted step's context changes. */
3
4
  export declare function preserveSerializedAgentTraceState(original: Record<string, unknown>, interrupted: Record<string, unknown>): Record<string, unknown>;
5
+ /**
6
+ * Reads a named session's trace window straight out of a serialized context,
7
+ * which {@link ContextAgentTraceStateStore} cannot do — its reads are scoped
8
+ * to the ambient session.
9
+ */
10
+ export declare function readSessionTraceContext(serializedContext: Readonly<Record<string, unknown>>, sessionId: string): SpanContext | undefined;
4
11
  /** Durable trace state backed by eve's serialized Workflow context. */
5
12
  export declare class ContextAgentTraceStateStore implements AgentTraceStateStore {
6
13
  deleteSession(sessionId: string): void;
@@ -0,0 +1 @@
1
+ import{contextStorage,loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";const AgentTraceContextKey=new ContextKey(`eve.harness.agentTrace`,{codec:{deserialize:deserializeState,serialize:serializeState}});function preserveSerializedAgentTraceState(e,t){let n=t[AgentTraceContextKey.name];return n===void 0?e:{...e,[AgentTraceContextKey.name]:n}}function readSessionTraceContext(e,t){let n=e[AgentTraceContextKey.name];if(n!==void 0)return deserializeState(n).sessions[t]?.context}var ContextAgentTraceStateStore=class{deleteSession(e){updateState(t=>{let n={...t.sessions};return delete n[e],{...t,sessions:n}})}deleteTurn(e,t){updateState(n=>{let r={...n.turns};return delete r[turnKey(e,t)],{...n,turns:r}})}getSession(t){return contextStorage.getStore()?.get(AgentTraceContextKey)?.sessions[t]}getTurn(t,n){return contextStorage.getStore()?.get(AgentTraceContextKey)?.turns[turnKey(t,n)]}setSession(e,t){updateState(n=>({...n,sessions:{...n.sessions,[e]:t}}))}setTurn(e,t,n){updateState(r=>({...r,turns:{...r.turns,[turnKey(e,t)]:n}}))}};function updateState(e){loadContext().set(AgentTraceContextKey,t=>e(t??{sessions:{},turns:{}}))}function turnKey(e,t){return`${e}\0${t}`}function serializeState(e){return{sessions:Object.fromEntries(Object.entries(e.sessions).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context)}])),turns:Object.fromEntries(Object.entries(e.turns).map(([e,t])=>[e,{...t,context:serializeSpanContext(t.context),terminal:t.terminal===void 0?void 0:{error:serializeError(t.terminal.error),type:t.terminal.type}}]))}}function deserializeState(e){return isRecord(e)?{sessions:deserializeRecord(e.sessions,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{agentName:typeof e.agentName==`string`?e.agentName:void 0,channelKind:typeof e.channelKind==`string`?e.channelKind:void 0,context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,turnsInWindow:typeof e.turnsInWindow==`number`?e.turnsInWindow:0,window:typeof e.window==`number`?e.window:0}}),turns:deserializeRecord(e.turns,e=>{if(!(!isRecord(e)||!isSpanContext(e.context)))return{context:e.context,rootSessionId:typeof e.rootSessionId==`string`?e.rootSessionId:``,sequence:typeof e.sequence==`number`?e.sequence:0,terminal:deserializeTerminal(e.terminal)}})}:{sessions:{},turns:{}}}function deserializeRecord(e,t){if(!isRecord(e))return{};let n={};for(let[r,i]of Object.entries(e)){let e=t(i);e!==void 0&&(n[r]=e)}return n}function deserializeTerminal(e){if(!isRecord(e)||typeof e.type!=`string`)return;let t=e.type;if(isTurnTerminalType(t))return{error:deserializeError(e.error),type:t}}function serializeSpanContext(e){return{isRemote:e.isRemote,spanId:e.spanId,traceFlags:e.traceFlags,traceId:e.traceId}}function isSpanContext(e){return isRecord(e)&&typeof e.spanId==`string`&&typeof e.traceFlags==`number`&&typeof e.traceId==`string`}function serializeError(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:void 0}function deserializeError(e){if(!isRecord(e)||typeof e.message!=`string`)return;let t=Error(e.message);return typeof e.name==`string`&&(t.name=e.name),typeof e.stack==`string`&&(t.stack=e.stack),t}function isTurnTerminalType(e){return e===`turn.cancelled`||e===`turn.completed`||e===`turn.failed`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{ContextAgentTraceStateStore,preserveSerializedAgentTraceState,readSessionTraceContext};
@@ -9,7 +9,11 @@ export declare class AgentTraceSpanProcessor implements SpanProcessor {
9
9
  onEnd(span: unknown): void;
10
10
  /** Trace ids whose session is still open, so retention never evicts them. */
11
11
  activeTraceIds(): ReadonlySet<string>;
12
- /** Forgets one session's trace, reporting whether it owned one. */
12
+ /**
13
+ * Forgets every trace one root session owned, reporting whether it owned any.
14
+ * A subagent child owns none, so releasing one reports `false` and leaves the
15
+ * shared trace pinned until its root finishes.
16
+ */
13
17
  releaseSession(sessionId: string): boolean;
14
18
  shutdown(): Promise<void>;
15
19
  }
@@ -0,0 +1 @@
1
+ var AgentTraceSpanProcessor=class{#e;#t=new Set;#n=new Map;#r=!1;constructor(e){this.#e=e}async forceFlush(){await Promise.all(this.#e.map(e=>e.forceFlush()))}isAttached(){return this.#r}onStart(e,t){if(this.#r=!0,!isSpanLike(e))return;let n=e.attributes[`agent.session.id`];if(typeof n==`string`){let t=e.attributes[`agent.root.session.id`],r=typeof t==`string`?t:n,i=e.spanContext().traceId;this.#t.add(i);let a=this.#n.get(r)??new Set;a.add(i),this.#n.set(r,a)}if(this.#i(e))for(let n of this.#e)n.onStart(e,t)}onEnd(e){if(!(!isSpanLike(e)||!this.#i(e)))for(let t of this.#e)t.onEnd(e)}activeTraceIds(){return this.#t}releaseSession(e){let t=this.#n.get(e);if(t===void 0)return!1;for(let e of t)this.#t.delete(e);return this.#n.delete(e),!0}async shutdown(){await Promise.all(this.#e.map(e=>e.shutdown()))}#i(e){return e.instrumentationScope?.name!==`workflow`&&this.#t.has(e.spanContext().traceId)}};function isSpanLike(e){return typeof e==`object`&&!!e&&`attributes`in e&&`spanContext`in e&&typeof e.spanContext==`function`}export{AgentTraceSpanProcessor};
@@ -0,0 +1 @@
1
+ import{LocalTraceSpanProcessor}from"#tracing/local-trace-span-processor.js";import{ContextAgentTraceStateStore}from"#tracing/agent-trace-context-store.js";import{getInstrumentationRuntime,registerInstrumentationRuntime}from"#harness/instrumentation-runtime.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{registerOTel}from"#compiled/@vercel/otel/index.js";import{createAgentOtelInstrumentation}from"#tracing/agent-otel-provider.js";import{AgentTraceSpanProcessor}from"#tracing/agent-trace-span-processor.js";import{createInstrumentationHooks}from"#harness/instrumentation-lifecycle.js";import{requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings}from"#tracing/local-trace-retention.js";function installLocalInstrumentationRuntime(t){let n=getInstrumentationRuntime();if(n!==void 0)return n;let r=resolveLocalTraceRetentionSettings(),i=new AgentTraceSpanProcessor(r.enabled?[new LocalTraceSpanProcessor(t.appRoot)]:[]);registerOTel({autoDetectResources:!1,instrumentations:[],propagators:[`none`],serviceName:t.serviceName,spanProcessors:[i]});let a=trace.getTracer(`eve.registration`).startSpan(`eve.otel.registration`),o=trace.setSpan(context.active(),a),s=context.with(o,()=>trace.getActiveSpan()===a);if(a.end(),!i.isAttached()||!s)throw Error(`eve could not register OpenTelemetry because another runtime already exists.`);let c=createAgentOtelInstrumentation({captureContent:process.env.EVE_TRACES_CONTENT!==`off`,frameworkVersion:t.frameworkVersion,stateStore:new ContextAgentTraceStateStore,tracer:trace.getTracer(`eve.agent`,t.frameworkVersion)}),l={events:{"session.completed":releaseSessionTrace,"session.failed":releaseSessionTrace}};return requestPrune(),registerInstrumentationRuntime({forceFlush:()=>i.forceFlush(),hooks:createInstrumentationHooks([c.hook,l]),runInContext:c.runInContext});async function releaseSessionTrace(e){await i.forceFlush(),i.releaseSession(e.sessionId)&&requestPrune()}function requestPrune(){r.enabled&&requestLocalTraceStorePrune({activeTraceIds:i.activeTraceIds(),appRoot:t.appRoot,maxAgeMs:r.maxAgeMs,maxTotalBytes:r.maxTotalBytes,retainCount:r.retainCount})}}export{installLocalInstrumentationRuntime};
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Reads the local trace spool written by {@link LocalTraceSpanProcessor}.
3
+ *
4
+ * The spool is the contract between the dev-time writer and every viewer
5
+ * (`eve traces`, the TUI trace viewer): one directory per trace under
6
+ * `.eve/traces/v1/<traceId>/segments/`, one immutable OTLP/JSON file per
7
+ * span. Parsing is defensive end to end — malformed or oversized segments
8
+ * are skipped so a partial write never breaks a view.
9
+ */
10
+ export interface LocalTraceSpan {
11
+ readonly attributes: Readonly<Record<string, unknown>>;
12
+ readonly endTimeNs: bigint;
13
+ readonly name: string;
14
+ readonly parentSpanId?: string;
15
+ readonly scope?: string;
16
+ readonly spanId: string;
17
+ readonly startTimeNs: bigint;
18
+ readonly statusCode: number;
19
+ readonly traceId: string;
20
+ }
21
+ export interface LocalTrace {
22
+ readonly agentName?: string;
23
+ readonly endTimeNs: bigint;
24
+ /** Session that opened the trace, which is the one the list shows. */
25
+ readonly sessionId?: string;
26
+ /**
27
+ * Every session recorded in the trace, opener first.
28
+ *
29
+ * A subagent child records into the window its parent had open, so one
30
+ * trace can hold several sessions and any of their ids resolves to it.
31
+ */
32
+ readonly sessionIds: readonly string[];
33
+ readonly spans: readonly LocalTraceSpan[];
34
+ readonly startTimeNs: bigint;
35
+ readonly traceId: string;
36
+ /** Zero-based session window this trace holds, when the spans record one. */
37
+ readonly window?: number;
38
+ }
39
+ /** Reads valid local traces, newest first, while ignoring malformed segments. */
40
+ export declare function listLocalTraces(appRoot: string): Promise<LocalTrace[]>;
41
+ /**
42
+ * Reads one trace from its segments directory, or `undefined` when the trace
43
+ * has no valid spans. Spans come back ordered by start time.
44
+ */
45
+ export declare function readLocalTrace(root: string, traceId: string): Promise<LocalTrace | undefined>;
46
+ /** Sorts spans into timeline order: start, then end, then a stable tiebreak. */
47
+ export declare function compareLocalTraceSpans(left: LocalTraceSpan, right: LocalTraceSpan): number;
48
+ /** Builds a trace from already-parsed spans, deriving summary fields. */
49
+ export declare function assembleLocalTrace(traceId: string, spans: readonly LocalTraceSpan[]): LocalTrace;
50
+ /**
51
+ * Extracts short human-facing details from a span's structural attributes
52
+ * (turn id, step index/attempt, action kind/name, model id) for one-line
53
+ * span labels. Raw values — callers sanitize for their output surface.
54
+ */
55
+ export declare function describeLocalTraceSpan(span: LocalTraceSpan): string[];
56
+ /** Parses one OTLP/JSON segment file into spans belonging to `expectedTraceId`. */
57
+ export declare function parseLocalTraceSegment(content: string, expectedTraceId: string): LocalTraceSpan[];
@@ -0,0 +1 @@
1
+ import{join}from"node:path";import{readFile,readdir,stat}from"node:fs/promises";import{resolveLocalTraceSchemaDirectory}from"#tracing/local-trace-span-processor.js";const TRACE_ID_PATTERN=/^[0-9a-f]{32}$/u,SPAN_FILE_PATTERN=/^([0-9a-f]{16})\.otlp\.json$/u;async function listLocalTraces(e){let t=resolveLocalTraceSchemaDirectory(e),r;try{r=await readdir(t,{withFileTypes:!0})}catch(e){if(isMissing(e))return[];throw e}let o=[];for(let e of r){if(!e.isDirectory()||!TRACE_ID_PATTERN.test(e.name))continue;let n=await readLocalTrace(t,e.name).catch(()=>void 0);n!==void 0&&o.push(n)}return o.sort((e,t)=>e.startTimeNs===t.startTimeNs?e.traceId.localeCompare(t.traceId):e.startTimeNs>t.startTimeNs?-1:1)}async function readLocalTrace(i,a){let s=join(i,a,`segments`),c;try{c=await readdir(s,{withFileTypes:!0})}catch(e){if(isMissing(e))return;throw e}let l=new Map;for(let n of c.sort((e,t)=>e.name.localeCompare(t.name))){if(!n.isFile()||!SPAN_FILE_PATTERN.test(n.name))continue;let i;try{let a=join(s,n.name);if((await stat(a)).size>8388608)continue;i=await readFile(a,`utf8`)}catch{continue}for(let e of parseLocalTraceSegment(i,a))l.has(e.spanId)||l.set(e.spanId,e)}if(l.size!==0)return assembleLocalTrace(a,[...l.values()])}function compareLocalTraceSpans(e,t){return e.startTimeNs===t.startTimeNs?e.endTimeNs===t.endTimeNs?e.name.localeCompare(t.name)||e.spanId.localeCompare(t.spanId):e.endTimeNs<t.endTimeNs?-1:1:e.startTimeNs<t.startTimeNs?-1:1}function assembleLocalTrace(e,t){let n=[...t].sort(compareLocalTraceSpans),r=n.map(e=>e.attributes),i=distinctAttributes(r,`agent.session.id`);return{agentName:firstAttribute(r,`agent.name`),endTimeNs:n.reduce((e,t)=>t.endTimeNs>e?t.endTimeNs:e,0n),sessionId:i[0],sessionIds:i,spans:n,startTimeNs:n.reduce((e,t)=>t.startTimeNs<e?t.startTimeNs:e,n[0].startTimeNs),traceId:e,window:firstNumberAttribute(r,`agent.session.window`)}}function describeLocalTraceSpan(e){let t=[],n=stringSpanAttribute(e,`agent.turn.id`),r=valueSpanAttribute(e,`agent.step.index`),i=valueSpanAttribute(e,`agent.step.attempt`),a=stringSpanAttribute(e,`agent.action.kind`),o=stringSpanAttribute(e,`agent.action.name`),s=stringSpanAttribute(e,`agent.model.id`)??stringSpanAttribute(e,`gen_ai.request.model`);return e.name===`agent.turn`&&n!==void 0&&t.push(n),e.name===`agent.step`&&r!==void 0&&t.push(`step ${String(r)}${i===void 0?``:`, attempt ${String(i)}`}`),e.name===`agent.action`&&o!==void 0&&t.push(`${a??`action`}: ${o}`),s!==void 0&&e.name.includes(`do`)&&t.push(`model ${s}`),t}function parseLocalTraceSegment(e,t){try{let n=JSON.parse(e);if(!isRecord(n)||!Array.isArray(n.resourceSpans))return[];let r=[];for(let e of n.resourceSpans)if(!(!isRecord(e)||!Array.isArray(e.scopeSpans)))for(let n of e.scopeSpans){if(!isRecord(n)||!Array.isArray(n.spans))continue;let e=isRecord(n.scope)&&typeof n.scope.name==`string`?n.scope.name:void 0;for(let i of n.spans){let n=parseLocalTraceSpan(i,t,e);n!==void 0&&r.push(n)}}return r}catch{return[]}}function parseLocalTraceSpan(e,t,n){if(!isRecord(e)||e.traceId!==t||typeof e.spanId!=`string`||!/^[0-9a-f]{16}$/u.test(e.spanId)||typeof e.name!=`string`)return;let r=parseNanos(e.startTimeUnixNano),i=parseNanos(e.endTimeUnixNano);if(!(r===void 0||i===void 0||i<r))return{attributes:parseAttributes(e.attributes),endTimeNs:i,name:e.name,parentSpanId:typeof e.parentSpanId==`string`&&/^[0-9a-f]{16}$/u.test(e.parentSpanId)?e.parentSpanId:void 0,scope:n,spanId:e.spanId,startTimeNs:r,statusCode:parseStatusCode(e.status),traceId:t}}function parseAttributes(e){if(!Array.isArray(e))return{};let t={};for(let n of e){if(!isRecord(n)||typeof n.key!=`string`||!isRecord(n.value))continue;let e=parseAnyValue(n.value);e!==void 0&&(t[n.key]=e)}return t}function parseAnyValue(e){for(let t of[`stringValue`,`boolValue`,`intValue`,`doubleValue`])if(t in e)return e[t];if(isRecord(e.arrayValue)&&Array.isArray(e.arrayValue.values))return e.arrayValue.values.filter(isRecord).map(e=>parseAnyValue(e))}function parseNanos(e){if(!(typeof e!=`string`&&typeof e!=`number`))try{let t=BigInt(e);return t>=0n&&t<=18446744073709551615n?t:void 0}catch{return}}function parseStatusCode(e){return isRecord(e)?typeof e.code==`number`?e.code:e.code===`STATUS_CODE_ERROR`?2:0:0}function firstAttribute(e,t){for(let n of e){let e=n[t];if(typeof e==`string`&&e.length>0)return e}}function firstNumberAttribute(e,t){for(let n of e){let e=n[t];if(typeof e==`number`)return e;if(typeof e==`bigint`)return Number(e)}}function distinctAttributes(e,t){let n=new Set;for(let r of e){let e=r[t];typeof e==`string`&&e.length>0&&n.add(e)}return[...n]}function stringSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`?n:void 0}function valueSpanAttribute(e,t){let n=e.attributes[t];return typeof n==`string`||typeof n==`number`?n:void 0}function isMissing(e){return e.code===`ENOENT`}function isRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}export{assembleLocalTrace,compareLocalTraceSpans,describeLocalTraceSpan,listLocalTraces,parseLocalTraceSegment,readLocalTrace};
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{join}from"node:path";import{readdir,rm,stat}from"node:fs/promises";import{resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory}from"#harness/local-trace-span-processor.js";const log=createLogger(`harness.local-trace-retention`),LOCAL_TRACE_QUIESCENCE_MS=300*1e3,LOCAL_TRACE_MAX_AGE_MS=10080*60*1e3,LOCAL_TRACE_MAX_TOTAL_BYTES=512*1024*1024;function resolveLocalTraceRetentionSettings(e=process.env){return{enabled:!isDisabled(e.EVE_TRACES),maxAgeMs:readBound(e,`EVE_TRACES_MAX_AGE_MS`,LOCAL_TRACE_MAX_AGE_MS),maxTotalBytes:readBound(e,`EVE_TRACES_MAX_TOTAL_BYTES`,LOCAL_TRACE_MAX_TOTAL_BYTES),retainCount:readBound(e,`EVE_TRACES_RETAIN_COUNT`,20)}}async function pruneLocalTraceStore(e){let t=e.now??Date.now(),n=clampBound(e.maxAgeMs??LOCAL_TRACE_MAX_AGE_MS),r=clampBound(e.maxTotalBytes??LOCAL_TRACE_MAX_TOTAL_BYTES),a=clampCount(e.retainCount??20),o=await readStoredTraces(e.appRoot,t),s=[...o].sort((e,t)=>t.modifiedAtMs-e.modifiedAtMs),c=new Set(s.slice(0,a===!1?0:a)),l=new Set,u=o.reduce((e,t)=>e+t.byteSize,0),d=0,f=0,p=0;for(let a of s.toReversed()){let o=t-a.modifiedAtMs,s=e.activeTraceIds.has(a.traceId)||c.has(a)||o<=LOCAL_TRACE_QUIESCENCE_MS?void 0:n!==!1&&o>n?`maxAgeMs`:r!==!1&&u>r?`maxTotalBytes`:void 0;if(s===void 0){p+=1;continue}await rm(a.path,{force:!0,recursive:!0}),u-=a.byteSize,d+=a.byteSize,f+=1,l.add(s)}return{reasons:[...l],reclaimedBytes:d,removedTraces:f,retainedTraces:p}}const pruneRequest={input:{activeTraceIds:new Set,appRoot:``},requested:!1,running:void 0};function requestLocalTraceStorePrune(e){pruneRequest.input=e,pruneRequest.requested=!0,pruneRequest.running===void 0&&startPruning()}function startPruning(){pruneRequest.running=(async()=>{for(;pruneRequest.requested;)pruneRequest.requested=!1,report(await pruneLocalTraceStore(pruneRequest.input))})().catch(e=>{log.warn(`local trace retention failed`,{error:formatError(e)})}).finally(()=>{pruneRequest.running=void 0,pruneRequest.requested&&startPruning()})}function report(e){e.removedTraces!==0&&log.info(`pruned local traces`,{reasons:e.reasons.join(`,`),reclaimedBytes:e.reclaimedBytes,removedTraces:e.removedTraces,retainedTraces:e.retainedTraces})}async function readStoredTraces(e,t){let r=resolveLocalTraceSchemaDirectory(e),i=await readDirectoryEntries(r);return i===void 0?[]:(await Promise.all(i.filter(e=>e.isDirectory()).map(async i=>{let a=join(r,i.name),o=resolveLocalTraceSegmentsDirectory(e,i.name);await reapAbandonedTemporaryFiles(o,t);let c=await directoryMtime(o)??await directoryMtime(a);if(c!==void 0)return{byteSize:await directorySize(a),modifiedAtMs:c,path:a,traceId:i.name}}))).filter(e=>e!==void 0)}async function reapAbandonedTemporaryFiles(e,t){let r=await readDirectoryEntries(e);if(r!==void 0)for(let o of r){if(!o.isFile()||!o.name.includes(`.tmp-`))continue;let r=join(e,o.name);try{if(t-(await stat(r)).mtimeMs<=LOCAL_TRACE_QUIESCENCE_MS)continue;await rm(r,{force:!0})}catch{}}}async function directoryMtime(e){try{return(await stat(e)).mtimeMs}catch{return}}async function directorySize(e){let t=await readDirectoryEntries(e);if(t===void 0)return 0;let r=0;for(let i of t){let t=join(e,i.name);if(i.isDirectory()){r+=await directorySize(t);continue}try{r+=(await stat(t)).size}catch{}}return r}async function readDirectoryEntries(e){try{return await readdir(e,{withFileTypes:!0})}catch(e){if(e instanceof Error&&`code`in e&&(e.code===`ENOENT`||e.code===`ENOTDIR`))return;throw e}}function isDisabled(e){if(e===void 0)return!1;let t=e.trim().toLowerCase();return t===`off`||t===`false`||t===`0`}function readBound(e,t,n){let r=e[t];if(r===void 0)return n;let i=r.trim().toLowerCase();if(i===`off`||i===`false`)return!1;let a=Number(i);return!Number.isFinite(a)||a<0?(log.warn(`ignoring invalid local trace retention value`,{fallback:n,name:t,value:r}),n):a}function clampBound(e){return e!==!1&&Math.max(0,e)}function clampCount(e){return e!==!1&&Math.max(0,Math.trunc(e))}export{pruneLocalTraceStore,requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{join}from"node:path";import{readdir,rm,stat}from"node:fs/promises";import{resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory}from"#tracing/local-trace-span-processor.js";const log=createLogger(`harness.local-trace-retention`),LOCAL_TRACE_QUIESCENCE_MS=300*1e3,LOCAL_TRACE_MAX_AGE_MS=10080*60*1e3,LOCAL_TRACE_MAX_TOTAL_BYTES=512*1024*1024;function resolveLocalTraceRetentionSettings(e=process.env){return{enabled:!isDisabled(e.EVE_TRACES),maxAgeMs:readBound(e,`EVE_TRACES_MAX_AGE_MS`,LOCAL_TRACE_MAX_AGE_MS),maxTotalBytes:readBound(e,`EVE_TRACES_MAX_TOTAL_BYTES`,LOCAL_TRACE_MAX_TOTAL_BYTES),retainCount:readBound(e,`EVE_TRACES_RETAIN_COUNT`,20)}}async function pruneLocalTraceStore(e){let t=e.now??Date.now(),n=clampBound(e.maxAgeMs??LOCAL_TRACE_MAX_AGE_MS),r=clampBound(e.maxTotalBytes??LOCAL_TRACE_MAX_TOTAL_BYTES),a=clampCount(e.retainCount??20),o=await readStoredTraces(e.appRoot,t),s=[...o].sort((e,t)=>t.modifiedAtMs-e.modifiedAtMs),c=new Set(s.slice(0,a===!1?0:a)),l=new Set,u=o.reduce((e,t)=>e+t.byteSize,0),d=0,f=0,p=0;for(let a of s.toReversed()){let o=t-a.modifiedAtMs,s=e.activeTraceIds.has(a.traceId)||c.has(a)||o<=LOCAL_TRACE_QUIESCENCE_MS?void 0:n!==!1&&o>n?`maxAgeMs`:r!==!1&&u>r?`maxTotalBytes`:void 0;if(s===void 0){p+=1;continue}await rm(a.path,{force:!0,recursive:!0}),u-=a.byteSize,d+=a.byteSize,f+=1,l.add(s)}return{reasons:[...l],reclaimedBytes:d,removedTraces:f,retainedTraces:p}}const pruneRequest={input:{activeTraceIds:new Set,appRoot:``},requested:!1,running:void 0};function requestLocalTraceStorePrune(e){pruneRequest.input=e,pruneRequest.requested=!0,pruneRequest.running===void 0&&startPruning()}function startPruning(){pruneRequest.running=(async()=>{for(;pruneRequest.requested;)pruneRequest.requested=!1,report(await pruneLocalTraceStore(pruneRequest.input))})().catch(e=>{log.warn(`local trace retention failed`,{error:formatError(e)})}).finally(()=>{pruneRequest.running=void 0,pruneRequest.requested&&startPruning()})}function report(e){e.removedTraces!==0&&log.info(`pruned local traces`,{reasons:e.reasons.join(`,`),reclaimedBytes:e.reclaimedBytes,removedTraces:e.removedTraces,retainedTraces:e.retainedTraces})}async function readStoredTraces(e,t){let r=resolveLocalTraceSchemaDirectory(e),i=await readDirectoryEntries(r);return i===void 0?[]:(await Promise.all(i.filter(e=>e.isDirectory()).map(async i=>{let a=join(r,i.name),o=resolveLocalTraceSegmentsDirectory(e,i.name);await reapAbandonedTemporaryFiles(o,t);let c=await directoryMtime(o)??await directoryMtime(a);if(c!==void 0)return{byteSize:await directorySize(a),modifiedAtMs:c,path:a,traceId:i.name}}))).filter(e=>e!==void 0)}async function reapAbandonedTemporaryFiles(e,t){let r=await readDirectoryEntries(e);if(r!==void 0)for(let o of r){if(!o.isFile()||!o.name.includes(`.tmp-`))continue;let r=join(e,o.name);try{if(t-(await stat(r)).mtimeMs<=LOCAL_TRACE_QUIESCENCE_MS)continue;await rm(r,{force:!0})}catch{}}}async function directoryMtime(e){try{return(await stat(e)).mtimeMs}catch{return}}async function directorySize(e){let t=await readDirectoryEntries(e);if(t===void 0)return 0;let r=0;for(let i of t){let t=join(e,i.name);if(i.isDirectory()){r+=await directorySize(t);continue}try{r+=(await stat(t)).size}catch{}}return r}async function readDirectoryEntries(e){try{return await readdir(e,{withFileTypes:!0})}catch(e){if(e instanceof Error&&`code`in e&&(e.code===`ENOENT`||e.code===`ENOTDIR`))return;throw e}}function isDisabled(e){if(e===void 0)return!1;let t=e.trim().toLowerCase();return t===`off`||t===`false`||t===`0`}function readBound(e,t,n){let r=e[t];if(r===void 0)return n;let i=r.trim().toLowerCase();if(i===`off`||i===`false`)return!1;let a=Number(i);return!Number.isFinite(a)||a<0?(log.warn(`ignoring invalid local trace retention value`,{fallback:n,name:t,value:r}),n):a}function clampBound(e){return e!==!1&&Math.max(0,e)}function clampCount(e){return e!==!1&&Math.max(0,Math.trunc(e))}export{pruneLocalTraceStore,requestLocalTraceStorePrune,resolveLocalTraceRetentionSettings};
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{join}from"node:path";import{mkdir}from"node:fs/promises";import{JsonTraceSerializer}from"#compiled/@opentelemetry/otlp-transformer/index.js";import{atomicWriteFile}from"#shared/atomic-write-file.js";const log=createLogger(`harness.local-trace-span-processor`);function resolveLocalTraceStoreDirectory(e){return join(e,`.eve`,`traces`)}function resolveLocalTraceSchemaDirectory(e){return join(resolveLocalTraceStoreDirectory(e),`v1`)}function resolveLocalTraceSegmentsDirectory(e,t){return join(resolveLocalTraceSchemaDirectory(e),t,`segments`)}var LocalTraceSpanProcessor=class{#e;#t=Promise.resolve();#n=!1;constructor(e){this.#e=e}forceFlush(){return this.#t}onStart(){}onEnd(e){if(!isReadableSpan(e))return;let{spanId:n,traceId:r}=e.spanContext();if(!isHexId(r,32)||!isHexId(n,16))return;let i=JsonTraceSerializer.serializeRequest([e]);i!==void 0&&(this.#t=this.#t.then(async()=>{let e=resolveLocalTraceSegmentsDirectory(this.#e,r);await mkdir(e,{recursive:!0}),await atomicWriteFile(join(e,`${n}.otlp.json`),i)}).catch(e=>{this.#n||(this.#n=!0,log.warn(`local trace persistence failed`,{error:formatError(e)}))}))}shutdown(){return this.forceFlush()}};function isReadableSpan(e){return typeof e==`object`&&!!e&&`attributes`in e&&`spanContext`in e&&typeof e.spanContext==`function`}function isHexId(e,t){return e.length===t&&/^[0-9a-f]+$/u.test(e)}export{LocalTraceSpanProcessor,resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{join}from"node:path";import{mkdir}from"node:fs/promises";import{atomicWriteFile}from"#shared/atomic-write-file.js";import{JsonTraceSerializer}from"#compiled/@opentelemetry/otlp-transformer/index.js";const log=createLogger(`harness.local-trace-span-processor`);function resolveLocalTraceStoreDirectory(e){return join(e,`.eve`,`traces`)}function resolveLocalTraceSchemaDirectory(e){return join(resolveLocalTraceStoreDirectory(e),`v1`)}function resolveLocalTraceSegmentsDirectory(e,t){return join(resolveLocalTraceSchemaDirectory(e),t,`segments`)}var LocalTraceSpanProcessor=class{#e;#t=Promise.resolve();#n=!1;constructor(e){this.#e=e}forceFlush(){return this.#t}onStart(){}onEnd(e){if(!isReadableSpan(e))return;let{spanId:n,traceId:r}=e.spanContext();if(!isHexId(r,32)||!isHexId(n,16))return;let i=JsonTraceSerializer.serializeRequest([e]);i!==void 0&&(this.#t=this.#t.then(async()=>{let e=resolveLocalTraceSegmentsDirectory(this.#e,r);await mkdir(e,{recursive:!0}),await atomicWriteFile(join(e,`${n}.otlp.json`),i)}).catch(e=>{this.#n||(this.#n=!0,log.warn(`local trace persistence failed`,{error:formatError(e)}))}))}shutdown(){return this.forceFlush()}};function isReadableSpan(e){return typeof e==`object`&&!!e&&`attributes`in e&&`spanContext`in e&&typeof e.spanContext==`function`}function isHexId(e,t){return e.length===t&&/^[0-9a-f]+$/u.test(e)}export{LocalTraceSpanProcessor,resolveLocalTraceSchemaDirectory,resolveLocalTraceSegmentsDirectory};
@@ -97,7 +97,24 @@ bot.onNewMention(async (thread, message) => {
97
97
  });
98
98
  ```
99
99
 
100
- `options` accepts `{ thread, auth?, title?, mode?, callback?, adapterName? }`. `title` sets the eve session's display title without changing the model message; `auth` attaches an authenticated principal to the turn.
100
+ `options` accepts `{ thread, auth?, title?, mode?, callback?, adapterName?, turnPolicy? }`. `title` sets the eve session's display title without changing the model message; `auth` attaches an authenticated principal to the turn.
101
+
102
+ ### Experimental steering
103
+
104
+ By default, a message sent while an eve turn is active waits for the next turn. Pass `turnPolicy: "experimental-steer"` to cancel the active turn before sending the new message:
105
+
106
+ ```ts
107
+ bot.onSubscribedMessage(async (thread, message) => {
108
+ await send(message.text, {
109
+ thread,
110
+ turnPolicy: "experimental-steer",
111
+ });
112
+ });
113
+ ```
114
+
115
+ This is cancellation-backed steering: the interrupted turn emits `turn.cancelled`, and the replacement message starts a new turn with a new turn ID. Partial output and completed side effects from the interrupted turn are not rolled back. If no turn is active, the message is sent normally. Use the default `turnPolicy: "queue"` when every turn should finish in order.
116
+
117
+ This policy controls overlapping eve turns. Chat SDK's separate `concurrency` option controls overlapping webhook handlers; use `concurrency: "concurrent"` when each inbound message should reach the experimental steering path immediately.
101
118
 
102
119
  ### Delivery
103
120
 
@@ -4,6 +4,7 @@
4
4
  "overview",
5
5
  "eve",
6
6
  "slack",
7
+ "photon",
7
8
  "discord",
8
9
  "teams",
9
10
  "telegram",
@@ -28,7 +28,7 @@ agent/
28
28
  intake.ts
29
29
  ```
30
30
 
31
- Install a built-in channel with `eve add channel/slack` or `eve add channel/web`. You can also author the file by hand.
31
+ Install a channel from the registry with `eve add channel/photon-imessage`, `eve add channel/slack`, or `eve add channel/web`. You can also author the file by hand.
32
32
 
33
33
  ## The eve HTTP channel (default)
34
34
 
@@ -0,0 +1,78 @@
1
+ ---
2
+ title: Photon
3
+ description: Connect an eve agent to iMessage through Photon.
4
+ ---
5
+
6
+ Use `photonIMessageChannel` to receive and reply to iMessages through a Photon project.
7
+
8
+ Run `eve add channel/photon-imessage` to create or use a Photon project and register
9
+ your phone number. It then asks whether to configure Vercel Connect or portable
10
+ credentials. With Vercel Connect, eve creates the connector and configures
11
+ Photon’s webhook through Connect. The channel resolves credentials lazily when
12
+ the adapter first initializes:
13
+
14
+ ```ts title="agent/channels/photon.ts"
15
+ import { connectPhotonCredentials } from "@vercel/connect/eve";
16
+ import { photonIMessageChannel } from "eve/channels/photon";
17
+
18
+ export default photonIMessageChannel({
19
+ credentials: connectPhotonCredentials("photon/my-agent"),
20
+ });
21
+ ```
22
+
23
+ The default webhook route is `/eve/v1/photon`. The channel verifies forwarded
24
+ webhooks with same-project Vercel OIDC by default, marks accepted messages as
25
+ read, and continues the same eve session for every message in an iMessage
26
+ conversation. A new accepted message cooperatively cancels an active turn and
27
+ steers its replacement turn.
28
+
29
+ Customize inbound dispatch with `onMessage`. Return `null` to ignore a message:
30
+
31
+ ```ts
32
+ export default photonIMessageChannel({
33
+ credentials: connectPhotonCredentials("photon/my-agent"),
34
+ onMessage(_ctx, message) {
35
+ if (message.author.isBot) return null;
36
+ return {
37
+ auth: null,
38
+ context: [`The sender is ${message.author.fullName}.`],
39
+ };
40
+ },
41
+ });
42
+ ```
43
+
44
+ Set `route` to override the webhook path or `webhookVerifier` to use a different
45
+ trusted-forwarder verifier.
46
+
47
+ ## Other hosts
48
+
49
+ For a host without Vercel Connect, choose **Use portable credentials** during
50
+ `eve add channel/photon-imessage`. eve scaffolds the channel and writes
51
+ `IMESSAGE_PROJECT_ID` and `IMESSAGE_PROJECT_SECRET` to `.env.local`.
52
+
53
+ After deploying the agent:
54
+
55
+ 1. Create a Photon webhook for your public `https://…/eve/v1/photon` URL.
56
+ 2. Copy its signing secret into `IMESSAGE_WEBHOOK_SECRET`.
57
+ 3. Set `IMESSAGE_PROJECT_ID`, `IMESSAGE_PROJECT_SECRET`, and
58
+ `IMESSAGE_WEBHOOK_SECRET` in the host’s encrypted environment variables.
59
+
60
+ To configure the channel by hand, use lazy environment-backed credentials:
61
+
62
+ ```ts title="agent/channels/photon.ts"
63
+ import { photonIMessageChannel } from "eve/channels/photon";
64
+
65
+ export default photonIMessageChannel({
66
+ async credentials() {
67
+ const projectId = process.env.IMESSAGE_PROJECT_ID;
68
+ const projectSecret = process.env.IMESSAGE_PROJECT_SECRET;
69
+ if (!projectId || !projectSecret) throw new Error("Photon project credentials are required.");
70
+ return { projectId, projectSecret };
71
+ },
72
+ webhookSecret: process.env.IMESSAGE_WEBHOOK_SECRET,
73
+ });
74
+ ```
75
+
76
+ For direct Photon webhooks, pass `webhookSecret` or set
77
+ `IMESSAGE_WEBHOOK_SECRET`; the signing secret takes precedence over the default
78
+ OIDC verifier.
@@ -41,6 +41,8 @@ The shell and file tools (`bash`, `read_file`, `write_file`, `glob`, `grep`) run
41
41
  | `load_skill` | Pull an on-demand [skill](../skills)'s instructions into the current turn. Present only when the agent declares skills. | App runtime |
42
42
  | `connection_search` | Discover tools across declared [connections](../connections); matched tools become directly callable. Present only when the agent declares connections. | App runtime |
43
43
 
44
+ The model-facing file tools accept absolute paths and paths beginning with `$HOME/`. eve resolves `$HOME` against the sandbox before invoking non-shell file operations, so packaged skill references such as `$HOME/.agents/skills/<skill>/references/...` work consistently across `read_file`, `write_file`, `glob`, and `grep`.
45
+
44
46
  Notes:
45
47
 
46
48
  - **`agent`** is available only in the root session. Its child uses the root's instructions, tools, connections, and sandbox, but starts with fresh conversation history and fresh [state](../guides/state). The child receives neither `agent` nor `Workflow`; declared subagents do not receive the built-in `agent` either. See [Subagents](../subagents).
@@ -40,11 +40,12 @@ Each command echoes as an invocation line, asks through a bordered panel that ta
40
40
  | `/vc:install` | Installs the Vercel CLI. Available locally and on a remote session. |
41
41
  | `/vc:login` | Logs in to Vercel locally. On a remote session, resolves the deployment's project, refreshes its OIDC token, and confirms any required Trusted Sources rule. |
42
42
  | `/loglevel` | Switches which logs the transcript shows. See [Control what logs show](#control-what-logs-show). |
43
+ | `/traces` | Opens the full-screen local trace viewer. See [Inspect traces](#inspect-traces). |
43
44
  | `/new` | Starts a fresh session. |
44
45
  | `/exit` | Quits the TUI. |
45
46
  | `/help` | Lists the commands available for the current local or remote session. |
46
47
 
47
- `/model`, `/add`, and `/deploy` manage the local agent or its linked project. They are available only when `eve dev` runs the server locally, not when connected to a remote server with `--url`.
48
+ `/model`, `/add`, `/deploy`, and `/traces` manage the local agent or its linked project. They are available only when `eve dev` runs the server locally, not when connected to a remote server with `--url`.
48
49
 
49
50
  ### Configure the model and provider
50
51
 
@@ -106,6 +107,24 @@ Every captured line, tool failure, workflow error, and eve framework log record
106
107
 
107
108
  Each log has a same-named `.dump` sibling — one JSON document with environment diagnostics (eve, Node.js, and Vercel CLI versions, the Vercel CLI path, and the size of the local session store) plus running session stats: prompts, token usage, tool calls by name, and subagent dispatches. `eve logs --dump` prints the dump and the log together as one parseable report to attach when filing an issue.
108
109
 
110
+ ## Inspect traces
111
+
112
+ `/traces` opens a full-screen viewer over the local trace spool (`.eve/traces/v1`) that `eve dev` writes while you chat — see [Zero-config local traces](instrumentation#zero-config-local-traces). It reads the spool files directly and refreshes about once a second, so spans from the current session stream in live. The viewer takes over the terminal's alternate screen; closing it restores the transcript exactly where you left it.
113
+
114
+ The viewer opens on your current session's trace (or the most recent one). It re-tells the trace as a chat-like flow of cards: the system prompt sits at the top as a collapsed card, then user and assistant messages (assistant cards carry model, duration, token, and gateway cost metadata), and tool calls show their inputs and results inline. Work a subagent performed appears in the same flow, ordered by when it happened — between the parent's dispatch step and its resumed reply — with a dim `subagent` badge (`subagent:<name>` when the dispatch named one) on every card of the delegated turn; the delegated prompt renders as a `task` card rather than a `user` message. Error spans paint their whole card red. The header names the agent, session, span count, and duration, plus a `● live` badge while spans are still landing. `/traces <trace>` opens a specific trace by id prefix.
115
+
116
+ | Key | Action |
117
+ | --------- | ------------------------------------------------------------------------ |
118
+ | `↑` / `↓` | Move the card selection (scroll the attributes panel when it has focus). |
119
+ | `←` / `→` | Expand / collapse the selected card (only cards with hidden content). |
120
+ | `Enter` | Open the attributes panel for the selected card; close it when open. |
121
+ | `Tab` | Move focus between the conversation and the attributes panel. |
122
+ | `[` / `]` | Cycle to the next / previous trace. |
123
+ | `Esc` | Close the panel (or unfocus it), otherwise close the viewer. |
124
+ | `q` | Close the viewer. |
125
+
126
+ `←`/`→` or a mouse click expands a card to its full contents (the system prompt, long payloads) and collapses it back; cards that already fit have nothing to expand. The scroll wheel scrolls the conversation (or the attributes panel when the pointer is over it). Dragging with the mouse selects text — the selection highlights as you drag, and releasing copies it to your clipboard with a `Copied to clipboard` toast in the top-right corner; `Esc` cancels an in-flight selection. The attributes panel opens on the right and stays metadata-only — status, timing, ids, and the span's non-payload OTLP attributes — since the cards already carry the payloads. Model spans capture the system prompt, the prompt messages (rendered as a chat transcript), and the response (text, tool calls, finish reason); tool-call spans capture the call arguments and result, pretty-printed — each capped at 32 KB, with provider transport metadata stripped. When a conversation's messages exceed the cap, the oldest messages are dropped and the transcript notes how many were omitted. Set `EVE_TRACES_CONTENT=off` to stop capturing payload content. With no spans yet, the viewer shows an empty state and keeps watching; if tracing is disabled (`EVE_TRACES=off`) it says so instead.
127
+
109
128
  ## Display flags
110
129
 
111
130
  Density flags control how much of each section renders. They accept `full`, `collapsed`, `auto-collapsed`, or `hidden`.
@@ -13,7 +13,7 @@ When no authored `instrumentation.ts` exists, `eve dev` records agent, AI SDK, a
13
13
 
14
14
  The directory is an immutable OTLP/JSON spool and remains available after `eve dev` exits, subject to the [retention policy](#local-trace-retention) below. Inspection tools may build a query index from these segments, but the index is derived and can be rebuilt without changing the captured trace data.
15
15
 
16
- Use `eve traces ls` to list captured traces and `eve traces <trace>` to inspect a session's span tree.
16
+ Use `eve traces ls` to list captured traces and `eve traces <trace>` to inspect a session's span tree. Inside the dev TUI, [`/traces`](dev-tui#inspect-traces) opens a full-screen live viewer that re-tells the trace as a chat-style conversation (expandable message and tool-call cards) with a right-side metadata drawer. Model and tool-call spans also carry the inputs and outputs (system prompt, prompt messages, and response text for models; call arguments and results for tools, each capped at 32 KB) — set `EVE_TRACES_CONTENT=off` to keep payloads out of the spool.
17
17
 
18
18
  When a model call is served by Vercel AI Gateway, its `agent.step` span also carries the cost the gateway reported: `gen_ai.usage.cost` (raw inference, USD), `gen_ai.usage.gateway_cost` (with the gateway surcharge), `gen_ai.usage.input_cost` / `gen_ai.usage.output_cost` (the split), and `gen_ai.generation.id` for reconciliation with the gateway dashboard. These attributes only exist for gateway-served calls — other providers emit nothing. Cost per turn is the sum across the turn's step spans.
19
19
 
@@ -34,12 +34,13 @@ Whatever survives is then evicted oldest-first while the store exceeds `EVE_TRAC
34
34
 
35
35
  Open sessions are tracked per dev worker, so a session waiting across a restart, or a trace a draining worker is still writing, is not covered by the first rule. The five-minute write-recency rule is what protects those, and it applies even when you set `EVE_TRACES_MAX_AGE_MS=0`.
36
36
 
37
- | Variable | Default | Effect |
38
- | ---------------------------- | -------------------- | --------------------------------------------- |
39
- | `EVE_TRACES` | on | `off` stops writing traces and stops sweeping |
40
- | `EVE_TRACES_MAX_AGE_MS` | `604800000` (7d) | Age after which a trace may be evicted |
41
- | `EVE_TRACES_MAX_TOTAL_BYTES` | `536870912` (512 MB) | Size budget for the whole store |
42
- | `EVE_TRACES_RETAIN_COUNT` | `20` | Newest traces kept regardless of age or size |
37
+ | Variable | Default | Effect |
38
+ | ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------- |
39
+ | `EVE_TRACES` | on | `off` stops writing traces and stops sweeping |
40
+ | `EVE_TRACES_CONTENT` | on | `off` stops capturing model prompt/response and tool input/output attributes on local spans |
41
+ | `EVE_TRACES_MAX_AGE_MS` | `604800000` (7d) | Age after which a trace may be evicted |
42
+ | `EVE_TRACES_MAX_TOTAL_BYTES` | `536870912` (512 MB) | Size budget for the whole store |
43
+ | `EVE_TRACES_RETAIN_COUNT` | `20` | Newest traces kept regardless of age or size |
43
44
 
44
45
  Set these in `.env.local`, which `eve dev` loads automatically. Each bound accepts `off` to disable it individually — note that `EVE_TRACES_RETAIN_COUNT=off` removes the keep-newest guarantee rather than retaining everything.
45
46
 
@@ -252,7 +252,15 @@ eve traces # show the most recent span tree
252
252
  eve traces <trace> # show one span tree
253
253
  ```
254
254
 
255
- `eve traces ls` reads the immutable OTLP/JSON segments captured under `.eve/traces/v1`; `eve dev` does not need to be running. `eve traces` accepts a full trace id, an `agent.session.id`, or an unambiguous prefix of either. Malformed or incomplete segments are skipped without hiding valid spans from the same trace.
255
+ Reads the immutable OTLP/JSON segments under `.eve/traces/v1`, so `eve dev` need not be running. Accepts a full trace id, an `agent.session.id`, or an unambiguous prefix of either. Malformed segments are skipped without hiding valid spans from the same trace.
256
+
257
+ A subagent keeps its own session id but records into the trace its parent had open at dispatch, so delegated work appears under the session that caused it, tagged with `agent.root.session.id`. Either session id resolves to that trace. A remote agent traces under its own deployment and is not recorded here.
258
+
259
+ A session long enough to outgrow one trace — far longer than anything you will drive locally — continues into a new one. Each is a session window, numbered from zero on `agent.session.window`; passing a session id shows every window it produced, oldest first, and a trace id shows just that window.
260
+
261
+ One parent turn can dispatch several subagents into the same window, so a child's turn spans name the dispatch: `agent.parent.session.id`, `agent.parent.turn.id`, and `agent.parent.call_id` identify the tool call that created the child, and `agent.subagent.name` the subagent it invoked. Top-level sessions carry none of these.
262
+
263
+ `agent.session` and `agent.turn` outlive the worker that opened them, so they are recorded as zero-duration markers. The span tree shows their descendant extent instead; `agent.step` and the model and tool spans beneath it carry real durations. A third-party OTel backend reports zero for the markers.
256
264
 
257
265
  ## `eve link`
258
266
 
package/docs/sandbox.mdx CHANGED
@@ -20,7 +20,7 @@ The model already has shell and file access through the default tools:
20
20
  | `glob` | find files by pattern |
21
21
  | `grep` | search file contents |
22
22
 
23
- All of them run with `/workspace` as the working directory. Any authored runtime function (a tool, a step, a model callback) can get a live sandbox handle with `ctx.getSandbox()`.
23
+ All of them run with `/workspace` as the working directory. The model-facing file tools accept both absolute paths and paths beginning with `$HOME/`; eve resolves the latter inside the sandbox before reading, writing, or searching. Any authored runtime function (a tool, a step, a model callback) can get a live sandbox handle with `ctx.getSandbox()`.
24
24
 
25
25
  ```ts title="agent/tools/run_analysis.ts"
26
26
  import { defineTool } from "eve/tools";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve",
3
- "version": "0.29.1",
3
+ "version": "0.29.3",
4
4
  "private": false,
5
5
  "description": "Filesystem-first framework for durable backend AI agents that run anywhere.",
6
6
  "keywords": [
@@ -236,6 +236,11 @@
236
236
  "import": "./dist/src/public/channels/chat-sdk/index.js",
237
237
  "default": "./dist/src/public/channels/chat-sdk/index.js"
238
238
  },
239
+ "./channels/photon": {
240
+ "types": "./dist/src/public/channels/photon/index.d.ts",
241
+ "import": "./dist/src/public/channels/photon/index.js",
242
+ "default": "./dist/src/public/channels/photon/index.js"
243
+ },
239
244
  "./channels/github": {
240
245
  "types": "./dist/src/public/channels/github/index.d.ts",
241
246
  "import": "./dist/src/public/channels/github/index.js",
@@ -303,6 +308,7 @@
303
308
  "@opentelemetry/context-async-hooks": "2.6.1",
304
309
  "@opentelemetry/otlp-transformer": "0.214.0",
305
310
  "@opentelemetry/sdk-trace-base": "2.6.1",
311
+ "@photon-ai/chat-adapter-imessage": "3.2.0",
306
312
  "@standard-schema/spec": "1.1.0",
307
313
  "@sveltejs/kit": "^2.0.0",
308
314
  "@types/json-schema": "7.0.15",