@jini-ai/daemon 0.3.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +50 -8
  3. package/dist/agent-executor/contracts.d.ts +736 -0
  4. package/dist/agent-executor/contracts.js +28 -0
  5. package/dist/agent-executor/event-translation.d.ts +87 -0
  6. package/dist/agent-executor/event-translation.js +230 -0
  7. package/dist/agent-executor/events.d.ts +190 -0
  8. package/dist/agent-executor/events.js +848 -0
  9. package/dist/agent-executor/handoff.d.ts +18 -0
  10. package/dist/agent-executor/handoff.js +16 -0
  11. package/dist/agent-executor/index.d.ts +98 -0
  12. package/dist/agent-executor/index.js +98 -0
  13. package/dist/agent-executor/launch.d.ts +375 -0
  14. package/dist/agent-executor/launch.js +1001 -0
  15. package/dist/agent-executor/mcp-bridge.d.ts +338 -0
  16. package/dist/agent-executor/mcp-bridge.js +835 -0
  17. package/dist/agent-executor/values.d.ts +5 -0
  18. package/dist/agent-executor/values.js +13 -0
  19. package/dist/agent-executor.d.ts +382 -177
  20. package/dist/agent-executor.js +408 -344
  21. package/dist/close-status.d.ts +11 -8
  22. package/dist/close-status.js +31 -19
  23. package/dist/continuation/continuation-transport.d.ts +3 -1
  24. package/dist/continuation/continuation-transport.js +1 -1
  25. package/dist/continuation/journal.d.ts +12 -5
  26. package/dist/continuation/journal.js +4 -4
  27. package/dist/continuation/run-scoped-context-store.d.ts +13 -4
  28. package/dist/continuation/run-scoped-context-store.js +6 -6
  29. package/dist/continuation/run-start-handler.d.ts +1 -1
  30. package/dist/continuation/run-start-handler.js +3 -16
  31. package/dist/delegated-tool-bridge.d.ts +7 -3
  32. package/dist/delegated-tool-bridge.js +32 -31
  33. package/dist/event-log.d.ts +7 -5
  34. package/dist/event-log.js +12 -11
  35. package/dist/frontend-capability-tools.d.ts +1 -1
  36. package/dist/frontend-capability-tools.js +3 -2
  37. package/dist/frontend-session-registry.d.ts +32 -9
  38. package/dist/frontend-session-registry.js +12 -11
  39. package/dist/http/active-context.d.ts +105 -0
  40. package/dist/http/active-context.js +67 -0
  41. package/dist/http/agents.d.ts +68 -0
  42. package/dist/http/agents.js +12 -0
  43. package/dist/http/attachments.d.ts +618 -0
  44. package/dist/http/attachments.js +1034 -0
  45. package/dist/http/cancel-owned-runs.d.ts +49 -0
  46. package/dist/http/cancel-owned-runs.js +35 -0
  47. package/dist/http/component-catalog.d.ts +84 -0
  48. package/dist/http/component-catalog.js +64 -0
  49. package/dist/http/connectors.d.ts +209 -0
  50. package/dist/http/connectors.js +409 -0
  51. package/dist/http/daemon-status.d.ts +76 -0
  52. package/dist/http/daemon-status.js +55 -0
  53. package/dist/http/db-ops.d.ts +123 -0
  54. package/dist/http/db-ops.js +189 -0
  55. package/dist/http/delegated-tools.d.ts +113 -0
  56. package/dist/http/delegated-tools.js +230 -0
  57. package/dist/http/frontend-control.d.ts +136 -0
  58. package/dist/http/frontend-control.js +79 -0
  59. package/dist/http/frontend-sessions.d.ts +110 -0
  60. package/dist/http/frontend-sessions.js +180 -0
  61. package/dist/http/health.d.ts +95 -0
  62. package/dist/http/health.js +40 -0
  63. package/dist/http/host-tools.d.ts +214 -0
  64. package/dist/http/host-tools.js +329 -0
  65. package/dist/http/index.d.ts +52 -0
  66. package/dist/http/index.js +27 -0
  67. package/dist/http/media.d.ts +146 -0
  68. package/dist/http/media.js +346 -0
  69. package/dist/http/memory.d.ts +265 -0
  70. package/dist/http/memory.js +328 -0
  71. package/dist/http/model-proxy.d.ts +49 -0
  72. package/dist/http/model-proxy.js +642 -0
  73. package/dist/http/remote-run-events.d.ts +70 -0
  74. package/dist/http/remote-run-events.js +224 -0
  75. package/dist/http/research.d.ts +48 -0
  76. package/dist/http/research.js +315 -0
  77. package/dist/http/route-manifest.d.ts +35 -0
  78. package/dist/http/route-manifest.js +103 -0
  79. package/dist/http/routines.d.ts +120 -0
  80. package/dist/http/routines.js +298 -0
  81. package/dist/http/run-credentials.d.ts +73 -0
  82. package/dist/http/run-credentials.js +71 -0
  83. package/dist/http/runs.d.ts +91 -0
  84. package/dist/http/runs.js +221 -0
  85. package/dist/http/terminals.d.ts +138 -0
  86. package/dist/http/terminals.js +325 -0
  87. package/dist/http/tool-catalog.d.ts +78 -0
  88. package/dist/http/tool-catalog.js +66 -0
  89. package/dist/http/workspace-root.d.ts +77 -0
  90. package/dist/http/workspace-root.js +63 -0
  91. package/dist/http/xai.d.ts +90 -0
  92. package/dist/http/xai.js +655 -0
  93. package/dist/image-prompt-delivery.d.ts +13 -3
  94. package/dist/image-prompt-delivery.js +5 -5
  95. package/dist/index.d.ts +556 -4
  96. package/dist/index.js +556 -4
  97. package/dist/legacy-data-migration.d.ts +59 -8
  98. package/dist/legacy-data-migration.js +38 -33
  99. package/dist/read-only-tools.d.ts +130 -0
  100. package/dist/read-only-tools.js +115 -0
  101. package/dist/remote-tool-bridge.d.ts +11 -5
  102. package/dist/remote-tool-bridge.js +15 -15
  103. package/dist/routines/index.d.ts +1 -1
  104. package/dist/routines/index.js +1 -1
  105. package/dist/routines/routine-store.d.ts +34 -12
  106. package/dist/routines/routine-store.js +21 -20
  107. package/dist/routines/schedule.d.ts +48 -10
  108. package/dist/routines/schedule.js +21 -21
  109. package/dist/routines/scheduler.d.ts +32 -11
  110. package/dist/routines/scheduler.js +61 -57
  111. package/dist/routines/types.d.ts +10 -3
  112. package/dist/run/core/result.d.ts +6 -2
  113. package/dist/run/core/result.js +3 -3
  114. package/dist/run/core/retry.d.ts +17 -4
  115. package/dist/run/core/retry.js +7 -11
  116. package/dist/run/diagnostics/diagnostics.d.ts +14 -4
  117. package/dist/run/diagnostics/diagnostics.js +7 -7
  118. package/dist/run/index.d.ts +1 -1
  119. package/dist/run/index.js +1 -1
  120. package/dist/run-credentials.d.ts +138 -0
  121. package/dist/run-credentials.js +152 -0
  122. package/dist/run-lifecycle.d.ts +92 -33
  123. package/dist/run-lifecycle.js +86 -73
  124. package/dist/scheduler.d.ts +8 -0
  125. package/dist/scheduler.js +2 -0
  126. package/dist/session-coordination.d.ts +115 -0
  127. package/dist/session-coordination.js +111 -0
  128. package/dist/store/agent-sessions/errors.d.ts +8 -0
  129. package/dist/store/agent-sessions/errors.js +10 -0
  130. package/dist/store/agent-sessions/index.d.ts +12 -0
  131. package/dist/store/agent-sessions/index.js +19 -0
  132. package/dist/store/agent-sessions/pglite.d.ts +12 -0
  133. package/dist/store/agent-sessions/pglite.js +7 -0
  134. package/dist/store/agent-sessions/ports.d.ts +24 -0
  135. package/dist/store/agent-sessions/ports.js +2 -0
  136. package/dist/store/agent-sessions/postgres.d.ts +12 -0
  137. package/dist/store/agent-sessions/postgres.js +7 -0
  138. package/dist/store/agent-sessions/schema.d.ts +3 -0
  139. package/dist/store/agent-sessions/schema.js +23 -0
  140. package/dist/store/agent-sessions/sql.d.ts +35 -0
  141. package/dist/store/agent-sessions/sql.js +59 -0
  142. package/dist/store/agent-sessions/sqlite.d.ts +117 -0
  143. package/dist/store/agent-sessions/sqlite.js +128 -0
  144. package/dist/store/event-log/sqlite.d.ts +75 -0
  145. package/dist/store/event-log/sqlite.js +230 -0
  146. package/dist/surface-exchanges.d.ts +97 -0
  147. package/dist/surface-exchanges.js +153 -0
  148. package/dist/terminal-session.d.ts +43 -11
  149. package/dist/terminal-session.js +22 -20
  150. package/dist/tokens.js +4 -4
  151. package/dist/tool-audit.d.ts +114 -0
  152. package/dist/tool-audit.js +95 -0
  153. package/dist/tool-executor.d.ts +22 -7
  154. package/dist/tool-executor.js +16 -16
  155. package/dist/tool-result-media.d.ts +3 -1
  156. package/dist/tool-result-media.js +1 -1
  157. package/dist/tool-result-surfaces.d.ts +3 -1
  158. package/dist/tool-result-surfaces.js +1 -1
  159. package/package.json +127 -9
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * 1. **A real `node-pty`-backed `PtySpawn`.** `@jini-ai/platform` intentionally
14
14
  * does not depend on `node-pty` (a native compiled addon); `@jini-ai/daemon`
