@jini-ai/daemon 0.3.8 → 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.
- package/CHANGELOG.md +119 -0
- package/README.md +50 -8
- package/dist/agent-executor/contracts.d.ts +736 -0
- package/dist/agent-executor/contracts.js +28 -0
- package/dist/agent-executor/event-translation.d.ts +87 -0
- package/dist/agent-executor/event-translation.js +230 -0
- package/dist/agent-executor/events.d.ts +190 -0
- package/dist/agent-executor/events.js +848 -0
- package/dist/agent-executor/handoff.d.ts +18 -0
- package/dist/agent-executor/handoff.js +16 -0
- package/dist/agent-executor/index.d.ts +98 -0
- package/dist/agent-executor/index.js +98 -0
- package/dist/agent-executor/launch.d.ts +375 -0
- package/dist/agent-executor/launch.js +1001 -0
- package/dist/agent-executor/mcp-bridge.d.ts +338 -0
- package/dist/agent-executor/mcp-bridge.js +835 -0
- package/dist/agent-executor/values.d.ts +5 -0
- package/dist/agent-executor/values.js +13 -0
- package/dist/agent-executor.d.ts +375 -176
- package/dist/agent-executor.js +388 -335
- package/dist/artifacts/index.d.ts +14 -0
- package/dist/artifacts/index.js +14 -0
- package/dist/artifacts/manifest.d.ts +90 -0
- package/dist/artifacts/manifest.js +229 -0
- package/dist/artifacts/publication-guard.d.ts +28 -0
- package/dist/artifacts/publication-guard.js +77 -0
- package/dist/artifacts/runtime-compat.d.ts +37 -0
- package/dist/artifacts/runtime-compat.js +33 -0
- package/dist/artifacts/store.d.ts +85 -0
- package/dist/artifacts/store.js +92 -0
- package/dist/artifacts/stub-guard.d.ts +73 -0
- package/dist/artifacts/stub-guard.js +198 -0
- package/dist/artifacts/text-suppression.d.ts +68 -0
- package/dist/artifacts/text-suppression.js +184 -0
- package/dist/close-status.d.ts +11 -8
- package/dist/close-status.js +31 -19
- package/dist/continuation/continuation-transport.d.ts +3 -1
- package/dist/continuation/continuation-transport.js +1 -1
- package/dist/continuation/journal.d.ts +12 -5
- package/dist/continuation/journal.js +4 -4
- package/dist/continuation/run-scoped-context-store.d.ts +13 -4
- package/dist/continuation/run-scoped-context-store.js +6 -6
- package/dist/continuation/run-start-handler.d.ts +1 -1
- package/dist/continuation/run-start-handler.js +3 -16
- package/dist/delegated-tool-bridge.d.ts +7 -3
- package/dist/delegated-tool-bridge.js +32 -31
- package/dist/event-log.d.ts +7 -5
- package/dist/event-log.js +12 -11
- package/dist/frontend-capability-tools.d.ts +1 -1
- package/dist/frontend-capability-tools.js +3 -2
- package/dist/frontend-session-registry.d.ts +32 -9
- package/dist/frontend-session-registry.js +12 -11
- package/dist/http/active-context.d.ts +105 -0
- package/dist/http/active-context.js +67 -0
- package/dist/http/agents.d.ts +68 -0
- package/dist/http/agents.js +12 -0
- package/dist/http/attachments.d.ts +618 -0
- package/dist/http/attachments.js +1034 -0
- package/dist/http/cancel-owned-runs.d.ts +49 -0
- package/dist/http/cancel-owned-runs.js +35 -0
- package/dist/http/component-catalog.d.ts +84 -0
- package/dist/http/component-catalog.js +64 -0
- package/dist/http/connectors.d.ts +209 -0
- package/dist/http/connectors.js +409 -0
- package/dist/http/daemon-status.d.ts +76 -0
- package/dist/http/daemon-status.js +55 -0
- package/dist/http/db-ops.d.ts +123 -0
- package/dist/http/db-ops.js +189 -0
- package/dist/http/delegated-tools.d.ts +113 -0
- package/dist/http/delegated-tools.js +230 -0
- package/dist/http/frontend-control.d.ts +136 -0
- package/dist/http/frontend-control.js +79 -0
- package/dist/http/frontend-sessions.d.ts +110 -0
- package/dist/http/frontend-sessions.js +180 -0
- package/dist/http/health.d.ts +95 -0
- package/dist/http/health.js +40 -0
- package/dist/http/host-tools.d.ts +214 -0
- package/dist/http/host-tools.js +329 -0
- package/dist/http/index.d.ts +52 -0
- package/dist/http/index.js +27 -0
- package/dist/http/media.d.ts +146 -0
- package/dist/http/media.js +346 -0
- package/dist/http/memory.d.ts +265 -0
- package/dist/http/memory.js +328 -0
- package/dist/http/model-proxy.d.ts +49 -0
- package/dist/http/model-proxy.js +642 -0
- package/dist/http/remote-run-events.d.ts +70 -0
- package/dist/http/remote-run-events.js +224 -0
- package/dist/http/research.d.ts +48 -0
- package/dist/http/research.js +315 -0
- package/dist/http/route-manifest.d.ts +35 -0
- package/dist/http/route-manifest.js +103 -0
- package/dist/http/routines.d.ts +120 -0
- package/dist/http/routines.js +298 -0
- package/dist/http/run-credentials.d.ts +73 -0
- package/dist/http/run-credentials.js +71 -0
- package/dist/http/runs.d.ts +91 -0
- package/dist/http/runs.js +221 -0
- package/dist/http/terminals.d.ts +138 -0
- package/dist/http/terminals.js +325 -0
- package/dist/http/tool-catalog.d.ts +78 -0
- package/dist/http/tool-catalog.js +66 -0
- package/dist/http/workspace-root.d.ts +77 -0
- package/dist/http/workspace-root.js +63 -0
- package/dist/http/xai.d.ts +90 -0
- package/dist/http/xai.js +655 -0
- package/dist/image-prompt-delivery.d.ts +13 -3
- package/dist/image-prompt-delivery.js +5 -5
- package/dist/index.d.ts +556 -4
- package/dist/index.js +556 -4
- package/dist/legacy-data-migration.d.ts +59 -8
- package/dist/legacy-data-migration.js +38 -33
- package/dist/read-only-tools.d.ts +130 -0
- package/dist/read-only-tools.js +115 -0
- package/dist/remote-tool-bridge.d.ts +11 -5
- package/dist/remote-tool-bridge.js +15 -15
- package/dist/routines/index.d.ts +1 -1
- package/dist/routines/index.js +1 -1
- package/dist/routines/routine-store.d.ts +34 -12
- package/dist/routines/routine-store.js +21 -20
- package/dist/routines/schedule.d.ts +48 -10
- package/dist/routines/schedule.js +21 -21
- package/dist/routines/scheduler.d.ts +32 -11
- package/dist/routines/scheduler.js +61 -57
- package/dist/routines/types.d.ts +10 -3
- package/dist/run/core/result.d.ts +6 -2
- package/dist/run/core/result.js +3 -3
- package/dist/run/core/retry.d.ts +17 -4
- package/dist/run/core/retry.js +7 -11
- package/dist/run/diagnostics/diagnostics.d.ts +14 -4
- package/dist/run/diagnostics/diagnostics.js +7 -7
- package/dist/run/index.d.ts +1 -1
- package/dist/run/index.js +1 -1
- package/dist/run-credentials.d.ts +138 -0
- package/dist/run-credentials.js +152 -0
- package/dist/run-lifecycle.d.ts +92 -33
- package/dist/run-lifecycle.js +81 -74
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.js +2 -0
- package/dist/session-coordination.d.ts +115 -0
- package/dist/session-coordination.js +111 -0
- package/dist/store/agent-sessions/errors.d.ts +8 -0
- package/dist/store/agent-sessions/errors.js +10 -0
- package/dist/store/agent-sessions/index.d.ts +12 -0
- package/dist/store/agent-sessions/index.js +19 -0
- package/dist/store/agent-sessions/pglite.d.ts +12 -0
- package/dist/store/agent-sessions/pglite.js +7 -0
- package/dist/store/agent-sessions/ports.d.ts +24 -0
- package/dist/store/agent-sessions/ports.js +2 -0
- package/dist/store/agent-sessions/postgres.d.ts +12 -0
- package/dist/store/agent-sessions/postgres.js +7 -0
- package/dist/store/agent-sessions/schema.d.ts +3 -0
- package/dist/store/agent-sessions/schema.js +23 -0
- package/dist/store/agent-sessions/sql.d.ts +35 -0
- package/dist/store/agent-sessions/sql.js +59 -0
- package/dist/store/agent-sessions/sqlite.d.ts +117 -0
- package/dist/store/agent-sessions/sqlite.js +128 -0
- package/dist/store/event-log/sqlite.d.ts +75 -0
- package/dist/store/event-log/sqlite.js +230 -0
- package/dist/surface-exchanges.d.ts +97 -0
- package/dist/surface-exchanges.js +153 -0
- package/dist/terminal-session.d.ts +43 -11
- package/dist/terminal-session.js +22 -20
- package/dist/tokens.js +4 -4
- package/dist/tool-audit.d.ts +114 -0
- package/dist/tool-audit.js +95 -0
- package/dist/tool-executor.d.ts +22 -7
- package/dist/tool-executor.js +16 -16
- package/dist/tool-result-media.d.ts +3 -1
- package/dist/tool-result-media.js +1 -1
- package/dist/tool-result-surfaces.d.ts +3 -1
- package/dist/tool-result-surfaces.js +1 -1
- package/package.json +127 -9
package/dist/terminal-session.js
CHANGED
|
@@ -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 `
|
|
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(
|
|
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(
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
package/dist/tool-executor.d.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
93
|
-
|
|
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
|
-
* `
|
|
121
|
+
* `archived provenance ledger`).
|
|
107
122
|
*
|
|
108
123
|
* @param options.registry - The `ToolRegistry` to resolve and authorize
|
|
109
|
-
* tools against, via `@jini-ai/core/
|
|
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(
|
|
131
|
+
export declare function createToolExecutor(requiredArgs: Pick<CreateToolExecutorOptions, "registry">, optionalArgs?: Pick<CreateToolExecutorOptions, "delegate" | "now">): ToolExecutor;
|
|
117
132
|
//# sourceMappingURL=tool-executor.d.ts.map
|
package/dist/tool-executor.js
CHANGED
|
@@ -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 `
|
|
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/
|
|
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
|
-
* `
|
|
77
|
+
* `archived provenance ledger`).
|
|
78
78
|
*
|
|
79
79
|
* @param options.registry - The `ToolRegistry` to resolve and authorize
|
|
80
|
-
* tools against, via `@jini-ai/core/
|
|
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(
|
|
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 }
|
|
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
|
-
// `
|
|
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
|
-
|
|
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
|
|
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
|
|
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'];
|