@anureo/sdk 0.0.0-stage → 0.3.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 (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +185 -2
  3. package/package.json +66 -4
  4. package/src/adapter/identity.d.ts +41 -0
  5. package/src/adapter/identity.js +71 -0
  6. package/src/client-options.d.ts +71 -0
  7. package/src/client-options.js +0 -0
  8. package/src/client.d.ts +106 -0
  9. package/src/client.js +254 -0
  10. package/src/diagnostics/errors.d.ts +15 -0
  11. package/src/diagnostics/errors.js +59 -0
  12. package/src/diagnostics/events.d.ts +46 -0
  13. package/src/diagnostics/events.js +47 -0
  14. package/src/diagnostics.d.ts +20 -0
  15. package/src/diagnostics.js +56 -0
  16. package/src/errors.d.ts +48 -0
  17. package/src/errors.js +63 -0
  18. package/src/index.d.ts +27 -0
  19. package/src/index.js +23 -0
  20. package/src/lifecycle/approval.d.ts +16 -0
  21. package/src/lifecycle/approval.js +18 -0
  22. package/src/lifecycle/deferred.d.ts +10 -0
  23. package/src/lifecycle/deferred.js +12 -0
  24. package/src/lifecycle/errors.d.ts +18 -0
  25. package/src/lifecycle/errors.js +45 -0
  26. package/src/lifecycle/events.d.ts +35 -0
  27. package/src/lifecycle/events.js +141 -0
  28. package/src/lifecycle/execution-observation.d.ts +33 -0
  29. package/src/lifecycle/execution-observation.js +0 -0
  30. package/src/lifecycle/immutable.d.ts +12 -0
  31. package/src/lifecycle/immutable.js +28 -0
  32. package/src/lifecycle/input.d.ts +9 -0
  33. package/src/lifecycle/input.js +19 -0
  34. package/src/lifecycle/interactions.d.ts +129 -0
  35. package/src/lifecycle/interactions.js +282 -0
  36. package/src/lifecycle/limits.d.ts +7 -0
  37. package/src/lifecycle/limits.js +13 -0
  38. package/src/lifecycle/result.d.ts +77 -0
  39. package/src/lifecycle/result.js +213 -0
  40. package/src/lifecycle/value.d.ts +4 -0
  41. package/src/lifecycle/value.js +5 -0
  42. package/src/ports.d.ts +224 -0
  43. package/src/ports.js +0 -0
  44. package/src/privacy/file-change-limits.d.ts +20 -0
  45. package/src/privacy/file-change-limits.js +124 -0
  46. package/src/privacy/secrets.d.ts +205 -0
  47. package/src/privacy/secrets.js +564 -0
  48. package/src/process.d.ts +92 -0
  49. package/src/process.js +331 -0
  50. package/src/queue.d.ts +143 -0
  51. package/src/queue.js +266 -0
  52. package/src/request.d.ts +28 -0
  53. package/src/request.js +81 -0
  54. package/src/requests/guard.d.ts +15 -0
  55. package/src/requests/guard.js +93 -0
  56. package/src/session.d.ts +459 -0
  57. package/src/session.js +1135 -0
  58. package/src/structured-output/adapter.d.ts +8 -0
  59. package/src/structured-output/adapter.js +54 -0
  60. package/src/structured-output/bridge.d.ts +81 -0
  61. package/src/structured-output/bridge.js +274 -0
  62. package/src/structured-output/errors.d.ts +17 -0
  63. package/src/structured-output/errors.js +31 -0
  64. package/src/structured-output/limits.d.ts +19 -0
  65. package/src/structured-output/limits.js +0 -0
  66. package/src/structured-output/schema.d.ts +20 -0
  67. package/src/structured-output/schema.js +122 -0
  68. package/src/structured-output/session.d.ts +329 -0
  69. package/src/structured-output/session.js +846 -0
  70. package/src/structured-output/stdio.mjs +83 -0
  71. package/src/structured-output/tool.d.ts +38 -0
  72. package/src/structured-output/tool.js +34 -0
  73. package/src/turn.d.ts +75 -0
  74. package/src/turn.js +95 -0
  75. package/src/types.d.ts +62 -0
  76. package/src/types.js +0 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Internal package entry resolved by OutputBridgeManager.create and launched by the native MCP
3
+ * client over stdio. ANUREO_OUTPUT_BRIDGE_URL and ANUREO_OUTPUT_BRIDGE_AUTH identify the private
4
+ * session lease. Forward tool listings and calls over HTTP. Begin shutdown on stdin end, SIGTERM,
5
+ * SIGINT, parent loss or failed upstream ping. Startup failures write a safe code to stderr.
6
+ */
7
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
8
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
9
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
10
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
11
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
12
+
13
+ const parent = process.ppid
14
+ const upstream = new Client({ name: 'anureo-output-proxy', version: '1.0.0' })
15
+ const downstream = new Server({ name: 'anureo_output', version: '1.0.0' }, { capabilities: { tools: {} } })
16
+ let closing = false
17
+ let ready = false
18
+ let checking = false
19
+
20
+ /**
21
+ * Stop heartbeat, settle both MCP peer closes (ignoring their rejections), then exit with code.
22
+ * Stdin-end and signal listeners start cleanup. checkLiveness and startup failure await it.
23
+ * The first call selects the exit code (default 0). Later calls return without waiting.
24
+ */
25
+ async function close(code = 0) {
26
+ if (closing) return
27
+ closing = true
28
+ clearInterval(heartbeat)
29
+ await Promise.allSettled([downstream.close(), upstream.close()])
30
+ process.exit(code)
31
+ }
32
+
33
+ /**
34
+ * The 2-second heartbeat invokes this without awaiting. Check owner identity even during startup,
35
+ * then ping only after upstream connects, allowing 3 seconds for a reply. Owner loss requests
36
+ * exit with 0 and ping failure requests exit with 1. Skip ticks while a ping is pending.
37
+ */
38
+ async function checkLiveness() {
39
+ if (closing || checking) return
40
+ if (process.ppid !== parent || process.ppid === 1) {
41
+ await close()
42
+ return
43
+ }
44
+ if (!ready) return
45
+ checking = true
46
+ try { await upstream.ping({ timeout: 3000 }) } catch { await close(1) }
47
+ finally { checking = false }
48
+ }
49
+
50
+ /**
51
+ * Connect upstream with the required lease URL/credential before attaching downstream stdio.
52
+ * Upstream initialization uses a 10-second request timeout. Connection enables heartbeat pings. The SDK
53
+ * invokes the registered downstream handlers, which return upstream tool lists/results or propagate rejection
54
+ * to protocol handling. Configuration/connection failures reject to the module entry for safe stderr
55
+ * reporting and close(1).
56
+ */
57
+ async function connectProxy() {
58
+ const url = process.env.ANUREO_OUTPUT_BRIDGE_URL
59
+ const credential = process.env.ANUREO_OUTPUT_BRIDGE_AUTH
60
+ if (!url || !credential) throw new Error('configuration')
61
+ const transport = new StreamableHTTPClientTransport(new URL(url), {
62
+ requestInit: { headers: { Authorization: `Bearer ${credential}` } },
63
+ })
64
+ await upstream.connect(transport, { timeout: 10000 })
65
+ ready = true
66
+ downstream.setRequestHandler(ListToolsRequestSchema, () => upstream.listTools())
67
+ downstream.setRequestHandler(CallToolRequestSchema, request => upstream.callTool(request.params))
68
+ await downstream.connect(new StdioServerTransport())
69
+ }
70
+
71
+ // Register shutdown before connecting so startup is covered by the owner and signal checks.
72
+ const heartbeat = setInterval(() => { void checkLiveness() }, 2000)
73
+ heartbeat.unref()
74
+ process.stdin.once('end', () => { void close() })
75
+ process.once('SIGTERM', () => { void close() })
76
+ process.once('SIGINT', () => { void close() })
77
+
78
+ try {
79
+ await connectProxy()
80
+ } catch {
81
+ process.stderr.write('STRUCTURED_OUTPUT_BRIDGE_FAILED\n')
82
+ await close(1)
83
+ }
@@ -0,0 +1,38 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ /**
3
+ * Internal reserved MCP identity for bridge configuration and fallback instructions.
4
+ * OutputSession.prepareBridge rejects configurations that already claim this name before lease
5
+ * allocation. OutputBridgeManager.create assigns it to the injected server configuration and
6
+ * fallbackInput names it in the model's submission instructions.
7
+ */
8
+ export declare const OUTPUT_SERVER_NAME = "anureo_output";
9
+ /**
10
+ * Wire contract exposed to MCP clients by the ListTools handler registered in createToolServer.
11
+ * Pass the current submission's exact outputId and its candidate JSON value. OutputSession.submitValue
12
+ * checks the envelope and ID before submitCandidate validates the value against the submission schema.
13
+ * Acceptance is provisional until terminal publication rechecks the value and its privacy projection.
14
+ */
15
+ export declare const OUTPUT_TOOL: {
16
+ name: string;
17
+ description: string;
18
+ inputSchema: {
19
+ type: "object";
20
+ properties: {
21
+ outputId: {
22
+ type: string;
23
+ minLength: number;
24
+ };
25
+ value: {};
26
+ };
27
+ required: string[];
28
+ additionalProperties: boolean;
29
+ };
30
+ };
31
+ /**
32
+ * Format a caller-supplied safe code, optional issues and recovery guidance as an MCP error result.
33
+ * The CallTool handler registered in createToolServer returns this for expired leases or unknown
34
+ * tools. OutputSession.submitValue returns it for envelope and candidate rejections. Supply safe,
35
+ * JSON-serializable details because they go directly to the client. Serialization failures throw.
36
+ * Falsy message and issues arguments are omitted from the JSON text.
37
+ */
38
+ export declare function toolError(code: string, issues?: unknown, message?: string): CallToolResult;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Internal reserved MCP identity for bridge configuration and fallback instructions.
3
+ * OutputSession.prepareBridge rejects configurations that already claim this name before lease
4
+ * allocation. OutputBridgeManager.create assigns it to the injected server configuration and
5
+ * fallbackInput names it in the model's submission instructions.
6
+ */
7
+ export const OUTPUT_SERVER_NAME = 'anureo_output';
8
+ /**
9
+ * Wire contract exposed to MCP clients by the ListTools handler registered in createToolServer.
10
+ * Pass the current submission's exact outputId and its candidate JSON value. OutputSession.submitValue
11
+ * checks the envelope and ID before submitCandidate validates the value against the submission schema.
12
+ * Acceptance is provisional until terminal publication rechecks the value and its privacy projection.
13
+ */
14
+ export const OUTPUT_TOOL = {
15
+ name: 'submit_result',
16
+ description: 'Submit the final structured result for the current task. Use its outputId and satisfy its JSON Schema. After acceptance, end the task. On validation errors, correct the value and submit again.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: { outputId: { type: 'string', minLength: 1 }, value: {} },
20
+ required: ['outputId', 'value'],
21
+ additionalProperties: false,
22
+ },
23
+ };
24
+ /**
25
+ * Format a caller-supplied safe code, optional issues and recovery guidance as an MCP error result.
26
+ * The CallTool handler registered in createToolServer returns this for expired leases or unknown
27
+ * tools. OutputSession.submitValue returns it for envelope and candidate rejections. Supply safe,
28
+ * JSON-serializable details because they go directly to the client. Serialization failures throw.
29
+ * Falsy message and issues arguments are omitted from the JSON text.
30
+ */
31
+ export function toolError(code, issues, message) {
32
+ const text = JSON.stringify({ code, ...(message ? { message } : {}), ...(issues ? { issues } : {}) });
33
+ return { isError: true, content: [{ type: 'text', text }] };
34
+ }
package/src/turn.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ import type { AhpEvent } from '@anureo/protocol';
2
+ import type { ExecutionObservation } from "./lifecycle/execution-observation.js";
3
+ import type { EventSubscriptionOptions, Input, RequestOptions, TurnResult } from "./types.js";
4
+ /** Internal Session-to-Turn port exposed here for constructor typing. */
5
+ export type { ExecutionObservation } from "./lifecycle/execution-observation.js";
6
+ /**
7
+ * Observe one execution's live events and retained terminal result, and explicitly steer or interrupt it.
8
+ * Get a handle from Session.submit or Session.review once the input or review is associated with
9
+ * an execution. Use observe to create an independently disposable sibling.
10
+ */
11
+ export declare class Turn {
12
+ private readonly execution;
13
+ /**
14
+ * Internal construction from the shared execution port. Session.submitExecution calls this when
15
+ * association resolves, and Turn.observe calls it to create a sibling with its own cancellation.
16
+ * The shared execution may already be terminal.
17
+ */
18
+ constructor(execution: ExecutionObservation);
19
+ /** The stable execution ID shared by sibling handles, available after disposal. */
20
+ get id(): string;
21
+ /**
22
+ * Subscribe immediately to future events for this execution without replay. Invalid queue limits
23
+ * throw INVALID_PARAMS synchronously. Buffered events drain before overflow or stream failure
24
+ * rejects reads. Exhaust or return the iterator to release it. While the subscription is live,
25
+ * abort or disposal completes pending reads and skips buffered events. An already ended stream's
26
+ * completion or failure is still observable, including when subscribing through a disposed handle.
27
+ */
28
+ events(options?: EventSubscriptionOptions): AsyncIterable<AhpEvent>;
29
+ /**
30
+ * Await the shared frozen terminal snapshot, including failed or interrupted status values.
31
+ * Reject with TURN_DISPOSED when called after disposal, or propagate observation closure and wait
32
+ * failures. Caller abort and the deadline (30 seconds by default) affect only this wait.
33
+ * Disposing the handle rejects its pending result waits with REQUEST_ABORTED.
34
+ */
35
+ result(wait?: RequestOptions): Promise<TurnResult>;
36
+ /**
37
+ * Append text or input blocks to this exact execution. Requires atomic turn matching support.
38
+ * Resolve after the native control receipt is validated. Use result to await execution completion.
39
+ * Normalization, admission, dispatch and wait failures reject. Omit the timeout to use the
40
+ * session's request deadline (30 seconds by default). A disposed handle throws TURN_DISPOSED
41
+ * synchronously. Wait cancellation leaves admitted work alive. Disposal leaves an already invoked
42
+ * control wait active.
43
+ */
44
+ steer(options: {
45
+ input: Input;
46
+ }, wait?: RequestOptions): Promise<void>;
47
+ /**
48
+ * Request interruption of this execution. Requires adapter support for turn interruption.
49
+ * Resolve after the native control receipt is validated. Use result to await terminal status.
50
+ * Admission, dispatch and wait failures reject. Omit the timeout to use the session's request
51
+ * deadline (30 seconds by default). A disposed handle throws TURN_DISPOSED synchronously. Wait
52
+ * cancellation leaves admitted work alive. Disposal leaves an already invoked control wait active.
53
+ */
54
+ interrupt(wait?: RequestOptions): Promise<void>;
55
+ /**
56
+ * Create an independently disposable handle for the same execution, including a retained terminal
57
+ * result when available. Throws TURN_DISPOSED synchronously if this handle is closed.
58
+ */
59
+ observe(): Turn;
60
+ /**
61
+ * Idempotently cancel this handle's live subscriptions and pending result waits before resolving.
62
+ * Sibling handles and explicit control waits retain their own lifetimes. Call interrupt to request
63
+ * native interruption while the handle is open.
64
+ */
65
+ dispose(): Promise<void>;
66
+ private disposed;
67
+ private readonly observationController;
68
+ /**
69
+ * events and result combine caller cancellation with this handle's disposal signal, so either can
70
+ * end a local observation without affecting siblings. Explicit control methods do not use it.
71
+ */
72
+ private observationSignal;
73
+ /** steer, interrupt and observe throw TURN_DISPOSED here before invoking a port or creating a sibling. */
74
+ private assertOpen;
75
+ }
package/src/turn.js ADDED
@@ -0,0 +1,95 @@
1
+ import { HarnessSdkError } from "./errors.js";
2
+ import { waitForRequest } from "./request.js";
3
+ /**
4
+ * Observe one execution's live events and retained terminal result, and explicitly steer or interrupt it.
5
+ * Get a handle from Session.submit or Session.review once the input or review is associated with
6
+ * an execution. Use observe to create an independently disposable sibling.
7
+ */
8
+ export class Turn {
9
+ execution;
10
+ /**
11
+ * Internal construction from the shared execution port. Session.submitExecution calls this when
12
+ * association resolves, and Turn.observe calls it to create a sibling with its own cancellation.
13
+ * The shared execution may already be terminal.
14
+ */
15
+ constructor(execution) {
16
+ this.execution = execution;
17
+ }
18
+ /** The stable execution ID shared by sibling handles, available after disposal. */
19
+ get id() { return this.execution.id; }
20
+ /**
21
+ * Subscribe immediately to future events for this execution without replay. Invalid queue limits
22
+ * throw INVALID_PARAMS synchronously. Buffered events drain before overflow or stream failure
23
+ * rejects reads. Exhaust or return the iterator to release it. While the subscription is live,
24
+ * abort or disposal completes pending reads and skips buffered events. An already ended stream's
25
+ * completion or failure is still observable, including when subscribing through a disposed handle.
26
+ */
27
+ events(options = {}) {
28
+ return this.execution.events.subscribe({ ...options, signal: this.observationSignal(options.signal) });
29
+ }
30
+ /**
31
+ * Await the shared frozen terminal snapshot, including failed or interrupted status values.
32
+ * Reject with TURN_DISPOSED when called after disposal, or propagate observation closure and wait
33
+ * failures. Caller abort and the deadline (30 seconds by default) affect only this wait.
34
+ * Disposing the handle rejects its pending result waits with REQUEST_ABORTED.
35
+ */
36
+ result(wait = {}) {
37
+ if (this.disposed)
38
+ return Promise.reject(new HarnessSdkError('TURN_DISPOSED'));
39
+ return waitForRequest(() => this.execution.result, { ...wait, signal: this.observationSignal(wait.signal) });
40
+ }
41
+ /**
42
+ * Append text or input blocks to this exact execution. Requires atomic turn matching support.
43
+ * Resolve after the native control receipt is validated. Use result to await execution completion.
44
+ * Normalization, admission, dispatch and wait failures reject. Omit the timeout to use the
45
+ * session's request deadline (30 seconds by default). A disposed handle throws TURN_DISPOSED
46
+ * synchronously. Wait cancellation leaves admitted work alive. Disposal leaves an already invoked
47
+ * control wait active.
48
+ */
49
+ steer(options, wait) {
50
+ this.assertOpen();
51
+ return this.execution.steer(options.input, wait);
52
+ }
53
+ /**
54
+ * Request interruption of this execution. Requires adapter support for turn interruption.
55
+ * Resolve after the native control receipt is validated. Use result to await terminal status.
56
+ * Admission, dispatch and wait failures reject. Omit the timeout to use the session's request
57
+ * deadline (30 seconds by default). A disposed handle throws TURN_DISPOSED synchronously. Wait
58
+ * cancellation leaves admitted work alive. Disposal leaves an already invoked control wait active.
59
+ */
60
+ interrupt(wait) {
61
+ this.assertOpen();
62
+ return this.execution.interrupt(wait);
63
+ }
64
+ /**
65
+ * Create an independently disposable handle for the same execution, including a retained terminal
66
+ * result when available. Throws TURN_DISPOSED synchronously if this handle is closed.
67
+ */
68
+ observe() {
69
+ this.assertOpen();
70
+ return new Turn(this.execution);
71
+ }
72
+ /**
73
+ * Idempotently cancel this handle's live subscriptions and pending result waits before resolving.
74
+ * Sibling handles and explicit control waits retain their own lifetimes. Call interrupt to request
75
+ * native interruption while the handle is open.
76
+ */
77
+ async dispose() {
78
+ this.disposed = true;
79
+ this.observationController.abort();
80
+ }
81
+ disposed = false;
82
+ observationController = new AbortController();
83
+ /**
84
+ * events and result combine caller cancellation with this handle's disposal signal, so either can
85
+ * end a local observation without affecting siblings. Explicit control methods do not use it.
86
+ */
87
+ observationSignal(signal) {
88
+ return signal ? AbortSignal.any([signal, this.observationController.signal]) : this.observationController.signal;
89
+ }
90
+ /** steer, interrupt and observe throw TURN_DISPOSED here before invoking a port or creating a sibling. */
91
+ assertOpen() {
92
+ if (this.disposed)
93
+ throw new HarnessSdkError('TURN_DISPOSED');
94
+ }
95
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import type { HarnessError, InputBlock, Item, Usage } from '@anureo/protocol';
2
+ /**
3
+ * Send text or protocol blocks through Session.submit or Turn.steer. Text becomes one text block
4
+ * and block arrays are cloned. Invalid input rejects before native dispatch.
5
+ */
6
+ export type Input = string | readonly InputBlock[];
7
+ /**
8
+ * Read the frozen terminal snapshot returned by Turn.result. Failed and interrupted executions
9
+ * resolve with those statuses. Closing observation before the terminal or ending the caller's wait
10
+ * rejects the promise. Request history/read separately for native history when supported.
11
+ */
12
+ export type TurnResult = {
13
+ /** Text of final-phase agent messages in retained item order, separated by newlines. */
14
+ text: string;
15
+ /** Privacy-projected snapshots retained for this execution, including non-message items. */
16
+ items: readonly Item[];
17
+ /** Terminal turn-scoped usage, or the latest observed turn-scoped usage when the terminal has none. */
18
+ usage?: Usage;
19
+ } & ({
20
+ status: 'completed';
21
+ } | {
22
+ status: 'interrupted';
23
+ /** Safe native interruption details when supplied by the terminal event. */
24
+ error?: HarnessError;
25
+ } | {
26
+ status: 'failed';
27
+ /** Safe terminal failure, with TURN_FAILED supplied when native details are absent. */
28
+ error: HarnessError;
29
+ });
30
+ /**
31
+ * Bound one caller's wait for a Client, Session or Turn operation. Abort or timeout after dispatch
32
+ * rejects the waiter with outcomeUnknown while native work can continue. Use an explicit interrupt
33
+ * operation to request that execution stop.
34
+ */
35
+ export interface RequestOptions {
36
+ /** Stops this caller waiting; does not interrupt accepted native execution. */
37
+ signal?: AbortSignal;
38
+ /**
39
+ * Positive finite observation deadline in milliseconds; omission uses the operation default.
40
+ * Client/Session admission also rejects values above 2_147_483_647; standalone waits use host timers.
41
+ */
42
+ timeoutMs?: number;
43
+ }
44
+ /**
45
+ * Configure an independent queue for Session.events or Turn.events. Invalid budgets throw at
46
+ * subscription. Overflow rejects that observer's reads after buffered events drain, without
47
+ * pausing the native producer. Exhaust or return the iterator, or abort this subscription.
48
+ */
49
+ export interface EventSubscriptionOptions {
50
+ /** Aborts only this observer, releasing its pending read and membership. */
51
+ signal?: AbortSignal;
52
+ /** Independent observer budgets, each a positive safe integer; omission selects hub defaults. */
53
+ queue?: {
54
+ /** Maximum buffered regular events; defaults to 2048. Direct delivery to a pending read needs no slot. */
55
+ maxEvents?: number;
56
+ /**
57
+ * Maximum serialized bytes buffered for this observer; defaults to 16 MiB.
58
+ * Single events are also limited to the smaller of this budget and 8 MiB.
59
+ */
60
+ maxBytes?: number;
61
+ };
62
+ }
package/src/types.js ADDED
File without changes