15
- * does (see this package's `package.json` and `source-map.md`'s dated
15
+ * does (see this package's `package.json` and `archived provenance ledger`'s dated
16
16
  * section for why that dependency now lives here, not in `@jini-ai/http-kit` or
17
17
  * `@jini-ai/platform`). {@link loadRealSpawnPty} is the real, dynamically
18
18
  * imported default the reference session manager below uses.
@@ -95,7 +95,7 @@ function toPtyProcess(pty) {
95
95
  * package (`@jini-ai/daemon`) actually declares — this is exactly the
96
96
  * resolver injection seam `spawnHelperCandidatePaths` was built for.
97
97
  */
98
- export async function loadRealSpawnPty() {
98
+ export async function loadRealSpawnPty(_requiredArgs) {
99
99
  ensureSpawnHelperExecutable(spawnHelperCandidatePaths({ resolve: (specifier) => daemonRequire.resolve(specifier) }));
100
100
  const nodePty = await import('node-pty');
101
101
  return (shell, args, options) => toPtyProcess(nodePty.spawn(shell, args, options));
@@ -141,10 +141,11 @@ async function runExclusive(locks, id, fn) {
141
141
  * the kill/write/resize lock, and (via {@link createTerminalToolRegistrations})
142
142
  * `ToolExecutor` gating for creation. See module doc for the full design.
143
143
  */
144
- export function createTerminalSessionManager(options = {}) {
144
+ export function createTerminalSessionManager(requiredArgs, optionalArgs = {}) {
145
+ const options = { ...requiredArgs, ...optionalArgs };
145
146
  const terminalService = options.terminalService ??
146
147
  createTerminalService({
147
- loadSpawnPty: options.loadSpawnPty ?? loadRealSpawnPty,
148
+ loadSpawnPty: options.loadSpawnPty ?? (() => loadRealSpawnPty({})),
148
149
  ...(options.maxEvents !== undefined ? { maxEvents: options.maxEvents } : {}),
149
150
  ...(options.maxBufferBytes !== undefined ? { maxBufferBytes: options.maxBufferBytes } : {}),
150
151
  ...(options.exitTailBytes !== undefined ? { exitTailBytes: options.exitTailBytes } : {}),
@@ -177,7 +178,7 @@ export function createTerminalSessionManager(options = {}) {
177
178
  return null;
178
179
  return { session, meta };
179
180
  }
180
- async function create(principal, createOptions) {
181
+ async function create({ principal, options: createOptions }) {
181
182
  const session = await terminalService.create({
182
183
  cwd: createOptions.cwd,
183
184
  ...(createOptions.cols !== undefined ? { cols: createOptions.cols } : {}),
@@ -188,11 +189,11 @@ export function createTerminalSessionManager(options = {}) {
188
189
  metadata.set(session.id, { principalId: principal.id, resourceRef, killed: false });
189
190
  return toSessionInfo(session, resourceRef);
190
191
  }
191
- function get(principal, id) {
192
+ function get({ principal, id }) {
192
193
  const found = checkOwnership(principal, id);
193
194
  return found ? { status: 'ok', session: toSessionInfo(found.session, found.meta.resourceRef) } : { status: 'not-found' };
194
195
  }
195
- function list(principal, filter = {}) {
196
+ function list({ principal }, { filter = {} } = {}) {
196
197
  const result = [];
197
198
  for (const session of terminalService.list()) {
198
199
  const meta = metadata.get(session.id);
@@ -209,7 +210,7 @@ export function createTerminalSessionManager(options = {}) {
209
210
  const session = terminalService.get(id);
210
211
  return session ? toSessionInfo(session, resourceRef) : null;
211
212
  }
212
- async function write(principal, id, input) {
213
+ async function write({ principal, id, input }) {
213
214
  const found = checkOwnership(principal, id);
214
215
  if (!found)
215
216
  return { status: 'not-found' };
@@ -224,7 +225,7 @@ export function createTerminalSessionManager(options = {}) {
224
225
  return { status: 'ok', ok, session: currentSnapshot(id, meta.resourceRef) };
225
226
  });
226
227
  }
227
- async function resize(principal, id, cols, rows) {
228
+ async function resize({ principal, id, cols, rows }) {
228
229
  const found = checkOwnership(principal, id);
229
230
  if (!found)
230
231
  return { status: 'not-found' };
@@ -237,7 +238,7 @@ export function createTerminalSessionManager(options = {}) {
237
238
  return { status: 'ok', ok, session: currentSnapshot(id, meta.resourceRef) };
238
239
  });
239
240
  }
240
- async function kill(principal, id, signal) {
241
+ async function kill({ principal, id }, { signal } = {}) {
241
242
  const found = checkOwnership(principal, id);
242
243
  if (!found)
243
244
  return { status: 'not-found' };
@@ -252,13 +253,13 @@ export function createTerminalSessionManager(options = {}) {
252
253
  return { status: 'ok', ok, session: currentSnapshot(id, meta?.resourceRef ?? null) };
253
254
  });
254
255
  }
255
- function attach(principal, id, lastEventId, sink) {
256
+ function attach({ principal, id, lastEventId, sink }) {
256
257
  const found = checkOwnership(principal, id);
257
258
  if (!found)
258
259
  return 'not-found';
259
260
  return terminalService.attach(id, lastEventId, sink);
260
261
  }
261
- function detach(id, sink) {
262
+ function detach({ id, sink }) {
262
263
  terminalService.detach(id, sink);
263
264
  }
264
265
  async function shutdownActive(shutdownOptions) {
@@ -278,7 +279,8 @@ function isRecord(value) {
278
279
  * audit trail (matching `db-ops.ts`'s `createDaemonDbToolRegistrations`
279
280
  * precedent).
280
281
  */
281
- export function createTerminalToolRegistrations(options) {
282
+ export function createTerminalToolRegistrations(requiredArgs, optionalArgs = {}) {
283
+ const options = { ...requiredArgs, ...optionalArgs };
282
284
  const { manager, policy = denyAllTerminalCreatePolicy, requiresConfirmation, timeoutMs } = options;
283
285
  const descriptorExtras = {
284
286
  ...(requiresConfirmation !== undefined ? { requiresConfirmation } : {}),
@@ -302,13 +304,13 @@ export function createTerminalToolRegistrations(options) {
302
304
  const cols = typeof record.cols === 'number' ? record.cols : undefined;
303
305
  const rows = typeof record.rows === 'number' ? record.rows : undefined;
304
306
  const shell = typeof record.shell === 'string' ? record.shell : null;
305
- return manager.create(ctx.principal, {
306
- resourceRef,
307
- cwd,
308
- ...(cols !== undefined ? { cols } : {}),
309
- ...(rows !== undefined ? { rows } : {}),
310
- shell,
311
- });
307
+ return manager.create({ principal: ctx.principal, options: {
308
+ resourceRef,
309
+ cwd,
310
+ ...(cols !== undefined ? { cols } : {}),
311
+ ...(rows !== undefined ? { rows } : {}),
312
+ shell,
313
+ } });
312
314
  },
313
315
  },
314
316
  };
package/dist/tokens.js CHANGED
@@ -12,8 +12,8 @@
12
12
  * architecture debate found it violated §2.1's "NO ... artifacts ... in the kernel" rule.
13
13
  */
14
14
  import { token } from '@jini-ai/core';
15
- export const RunLifecycleToken = token('jini.runLifecycle');
16
- export const EventLogToken = token('jini.eventLog');
17
- export const ToolExecutorToken = token('jini.toolExecutor');
18
- export const AgentExecutorToken = token('jini.agentExecutor');
15
+ export const RunLifecycleToken = token({ id: 'jini.runLifecycle' });
16
+ export const EventLogToken = token({ id: 'jini.eventLog' });
17
+ export const ToolExecutorToken = token({ id: 'jini.toolExecutor' });
18
+ export const AgentExecutorToken = token({ id: 'jini.agentExecutor' });
19
19
  //# sourceMappingURL=tokens.js.map
@@ -0,0 +1,114 @@
1
+ import type { Principal, RunRef, SurfaceEmitter } from '@jini-ai/core';
2
+ import type { ConfirmationDecision, ToolExecutionAuditRecord, ToolExecutionPhase, ToolExecutionResult, ToolExecutionStatus, ToolExecutor } from './tool-executor.js';
3
+ /** The daemon vocabulary, plus attempts that never acquired an execution ID. */
4
+ export type ToolAttemptPhase = Extract<ToolExecutionPhase, 'requested' | ToolExecutionStatus> | 'unknown-tool';
5
+ export declare const TOOL_ATTEMPT_PHASES: readonly ["requested", "completed", "denied", "confirmation-denied", "timed-out", "cancelled", "failed", "unknown-tool"];
6
+ export interface ToolAttemptEvent {
7
+ readonly attemptId: string;
8
+ readonly executionId: string | null;
9
+ readonly workspaceId: string;
10
+ readonly runId: string;
11
+ readonly toolId: string;
12
+ readonly principalId: string;
13
+ readonly phase: ToolAttemptPhase;
14
+ readonly at: string;
15
+ /** Value-free metadata; never raw input, query, output or error text. */
16
+ readonly detail?: string | null;
17
+ }
18
+ export interface ToolAttemptAuditSink {
19
+ append(requiredArgs: Pick<ToolAttemptEvent, "attemptId" | "executionId" | "workspaceId" | "runId" | "toolId" | "principalId" | "phase" | "at">, optionalArgs?: Pick<ToolAttemptEvent, "detail">): Promise<void>;
20
+ }
21
+ export interface ToolAuditDependencies {
22
+ readonly sink: ToolAttemptAuditSink;
23
+ readonly now: () => string;
24
+ readonly newAttemptId: () => string;
25
+ }
26
+ export interface ToolAuditOptions {
27
+ readonly onSinkError?: (error: unknown) => void;
28
+ }
29
+ export declare function describeInput({ input }: {
30
+ readonly input: unknown;
31
+ }): string;
32
+ export interface AuditedToolExecutor {
33
+ execute(args: {
34
+ principal: Principal;
35
+ run: RunRef;
36
+ toolId: string;
37
+ input: unknown;
38
+ }, options?: {
39
+ signal?: AbortSignal;
40
+ emitSurface?: SurfaceEmitter;
41
+ }): Promise<ToolExecutionResult>;
42
+ resumeConfirmation(args: {
43
+ executionId: string;
44
+ decision: ConfirmationDecision;
45
+ }): void;
46
+ cancel(args: {
47
+ executionId: string;
48
+ }): void;
49
+ getAuditRecord(args: {
50
+ executionId: string;
51
+ }): ToolExecutionAuditRecord | null;
52
+ }
53
+ /** Wraps the existing boundary without creating another executor or another phase vocabulary. */
54
+ export declare function withToolAttemptAudit(deps: ToolAuditDependencies & {
55
+ readonly inner: ToolExecutor;
56
+ readonly workspaceId: string;
57
+ readonly readErrorId: (args: {
58
+ result: ToolExecutionResult;
59
+ }) => string | undefined;
60
+ }, options?: ToolAuditOptions): AuditedToolExecutor;
61
+ export declare function searchToolsAuditDetail({ query, limit, hits }: {
62
+ query: string;
63
+ limit: number | null;
64
+ hits: readonly {
65
+ id: string;
66
+ }[];
67
+ }): string;
68
+ export declare function describeToolAuditDetail({ id, entry }: {
69
+ id: string;
70
+ entry: unknown | null;
71
+ }): string;
72
+ export interface ToolCatalogAuditIdentity {
73
+ readonly workspaceId: string;
74
+ readonly runId: string;
75
+ readonly principalId: string;
76
+ }
77
+ export interface ToolCatalogAttemptEvent extends ToolCatalogAuditIdentity {
78
+ readonly toolId: string;
79
+ readonly detail: string;
80
+ }
81
+ export declare function appendToolCatalogAttempt(deps: ToolAuditDependencies & {
82
+ readonly event: ToolCatalogAttemptEvent;
83
+ }, options?: ToolAuditOptions): void;
84
+ /** Structural source port: no database or catalog-provider dependency. */
85
+ export interface ToolCatalogAuditSource<Hit extends {
86
+ id: string;
87
+ }, Entry> {
88
+ search(args: {
89
+ query: string;
90
+ }, options?: {
91
+ limit?: number;
92
+ }): readonly Hit[];
93
+ describe(args: {
94
+ id: string;
95
+ }): Entry | null;
96
+ }
97
+ export declare function withToolCatalogAudit<Hit extends {
98
+ id: string;
99
+ }, Entry>(deps: ToolAuditDependencies & {
100
+ catalog: ToolCatalogAuditSource<Hit, Entry>;
101
+ identity: ToolCatalogAuditIdentity;
102
+ searchToolId: string;
103
+ describeToolId: string;
104
+ }, options?: ToolAuditOptions): {
105
+ search({ query }: {
106
+ query: string;
107
+ }, { limit }?: {
108
+ limit?: number;
109
+ }): readonly Hit[];
110
+ describe({ id }: {
111
+ id: string;
112
+ }): Entry | null;
113
+ };
114
+ //# sourceMappingURL=tool-audit.d.ts.map
@@ -0,0 +1,95 @@
1
+ export const TOOL_ATTEMPT_PHASES = [
2
+ 'requested', 'completed', 'denied', 'confirmation-denied', 'timed-out', 'cancelled', 'failed', 'unknown-tool',
3
+ ];
4
+ function reportSinkError(error, options) {
5
+ // An observer's reporter is itself an observer: a throwing reporter cannot gate a tool.
6
+ try {
7
+ options.onSinkError?.(error);
8
+ }
9
+ catch { /* execution remains authoritative */ }
10
+ }
11
+ async function appendSafely(event, deps, options) {
12
+ try {
13
+ await deps.sink.append(event);
14
+ }
15
+ catch (error) {
16
+ reportSinkError(error, options);
17
+ }
18
+ }
19
+ export function describeInput({ input }) {
20
+ if (input === null || input === undefined)
21
+ return `input: ${String(input)}`;
22
+ if (Array.isArray(input))
23
+ return `input: an array of ${input.length}`;
24
+ if (typeof input !== 'object')
25
+ return `input: a ${typeof input}`;
26
+ try {
27
+ const parts = Object.entries(input).map(([key, value]) => Array.isArray(value) ? `${key}[${value.length}]` : key).sort();
28
+ return parts.length === 0 ? 'keys: none' : `keys: ${parts.join(', ')}`;
29
+ }
30
+ catch {
31
+ return 'input: an unreadable object';
32
+ }
33
+ }
34
+ /** Wraps the existing boundary without creating another executor or another phase vocabulary. */
35
+ export function withToolAttemptAudit(deps, options = {}) {
36
+ return {
37
+ async execute({ principal, run, toolId, input }, { signal, emitSurface } = {}) {
38
+ const base = { attemptId: deps.newAttemptId(), workspaceId: deps.workspaceId, runId: run.id, toolId, principalId: principal.id };
39
+ await appendSafely({ ...base, executionId: null, phase: 'requested', at: deps.now(), detail: describeInput({ input }) }, deps, options);
40
+ let result;
41
+ try {
42
+ result = await deps.inner.execute({ principal, run, toolId, input }, {
43
+ ...(signal === undefined ? {} : { signal }),
44
+ ...(emitSurface === undefined ? {} : { emitSurface }),
45
+ });
46
+ }
47
+ catch (error) {
48
+ const phase = error instanceof Error && /unknown tool/i.test(error.message) ? 'unknown-tool' : 'failed';
49
+ await appendSafely({ ...base, executionId: null, phase, at: deps.now(), detail: error instanceof Error ? error.name : typeof error }, deps, options);
50
+ throw error;
51
+ }
52
+ let detail = result.truncated ? 'output truncated' : null;
53
+ // Host error-ID metadata is optional observation, never part of execution success.
54
+ if (detail === null) {
55
+ try {
56
+ const id = deps.readErrorId({ result });
57
+ if (id !== undefined)
58
+ detail = `errorId=${id}`;
59
+ }
60
+ catch (error) {
61
+ reportSinkError(error, options);
62
+ }
63
+ }
64
+ await appendSafely({ ...base, executionId: result.executionId, phase: result.status, at: deps.now(), detail }, deps, options);
65
+ return result;
66
+ },
67
+ resumeConfirmation: ({ executionId, decision }) => deps.inner.resumeConfirmation({ executionId: executionId, decision: decision }),
68
+ cancel: ({ executionId }) => deps.inner.cancel({ executionId }),
69
+ getAuditRecord: ({ executionId }) => deps.inner.getAuditRecord({ executionId: executionId }),
70
+ };
71
+ }
72
+ export function searchToolsAuditDetail({ query, limit, hits }) {
73
+ return JSON.stringify({ queryLength: query.length, limit, resultIds: hits.map(hit => hit.id), resultCount: hits.length });
74
+ }
75
+ export function describeToolAuditDetail({ id, entry }) {
76
+ return JSON.stringify({ id, found: entry !== null });
77
+ }
78
+ export function appendToolCatalogAttempt(deps, options = {}) {
79
+ void appendSafely({ ...deps.event, attemptId: deps.newAttemptId(), executionId: null, phase: 'completed', at: deps.now() }, deps, options);
80
+ }
81
+ export function withToolCatalogAudit(deps, options = {}) {
82
+ return {
83
+ search({ query }, { limit } = {}) {
84
+ const hits = deps.catalog.search({ query }, limit === undefined ? {} : { limit });
85
+ appendToolCatalogAttempt({ ...deps, event: { ...deps.identity, toolId: deps.searchToolId, detail: searchToolsAuditDetail({ query, limit: limit ?? null, hits }) } }, options);
86
+ return hits;
87
+ },
88
+ describe({ id }) {
89
+ const entry = deps.catalog.describe({ id });
90
+ appendToolCatalogAttempt({ ...deps, event: { ...deps.identity, toolId: deps.describeToolId, detail: describeToolAuditDetail({ id, entry }) } }, options);
91
+ return entry;
92
+ },
93
+ };
94
+ }
95
+ //# sourceMappingURL=tool-audit.js.map
@@ -85,12 +85,27 @@ export interface ToolExecutor {
85
85
  * see `@jini-ai/core`'s `SurfaceEmitter` for why a handler that waits on a human needs it.
86
86
  * @throws If `toolId` isn't registered — a routing/programming error, distinct from the denial/confirmation-denial states `ToolExecutionResult.status` covers.
87
87
  */
88
- execute(principal: Principal, run: RunRef, toolId: string, input: unknown, signal?: AbortSignal, emitSurface?: SurfaceEmitter): Promise<ToolExecutionResult>;
88
+ execute(args: {
89
+ readonly principal: Principal;
90
+ readonly run: RunRef;
91
+ readonly toolId: string;
92
+ readonly input: unknown;
93
+ }, optionalArgs?: {
94
+ readonly signal?: AbortSignal;
95
+ readonly emitSurface?: SurfaceEmitter;
96
+ }): Promise<ToolExecutionResult>;
89
97
  /** @throws If `executionId` has no confirmation currently pending (already resumed, never required one, or unknown). */
90
- resumeConfirmation(executionId: string, decision: ConfirmationDecision): void;
98
+ resumeConfirmation(args: {
99
+ readonly executionId: string;
100
+ readonly decision: ConfirmationDecision;
101
+ }): void;
91
102
  /** Aborts an in-flight handler, or resolves a still-pending confirmation as denied. No-op if `executionId` is already terminal or unknown. */
92
- cancel(executionId: string): void;
93
- getAuditRecord(executionId: string): ToolExecutionAuditRecord | null;
103
+ cancel(args: {
104
+ readonly executionId: string;
105
+ }): void;
106
+ getAuditRecord(args: {
107
+ readonly executionId: string;
108
+ }): ToolExecutionAuditRecord | null;
94
109
  }
95
110
  export interface CreateToolExecutorOptions {
96
111
  readonly registry: ToolRegistry;
@@ -103,15 +118,15 @@ export interface CreateToolExecutorOptions {
103
118
  * in-memory gate over a `ToolRegistry`. No persistence — a real host that
104
119
  * needs audit records to survive a restart layers a durable store behind
105
120
  * `getAuditRecord`/an append hook later; out of this task's scope (see
106
- * `source-map.md`).
121
+ * `archived provenance ledger`).
107
122
  *
108
123
  * @param options.registry - The `ToolRegistry` to resolve and authorize
109
- * tools against, via `@jini-ai/core/internal`'s `authorizeToolInvocation`
124
+ * tools against, via `@jini-ai/core/composition`'s `authorizeToolInvocation`
110
125
  * — the one and only caller of that internal export.
111
126
  * @param options.delegate - Transport-supplied authorize/confirm UI seam;
112
127
  * omit for a headless caller whose tools never need interactive gating.
113
128
  * @complexity `execute` is O(1) plus the handler's own cost; `resumeConfirmation`/`cancel`/`getAuditRecord` are O(1) map lookups.
114
129
  * @overallScore 100/100
115
130
  */
116
- export declare function createToolExecutor(options: CreateToolExecutorOptions): ToolExecutor;
131
+ export declare function createToolExecutor(requiredArgs: Pick<CreateToolExecutorOptions, "registry">, optionalArgs?: Pick<CreateToolExecutorOptions, "delegate" | "now">): ToolExecutor;
117
132
  //# sourceMappingURL=tool-executor.d.ts.map
@@ -3,7 +3,7 @@
3
3
  * §8 task 6). This is **new design work, not a port**: OD only *observes*
4
4
  * `tool_use` (`apps/daemon/src/runtimes/tool-loop-guard.ts` watches for
5
5
  * runaway repetition; it never gates a call before it runs), so there is
6
- * no upstream source to lift here — see `source-map.md` for the fuller
6
+ * no upstream source to lift here — see `archived provenance ledger` for the fuller
7
7
  * account of why and where this diverges from every other file in this
8
8
  * repo.
9
9
  *
@@ -47,7 +47,7 @@
47
47
  */
48
48
  import { randomUUID } from 'node:crypto';
49
49
  import { ToolInputError, } from '@jini-ai/core';
50
- import { authorizeToolInvocation } from '@jini-ai/core/internal';
50
+ import { authorizeToolInvocation } from '@jini-ai/core/composition';
51
51
  /**
52
52
  * Enforces `maxOutputBytes` in BYTES, which is what the option is named for.
53
53
  *
@@ -74,17 +74,18 @@ function truncateOutput(output, maxOutputBytes) {
74
74
  * in-memory gate over a `ToolRegistry`. No persistence — a real host that
75
75
  * needs audit records to survive a restart layers a durable store behind
76
76
  * `getAuditRecord`/an append hook later; out of this task's scope (see
77
- * `source-map.md`).
77
+ * `archived provenance ledger`).
78
78
  *
79
79
  * @param options.registry - The `ToolRegistry` to resolve and authorize
80
- * tools against, via `@jini-ai/core/internal`'s `authorizeToolInvocation`
80
+ * tools against, via `@jini-ai/core/composition`'s `authorizeToolInvocation`
81
81
  * — the one and only caller of that internal export.
82
82
  * @param options.delegate - Transport-supplied authorize/confirm UI seam;
83
83
  * omit for a headless caller whose tools never need interactive gating.
84
84
  * @complexity `execute` is O(1) plus the handler's own cost; `resumeConfirmation`/`cancel`/`getAuditRecord` are O(1) map lookups.
85
85
  * @overallScore 100/100
86
86
  */
87
- export function createToolExecutor(options) {
87
+ export function createToolExecutor(requiredArgs, optionalArgs = {}) {
88
+ const options = { ...requiredArgs, ...optionalArgs };
88
89
  const { registry, delegate = {} } = options;
89
90
  const now = options.now ?? Date.now;
90
91
  const audits = new Map();
@@ -140,7 +141,7 @@ export function createToolExecutor(options) {
140
141
  // to `createToolExecutor`) so a delegate that gains `onAuthorize` after construction still
141
142
  // gets consulted, matching the pre-`authorizeToolInvocation` behaviour.
142
143
  const onAuthorize = delegate.onAuthorize?.bind(delegate);
143
- return authorizeToolInvocation(registry, toolId, principal, run, input, onAuthorize ? { onAuthorize } : undefined);
144
+ return authorizeToolInvocation({ registry: registry, toolId: toolId, principal: principal, run: run, input: input }, onAuthorize === undefined ? {} : { delegate: { onAuthorize } });
144
145
  }
145
146
  /**
146
147
  * Arms the descriptor's execution timeout, if it declares one. The returned `timedOut()` reports
@@ -173,14 +174,14 @@ export function createToolExecutor(options) {
173
174
  const errorKind = err instanceof ToolInputError ? 'validation' : 'internal';
174
175
  return { executionId, status: 'failed', error: message, errorKind };
175
176
  }
176
- async function execute(principal, run, toolId, input, signal, emitSurface) {
177
+ async function execute({ principal, run, toolId, input }, { signal, emitSurface } = {}) {
177
178
  // Existence is checked separately from — and before — authorization so the audit record can
178
179
  // be opened before the (possibly slow, possibly throwing) authorization gate runs, matching
179
180
  // pre-authorizeToolInvocation timing: `requested` used to precede `policy.authorize`, and a
180
181
  // policy that hangs or throws used to still leave a record behind. `has()` only confirms
181
182
  // existence, never returns a handler, so this doesn't reopen the leak authorizeToolInvocation
182
183
  // closed — that gate still runs, still unconditionally, inside the call below.
183
- if (!registry.has(toolId)) {
184
+ if (!registry.has({ toolId })) {
184
185
  throw new Error(`ToolExecutor: unknown tool "${toolId}"`);
185
186
  }
186
187
  const executionId = openAudit(principal, run, toolId);
@@ -202,7 +203,7 @@ export function createToolExecutor(options) {
202
203
  appendEvent(executionId, 'cancelled');
203
204
  return { executionId, status: 'cancelled' };
204
205
  }
205
- onTransportAbort = () => cancel(executionId);
206
+ onTransportAbort = () => cancel({ executionId: executionId });
206
207
  signal.addEventListener('abort', onTransportAbort, { once: true });
207
208
  }
208
209
  // Both gates below can throw on INFRASTRUCTURE failure — a policy that rejects, a confirmation
@@ -234,7 +235,7 @@ export function createToolExecutor(options) {
234
235
  appendEvent(executionId, 'cancelled');
235
236
  return { executionId, status: 'cancelled' };
236
237
  }
237
- // `registry.has(toolId)` above already confirmed the tool exists, and `ToolRegistry` is
238
+ // `registry.has({ toolId })` above already confirmed the tool exists, and `ToolRegistry` is
238
239
  // append-only (no unregister — see `createToolRegistry`'s doc), so `authorizeToolInvocation`
239
240
  // resolving the same `toolId` against the same `registry` cannot come back `undefined` here.
240
241
  const { descriptor } = resolved;
@@ -300,7 +301,7 @@ export function createToolExecutor(options) {
300
301
  appendEvent(executionId, 'started');
301
302
  try {
302
303
  // `emitSurface` is spread in only when the transport supplied one, so a handler can test
303
- // `ctx.emitSurface` and get a truthful answer about whether a surface it emits would actually
304
+ // `options.emitSurface` and get a truthful answer about whether a surface it emits would actually
304
305
  // reach anybody. An always-present no-op would read as "yes" and let a handler park forever.
305
306
  const rawOutput = await handler({
306
307
  executionId,
@@ -308,8 +309,7 @@ export function createToolExecutor(options) {
308
309
  run,
309
310
  input,
310
311
  signal: controller.signal,
311
- ...(emitSurface !== undefined ? { emitSurface } : {}),
312
- });
312
+ }, { ...(emitSurface !== undefined ? { emitSurface } : {}) });
313
313
  // A handler is not OBLIGED to honour `signal` — it is handed one, not policed by one. A
314
314
  // handler that ignores it and resolves normally after the timer fired (or after `cancel`)
315
315
  // used to land here and be recorded `completed`, because the timeout/abort state was only
@@ -334,7 +334,7 @@ export function createToolExecutor(options) {
334
334
  return failureResult(executionId, err, timeout.timedOut(), controller.signal.aborted);
335
335
  }
336
336
  }
337
- function resumeConfirmation(executionId, decision) {
337
+ function resumeConfirmation({ executionId, decision }) {
338
338
  const resolve = pendingConfirmations.get(executionId);
339
339
  if (!resolve) {
340
340
  throw new Error(`ToolExecutor: no pending confirmation for execution "${executionId}"`);
@@ -342,7 +342,7 @@ export function createToolExecutor(options) {
342
342
  pendingConfirmations.delete(executionId);
343
343
  resolve(decision);
344
344
  }
345
- function cancel(executionId) {
345
+ function cancel({ executionId }) {
346
346
  const controller = activeControllers.get(executionId);
347
347
  if (controller) {
348
348
  controller.abort();
@@ -355,7 +355,7 @@ export function createToolExecutor(options) {
355
355
  resolve('deny');
356
356
  }
357
357
  }
358
- function getAuditRecord(executionId) {
358
+ function getAuditRecord({ executionId }) {
359
359
  return audits.get(executionId) ?? null;
360
360
  }
361
361
  return { execute, resumeConfirmation, cancel, getAuditRecord };
@@ -75,5 +75,7 @@ export interface ExtractResultMedia {
75
75
  * plain JSON) pass through untouched with no media.
76
76
  * @complexity O(n) in the number of content blocks.
77
77
  */
78
- export declare function extractResultMedia(output: unknown): ExtractResultMedia;
78
+ export declare function extractResultMedia({ output }: {
79
+ readonly output: unknown;
80
+ }): ExtractResultMedia;
79
81
  //# sourceMappingURL=tool-result-media.d.ts.map
@@ -58,7 +58,7 @@ function asImageBlock(block) {
58
58
  * plain JSON) pass through untouched with no media.
59
59
  * @complexity O(n) in the number of content blocks.
60
60
  */
61
- export function extractResultMedia(output) {
61
+ export function extractResultMedia({ output }) {
62
62
  if (!isRecord(output))
63
63
  return { remainder: output, media: [] };
64
64
  const content = output['content'];
@@ -74,5 +74,7 @@ export interface SplitToolResultSurfaces {
74
74
  * non-envelope value is passed through by reference with no surfaces.
75
75
  * @complexity O(n) in the number of content blocks.
76
76
  */
77
- export declare function splitToolResultSurfaces(output: unknown): SplitToolResultSurfaces;
77
+ export declare function splitToolResultSurfaces({ output }: {
78
+ readonly output: unknown;
79
+ }): SplitToolResultSurfaces;
78
80
  //# sourceMappingURL=tool-result-surfaces.d.ts.map
@@ -63,7 +63,7 @@ function isRecord(value) {
63
63
  * non-envelope value is passed through by reference with no surfaces.
64
64
  * @complexity O(n) in the number of content blocks.
65
65
  */
66
- export function splitToolResultSurfaces(output) {
66
+ export function splitToolResultSurfaces({ output }) {
67
67
  if (!isRecord(output))
68
68
  return { modelOutput: output, surfaces: [] };
69
69
  const content = output['content'];