@anureo/sdk 0.2.0 → 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.
- package/README.md +162 -65
- package/package.json +29 -24
- package/src/adapter/identity.d.ts +41 -0
- package/src/adapter/identity.js +71 -0
- package/src/client-options.d.ts +71 -0
- package/src/client-options.js +0 -0
- package/src/client.d.ts +106 -0
- package/src/client.js +254 -0
- package/src/diagnostics/errors.d.ts +15 -0
- package/src/diagnostics/errors.js +59 -0
- package/src/diagnostics/events.d.ts +46 -0
- package/src/diagnostics/events.js +47 -0
- package/src/diagnostics.d.ts +20 -0
- package/src/diagnostics.js +56 -0
- package/src/errors.d.ts +48 -0
- package/src/errors.js +63 -0
- package/src/index.d.ts +27 -0
- package/src/index.js +23 -0
- package/src/lifecycle/approval.d.ts +16 -0
- package/src/lifecycle/approval.js +18 -0
- package/src/lifecycle/deferred.d.ts +10 -0
- package/src/lifecycle/deferred.js +12 -0
- package/src/lifecycle/errors.d.ts +18 -0
- package/src/lifecycle/errors.js +45 -0
- package/src/lifecycle/events.d.ts +35 -0
- package/src/lifecycle/events.js +141 -0
- package/src/lifecycle/execution-observation.d.ts +33 -0
- package/src/lifecycle/execution-observation.js +0 -0
- package/src/lifecycle/immutable.d.ts +12 -0
- package/src/lifecycle/immutable.js +28 -0
- package/src/lifecycle/input.d.ts +9 -0
- package/src/lifecycle/input.js +19 -0
- package/src/lifecycle/interactions.d.ts +129 -0
- package/src/lifecycle/interactions.js +282 -0
- package/src/lifecycle/limits.d.ts +7 -0
- package/src/lifecycle/limits.js +13 -0
- package/src/lifecycle/result.d.ts +77 -0
- package/src/lifecycle/result.js +213 -0
- package/src/lifecycle/value.d.ts +4 -0
- package/src/lifecycle/value.js +5 -0
- package/src/ports.d.ts +224 -0
- package/src/ports.js +0 -0
- package/src/privacy/file-change-limits.d.ts +20 -0
- package/src/privacy/file-change-limits.js +124 -0
- package/src/privacy/secrets.d.ts +205 -0
- package/src/privacy/secrets.js +564 -0
- package/src/process.d.ts +92 -0
- package/src/process.js +331 -0
- package/src/queue.d.ts +143 -0
- package/src/queue.js +266 -0
- package/src/request.d.ts +28 -0
- package/src/request.js +81 -0
- package/src/requests/guard.d.ts +15 -0
- package/src/requests/guard.js +93 -0
- package/src/session.d.ts +459 -0
- package/src/session.js +1135 -0
- package/src/structured-output/adapter.d.ts +8 -0
- package/src/structured-output/adapter.js +54 -0
- package/src/structured-output/bridge.d.ts +81 -0
- package/src/structured-output/bridge.js +274 -0
- package/src/structured-output/errors.d.ts +17 -0
- package/src/structured-output/errors.js +31 -0
- package/src/structured-output/limits.d.ts +19 -0
- package/src/structured-output/limits.js +0 -0
- package/src/structured-output/schema.d.ts +20 -0
- package/src/structured-output/schema.js +122 -0
- package/src/structured-output/session.d.ts +329 -0
- package/src/structured-output/session.js +846 -0
- package/src/structured-output/stdio.mjs +83 -0
- package/src/structured-output/tool.d.ts +38 -0
- package/src/structured-output/tool.js +34 -0
- package/src/turn.d.ts +75 -0
- package/src/turn.js +95 -0
- package/src/types.d.ts +62 -0
- package/src/types.js +0 -0
- package/dist/diagnostics.d.ts +0 -15
- package/dist/diagnostics.js +0 -34
- package/dist/errors.d.ts +0 -15
- package/dist/file-change-limits.d.ts +0 -5
- package/dist/identity.d.ts +0 -13
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -1474
- package/dist/interactions.d.ts +0 -20
- package/dist/ports.d.ts +0 -66
- package/dist/process.d.ts +0 -34
- package/dist/process.js +0 -191
- package/dist/queue.d.ts +0 -36
- package/dist/queue.js +0 -166
- package/dist/runtime.d.ts +0 -85
- package/dist/secrets.d.ts +0 -28
package/src/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Root entry point for the application API, adapter contracts and host utilities.
|
|
2
|
+
export { createClient, Client } from "./client.js";
|
|
3
|
+
export { Session } from "./session.js";
|
|
4
|
+
export { Turn } from "./turn.js";
|
|
5
|
+
export * from "./errors.js";
|
|
6
|
+
export { waitForRequest } from "./request.js";
|
|
7
|
+
/** Adapter/observer buffering primitives, also available from the package's queue entry point. */
|
|
8
|
+
export * from "./queue.js";
|
|
9
|
+
/** Session-local native ID mapping implementation. */
|
|
10
|
+
export * from "./adapter/identity.js";
|
|
11
|
+
/** Interaction settlement and replay implementation used by Session. */
|
|
12
|
+
export * from "./lifecycle/interactions.js";
|
|
13
|
+
/** Adapter subprocess handles and launch helpers, also available from the process entry point; callers drain output and await handle disposal. */
|
|
14
|
+
export * from "./process.js";
|
|
15
|
+
/** Marked-secret projection implementation used by Session. */
|
|
16
|
+
export * from "./privacy/secrets.js";
|
|
17
|
+
/**
|
|
18
|
+
* Forward LogTape's host logging controls without adding SDK ownership. The exported dispose is
|
|
19
|
+
* LogTape's logging teardown; native resources are released through Client.dispose separately.
|
|
20
|
+
*/
|
|
21
|
+
export { configure, configureSync, dispose, disposeSync, drain, getLogger, reset, resetSync } from '@logtape/logtape';
|
|
22
|
+
/** Explicit unattended approval policy for hosts; never enabled by Client automatically. */
|
|
23
|
+
export { approveWithNarrowestScope } from "./lifecycle/approval.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { InteractionRequested, InteractionResponse } from '@anureo/protocol';
|
|
2
|
+
/**
|
|
3
|
+
* Select an advertised grant for unattended approval. Prefer allow-once, then session, then
|
|
4
|
+
* rule grants. When several options have the same scope, return the first advertised option.
|
|
5
|
+
* Reject with Error when the request offers none of these grants.
|
|
6
|
+
*
|
|
7
|
+
* Opt in through ClientOptions.interactions.approval, as createReviewClient and
|
|
8
|
+
* createRefactorClient do. The returned promise completes when the answer is selected.
|
|
9
|
+
* Session.startInteractionHandler invokes the registered policy and awaits native delivery
|
|
10
|
+
* separately. If selection rejects while the signal is active, Session attempts cancellation.
|
|
11
|
+
*/
|
|
12
|
+
export declare function approveWithNarrowestScope(request: Extract<InteractionRequested, {
|
|
13
|
+
kind: 'approval';
|
|
14
|
+
}>): Promise<Extract<InteractionResponse, {
|
|
15
|
+
kind: 'approval';
|
|
16
|
+
}>>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Select an advertised grant for unattended approval. Prefer allow-once, then session, then
|
|
3
|
+
* rule grants. When several options have the same scope, return the first advertised option.
|
|
4
|
+
* Reject with Error when the request offers none of these grants.
|
|
5
|
+
*
|
|
6
|
+
* Opt in through ClientOptions.interactions.approval, as createReviewClient and
|
|
7
|
+
* createRefactorClient do. The returned promise completes when the answer is selected.
|
|
8
|
+
* Session.startInteractionHandler invokes the registered policy and awaits native delivery
|
|
9
|
+
* separately. If selection rejects while the signal is active, Session attempts cancellation.
|
|
10
|
+
*/
|
|
11
|
+
export async function approveWithNarrowestScope(request) {
|
|
12
|
+
const allow = request.options.find(option => option.semantic === 'allowOnce')
|
|
13
|
+
?? request.options.find(option => option.semantic === 'allowSession')
|
|
14
|
+
?? request.options.find(option => option.semantic === 'allowWithRule');
|
|
15
|
+
if (!allow)
|
|
16
|
+
throw new Error('Approval request has no allow option');
|
|
17
|
+
return { kind: 'approval', optionId: allow.id };
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a package-internal promise with caller-owned settlement. Session uses it for observation
|
|
3
|
+
* closure, retained execution results and submission binding. A preinstalled rejection observer
|
|
4
|
+
* allows callers to attach later and still receive the original rejection.
|
|
5
|
+
*/
|
|
6
|
+
export declare function deferred<T>(): {
|
|
7
|
+
promise: Promise<T>;
|
|
8
|
+
resolve: (value: T) => void;
|
|
9
|
+
reject: (cause: unknown) => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a package-internal promise with caller-owned settlement. Session uses it for observation
|
|
3
|
+
* closure, retained execution results and submission binding. A preinstalled rejection observer
|
|
4
|
+
* allows callers to attach later and still receive the original rejection.
|
|
5
|
+
*/
|
|
6
|
+
export function deferred() {
|
|
7
|
+
let resolve;
|
|
8
|
+
let reject;
|
|
9
|
+
const promise = new Promise((yes, no) => { resolve = yes; reject = no; });
|
|
10
|
+
void promise.catch(() => { });
|
|
11
|
+
return { promise, resolve, reject };
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HarnessSdkError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Construct the package-internal error used to reject unfinished Turn results and streams.
|
|
4
|
+
* Session.finishClose passes native stop confirmation; Session.dispose uses the unconfirmed default.
|
|
5
|
+
* Even confirmed stop supplies no terminal result, so outcomeUnknown stays true. Callers still own
|
|
6
|
+
* interaction cancellation and backend release; constructing this error performs neither.
|
|
7
|
+
*/
|
|
8
|
+
export declare function observationClosed({ executionStopped }?: {
|
|
9
|
+
executionStopped?: 'confirmed' | 'unconfirmed';
|
|
10
|
+
}): HarnessSdkError;
|
|
11
|
+
/**
|
|
12
|
+
* Construct a package-internal aggregate of cleanup failures without releasing resources.
|
|
13
|
+
* Client.dispose and Session.finishClose throw this after their release attempts. The client's
|
|
14
|
+
* rethrowAfterCleanup uses it when setup and cleanup both fail. Deduplicate input causes and
|
|
15
|
+
* flatten nested release diagnostics in order. The returned error contains codes and unknown-outcome
|
|
16
|
+
* flags so raw failure messages stay out of aggregate diagnostics.
|
|
17
|
+
*/
|
|
18
|
+
export declare function combined(causes: unknown[]): HarnessSdkError;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { HarnessSdkError, sdkError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Construct the package-internal error used to reject unfinished Turn results and streams.
|
|
4
|
+
* Session.finishClose passes native stop confirmation; Session.dispose uses the unconfirmed default.
|
|
5
|
+
* Even confirmed stop supplies no terminal result, so outcomeUnknown stays true. Callers still own
|
|
6
|
+
* interaction cancellation and backend release; constructing this error performs neither.
|
|
7
|
+
*/
|
|
8
|
+
export function observationClosed({ executionStopped = 'unconfirmed' } = {}) {
|
|
9
|
+
const message = executionStopped === 'confirmed'
|
|
10
|
+
? 'Observation closed before a native terminal result'
|
|
11
|
+
: 'Observation closed; execution stop is unconfirmed';
|
|
12
|
+
return new HarnessSdkError('OBSERVATION_CLOSED', message, false, true, { source: 'sdk', executionStopped });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Construct a package-internal aggregate of cleanup failures without releasing resources.
|
|
16
|
+
* Client.dispose and Session.finishClose throw this after their release attempts. The client's
|
|
17
|
+
* rethrowAfterCleanup uses it when setup and cleanup both fail. Deduplicate input causes and
|
|
18
|
+
* flatten nested release diagnostics in order. The returned error contains codes and unknown-outcome
|
|
19
|
+
* flags so raw failure messages stay out of aggregate diagnostics.
|
|
20
|
+
*/
|
|
21
|
+
export function combined(causes) {
|
|
22
|
+
const errors = [...new Set(causes)].flatMap(releaseDiagnostics);
|
|
23
|
+
return new HarnessSdkError('RESOURCE_RELEASE_FAILED', 'One or more owned resources could not be released', false, true, { source: 'sdk', errors });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* releaseDiagnostics filters nested aggregates with this guard before copying their codes. Accept
|
|
27
|
+
* only bounded machine-readable codes; do not trust nested messages or arbitrary error details.
|
|
28
|
+
*/
|
|
29
|
+
function isReleaseDiagnostic(value) {
|
|
30
|
+
return !!value && typeof value === 'object' && 'code' in value
|
|
31
|
+
&& typeof value.code === 'string' && /^[A-Z][A-Z0-9_]{0,80}$/.test(value.code);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* combined flat-maps each distinct cause through this normalization. Flatten an existing release
|
|
35
|
+
* aggregate or return one safe code/unknown flag, preserving input order and discarding raw causes.
|
|
36
|
+
* No cleanup is attempted; callers decide when to throw the aggregate.
|
|
37
|
+
*/
|
|
38
|
+
function releaseDiagnostics(cause) {
|
|
39
|
+
const error = sdkError(cause);
|
|
40
|
+
if (error.code === 'RESOURCE_RELEASE_FAILED' && Array.isArray(error.details?.errors)) {
|
|
41
|
+
return error.details.errors.filter(isReleaseDiagnostic)
|
|
42
|
+
.map(value => ({ code: value.code, outcomeUnknown: value.outcomeUnknown === true }));
|
|
43
|
+
}
|
|
44
|
+
return [{ code: error.code, outcomeUnknown: error.outcomeUnknown }];
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { EventSubscriptionOptions } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Package-internal fan-out for bounded live subscriptions without replay or producer backpressure.
|
|
4
|
+
* Session.publish feeds the session hub and Session.retainEvent feeds each execution hub.
|
|
5
|
+
* Session orders and ends those streams. Consumers release their own subscriptions.
|
|
6
|
+
*/
|
|
7
|
+
export declare class EventHub<T> {
|
|
8
|
+
/**
|
|
9
|
+
* Subscribe to future events with an independent queue. Session.events and Turn.events return
|
|
10
|
+
* this iterator to host consumers, who exhaust or return it, or abort the signal to release it.
|
|
11
|
+
* Invalid budgets throw synchronously. The optional filter defaults to accepting all events. An
|
|
12
|
+
* overlapping blocked read rejects with CONCURRENT_EVENT_READ and ends the subscription.
|
|
13
|
+
* Cancelling a live subscription completes its blocked and future reads and skips any
|
|
14
|
+
* buffered events. Once detached by termination or failure, abort no longer affects the reader.
|
|
15
|
+
* Late subscribers observe the stored termination even if their signal is already aborted.
|
|
16
|
+
*/
|
|
17
|
+
subscribe(options?: EventSubscriptionOptions, accepts?: (value: T) => boolean): AsyncIterableIterator<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Session.publish sends session events here; Session.retainEvent sends execution events here before
|
|
20
|
+
* ending terminal streams. Offer the event synchronously to each subscriber in registration
|
|
21
|
+
* order. Each subscriber filters and clones it independently. A filter, clone or queue admission
|
|
22
|
+
* failure closes that subscription and lets delivery continue. Ignore publication after hub end.
|
|
23
|
+
*/
|
|
24
|
+
publish(value: T): void;
|
|
25
|
+
/**
|
|
26
|
+
* Session.retainEvent ends terminal execution streams; Session.endObservation ends remaining
|
|
27
|
+
* execution and session streams with a failure. Idempotently end all observers with an optional read
|
|
28
|
+
* failure and remove their abort listeners. Queued events drain before a truthy cause rejects reads
|
|
29
|
+
* (otherwise reads complete). Later subscribers observe the first termination without replay.
|
|
30
|
+
* Consumers still own returning or exhausting their readers.
|
|
31
|
+
*/
|
|
32
|
+
end(cause?: unknown): void;
|
|
33
|
+
private readonly subscribers;
|
|
34
|
+
private termination?;
|
|
35
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Owns isolated live event fan-out and each observer's queue, reader and cancellation lifetime.
|
|
2
|
+
import { BoundedEventQueue } from "../queue.js";
|
|
3
|
+
/**
|
|
4
|
+
* Package-internal fan-out for bounded live subscriptions without replay or producer backpressure.
|
|
5
|
+
* Session.publish feeds the session hub and Session.retainEvent feeds each execution hub.
|
|
6
|
+
* Session orders and ends those streams. Consumers release their own subscriptions.
|
|
7
|
+
*/
|
|
8
|
+
export class EventHub {
|
|
9
|
+
/**
|
|
10
|
+
* Subscribe to future events with an independent queue. Session.events and Turn.events return
|
|
11
|
+
* this iterator to host consumers, who exhaust or return it, or abort the signal to release it.
|
|
12
|
+
* Invalid budgets throw synchronously. The optional filter defaults to accepting all events. An
|
|
13
|
+
* overlapping blocked read rejects with CONCURRENT_EVENT_READ and ends the subscription.
|
|
14
|
+
* Cancelling a live subscription completes its blocked and future reads and skips any
|
|
15
|
+
* buffered events. Once detached by termination or failure, abort no longer affects the reader.
|
|
16
|
+
* Late subscribers observe the stored termination even if their signal is already aborted.
|
|
17
|
+
*/
|
|
18
|
+
subscribe(options = {}, accepts = () => true) {
|
|
19
|
+
const subscription = createSubscription({ options, accepts, remove: observer => this.subscribers.delete(observer) });
|
|
20
|
+
if (this.termination) {
|
|
21
|
+
subscription.queue.end(this.termination.cause);
|
|
22
|
+
}
|
|
23
|
+
else if (options.signal?.aborted) {
|
|
24
|
+
subscription.cancel();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.subscribers.add(subscription);
|
|
28
|
+
options.signal?.addEventListener('abort', subscription.cancel, { once: true });
|
|
29
|
+
}
|
|
30
|
+
return subscription.iterator;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Session.publish sends session events here; Session.retainEvent sends execution events here before
|
|
34
|
+
* ending terminal streams. Offer the event synchronously to each subscriber in registration
|
|
35
|
+
* order. Each subscriber filters and clones it independently. A filter, clone or queue admission
|
|
36
|
+
* failure closes that subscription and lets delivery continue. Ignore publication after hub end.
|
|
37
|
+
*/
|
|
38
|
+
publish(value) {
|
|
39
|
+
if (this.termination)
|
|
40
|
+
return;
|
|
41
|
+
for (const subscriber of this.subscribers)
|
|
42
|
+
deliverToSubscriber(subscriber, value);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Session.retainEvent ends terminal execution streams; Session.endObservation ends remaining
|
|
46
|
+
* execution and session streams with a failure. Idempotently end all observers with an optional read
|
|
47
|
+
* failure and remove their abort listeners. Queued events drain before a truthy cause rejects reads
|
|
48
|
+
* (otherwise reads complete). Later subscribers observe the first termination without replay.
|
|
49
|
+
* Consumers still own returning or exhausting their readers.
|
|
50
|
+
*/
|
|
51
|
+
end(cause) {
|
|
52
|
+
if (this.termination)
|
|
53
|
+
return;
|
|
54
|
+
this.termination = { cause };
|
|
55
|
+
for (const subscriber of this.subscribers) {
|
|
56
|
+
subscriber.end(cause);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
subscribers = new Set();
|
|
60
|
+
termination;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* EventHub.subscribe allocates an observer here before deciding whether to register it or close it.
|
|
64
|
+
* Queue construction can throw before registration. Closure calls remove to release hub membership
|
|
65
|
+
* and leaves the reader available to drain. Cancellation also returns the reader to release ownership.
|
|
66
|
+
*/
|
|
67
|
+
function createSubscription({ options, accepts, remove }) {
|
|
68
|
+
const queue = createSubscriptionQueue(options);
|
|
69
|
+
const reader = queue[Symbol.asyncIterator]();
|
|
70
|
+
let cancelled = false;
|
|
71
|
+
/** Remove hub membership and the listener registered by subscribe, leaving the reader available to drain. */
|
|
72
|
+
const detach = () => {
|
|
73
|
+
remove(subscription);
|
|
74
|
+
options.signal?.removeEventListener('abort', cancelSubscription);
|
|
75
|
+
};
|
|
76
|
+
/** Hub termination and delivery/read failures detach before closing admission, preserving buffered drain. */
|
|
77
|
+
const endSubscription = (cause) => {
|
|
78
|
+
detach();
|
|
79
|
+
queue.end(cause);
|
|
80
|
+
};
|
|
81
|
+
/** subscribe registers this abort listener; early iterator exit also skips retained data and releases ownership. */
|
|
82
|
+
const cancelSubscription = () => {
|
|
83
|
+
cancelled = true;
|
|
84
|
+
endSubscription();
|
|
85
|
+
void reader.return?.();
|
|
86
|
+
};
|
|
87
|
+
const iterator = {
|
|
88
|
+
/** Reuse the claimed reader for for-await, without acquiring a second queue reader. */
|
|
89
|
+
[Symbol.asyncIterator]() { return this; },
|
|
90
|
+
/** Await queue delivery/termination and propagate read failures after detaching; cancellation skips retained data. */
|
|
91
|
+
async next() {
|
|
92
|
+
if (cancelled)
|
|
93
|
+
return { done: true, value: undefined };
|
|
94
|
+
try {
|
|
95
|
+
const result = await reader.next();
|
|
96
|
+
if (result.done)
|
|
97
|
+
detach();
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
catch (cause) {
|
|
101
|
+
endSubscription(cause);
|
|
102
|
+
throw cause;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
/** Early exit completes this observer's blocked reads without stopping the shared producer. */
|
|
106
|
+
async return() {
|
|
107
|
+
cancelSubscription();
|
|
108
|
+
return { done: true, value: undefined };
|
|
109
|
+
},
|
|
110
|
+
/** Release this observer before rejecting with the consumer's supplied cause. */
|
|
111
|
+
async throw(cause) {
|
|
112
|
+
cancelSubscription();
|
|
113
|
+
throw cause;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const subscription = { queue, accepts, end: endSubscription, cancel: cancelSubscription, iterator };
|
|
117
|
+
return subscription;
|
|
118
|
+
}
|
|
119
|
+
/** Allocate an observer's queue, capping individual events at 8 MiB or the smaller total byte budget. Invalid limits throw before registration. */
|
|
120
|
+
function createSubscriptionQueue(options) {
|
|
121
|
+
const maxBytes = options.queue?.maxBytes ?? 16 * 1024 * 1024;
|
|
122
|
+
return new BoundedEventQueue({
|
|
123
|
+
...options.queue,
|
|
124
|
+
maxBytes,
|
|
125
|
+
maxFrameBytes: Math.min(maxBytes, 8 * 1024 * 1024),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* EventHub.publish offers each event here. Filter before cloning so each accepting subscriber gets
|
|
130
|
+
* its own copy. On failure, close only this subscriber and preserve its buffered events before a
|
|
131
|
+
* truthy cause rejects reads or a falsy cause completes them.
|
|
132
|
+
*/
|
|
133
|
+
function deliverToSubscriber(subscriber, value) {
|
|
134
|
+
try {
|
|
135
|
+
if (subscriber.accepts(value))
|
|
136
|
+
subscriber.queue.push(structuredClone(value));
|
|
137
|
+
}
|
|
138
|
+
catch (cause) {
|
|
139
|
+
subscriber.end(cause);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AhpEvent } from '@anureo/protocol';
|
|
2
|
+
import type { EventHub } from "./events.js";
|
|
3
|
+
import type { Input, RequestOptions, TurnResult } from "../types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Internal port allocated by Session.context and passed to Turn by Session.submitExecution when
|
|
6
|
+
* association resolves. Turn uses it for live subscriptions, terminal results and explicit controls.
|
|
7
|
+
* Session owns event ordering, shared settlement and native resources. Each Turn owns its wait
|
|
8
|
+
* and subscription cancellation.
|
|
9
|
+
*/
|
|
10
|
+
export interface ExecutionObservation {
|
|
11
|
+
/** Stable execution ID reported by the adapter and shared by submissions bound to the same turn. */
|
|
12
|
+
readonly id: string;
|
|
13
|
+
/** Session-owned live producer hub; Turn.events creates an isolated subscription without replay. */
|
|
14
|
+
readonly events: EventHub<AhpEvent>;
|
|
15
|
+
/** Session.retainEvent resolves the frozen snapshot at native completion. Session.endObservation rejects unfinished results on closure. */
|
|
16
|
+
readonly result: Promise<TurnResult>;
|
|
17
|
+
/** Session.retainEvent sets this on turn/completed before publishing the terminal event and ending its stream. */
|
|
18
|
+
readonly terminal: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Turn.steer explicitly invokes the callback installed by Session.context. It normalizes input and
|
|
21
|
+
* awaits Session.request('turn/steer') with this execution as the atomic target. It discards the
|
|
22
|
+
* validated receipt and propagates normalization, admission, dispatch and wait rejections. Native
|
|
23
|
+
* work continues if the caller ends its wait after admission.
|
|
24
|
+
*/
|
|
25
|
+
steer(input: Input, wait?: RequestOptions): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Turn.interrupt explicitly invokes the callback installed by Session.context. It awaits
|
|
28
|
+
* Session.request('turn/interrupt') for this execution, discarding the validated receipt or
|
|
29
|
+
* propagating admission, dispatch and wait rejections. Session.retainEvent settles result
|
|
30
|
+
* separately when the terminal event arrives.
|
|
31
|
+
*/
|
|
32
|
+
interrupt(wait?: RequestOptions): Promise<void>;
|
|
33
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clone acyclic protocol data and freeze enumerable object and array descendants. Cloning or
|
|
3
|
+
* freezing failures throw and leave the original value untouched.
|
|
4
|
+
*
|
|
5
|
+
* Called by:
|
|
6
|
+
* - Client.initialize to store negotiated capabilities; failure rejects initialization.
|
|
7
|
+
* - Session's constructor/attach and binding/reference/capabilities/snapshot getters to detach stored
|
|
8
|
+
* or exposed state. Session.executeRequest returns frozen receipts; failure rejects its request.
|
|
9
|
+
* - ResultAccumulator.finish to return a terminal snapshot. Session.retainEvent settles the shared
|
|
10
|
+
* Turn result with that snapshot, or lets a failure propagate through ordered publication.
|
|
11
|
+
*/
|
|
12
|
+
export declare function immutable<T>(value: T): T;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clone acyclic protocol data and freeze enumerable object and array descendants. Cloning or
|
|
3
|
+
* freezing failures throw and leave the original value untouched.
|
|
4
|
+
*
|
|
5
|
+
* Called by:
|
|
6
|
+
* - Client.initialize to store negotiated capabilities; failure rejects initialization.
|
|
7
|
+
* - Session's constructor/attach and binding/reference/capabilities/snapshot getters to detach stored
|
|
8
|
+
* or exposed state. Session.executeRequest returns frozen receipts; failure rejects its request.
|
|
9
|
+
* - ResultAccumulator.finish to return a terminal snapshot. Session.retainEvent settles the shared
|
|
10
|
+
* Turn result with that snapshot, or lets a failure propagate through ordered publication.
|
|
11
|
+
*/
|
|
12
|
+
export function immutable(value) {
|
|
13
|
+
const copy = structuredClone(value);
|
|
14
|
+
/**
|
|
15
|
+
* immutable invokes this recursive walk on its detached clone. Freeze descendants first so no
|
|
16
|
+
* enumerable object/array child of a returned protocol snapshot remains writable; failures propagate.
|
|
17
|
+
*/
|
|
18
|
+
const freeze = (entry) => {
|
|
19
|
+
if (entry && typeof entry === 'object') {
|
|
20
|
+
for (const child of Object.values(entry)) {
|
|
21
|
+
freeze(child);
|
|
22
|
+
}
|
|
23
|
+
Object.freeze(entry);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
freeze(copy);
|
|
27
|
+
return copy;
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InputBlock } from '@anureo/protocol';
|
|
2
|
+
import type { Input } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Normalize host input into one text block for a string or a detached copy of a block array.
|
|
5
|
+
* Throw INVALID_PARAMS for other values or clone failure. Session.submit and the callback installed
|
|
6
|
+
* by Session.context for Turn.steer pass the resulting blocks to request admission, which validates
|
|
7
|
+
* their contents and capabilities. Both expose normalization failures as promise rejections.
|
|
8
|
+
*/
|
|
9
|
+
export declare function inputBlocks(input: Input): InputBlock[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HarnessSdkError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize host input into one text block for a string or a detached copy of a block array.
|
|
4
|
+
* Throw INVALID_PARAMS for other values or clone failure. Session.submit and the callback installed
|
|
5
|
+
* by Session.context for Turn.steer pass the resulting blocks to request admission, which validates
|
|
6
|
+
* their contents and capabilities. Both expose normalization failures as promise rejections.
|
|
7
|
+
*/
|
|
8
|
+
export function inputBlocks(input) {
|
|
9
|
+
if (typeof input === 'string')
|
|
10
|
+
return [{ type: 'text', text: input }];
|
|
11
|
+
if (!Array.isArray(input))
|
|
12
|
+
throw new HarnessSdkError('INVALID_PARAMS');
|
|
13
|
+
try {
|
|
14
|
+
return structuredClone(input);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new HarnessSdkError('INVALID_PARAMS');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type InteractionRequested, type InteractionResponse, type InteractionResult } from "@anureo/protocol";
|
|
2
|
+
import type { NativeInteractionResponder } from "../ports.js";
|
|
3
|
+
/**
|
|
4
|
+
* Track native requests, settle their answers and retain bounded response replay state.
|
|
5
|
+
* Session uses this SDK implementation helper and supplies publication and privacy hooks.
|
|
6
|
+
* Direct consumers own those integrations and should await cancelAll with a teardown reason
|
|
7
|
+
* when releasing a session. The registry owns request expiry and native response wait timers.
|
|
8
|
+
*/
|
|
9
|
+
export declare class InteractionRegistry {
|
|
10
|
+
private readonly publish;
|
|
11
|
+
private readonly limits;
|
|
12
|
+
private readonly beforeResponse?;
|
|
13
|
+
/**
|
|
14
|
+
* Configure settlement publication and synchronous pre-response checks without starting native
|
|
15
|
+
* work. Session supplies a publisher that aborts the host handler and awaits publication of
|
|
16
|
+
* interaction/resolved while observation is open. It also supplies SecretRedactor.rememberResponse
|
|
17
|
+
* to register secrets before delivery. respond runs the check for every validated attempt, including
|
|
18
|
+
* replays, and propagates a throw before delivery. Settlement awaits publication without a deadline.
|
|
19
|
+
*/
|
|
20
|
+
constructor(publish: (result: InteractionResult) => Promise<void>, limits: {
|
|
21
|
+
/** Pending and settling capacity. Admission trims resolved records at maxPending + 1024. */
|
|
22
|
+
maxPending: number;
|
|
23
|
+
/** Interaction lifetime in milliseconds, shortened by an earlier request expiresAt. */
|
|
24
|
+
timeoutMs: number;
|
|
25
|
+
/** Native response/cancellation wait in milliseconds, defaulting to 30,000. Native work can continue after timeout. */
|
|
26
|
+
responseTimeoutMs?: number;
|
|
27
|
+
}, beforeResponse?: ((request: InteractionRequested, response: InteractionResponse) => void) | undefined);
|
|
28
|
+
/**
|
|
29
|
+
* Count requests that consume admission capacity in begin, including native delivery and
|
|
30
|
+
* cancellation in flight. Capacity becomes available when a request is marked resolved.
|
|
31
|
+
*/
|
|
32
|
+
get pendingCount(): number;
|
|
33
|
+
/**
|
|
34
|
+
* List detached copies of actionable requests for manual answering. Cloning failures throw
|
|
35
|
+
* without changing registry state. These copies retain native fields, so callers own projection
|
|
36
|
+
* before exposing them to observers.
|
|
37
|
+
*/
|
|
38
|
+
pending(): InteractionRequested[];
|
|
39
|
+
/**
|
|
40
|
+
* Register a unique unexpired request and an unrefed expiry timer. Duplicate IDs, exhausted
|
|
41
|
+
* capacity and invalid/elapsed lifetimes throw synchronously. A request-cloning failure rejects
|
|
42
|
+
* the returned promise after the expiry timer is scheduled. On success, the promise resolves
|
|
43
|
+
* with the answer after native delivery and result publication.
|
|
44
|
+
* Cancellation and peer settlement reject it after publication is attempted. If native delivery
|
|
45
|
+
* fails and publishing its unknown outcome also fails, this waiter stays unresolved; respond
|
|
46
|
+
* rejects the publication failure instead.
|
|
47
|
+
* Session.requestInteraction adopts this promise after publishing interaction/requested and
|
|
48
|
+
* scheduling any host handler. Adapters supply the responder through that sink method and own
|
|
49
|
+
* the native resources it uses.
|
|
50
|
+
*/
|
|
51
|
+
begin(request: InteractionRequested, responder: NativeInteractionResponder): Promise<InteractionResponse>;
|
|
52
|
+
/**
|
|
53
|
+
* Deliver an answer or replay the retained operation for the same request ID and serialized
|
|
54
|
+
* response. Validation and the synchronous pre-response hook run before any replay or delivery.
|
|
55
|
+
* Missing IDs reject with INTERACTION_EXPIRED. Conflicting answers to settling or resolved
|
|
56
|
+
* requests reject with INTERACTION_ALREADY_RESOLVED. The operation clears expiry, retains
|
|
57
|
+
* capacity during native delivery, and settles after the publication it initiates. If another
|
|
58
|
+
* settlement wins during delivery, late native success rejects with RESULT_UNKNOWN.
|
|
59
|
+
* Session.invokeRequest returns the promise for manual requests. Session.startInteractionHandler
|
|
60
|
+
* awaits it for automatic answers and attempts handler_failed cancellation on rejection while
|
|
61
|
+
* its signal remains active.
|
|
62
|
+
*/
|
|
63
|
+
respond(id: string, requestId: string, response: InteractionResponse): Promise<InteractionResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Cancel or expire an unresolved request, notifying native once when pending. The reason and
|
|
66
|
+
* outcome both default to "cancelled". Missing IDs reject with INTERACTION_EXPIRED. Resolved IDs
|
|
67
|
+
* and in-flight settlement with reason "cancelled" reject with INTERACTION_ALREADY_RESOLVED.
|
|
68
|
+
* Other reasons mark in-flight settlement unknown without another native call. Native notification
|
|
69
|
+
* failure or timeout also changes the outcome to unknown.
|
|
70
|
+
* Await publication, reject begin's waiter with INTERACTION_CANCELLED or RESULT_UNKNOWN, and
|
|
71
|
+
* return the outcome. If publication fails, the cancellation caller receives that failure and
|
|
72
|
+
* the original waiter still receives its lifecycle error. Session.invokeRequest returns this
|
|
73
|
+
* promise for manual cancellation. Session.startInteractionHandler and the expiry timer observe
|
|
74
|
+
* and suppress its rejection. cancelAll collects its outcome during teardown.
|
|
75
|
+
*/
|
|
76
|
+
cancel(id: string, reason?: string, outcome?: CancellationOutcome): Promise<InteractionResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Attempt concurrent cancellation of unresolved interactions, optionally limited to turnId; await
|
|
79
|
+
* every attempt and suppress individual failures. Session.fail, Session.finishClose and
|
|
80
|
+
* Session.dispose await this before releasing the backend. Use a reason other than "cancelled"
|
|
81
|
+
* to settle in-flight requests. The literal reason "connection_lost" requests an unknown outcome.
|
|
82
|
+
*/
|
|
83
|
+
cancelAll(reason: string, turnId?: string): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Publish native-side settlement without calling the responder; ignore unknown/resolved IDs
|
|
86
|
+
* and reject the waiter even for responded because no value is supplied. Clear expiry and release
|
|
87
|
+
* capacity before publication; publication failure rejects this call but still rejects the waiter.
|
|
88
|
+
* An unknown outcome rejects the waiter with RESULT_UNKNOWN; other outcomes use INTERACTION_ALREADY_RESOLVED.
|
|
89
|
+
* Session.resolveInteraction returns this promise to adapters reporting an already-settled native request.
|
|
90
|
+
*/
|
|
91
|
+
resolveExternally(result: InteractionResult): Promise<void>;
|
|
92
|
+
private readonly records;
|
|
93
|
+
/**
|
|
94
|
+
* begin calls after admission checks, before inserting a new request. At the replay bound, evict
|
|
95
|
+
* only the oldest resolved record so unresolved native work and its capacity remain accounted for.
|
|
96
|
+
* Duplicate checks run first: a retained ID cannot be reused by evicting its own replay guard.
|
|
97
|
+
*/
|
|
98
|
+
private evictOldestResolvedRecord;
|
|
99
|
+
/**
|
|
100
|
+
* respond caches this operation after admission. Bound native delivery without retrying it, then
|
|
101
|
+
* publish before resolving begin's waiter. If delivery succeeds after another settlement resolves
|
|
102
|
+
* the record, reject with RESULT_UNKNOWN without publishing again. Normalize delivery/publication
|
|
103
|
+
* failures for both callers. If publishing the unknown outcome itself fails, that failure escapes
|
|
104
|
+
* before the original waiter is rejected. Cancellation uses publishRejectedSettlement's finally instead.
|
|
105
|
+
*/
|
|
106
|
+
private deliverResponse;
|
|
107
|
+
/**
|
|
108
|
+
* cancel calls only for an actionable record. Hold capacity and clear expiry before notifying native;
|
|
109
|
+
* timeout or notification failure changes the requested outcome to unknown. Release capacity before
|
|
110
|
+
* publication, then reject begin's waiter even if publication fails. Once started, this attempt
|
|
111
|
+
* still publishes after its native wait, even if teardown or peer settlement has already published.
|
|
112
|
+
*/
|
|
113
|
+
private cancelPending;
|
|
114
|
+
/**
|
|
115
|
+
* Wait for publication before rejecting begin's waiter with its lifecycle error. A publication
|
|
116
|
+
* failure still reaches cancel, cancelPending or resolveExternally. Those callers clear expiry
|
|
117
|
+
* and release capacity before entering this step.
|
|
118
|
+
*/
|
|
119
|
+
private publishRejectedSettlement;
|
|
120
|
+
/**
|
|
121
|
+
* Bound one invocation of the adapter's registered callback. deliverResponse supplies the answer
|
|
122
|
+
* and cancelPending supplies null and a reason. Both await and classify native errors or timeout.
|
|
123
|
+
* A timeout ends the registry's wait and keeps observing late rejection. The adapter still owns
|
|
124
|
+
* completion and native resource cleanup.
|
|
125
|
+
*/
|
|
126
|
+
private notifyNative;
|
|
127
|
+
}
|
|
128
|
+
type CancellationOutcome = "cancelled" | "expired" | "unknown";
|
|
129
|
+
export {};
|