@jini-ai/daemon 0.1.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/LICENSE +201 -0
- package/dist/agent-executor.d.ts +458 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +1277 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/artifacts/index.d.ts +14 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +14 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/artifacts/manifest.d.ts +90 -0
- package/dist/artifacts/manifest.d.ts.map +1 -0
- package/dist/artifacts/manifest.js +229 -0
- package/dist/artifacts/manifest.js.map +1 -0
- package/dist/artifacts/publication-guard.d.ts +28 -0
- package/dist/artifacts/publication-guard.d.ts.map +1 -0
- package/dist/artifacts/publication-guard.js +77 -0
- package/dist/artifacts/publication-guard.js.map +1 -0
- package/dist/artifacts/runtime-compat.d.ts +37 -0
- package/dist/artifacts/runtime-compat.d.ts.map +1 -0
- package/dist/artifacts/runtime-compat.js +33 -0
- package/dist/artifacts/runtime-compat.js.map +1 -0
- package/dist/artifacts/store.d.ts +85 -0
- package/dist/artifacts/store.d.ts.map +1 -0
- package/dist/artifacts/store.js +92 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/artifacts/stub-guard.d.ts +73 -0
- package/dist/artifacts/stub-guard.d.ts.map +1 -0
- package/dist/artifacts/stub-guard.js +198 -0
- package/dist/artifacts/stub-guard.js.map +1 -0
- package/dist/artifacts/text-suppression.d.ts +68 -0
- package/dist/artifacts/text-suppression.d.ts.map +1 -0
- package/dist/artifacts/text-suppression.js +184 -0
- package/dist/artifacts/text-suppression.js.map +1 -0
- package/dist/close-status.d.ts +86 -0
- package/dist/close-status.d.ts.map +1 -0
- package/dist/close-status.js +91 -0
- package/dist/close-status.js.map +1 -0
- package/dist/continuation/continuation-transport.d.ts +41 -0
- package/dist/continuation/continuation-transport.d.ts.map +1 -0
- package/dist/continuation/continuation-transport.js +18 -0
- package/dist/continuation/continuation-transport.js.map +1 -0
- package/dist/continuation/index.d.ts +4 -0
- package/dist/continuation/index.d.ts.map +1 -0
- package/dist/continuation/index.js +4 -0
- package/dist/continuation/index.js.map +1 -0
- package/dist/continuation/journal.d.ts +37 -0
- package/dist/continuation/journal.d.ts.map +1 -0
- package/dist/continuation/journal.js +23 -0
- package/dist/continuation/journal.js.map +1 -0
- package/dist/continuation/run-start-handler.d.ts +75 -0
- package/dist/continuation/run-start-handler.d.ts.map +1 -0
- package/dist/continuation/run-start-handler.js +32 -0
- package/dist/continuation/run-start-handler.js.map +1 -0
- package/dist/delegated-tool-bridge.d.ts +53 -0
- package/dist/delegated-tool-bridge.d.ts.map +1 -0
- package/dist/delegated-tool-bridge.js +92 -0
- package/dist/delegated-tool-bridge.js.map +1 -0
- package/dist/event-log.d.ts +134 -0
- package/dist/event-log.d.ts.map +1 -0
- package/dist/event-log.js +108 -0
- package/dist/event-log.js.map +1 -0
- package/dist/frontend-capability-tools.d.ts +102 -0
- package/dist/frontend-capability-tools.d.ts.map +1 -0
- package/dist/frontend-capability-tools.js +68 -0
- package/dist/frontend-capability-tools.js.map +1 -0
- package/dist/frontend-session-registry.d.ts +127 -0
- package/dist/frontend-session-registry.d.ts.map +1 -0
- package/dist/frontend-session-registry.js +180 -0
- package/dist/frontend-session-registry.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy-data-migration.d.ts +104 -0
- package/dist/legacy-data-migration.d.ts.map +1 -0
- package/dist/legacy-data-migration.js +239 -0
- package/dist/legacy-data-migration.js.map +1 -0
- package/dist/routines/index.d.ts +12 -0
- package/dist/routines/index.d.ts.map +1 -0
- package/dist/routines/index.js +12 -0
- package/dist/routines/index.js.map +1 -0
- package/dist/routines/routine-store.d.ts +77 -0
- package/dist/routines/routine-store.d.ts.map +1 -0
- package/dist/routines/routine-store.js +162 -0
- package/dist/routines/routine-store.js.map +1 -0
- package/dist/routines/schedule.d.ts +72 -0
- package/dist/routines/schedule.d.ts.map +1 -0
- package/dist/routines/schedule.js +263 -0
- package/dist/routines/schedule.js.map +1 -0
- package/dist/routines/scheduler.d.ts +34 -0
- package/dist/routines/scheduler.d.ts.map +1 -0
- package/dist/routines/scheduler.js +330 -0
- package/dist/routines/scheduler.js.map +1 -0
- package/dist/routines/types.d.ts +131 -0
- package/dist/routines/types.d.ts.map +1 -0
- package/dist/routines/types.js +9 -0
- package/dist/routines/types.js.map +1 -0
- package/dist/run/core/failure-taxonomy.d.ts +27 -0
- package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
- package/dist/run/core/failure-taxonomy.js +16 -0
- package/dist/run/core/failure-taxonomy.js.map +1 -0
- package/dist/run/core/index.d.ts +5 -0
- package/dist/run/core/index.d.ts.map +1 -0
- package/dist/run/core/index.js +5 -0
- package/dist/run/core/index.js.map +1 -0
- package/dist/run/core/result.d.ts +25 -0
- package/dist/run/core/result.d.ts.map +1 -0
- package/dist/run/core/result.js +52 -0
- package/dist/run/core/result.js.map +1 -0
- package/dist/run/core/retry.d.ts +106 -0
- package/dist/run/core/retry.d.ts.map +1 -0
- package/dist/run/core/retry.js +210 -0
- package/dist/run/core/retry.js.map +1 -0
- package/dist/run/diagnostics/diagnostics.d.ts +86 -0
- package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/run/diagnostics/diagnostics.js +211 -0
- package/dist/run/diagnostics/diagnostics.js.map +1 -0
- package/dist/run/diagnostics/index.d.ts +3 -0
- package/dist/run/diagnostics/index.d.ts.map +1 -0
- package/dist/run/diagnostics/index.js +3 -0
- package/dist/run/diagnostics/index.js.map +1 -0
- package/dist/run/index.d.ts +16 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +16 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run-lifecycle.d.ts +118 -0
- package/dist/run-lifecycle.d.ts.map +1 -0
- package/dist/run-lifecycle.js +451 -0
- package/dist/run-lifecycle.js.map +1 -0
- package/dist/terminal-session.d.ts +139 -0
- package/dist/terminal-session.d.ts.map +1 -0
- package/dist/terminal-session.js +316 -0
- package/dist/terminal-session.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +19 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tool-executor.d.ts +97 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +198 -0
- package/dist/tool-executor.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the default `RunStartHandler`-shaped driver: resolves the real
|
|
3
|
+
* agent input via `options.resolveRunInput`, then hands it straight to
|
|
4
|
+
* `options.agentExecutor.run()`. A host wires this in as
|
|
5
|
+
* `RunHttpDeps.onStarted` (or `CreateLocalNodeDaemonConfig.onRunStarted`)
|
|
6
|
+
* instead of writing its own driver from scratch — it still owns
|
|
7
|
+
* `resolveRunInput` itself (gap 2 stays host-owned), but no longer needs to
|
|
8
|
+
* know `AgentExecutor`'s call shape or wire cancellation/journaling by hand.
|
|
9
|
+
* @param options.agentExecutor - The executor this handler drives. Any byte-journaling is the executor's own concern (see `CreateAgentExecutorOptions.journal`) — this handler does not journal directly.
|
|
10
|
+
* @param options.resolveRunInput - Host-owned composition seam — see module doc.
|
|
11
|
+
* @returns A handler structurally assignable to `@jini-ai/http`'s `RunStartHandler`.
|
|
12
|
+
* @throws Whatever `resolveRunInput` or `agentExecutor.run()` throw — `@jini-ai/http`'s `runStartRoute` already treats a rejecting `onStarted` as a failed run (finishes it, reports the internal error), so this handler deliberately does not swallow either failure itself.
|
|
13
|
+
* @complexity O(1) plus `resolveRunInput`'s and `agentExecutor.run()`'s own costs.
|
|
14
|
+
* @overallScore 100/100
|
|
15
|
+
*/
|
|
16
|
+
export function createDefaultRunStartHandler(options) {
|
|
17
|
+
return async (context) => {
|
|
18
|
+
const resolved = await options.resolveRunInput({
|
|
19
|
+
runId: context.run.id,
|
|
20
|
+
contextRef: context.request.contextRef,
|
|
21
|
+
...(context.request.agentId !== undefined ? { agentId: context.request.agentId } : {}),
|
|
22
|
+
});
|
|
23
|
+
await options.agentExecutor.run({
|
|
24
|
+
runId: context.run.id,
|
|
25
|
+
agentId: resolved.agentId,
|
|
26
|
+
prompt: resolved.prompt,
|
|
27
|
+
cwd: resolved.cwd,
|
|
28
|
+
...(resolved.env !== undefined ? { env: resolved.env } : {}),
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=run-start-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-start-handler.js","sourceRoot":"","sources":["../../src/continuation/run-start-handler.ts"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAA4C;IAE5C,OAAO,KAAK,EAAE,OAA8B,EAAiB,EAAE;QAC7D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;YAC7C,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;YACrB,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU;YACtC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvF,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC;YAC9B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;YACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,GAAG,CAAC,QAAQ,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7D,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DelegatedToolBridge` — the execution path for agents/protocols that ask
|
|
3
|
+
* Jini to run a registered tool on their behalf. It is deliberately separate
|
|
4
|
+
* from ACP's `session/request_permission`: ACP authorizes an agent's *native*
|
|
5
|
+
* tool loop, whereas this bridge invokes Jini's `ToolExecutor` and therefore
|
|
6
|
+
* enforces Jini's registry policy, confirmation, timeout, cancellation, and
|
|
7
|
+
* audit trail before any registered handler runs.
|
|
8
|
+
*/
|
|
9
|
+
import type { Principal } from '@jini-ai/core';
|
|
10
|
+
import type { RunLifecycle } from './run-lifecycle.js';
|
|
11
|
+
import type { ToolExecutionResult, ToolExecutor } from './tool-executor.js';
|
|
12
|
+
/** A tool request received through a Jini-owned delegated-execution protocol. */
|
|
13
|
+
export interface DelegatedToolInvocation {
|
|
14
|
+
/** The already-started run that owns the request. */
|
|
15
|
+
readonly runId: string;
|
|
16
|
+
/** Stable agent-side correlation id, mirrored in run `tool_use`/`tool_result` events. */
|
|
17
|
+
readonly toolUseId: string;
|
|
18
|
+
/** Jini registry id — never an agent-vendor-specific tool name. */
|
|
19
|
+
readonly toolId: string;
|
|
20
|
+
readonly principal: Principal;
|
|
21
|
+
readonly input: unknown;
|
|
22
|
+
/** Optional transport disconnect/abort signal, combined with run cancellation. */
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
export interface DelegatedToolBridge {
|
|
26
|
+
/**
|
|
27
|
+
* Emits a canonical `tool_use`, executes through `ToolExecutor`, then emits
|
|
28
|
+
* exactly one matching `tool_result`. Unknown tool ids remain programmer or
|
|
29
|
+
* routing errors from `ToolExecutor` and are rethrown after their error
|
|
30
|
+
* result is recorded.
|
|
31
|
+
*/
|
|
32
|
+
execute(invocation: DelegatedToolInvocation): Promise<ToolExecutionResult>;
|
|
33
|
+
}
|
|
34
|
+
export interface CreateDelegatedToolBridgeOptions {
|
|
35
|
+
readonly lifecycle: RunLifecycle;
|
|
36
|
+
readonly toolExecutor: ToolExecutor;
|
|
37
|
+
}
|
|
38
|
+
/** Converts arbitrary registered-tool output into the string-bearing run protocol. */
|
|
39
|
+
export declare function serializeDelegatedToolOutput(output: unknown): string;
|
|
40
|
+
/**
|
|
41
|
+
* Maps a `ToolExecutionResult`'s status to the string a delegated-tool caller (this bridge, or
|
|
42
|
+
* gap 3's stdin-tool-result injector in `agent-executor.ts`) reports back as the tool's visible
|
|
43
|
+
* output/failure reason. Exported so both real callers share one mapping rather than drifting.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resultContent(result: ToolExecutionResult): string;
|
|
46
|
+
/**
|
|
47
|
+
* Creates the transport-neutral bridge used by future ACP-delegate, MCP, or
|
|
48
|
+
* other host protocols. This module does not invent a server transport: a
|
|
49
|
+
* concrete protocol calls this bridge after it has decoded and authenticated a
|
|
50
|
+
* delegated tool request.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createDelegatedToolBridge(options: CreateDelegatedToolBridgeOptions): DelegatedToolBridge;
|
|
53
|
+
//# sourceMappingURL=delegated-tool-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegated-tool-bridge.d.ts","sourceRoot":"","sources":["../src/delegated-tool-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,SAAS,EAAU,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE5E,iFAAiF;AACjF,MAAM,WAAW,uBAAuB;IACtC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,uBAAuB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;CACrC;AAMD,sFAAsF;AACtF,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CASpE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAejE;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,mBAAmB,CA8CxG"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
function errorMessage(error) {
|
|
2
|
+
return error instanceof Error ? error.message : String(error);
|
|
3
|
+
}
|
|
4
|
+
/** Converts arbitrary registered-tool output into the string-bearing run protocol. */
|
|
5
|
+
export function serializeDelegatedToolOutput(output) {
|
|
6
|
+
if (typeof output === 'string')
|
|
7
|
+
return output;
|
|
8
|
+
if (output === undefined)
|
|
9
|
+
return '';
|
|
10
|
+
try {
|
|
11
|
+
const serialized = JSON.stringify(output);
|
|
12
|
+
return serialized === undefined ? String(output) : serialized;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return String(output);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Maps a `ToolExecutionResult`'s status to the string a delegated-tool caller (this bridge, or
|
|
20
|
+
* gap 3's stdin-tool-result injector in `agent-executor.ts`) reports back as the tool's visible
|
|
21
|
+
* output/failure reason. Exported so both real callers share one mapping rather than drifting.
|
|
22
|
+
*/
|
|
23
|
+
export function resultContent(result) {
|
|
24
|
+
switch (result.status) {
|
|
25
|
+
case 'completed':
|
|
26
|
+
return serializeDelegatedToolOutput(result.output);
|
|
27
|
+
case 'denied':
|
|
28
|
+
return 'Tool execution denied by policy.';
|
|
29
|
+
case 'confirmation-denied':
|
|
30
|
+
return 'Tool execution denied during confirmation.';
|
|
31
|
+
case 'timed-out':
|
|
32
|
+
return 'Tool execution timed out.';
|
|
33
|
+
case 'cancelled':
|
|
34
|
+
return 'Tool execution cancelled.';
|
|
35
|
+
case 'failed':
|
|
36
|
+
return result.error ?? 'Tool execution failed.';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates the transport-neutral bridge used by future ACP-delegate, MCP, or
|
|
41
|
+
* other host protocols. This module does not invent a server transport: a
|
|
42
|
+
* concrete protocol calls this bridge after it has decoded and authenticated a
|
|
43
|
+
* delegated tool request.
|
|
44
|
+
*/
|
|
45
|
+
export function createDelegatedToolBridge(options) {
|
|
46
|
+
const { lifecycle, toolExecutor } = options;
|
|
47
|
+
async function execute(invocation) {
|
|
48
|
+
const { runId, toolUseId, toolId, principal, input } = invocation;
|
|
49
|
+
await lifecycle.emit(runId, {
|
|
50
|
+
event: 'agent',
|
|
51
|
+
data: { type: 'tool_use', id: toolUseId, name: toolId, input },
|
|
52
|
+
});
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const unsubscribeCancel = lifecycle.onCancelRequested(runId, () => controller.abort());
|
|
55
|
+
const abortFromTransport = () => controller.abort();
|
|
56
|
+
if (invocation.signal) {
|
|
57
|
+
if (invocation.signal.aborted)
|
|
58
|
+
controller.abort();
|
|
59
|
+
else
|
|
60
|
+
invocation.signal.addEventListener('abort', abortFromTransport, { once: true });
|
|
61
|
+
}
|
|
62
|
+
const run = { id: runId };
|
|
63
|
+
try {
|
|
64
|
+
try {
|
|
65
|
+
const result = await toolExecutor.execute(principal, run, toolId, input, controller.signal);
|
|
66
|
+
await lifecycle.emit(runId, {
|
|
67
|
+
event: 'agent',
|
|
68
|
+
data: {
|
|
69
|
+
type: 'tool_result',
|
|
70
|
+
toolUseId,
|
|
71
|
+
content: resultContent(result),
|
|
72
|
+
...(result.status === 'completed' ? {} : { isError: true }),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
await lifecycle.emit(runId, {
|
|
79
|
+
event: 'agent',
|
|
80
|
+
data: { type: 'tool_result', toolUseId, content: errorMessage(error), isError: true },
|
|
81
|
+
});
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
unsubscribeCancel();
|
|
87
|
+
invocation.signal?.removeEventListener('abort', abortFromTransport);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { execute };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=delegated-tool-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegated-tool-bridge.js","sourceRoot":"","sources":["../src/delegated-tool-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EventLog` — the durable event-log kernel port (extraction-plan §12 C1).
|
|
3
|
+
*
|
|
4
|
+
* OD's own event durability is split across three tiers today: a ~2000-event
|
|
5
|
+
* in-memory ring (`apps/daemon/src/runtimes/runs.ts` on the researched
|
|
6
|
+
* `arch/server-startserver-endgame` branch), a durable copy written into the
|
|
7
|
+
* product's own `messages.events_json` column, and a best-effort JSONL tail
|
|
8
|
+
* file that nothing ever reads back for replay. Because the durable half
|
|
9
|
+
* lives outside any port, a non-OD consumer that only wires the in-memory
|
|
10
|
+
* ring silently loses in-flight output on a long-run reload. `EventLog` is
|
|
11
|
+
* the fix: a single, storage-agnostic port that any adapter (this package's
|
|
12
|
+
* in-memory reference implementation, or `@jini-ai/sqlite`'s future durable
|
|
13
|
+
* adapter — task 8) can satisfy. Every method returns a `Promise` even
|
|
14
|
+
* though the in-memory implementation resolves synchronously, so a real
|
|
15
|
+
* persistent adapter is a drop-in swap rather than an API break
|
|
16
|
+
* (extraction-plan §2.6: "ports are async-only from day one").
|
|
17
|
+
*/
|
|
18
|
+
/** One durably-ordered record in a run's event log. */
|
|
19
|
+
export interface EventLogEntry<Payload = unknown> {
|
|
20
|
+
/** Monotonic per-run cursor, assigned by the log — never by the caller. */
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly event: string;
|
|
23
|
+
readonly data: Payload;
|
|
24
|
+
/** Epoch-ms wall-clock time the entry was recorded. */
|
|
25
|
+
readonly recordedAt: number;
|
|
26
|
+
}
|
|
27
|
+
export interface EventLogAppendInput<Payload = unknown> {
|
|
28
|
+
readonly runId: string;
|
|
29
|
+
readonly event: string;
|
|
30
|
+
readonly data: Payload;
|
|
31
|
+
/**
|
|
32
|
+
* Optional producer-supplied dedup token. Appending twice with the same
|
|
33
|
+
* `dedupeKey` for the same run returns the original entry unchanged rather
|
|
34
|
+
* than recording a second one — this is what makes at-least-once producers
|
|
35
|
+
* (a retried emit after a timeout, a driver that re-delivers on
|
|
36
|
+
* reconnect) safe to layer on top of an otherwise plain ordered log.
|
|
37
|
+
*/
|
|
38
|
+
readonly dedupeKey?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Result of `EventLog.replay`. `'ok'` and `'unknown-run'` are the two happy
|
|
42
|
+
* paths (a fresh run and a run this log has never heard of are both valid,
|
|
43
|
+
* un-exceptional states). `'replay-gap'` is the case OD's own ring buffer
|
|
44
|
+
* gets wrong today (see the module doc): the requested cursor references
|
|
45
|
+
* events that have already been evicted, so silently returning "whatever is
|
|
46
|
+
* still buffered" would hand the caller a stream with an undetectable hole
|
|
47
|
+
* in it. Making the gap a distinguishable result forces every caller
|
|
48
|
+
* (a transport reconnect handler, a test) to decide explicitly how to
|
|
49
|
+
* recover (full resync, error to the end user, etc.) instead of trusting
|
|
50
|
+
* data that looks contiguous but isn't. `'invalid-cursor'` covers a
|
|
51
|
+
* non-numeric cursor string, which is a caller/transport bug rather than a
|
|
52
|
+
* storage-shape problem and should not be conflated with a real gap.
|
|
53
|
+
*/
|
|
54
|
+
export type EventLogReplayResult<Payload = unknown> = {
|
|
55
|
+
readonly kind: 'ok';
|
|
56
|
+
readonly entries: readonly EventLogEntry<Payload>[];
|
|
57
|
+
/**
|
|
58
|
+
* `true` only when `afterCursor` was `null` (a first-time subscribe) AND this run's
|
|
59
|
+
* earliest entries have already been evicted, so `entries` starts after cursor 1 rather
|
|
60
|
+
* than at the true beginning. Omitted (not `false`) on every non-truncated result, so
|
|
61
|
+
* existing exact-match assertions against untruncated replays are unaffected. This does
|
|
62
|
+
* NOT make a first-time null-cursor replay a `'replay-gap'` — that would break the
|
|
63
|
+
* documented "nothing was ever promised to a caller that never asked" contract below and
|
|
64
|
+
* turn every legitimate first-time subscribe of a long-lived, intentionally-bounded run
|
|
65
|
+
* into a hard error. It exists so a caller that *cares* (a dashboard, a consumer with its
|
|
66
|
+
* own durability expectations) can distinguish "this run only ever had N events" from
|
|
67
|
+
* "this run had more, but some were evicted before I asked" instead of the two being
|
|
68
|
+
* silently indistinguishable on the wire.
|
|
69
|
+
*/
|
|
70
|
+
readonly truncated?: true;
|
|
71
|
+
} | {
|
|
72
|
+
readonly kind: 'replay-gap';
|
|
73
|
+
readonly requestedCursor: string;
|
|
74
|
+
/** The oldest cursor the log can still furnish, or `null` if the run's log is currently empty. */
|
|
75
|
+
readonly oldestAvailableCursor: string | null;
|
|
76
|
+
} | {
|
|
77
|
+
readonly kind: 'invalid-cursor';
|
|
78
|
+
readonly requestedCursor: string;
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: 'unknown-run';
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A replayable, ordered, per-run event log. Kernel port — `@jini-ai/daemon`
|
|
84
|
+
* ships `createInMemoryEventLog` as the reference implementation; a durable
|
|
85
|
+
* adapter (`@jini-ai/sqlite`, task 8) implements the same interface.
|
|
86
|
+
*/
|
|
87
|
+
export interface EventLog {
|
|
88
|
+
/**
|
|
89
|
+
* Appends one event to `input.runId`'s ordered log.
|
|
90
|
+
*
|
|
91
|
+
* @returns The recorded entry (with its assigned cursor `id`), or the
|
|
92
|
+
* original entry unchanged if `input.dedupeKey` matches a prior append.
|
|
93
|
+
*/
|
|
94
|
+
append<Payload>(input: EventLogAppendInput<Payload>): Promise<EventLogEntry<Payload>>;
|
|
95
|
+
/**
|
|
96
|
+
* Returns every entry recorded after `afterCursor` for `runId`, or a
|
|
97
|
+
* distinguishable non-`'ok'` result — see {@link EventLogReplayResult}.
|
|
98
|
+
* `afterCursor: null` means "from the beginning of whatever is still
|
|
99
|
+
* retained" (a first-time subscribe, not a reconnect, so a run whose
|
|
100
|
+
* earliest events were already evicted before this call is not a gap —
|
|
101
|
+
* nothing was ever promised to this caller).
|
|
102
|
+
*/
|
|
103
|
+
replay(runId: string, afterCursor: string | null): Promise<EventLogReplayResult>;
|
|
104
|
+
/** Lists every run for which this log retains durable state. Used by a host at boot to rehydrate its `RunLifecycle` index. */
|
|
105
|
+
listRunIds(): Promise<readonly string[]>;
|
|
106
|
+
/** Discards all retained state for `runId` (e.g. once a terminal run's retention window has passed). */
|
|
107
|
+
drop(runId: string): Promise<void>;
|
|
108
|
+
}
|
|
109
|
+
export interface InMemoryEventLogOptions {
|
|
110
|
+
/**
|
|
111
|
+
* Maximum entries retained per run before the oldest are evicted. Eviction is opt-in: if
|
|
112
|
+
* omitted, retention is unbounded and nothing is ever silently dropped. Pass an explicit
|
|
113
|
+
* value only when bounded memory (or, for `@jini-ai/sqlite`, bounded disk) is a deliberate
|
|
114
|
+
* choice — the caller then owns the tradeoff, rather than inheriting a hidden 2000-entry
|
|
115
|
+
* cap OD's own in-memory ring happened to use.
|
|
116
|
+
*/
|
|
117
|
+
readonly maxEntriesPerRun?: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reference `EventLog` implementation: a per-run FIFO array, optionally capped at
|
|
121
|
+
* `maxEntriesPerRun` (opt-in — see {@link InMemoryEventLogOptions}), functionally a ring
|
|
122
|
+
* buffer once a cap is set (oldest entries evicted once the cap is exceeded) without needing
|
|
123
|
+
* an actual circular-index structure at this scale. This is the in-memory half only — no
|
|
124
|
+
* durable copy — matching this task's scope (a real persistent adapter is `@jini-ai/sqlite`'s
|
|
125
|
+
* job).
|
|
126
|
+
*
|
|
127
|
+
* @param options.maxEntriesPerRun - Retention cap per run, see {@link InMemoryEventLogOptions}.
|
|
128
|
+
* @returns An `EventLog` port implementation.
|
|
129
|
+
* @complexity `append`/`drop` are O(1) amortized (O(k) only on the eviction
|
|
130
|
+
* splice, k = entries over cap, which is normally 1). `replay` is O(n) in
|
|
131
|
+
* the number of retained entries for the run.
|
|
132
|
+
*/
|
|
133
|
+
export declare function createInMemoryEventLog(options?: InMemoryEventLogOptions): EventLog;
|
|
134
|
+
//# sourceMappingURL=event-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.d.ts","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,uDAAuD;AACvD,MAAM,WAAW,aAAa,CAAC,OAAO,GAAG,OAAO;IAC9C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB,CAAC,OAAO,GAAG,OAAO;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,oBAAoB,CAAC,OAAO,GAAG,OAAO,IAC9C;IACE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;IACpD;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,kGAAkG;IAClG,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACtF;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjF,8HAA8H;IAC9H,UAAU,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IACzC,wGAAwG;IACxG,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,QAAQ,CAgFtF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EventLog` — the durable event-log kernel port (extraction-plan §12 C1).
|
|
3
|
+
*
|
|
4
|
+
* OD's own event durability is split across three tiers today: a ~2000-event
|
|
5
|
+
* in-memory ring (`apps/daemon/src/runtimes/runs.ts` on the researched
|
|
6
|
+
* `arch/server-startserver-endgame` branch), a durable copy written into the
|
|
7
|
+
* product's own `messages.events_json` column, and a best-effort JSONL tail
|
|
8
|
+
* file that nothing ever reads back for replay. Because the durable half
|
|
9
|
+
* lives outside any port, a non-OD consumer that only wires the in-memory
|
|
10
|
+
* ring silently loses in-flight output on a long-run reload. `EventLog` is
|
|
11
|
+
* the fix: a single, storage-agnostic port that any adapter (this package's
|
|
12
|
+
* in-memory reference implementation, or `@jini-ai/sqlite`'s future durable
|
|
13
|
+
* adapter — task 8) can satisfy. Every method returns a `Promise` even
|
|
14
|
+
* though the in-memory implementation resolves synchronously, so a real
|
|
15
|
+
* persistent adapter is a drop-in swap rather than an API break
|
|
16
|
+
* (extraction-plan §2.6: "ports are async-only from day one").
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Reference `EventLog` implementation: a per-run FIFO array, optionally capped at
|
|
20
|
+
* `maxEntriesPerRun` (opt-in — see {@link InMemoryEventLogOptions}), functionally a ring
|
|
21
|
+
* buffer once a cap is set (oldest entries evicted once the cap is exceeded) without needing
|
|
22
|
+
* an actual circular-index structure at this scale. This is the in-memory half only — no
|
|
23
|
+
* durable copy — matching this task's scope (a real persistent adapter is `@jini-ai/sqlite`'s
|
|
24
|
+
* job).
|
|
25
|
+
*
|
|
26
|
+
* @param options.maxEntriesPerRun - Retention cap per run, see {@link InMemoryEventLogOptions}.
|
|
27
|
+
* @returns An `EventLog` port implementation.
|
|
28
|
+
* @complexity `append`/`drop` are O(1) amortized (O(k) only on the eviction
|
|
29
|
+
* splice, k = entries over cap, which is normally 1). `replay` is O(n) in
|
|
30
|
+
* the number of retained entries for the run.
|
|
31
|
+
*/
|
|
32
|
+
export function createInMemoryEventLog(options = {}) {
|
|
33
|
+
const maxEntriesPerRun = options.maxEntriesPerRun;
|
|
34
|
+
const runs = new Map();
|
|
35
|
+
function getOrCreateRunLog(runId) {
|
|
36
|
+
let runLog = runs.get(runId);
|
|
37
|
+
if (!runLog) {
|
|
38
|
+
runLog = { entries: [], nextId: 1, dedupeIndex: new Map() };
|
|
39
|
+
runs.set(runId, runLog);
|
|
40
|
+
}
|
|
41
|
+
return runLog;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
async append(input) {
|
|
45
|
+
const runLog = getOrCreateRunLog(input.runId);
|
|
46
|
+
if (input.dedupeKey !== undefined) {
|
|
47
|
+
const existing = runLog.dedupeIndex.get(input.dedupeKey);
|
|
48
|
+
if (existing) {
|
|
49
|
+
return existing;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const entry = {
|
|
53
|
+
id: String(runLog.nextId),
|
|
54
|
+
event: input.event,
|
|
55
|
+
data: input.data,
|
|
56
|
+
recordedAt: Date.now(),
|
|
57
|
+
};
|
|
58
|
+
runLog.nextId += 1;
|
|
59
|
+
runLog.entries.push(entry);
|
|
60
|
+
if (input.dedupeKey !== undefined) {
|
|
61
|
+
runLog.dedupeIndex.set(input.dedupeKey, entry);
|
|
62
|
+
}
|
|
63
|
+
if (maxEntriesPerRun !== undefined && runLog.entries.length > maxEntriesPerRun) {
|
|
64
|
+
runLog.entries.splice(0, runLog.entries.length - maxEntriesPerRun);
|
|
65
|
+
}
|
|
66
|
+
return entry;
|
|
67
|
+
},
|
|
68
|
+
async replay(runId, afterCursor) {
|
|
69
|
+
const runLog = runs.get(runId);
|
|
70
|
+
if (!runLog) {
|
|
71
|
+
return { kind: 'unknown-run' };
|
|
72
|
+
}
|
|
73
|
+
if (afterCursor === null) {
|
|
74
|
+
const oldest = runLog.entries[0];
|
|
75
|
+
const truncated = oldest !== undefined && Number(oldest.id) > 1;
|
|
76
|
+
return {
|
|
77
|
+
kind: 'ok',
|
|
78
|
+
entries: runLog.entries.slice(),
|
|
79
|
+
...(truncated ? { truncated: true } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const afterCursorNum = Number(afterCursor);
|
|
83
|
+
if (!Number.isFinite(afterCursorNum)) {
|
|
84
|
+
return { kind: 'invalid-cursor', requestedCursor: afterCursor };
|
|
85
|
+
}
|
|
86
|
+
const oldestRetained = runLog.entries[0];
|
|
87
|
+
const oldestRetainedId = oldestRetained ? Number(oldestRetained.id) : runLog.nextId;
|
|
88
|
+
if (afterCursorNum < oldestRetainedId - 1) {
|
|
89
|
+
return {
|
|
90
|
+
kind: 'replay-gap',
|
|
91
|
+
requestedCursor: afterCursor,
|
|
92
|
+
oldestAvailableCursor: oldestRetained ? oldestRetained.id : null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
kind: 'ok',
|
|
97
|
+
entries: runLog.entries.filter((entry) => Number(entry.id) > afterCursorNum),
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
async listRunIds() {
|
|
101
|
+
return Array.from(runs.keys()).sort();
|
|
102
|
+
},
|
|
103
|
+
async drop(runId) {
|
|
104
|
+
runs.delete(runId);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=event-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-log.js","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module frontend-capability-tools
|
|
3
|
+
*
|
|
4
|
+
* The half of frontend control that makes it *reachable*. `FrontendSessionRegistry` can route a
|
|
5
|
+
* call to the surface bound to a run, but a registry is not something an agent can call — only a
|
|
6
|
+
* registered tool is. This module projects a frontend's capability manifest into
|
|
7
|
+
* `{descriptor, handler, policy}` triples a host registers into the same `ToolRegistry` its
|
|
8
|
+
* server-side tools live in.
|
|
9
|
+
*
|
|
10
|
+
* That projection is the entire point, and it is deliberately the ONLY door. Each handler is
|
|
11
|
+
* invoked by `ToolExecutor` *after* authorization, confirmation, timeout, cancellation, output
|
|
12
|
+
* truncation and audit have already run, and its whole body is "route this call and await the
|
|
13
|
+
* answer". A route that called `registry.invoke` directly would reach the same tab having skipped
|
|
14
|
+
* every one of those, which is precisely the second, weaker execution path this layering exists to
|
|
15
|
+
* prevent. There is no reason to add one: anything that can execute a tool can execute these.
|
|
16
|
+
*
|
|
17
|
+
* **Typed structurally, not against the frontend packages.** A capability spec here is anything
|
|
18
|
+
* with an `id` and a `description` — `@jini-ai/agentic`'s `CapabilityDef` satisfies it without
|
|
19
|
+
* knowing this module exists. Importing that type would point a server package at the browser
|
|
20
|
+
* vocabulary and invert the one-way edge the engine's layering depends on; the same reasoning as
|
|
21
|
+
* `@jini-ai/core`'s `RunRef`.
|
|
22
|
+
*
|
|
23
|
+
* **Every registration carries a timeout by default.** A surface that never answers — a
|
|
24
|
+
* backgrounded tab, a closed laptop, a tab killed between the call and the reply — is the normal
|
|
25
|
+
* case here, not the exotic one, and `FrontendSessionRegistry.invoke` only rejects when its
|
|
26
|
+
* caller's signal aborts. Without a `timeoutMs` on the descriptor that signal never fires and the
|
|
27
|
+
* run waits forever, so the default is applied rather than left to each host to remember.
|
|
28
|
+
*/
|
|
29
|
+
import type { ToolPolicy, ToolRegistration } from '@jini-ai/core';
|
|
30
|
+
import type { FrontendSessionRegistry } from './frontend-session-registry.js';
|
|
31
|
+
/**
|
|
32
|
+
* Deny-by-default `ToolPolicy` for frontend capabilities, matching
|
|
33
|
+
* `terminal-session.ts`'s `denyAllTerminalCreatePolicy` and `db-ops.ts`'s `denyAllDaemonDbPolicy`.
|
|
34
|
+
*
|
|
35
|
+
* A permissive default was rejected for the same reason as those: these tools drive a real user's
|
|
36
|
+
* real screen — clicking controls, filling fields, navigating away from what they were reading —
|
|
37
|
+
* and a host must opt into that explicitly, not acquire it merely by registering a manifest.
|
|
38
|
+
*/
|
|
39
|
+
export declare const denyAllFrontendCapabilityPolicy: ToolPolicy;
|
|
40
|
+
/**
|
|
41
|
+
* How long a handler waits for the surface before `ToolExecutor` aborts it and reports
|
|
42
|
+
* `'timed-out'`. Generous enough for a slow render or a busy main thread, short enough that a tab
|
|
43
|
+
* that is never coming back fails the tool call rather than the whole run.
|
|
44
|
+
*/
|
|
45
|
+
export declare const DEFAULT_FRONTEND_CAPABILITY_TIMEOUT_MS = 30000;
|
|
46
|
+
/**
|
|
47
|
+
* The minimum a manifest entry must describe. Structural on purpose — see the module doc.
|
|
48
|
+
*
|
|
49
|
+
* Every field here exists on `@jini-ai/agentic`'s `CapabilityDef`, so a manifest satisfies this
|
|
50
|
+
* without conversion. The first version of this type accepted only `id`, `description`, and
|
|
51
|
+
* `requiresConfirmation` and silently dropped the rest, which made the projection **lossy**: a
|
|
52
|
+
* registered capability could no longer say what arguments it took or how dangerous it was, so
|
|
53
|
+
* nothing downstream could describe it to an agent or reason about it. Discovery cannot be correct
|
|
54
|
+
* on top of a projection that has already thrown the answer away.
|
|
55
|
+
*/
|
|
56
|
+
export interface FrontendCapabilitySpec {
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly description: string;
|
|
59
|
+
/** Surfaced on the descriptor so `ToolExecutor`'s confirmation gate enforces it. */
|
|
60
|
+
readonly requiresConfirmation?: boolean;
|
|
61
|
+
/** Carried onto `ToolDescriptor.inputSchema` so a discovery surface can state the arguments. */
|
|
62
|
+
readonly inputSchema?: unknown;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Deliberately NOT projected yet: `CapabilityDef`'s `risk` and `surface`.
|
|
66
|
+
*
|
|
67
|
+
* They belong on whatever a discovery surface returns — `risk` labels how dangerous a capability
|
|
68
|
+
* is, and `surface` says whether "no frontend is bound" is a temporary condition or a permanent
|
|
69
|
+
* refusal, which decides whether a caller should wait or give up. But nothing reads them until
|
|
70
|
+
* that surface exists, and a public type carrying fields no code consumes is indistinguishable
|
|
71
|
+
* from one carrying fields whose consumer was deleted. They graduate here when the catalog that
|
|
72
|
+
* needs them lands, together with the decision about whether they belong on `ToolDescriptor` (and
|
|
73
|
+
* so on `ToolRegistry.list()`) or are joined from the host's manifest.
|
|
74
|
+
*
|
|
75
|
+
* A host still loses nothing today: a `CapabilityDef` satisfies {@link FrontendCapabilitySpec}
|
|
76
|
+
* structurally with those fields intact on the value it already holds.
|
|
77
|
+
*/
|
|
78
|
+
export interface CreateFrontendCapabilityRegistrationsOptions {
|
|
79
|
+
readonly registry: FrontendSessionRegistry;
|
|
80
|
+
/** The capabilities to expose. Ids become tool ids verbatim, so an agent names `page.click`. */
|
|
81
|
+
readonly capabilities: readonly FrontendCapabilitySpec[];
|
|
82
|
+
/** @default {@link denyAllFrontendCapabilityPolicy} — see its doc for why a permissive default was rejected. */
|
|
83
|
+
readonly policy?: ToolPolicy;
|
|
84
|
+
/** @default {@link DEFAULT_FRONTEND_CAPABILITY_TIMEOUT_MS} — see the module doc for why this is defaulted rather than optional. */
|
|
85
|
+
readonly timeoutMs?: number;
|
|
86
|
+
/** Passed through to every descriptor. Frontend output is untrusted text; a host that reads it back into a prompt should bound it. */
|
|
87
|
+
readonly maxOutputBytes?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Builds one `ToolRegistration` per capability, each routing through `registry.invoke` on the
|
|
91
|
+
* calling run's bound surface.
|
|
92
|
+
*
|
|
93
|
+
* @param options - The registry to route through, the manifest to expose, and the gate to apply.
|
|
94
|
+
* @returns Registrations in manifest order, ready to hand to a `ToolRegistry` (or to
|
|
95
|
+
* `@jini-ai/node-host`'s `toolRegistrations` config option).
|
|
96
|
+
* @throws Never. A duplicate id is reported by `ToolRegistry.register` at registration time,
|
|
97
|
+
* naming the id — this function does not pre-empt that with a second, differently-worded error.
|
|
98
|
+
*
|
|
99
|
+
* @complexity O(n) in `capabilities`; each handler is O(1) plus the round trip to the surface.
|
|
100
|
+
*/
|
|
101
|
+
export declare function createFrontendCapabilityRegistrations(options: CreateFrontendCapabilityRegistrationsOptions): readonly ToolRegistration[];
|
|
102
|
+
//# sourceMappingURL=frontend-capability-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend-capability-tools.d.ts","sourceRoot":"","sources":["../src/frontend-capability-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,UAE7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,QAAS,CAAC;AAE7D;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IACxC,gGAAgG;IAChG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,4CAA4C;IAC3D,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,gGAAgG;IAChG,QAAQ,CAAC,YAAY,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzD,gHAAgH;IAChH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,mIAAmI;IACnI,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sIAAsI;IACtI,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAoBD;;;;;;;;;;;GAWG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,4CAA4C,GACpD,SAAS,gBAAgB,EAAE,CAkC7B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deny-by-default `ToolPolicy` for frontend capabilities, matching
|
|
3
|
+
* `terminal-session.ts`'s `denyAllTerminalCreatePolicy` and `db-ops.ts`'s `denyAllDaemonDbPolicy`.
|
|
4
|
+
*
|
|
5
|
+
* A permissive default was rejected for the same reason as those: these tools drive a real user's
|
|
6
|
+
* real screen — clicking controls, filling fields, navigating away from what they were reading —
|
|
7
|
+
* and a host must opt into that explicitly, not acquire it merely by registering a manifest.
|
|
8
|
+
*/
|
|
9
|
+
export const denyAllFrontendCapabilityPolicy = {
|
|
10
|
+
authorize: () => 'deny',
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* How long a handler waits for the surface before `ToolExecutor` aborts it and reports
|
|
14
|
+
* `'timed-out'`. Generous enough for a slow render or a busy main thread, short enough that a tab
|
|
15
|
+
* that is never coming back fails the tool call rather than the whole run.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_FRONTEND_CAPABILITY_TIMEOUT_MS = 30_000;
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes a tool's `unknown` input into the record `FrontendSessionRegistry.invoke` takes.
|
|
20
|
+
*
|
|
21
|
+
* A missing input is `{}` — several capabilities legitimately take no arguments, and forcing a
|
|
22
|
+
* caller to send an empty object to use them would be noise. Anything else non-object is a caller
|
|
23
|
+
* error reported by name, not silently coerced into `{}`, which would turn "you sent a string"
|
|
24
|
+
* into the much more confusing "the capability says a required field is missing".
|
|
25
|
+
*/
|
|
26
|
+
function toCapabilityInput(capabilityId, input) {
|
|
27
|
+
if (input === undefined || input === null)
|
|
28
|
+
return {};
|
|
29
|
+
if (typeof input !== 'object' || Array.isArray(input)) {
|
|
30
|
+
throw new Error(`${capabilityId}: input must be a JSON object, received ${Array.isArray(input) ? 'array' : typeof input}`);
|
|
31
|
+
}
|
|
32
|
+
return input;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds one `ToolRegistration` per capability, each routing through `registry.invoke` on the
|
|
36
|
+
* calling run's bound surface.
|
|
37
|
+
*
|
|
38
|
+
* @param options - The registry to route through, the manifest to expose, and the gate to apply.
|
|
39
|
+
* @returns Registrations in manifest order, ready to hand to a `ToolRegistry` (or to
|
|
40
|
+
* `@jini-ai/node-host`'s `toolRegistrations` config option).
|
|
41
|
+
* @throws Never. A duplicate id is reported by `ToolRegistry.register` at registration time,
|
|
42
|
+
* naming the id — this function does not pre-empt that with a second, differently-worded error.
|
|
43
|
+
*
|
|
44
|
+
* @complexity O(n) in `capabilities`; each handler is O(1) plus the round trip to the surface.
|
|
45
|
+
*/
|
|
46
|
+
export function createFrontendCapabilityRegistrations(options) {
|
|
47
|
+
const { registry, capabilities, policy = denyAllFrontendCapabilityPolicy, timeoutMs = DEFAULT_FRONTEND_CAPABILITY_TIMEOUT_MS, maxOutputBytes, } = options;
|
|
48
|
+
return capabilities.map((capability) => ({
|
|
49
|
+
descriptor: {
|
|
50
|
+
id: capability.id,
|
|
51
|
+
description: capability.description,
|
|
52
|
+
timeoutMs,
|
|
53
|
+
...(capability.requiresConfirmation !== undefined
|
|
54
|
+
? { requiresConfirmation: capability.requiresConfirmation }
|
|
55
|
+
: {}),
|
|
56
|
+
// Absent rather than `undefined`: a descriptor that *has* an `inputSchema` key holding
|
|
57
|
+
// `undefined` is indistinguishable from one describing a schemaless tool once serialized,
|
|
58
|
+
// and a catalog would render "takes no arguments" for a capability that takes several.
|
|
59
|
+
...(capability.inputSchema !== undefined ? { inputSchema: capability.inputSchema } : {}),
|
|
60
|
+
...(maxOutputBytes !== undefined ? { maxOutputBytes } : {}),
|
|
61
|
+
},
|
|
62
|
+
policy,
|
|
63
|
+
// `ctx.signal` is `ToolExecutor`'s own timeout/cancel signal, so the descriptor's `timeoutMs`
|
|
64
|
+
// and a cancelled run both reach the pending invocation without this module owning a timer.
|
|
65
|
+
handler: async (ctx) => registry.invoke(ctx.run.id, capability.id, toCapabilityInput(capability.id, ctx.input), ctx.signal),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=frontend-capability-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend-capability-tools.js","sourceRoot":"","sources":["../src/frontend-capability-tools.ts"],"names":[],"mappings":"AA+BA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAe;IACzD,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,MAAM,CAAC;AAgD7D;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,YAAoB,EAAE,KAAc;IAC7D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CACb,GAAG,YAAY,2CAA2C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAC1G,CAAC;IACJ,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qCAAqC,CACnD,OAAqD;IAErD,MAAM,EACJ,QAAQ,EACR,YAAY,EACZ,MAAM,GAAG,+BAA+B,EACxC,SAAS,GAAG,sCAAsC,EAClD,cAAc,GACf,GAAG,OAAO,CAAC;IAEZ,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACvC,UAAU,EAAE;YACV,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,WAAW,EAAE,UAAU,CAAC,WAAW;YACnC,SAAS;YACT,GAAG,CAAC,UAAU,CAAC,oBAAoB,KAAK,SAAS;gBAC/C,CAAC,CAAC,EAAE,oBAAoB,EAAE,UAAU,CAAC,oBAAoB,EAAE;gBAC3D,CAAC,CAAC,EAAE,CAAC;YACP,uFAAuF;YACvF,0FAA0F;YAC1F,uFAAuF;YACvF,GAAG,CAAC,UAAU,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D;QACD,MAAM;QACN,8FAA8F;QAC9F,4FAA4F;QAC5F,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CACrB,QAAQ,CAAC,MAAM,CACb,GAAG,CAAC,GAAG,CAAC,EAAE,EACV,UAAU,CAAC,EAAE,EACb,iBAAiB,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,EAC3C,GAAG,CAAC,MAAM,CACX;KACJ,CAAC,CAAC,CAAC;AACN,CAAC"}
|