@yanlinglabs/winter-runtime-sdk 0.0.1
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 +21 -0
- package/README.md +249 -0
- package/dist/directory/directory.d.ts +55 -0
- package/dist/directory/entries.d.ts +82 -0
- package/dist/directory/recovery.d.ts +49 -0
- package/dist/door.d.ts +247 -0
- package/dist/errors.d.ts +104 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +6679 -0
- package/dist/messaging/attribution.d.ts +47 -0
- package/dist/messaging/dispatch.d.ts +78 -0
- package/dist/messaging/handlers.d.ts +56 -0
- package/dist/messaging/inbound.d.ts +110 -0
- package/dist/messaging/index.d.ts +39 -0
- package/dist/messaging/official-adapter.d.ts +36 -0
- package/dist/messaging/router.d.ts +101 -0
- package/dist/messaging/sessions.d.ts +49 -0
- package/dist/messaging/winter-adapter.d.ts +47 -0
- package/dist/native-args.d.ts +76 -0
- package/dist/official/adapter.d.ts +88 -0
- package/dist/official/aliases.d.ts +48 -0
- package/dist/official/auth.d.ts +117 -0
- package/dist/official/branding.d.ts +31 -0
- package/dist/official/callbacks.d.ts +143 -0
- package/dist/official/containment.d.ts +130 -0
- package/dist/official/env-allowlist.d.ts +237 -0
- package/dist/official/env-registry-rule.d.ts +12 -0
- package/dist/official/env-registry.d.ts +3 -0
- package/dist/official/errors.d.ts +250 -0
- package/dist/official/index.d.ts +31 -0
- package/dist/official/mcp-descriptors.d.ts +130 -0
- package/dist/official/options-template.d.ts +110 -0
- package/dist/official/spawn-proxy.d.ts +144 -0
- package/dist/official/spool.d.ts +80 -0
- package/dist/official/supervision.d.ts +49 -0
- package/dist/official/sweep.d.ts +65 -0
- package/dist/sdk.d.ts +214 -0
- package/dist/seams/context.d.ts +26 -0
- package/dist/seams/directory-store.d.ts +274 -0
- package/dist/seams/directory.d.ts +46 -0
- package/dist/seams/global-messaging.d.ts +30 -0
- package/dist/seams/handoff.d.ts +77 -0
- package/dist/seams/index.d.ts +11 -0
- package/dist/seams/keychain.d.ts +11 -0
- package/dist/seams/materialized-resume.d.ts +46 -0
- package/dist/seams/messaging-contract.d.ts +29 -0
- package/dist/seams/official-adapter.d.ts +125 -0
- package/dist/seams/official-sdk-shapes.d.ts +126 -0
- package/dist/seams/stubs.d.ts +34 -0
- package/dist/selection/child-runtime.d.ts +81 -0
- package/dist/selection/runtime-selection.d.ts +217 -0
- package/dist/selection/select-runtime.d.ts +213 -0
- package/dist/store/handoff-barrier.d.ts +238 -0
- package/dist/store/index.d.ts +11 -0
- package/dist/store/materialized-resume.d.ts +100 -0
- package/dist/store/pinned-probes.d.ts +17 -0
- package/dist/store/reconcile.d.ts +157 -0
- package/dist/store/temp-continuity.d.ts +92 -0
- package/dist/store/wiring.d.ts +250 -0
- package/dist/vendor-paths.d.ts +21 -0
- package/dist/version-matrix.d.ts +84 -0
- package/docs/conformance-rows.md +195 -0
- package/package.json +65 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { GlobalAgentMessage, RuntimeAddress } from "../seams/messaging-contract.js";
|
|
2
|
+
/** A sender whose `from` cannot be turned into a canonical address cannot be attributed at all. */
|
|
3
|
+
export declare class UnattributableSenderError extends Error {
|
|
4
|
+
constructor(reason: string);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* THE OWNER CHECK ON ITS OWN — the reason this envelope cannot be attributed here, or `undefined`.
|
|
8
|
+
*
|
|
9
|
+
* SEPARATED FROM THE RENDERING (review r1, D1) because it is a DECISION and rendering is an effect,
|
|
10
|
+
* and the three delivery paths need the decision at a different moment than the string: before any
|
|
11
|
+
* push, outside every `try`, so that one envelope gets one answer whichever handle shape a host
|
|
12
|
+
* attached. Evaluated inside a `try` whose `catch` maps to `delivery_uncertain`, the same refusal
|
|
13
|
+
* became "the delivery may have occurred" for a message that provably never left the router — and
|
|
14
|
+
* WS-10 §12's recovery contract then forbids retrying it, so a clean side-effect-free "no" turned into
|
|
15
|
+
* a permanently ambiguous record.
|
|
16
|
+
*
|
|
17
|
+
* `owner` is the session whose input stream this is going into. A claimed CHILD sender is only
|
|
18
|
+
* attributable by the session that owns that child (WS-10 §10.3), so an `agent:` origin from somewhere
|
|
19
|
+
* else is refused rather than rendered with an address this side cannot vouch for. Pass `undefined` to
|
|
20
|
+
* skip the check — for the official branch's owner-qualified relay, where the message is deliberately
|
|
21
|
+
* being handed to a parent that does NOT own the sender.
|
|
22
|
+
*/
|
|
23
|
+
export declare function unattributableReason(message: GlobalAgentMessage, owner?: {
|
|
24
|
+
winterSessionId: string;
|
|
25
|
+
}): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The attributed turn, byte-for-byte as the Winter runtime renders it.
|
|
28
|
+
*
|
|
29
|
+
* Throws `UnattributableSenderError` on the same condition `unattributableReason` names, so a caller
|
|
30
|
+
* that renders without checking still cannot produce an unattributable frame — but every caller in
|
|
31
|
+
* this package checks FIRST, because a throw at this point is an effect-free refusal and must be
|
|
32
|
+
* reported as one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderAttributedTurn(message: GlobalAgentMessage, owner?: {
|
|
35
|
+
winterSessionId: string;
|
|
36
|
+
}): string;
|
|
37
|
+
/**
|
|
38
|
+
* The owner-qualified form: the same frame, plus the address the message was actually FOR.
|
|
39
|
+
*
|
|
40
|
+
* WS-15 §6.2's official-child rows are the only user: "Native child messaging through the active
|
|
41
|
+
* owning parent session", and "the public result stays owner-qualified" — the official runtime has no
|
|
42
|
+
* out-of-band door into a child, so a message for one is delivered to the owning parent, which is a
|
|
43
|
+
* different fact from a message TO the parent and must not read like one. The `for` attribute is
|
|
44
|
+
* router-authored (it is the resolved canonical address, not anything a sender wrote), so it is
|
|
45
|
+
* escaped for the same defensive reason `from` is and can no more be forged than `from` can.
|
|
46
|
+
*/
|
|
47
|
+
export declare function renderOwnerQualifiedTurn(message: GlobalAgentMessage, target: RuntimeAddress): string;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { RuntimeMessagingAdapter } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
2
|
+
import type { DirectorySnapshot } from "../directory/directory.js";
|
|
3
|
+
import type { RuntimeDirectoryStore } from "../seams/directory-store.js";
|
|
4
|
+
import type { DeliveryOutcome, RuntimeAddress, RuntimeKind } from "../seams/messaging-contract.js";
|
|
5
|
+
import type { InboundPolicy } from "./inbound.js";
|
|
6
|
+
/**
|
|
7
|
+
* WS-10 §15's adapter, plus the ONE fact the router must know about a runtime that the interface has
|
|
8
|
+
* nowhere to put: whether it has a reliable idle signal at all.
|
|
9
|
+
*
|
|
10
|
+
* WS-10 §14 requires that "subagents, teammates, cloud/remote targets, AND ADAPTERS WITHOUT A RELIABLE
|
|
11
|
+
* IDLE SIGNAL MUST refuse the ENTIRE call (including any attached message)". The shared core enforces
|
|
12
|
+
* that from the target row's `capabilities.notifyWhenIdle` flag — which is a DIRECTORY field, written
|
|
13
|
+
* by a host. A host that sets it optimistically on an official-runtime row would get the body
|
|
14
|
+
* delivered and only the subscription refused, which is precisely the "delivered and then
|
|
15
|
+
* un-delivered" shape §14 exists to prevent. So the adapter declares it, and the dispatcher clears the
|
|
16
|
+
* flag on every row belonging to a runtime that cannot back it. Absent means "yes" — the honest
|
|
17
|
+
* default for a host-registered adapter this package knows nothing about.
|
|
18
|
+
*/
|
|
19
|
+
export interface RouterMessagingAdapter extends RuntimeMessagingAdapter {
|
|
20
|
+
readonly supportsIdleSubscriptions?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* PER ADDRESS, where `supportsIdleSubscriptions` is per RUNTIME (review r2, NEW-2).
|
|
23
|
+
*
|
|
24
|
+
* The runtime-wide flag is not fine enough: the Winter branch genuinely has a reliable idle signal
|
|
25
|
+
* (its engine is the session-status event source), but only for a session this process holds a
|
|
26
|
+
* FACET for — its own `subscribeIdle` refuses a facet-less one by address. So a row whose
|
|
27
|
+
* `capabilities.notifyWhenIdle` a host wrote `true` (the ordinary case) attached through a plain
|
|
28
|
+
* input-stream writer passed the core's eligibility check, the body was DELIVERED, and only the
|
|
29
|
+
* subscription came back refused — which is exactly what WS-10 §14's "refuse the ENTIRE call
|
|
30
|
+
* (including any attached message) so the sender can retry without the flag" forbids.
|
|
31
|
+
*
|
|
32
|
+
* Absent means "the runtime-wide flag is the whole answer", which is the honest default for a
|
|
33
|
+
* host-registered adapter this package cannot see inside.
|
|
34
|
+
*/
|
|
35
|
+
canSubscribeIdle?(address: RuntimeAddress): Promise<boolean> | boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface DispatchDeps {
|
|
38
|
+
snapshot: DirectorySnapshot;
|
|
39
|
+
adapters: ReadonlyMap<RuntimeKind, RouterMessagingAdapter>;
|
|
40
|
+
policy: InboundPolicy;
|
|
41
|
+
store: RuntimeDirectoryStore;
|
|
42
|
+
now(): number;
|
|
43
|
+
/**
|
|
44
|
+
* Which rows the core is allowed to see through `listReachable`.
|
|
45
|
+
*
|
|
46
|
+
* `resolve` is every object this caller may ADDRESS — including an exited session, which WS-15 §6.2
|
|
47
|
+
* has a routing row for. `list` is WS-10 §10.2's narrower listing eligibility. They are two views of
|
|
48
|
+
* the same snapshot rather than two reads, so a listing and a resolution can never disagree about a
|
|
49
|
+
* row that changed in between.
|
|
50
|
+
*/
|
|
51
|
+
view: "resolve" | "list";
|
|
52
|
+
/**
|
|
53
|
+
* WHERE A SUBSCRIBE GOES (review r1, M1). Absent = straight to the owner adapter.
|
|
54
|
+
*
|
|
55
|
+
* The shared core calls `adapter.subscribeIdle` for both the pure-subscription and the combined
|
|
56
|
+
* `SendMessage{notify_when_idle}` call — the MODEL-facing path. Delegating that to the owner adapter
|
|
57
|
+
* subscribes at the RUNTIME and writes nothing durable, so the router's own `store.subscriptions` —
|
|
58
|
+
* the only thing `noteIdle()` reads — stayed empty and the one notice WS-10 §14 promises could never
|
|
59
|
+
* be routed to anyone. The model was told `subscribed: true` and nothing ever arrived.
|
|
60
|
+
*
|
|
61
|
+
* So the core's dispatcher is given the ROUTER's own subscription door here, and only the router's
|
|
62
|
+
* door talks to the owner adapter (with this field absent, which is also what stops the two from
|
|
63
|
+
* recursing into each other).
|
|
64
|
+
*/
|
|
65
|
+
subscribeIdleVia?: (target: RuntimeAddress, request: {
|
|
66
|
+
messageId: string;
|
|
67
|
+
}) => Promise<DeliveryOutcome>;
|
|
68
|
+
/**
|
|
69
|
+
* The inbound decision has ALREADY been made for this envelope (review r1, low 2).
|
|
70
|
+
*
|
|
71
|
+
* Set when a HELD message is released: `InboundPolicy.reevaluate` decided it and removed its durable
|
|
72
|
+
* record, so re-running `decide` on the way out could produce a second, divergent answer that holds
|
|
73
|
+
* a message whose record is gone — losing it. The accepted-queue slot is still reserved, because
|
|
74
|
+
* that is the half a release genuinely changes.
|
|
75
|
+
*/
|
|
76
|
+
inboundDecided?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export declare function createDispatchingAdapter(deps: DispatchDeps): RuntimeMessagingAdapter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type GlobalMessagingHandle } from "./router.js";
|
|
2
|
+
/** The MCP result shape both branches return — structurally the descriptor's own (WS-14 §11). */
|
|
3
|
+
export interface MessagingToolResult {
|
|
4
|
+
content: Array<{
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
}>;
|
|
8
|
+
isError?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export type MessagingToolHandler = (args: unknown, extra?: unknown) => Promise<MessagingToolResult>;
|
|
11
|
+
/**
|
|
12
|
+
* WS-10 §12's RETRY KEY, on the official branch — and it exists, which was not known until it was
|
|
13
|
+
* measured (item 15; `test/official/runtime-aliases.test.ts` is the measurement).
|
|
14
|
+
*
|
|
15
|
+
* §12 wants a message id derived from (sender session, TOOL-CALL id) so "a retry allocates the SAME
|
|
16
|
+
* id" and returns the stored outcome instead of starting a second turn. On the Winter branch the
|
|
17
|
+
* caller binds `toolUseId` at registration. On the official branch the handler is inside the vendor's
|
|
18
|
+
* in-process MCP server, where the only per-call channel is the second argument the vendor passes —
|
|
19
|
+
* and the reasonable expectation was that it carries MCP request context (a JSON-RPC request id,
|
|
20
|
+
* `_meta`) rather than an Anthropic-API `tool_use_id`, which is one layer up.
|
|
21
|
+
*
|
|
22
|
+
* THE PINNED RUNTIME BRIDGES THEM. Measured on 0.3.250: `extra._meta["claudecode/toolUseId"]` is the
|
|
23
|
+
* exact id the model emitted. So the official branch gets a real §12 key rather than depending on the
|
|
24
|
+
* rapid-repeat guard, and the vendor's own namespaced `_meta` name is read rather than guessed at.
|
|
25
|
+
*
|
|
26
|
+
* A VENDOR-NAMESPACED KEY IS NEVER REBRANDED (WS-01 §5): `claudecode/toolUseId` is the vendor's name
|
|
27
|
+
* for the vendor's field, exactly like `CLAUDE_CONFIG_DIR`. It is read defensively — an absent or
|
|
28
|
+
* non-string value simply falls back to the bound caller's id — because a future pin may move it, and
|
|
29
|
+
* losing the key must degrade to today's behaviour rather than to a crash.
|
|
30
|
+
*/
|
|
31
|
+
export declare const VENDOR_TOOL_USE_ID_META_KEY = "claudecode/toolUseId";
|
|
32
|
+
export declare function toolUseIdFromExtra(extra: unknown): string | undefined;
|
|
33
|
+
export interface MessagingToolHandlers {
|
|
34
|
+
sendMessage: MessagingToolHandler;
|
|
35
|
+
listAgents: MessagingToolHandler;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* WHO IS CALLING — bound at registration, never read out of the arguments.
|
|
39
|
+
*
|
|
40
|
+
* The standing MCP server is materialized per session (WS-14 §11), so the caller is known when the
|
|
41
|
+
* handler is built. Taking it from the ARGUMENTS instead would make the sender's identity something a
|
|
42
|
+
* model could write, and every fence in this package — the owning-parent rule, the self-target
|
|
43
|
+
* refusal, WS-10 §13's sender class, WS-15 §6.2's dedupe key — is keyed on it.
|
|
44
|
+
*
|
|
45
|
+
* `toolUseId` is the second half of WS-10 §12's retry key. It is OPTIONAL because on the official
|
|
46
|
+
* branch it is not available: the in-process MCP server hands a tool handler its arguments, and the
|
|
47
|
+
* router's own materialization does not forward the vendor's `extra` (which is where a request id
|
|
48
|
+
* would be). A caller that cannot supply one gets no dedupe — stated at the door rather than faked
|
|
49
|
+
* with a stable-looking key that would make two different messages one.
|
|
50
|
+
*/
|
|
51
|
+
export interface MessagingToolCaller {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
agentId?: string;
|
|
54
|
+
toolUseId?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare function createMessagingToolHandlers(messaging: GlobalMessagingHandle, caller: MessagingToolCaller | (() => MessagingToolCaller)): MessagingToolHandlers;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { CrossSessionInbound, HeldEntry, Mailbox, PermissionClassLabel } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
2
|
+
import type { HeldMessageRecord, RuntimeDirectoryEntry, RuntimeDirectoryStore } from "../seams/directory-store.js";
|
|
3
|
+
import type { DeliveryOutcome, GlobalAgentMessage, SerializedRuntimeAddress } from "../seams/messaging-contract.js";
|
|
4
|
+
export interface InboundPolicyHooks {
|
|
5
|
+
/**
|
|
6
|
+
* The receiver's own explicit `crossSessionInbound` setting, when the host has one.
|
|
7
|
+
*
|
|
8
|
+
* WS-10 §13: an explicit setting ALWAYS WINS over the class matrix. It is a host fact (the settings
|
|
9
|
+
* hierarchy is the host's), so it is asked for rather than assumed; absent means "no explicit
|
|
10
|
+
* setting", which is what makes the matrix the default rather than the only rule.
|
|
11
|
+
*/
|
|
12
|
+
explicitSetting?: (receiver: RuntimeDirectoryEntry) => Promise<CrossSessionInbound | undefined> | CrossSessionInbound | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* WS-10 §13: "an unauthenticated route is refused BEFORE the matrix."
|
|
15
|
+
*
|
|
16
|
+
* The router's own default answer is "authenticated exactly when the sender is an object this
|
|
17
|
+
* directory authored" — a canonical address the router itself issued and can still resolve. That is
|
|
18
|
+
* the strongest claim a library can make on its own; a cross-machine or phone-originated route
|
|
19
|
+
* "requires an authenticated Winter transport plus its own policy gate", which is the host's, and
|
|
20
|
+
* this hook is where the host says so.
|
|
21
|
+
*/
|
|
22
|
+
authenticatedRoute?: (input: {
|
|
23
|
+
message: GlobalAgentMessage;
|
|
24
|
+
receiver: RuntimeDirectoryEntry;
|
|
25
|
+
senderKnown: boolean;
|
|
26
|
+
}) => Promise<boolean> | boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Called when a held message LEAVES THE MAILBOX WITHOUT BEING DELIVERED — swept by the five-minute
|
|
29
|
+
* dialog expiry, or re-evaluated to `refuse` (review r3, NEW-10).
|
|
30
|
+
*
|
|
31
|
+
* The router supplies it and writes the delivery receipt through, because the ledger is the router's.
|
|
32
|
+
* Without it a sender's durable receipt reads `held` forever for a message that no longer exists —
|
|
33
|
+
* and "held" reads as "waiting for you".
|
|
34
|
+
*/
|
|
35
|
+
onHoldTerminal?: (message: GlobalAgentMessage, outcome: DeliveryOutcome) => Promise<void> | void;
|
|
36
|
+
/** An explicit hold is the receiver's own choice to queue rather than to take; the kind is its own. */
|
|
37
|
+
holdKind?: (input: {
|
|
38
|
+
message: GlobalAgentMessage;
|
|
39
|
+
receiver: RuntimeDirectoryEntry;
|
|
40
|
+
decision: CrossSessionInbound;
|
|
41
|
+
}) => "default" | "explicit";
|
|
42
|
+
}
|
|
43
|
+
export interface InboundPolicyDeps extends InboundPolicyHooks {
|
|
44
|
+
store: RuntimeDirectoryStore;
|
|
45
|
+
now(): number;
|
|
46
|
+
/** The receiver's permission class, asked of the runtime that actually holds it (WS-10 §13). */
|
|
47
|
+
receiverClass(receiver: RuntimeDirectoryEntry): Promise<PermissionClassLabel>;
|
|
48
|
+
}
|
|
49
|
+
/** The two receiver-scoped facts a decision needs, read once per sweep (NEW-9). */
|
|
50
|
+
export interface ReceiverFacts {
|
|
51
|
+
explicitSetting: CrossSessionInbound | undefined;
|
|
52
|
+
receiverClass: PermissionClassLabel;
|
|
53
|
+
}
|
|
54
|
+
export type InboundVerdict =
|
|
55
|
+
/** Deliver. `release()` MUST be called with the eventual outcome so the accepted-queue slot is freed. */
|
|
56
|
+
{
|
|
57
|
+
kind: "accept";
|
|
58
|
+
release(outcome: DeliveryOutcome): void;
|
|
59
|
+
} | {
|
|
60
|
+
kind: "settled";
|
|
61
|
+
outcome: DeliveryOutcome;
|
|
62
|
+
};
|
|
63
|
+
export interface InboundPolicy {
|
|
64
|
+
/** Runs the whole of WS-10 §13 for one envelope against one receiver. */
|
|
65
|
+
decide(receiver: RuntimeDirectoryEntry, message: GlobalAgentMessage, senderKnown: boolean): Promise<InboundVerdict>;
|
|
66
|
+
/**
|
|
67
|
+
* The DECISION alone, with no mailbox effect — the matrix, the explicit setting and the
|
|
68
|
+
* authentication gate, and nothing else.
|
|
69
|
+
*
|
|
70
|
+
* WS-10 §14's returning idle notice is the caller that needs this: "inbound policy applies to the
|
|
71
|
+
* returning notice", and a held subscription "delivers a REDUCED-STATUS notice, not treated as
|
|
72
|
+
* ordinary delivered text". A notice is therefore never held in the mailbox and never occupies an
|
|
73
|
+
* accepted slot — it is reduced or dropped — so it needs the verdict without the bookkeeping that
|
|
74
|
+
* ordinary mail earns.
|
|
75
|
+
*/
|
|
76
|
+
classify(receiver: RuntimeDirectoryEntry, message: GlobalAgentMessage, senderKnown: boolean): Promise<CrossSessionInbound>;
|
|
77
|
+
/**
|
|
78
|
+
* WS-10 §13's "held messages are re-evaluated when the receiver's mode or settings change".
|
|
79
|
+
*
|
|
80
|
+
* Sweeps the 5-minute expiry first, then re-runs the decision for every DEFAULT-class hold (an
|
|
81
|
+
* explicit hold "persists until a later accept/refusal/session end", so it is never auto-promoted),
|
|
82
|
+
* and hands back the envelopes that are now deliverable. Delivering them is the router's job — this
|
|
83
|
+
* module never calls an adapter.
|
|
84
|
+
*/
|
|
85
|
+
reevaluate(receiver: RuntimeDirectoryEntry): Promise<{
|
|
86
|
+
released: GlobalAgentMessage[];
|
|
87
|
+
expired: HeldEntry[];
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Take an accepted-queue slot WITHOUT re-running the decision (review r1, low 2).
|
|
91
|
+
*
|
|
92
|
+
* A RELEASED HOLD HAS ALREADY BEEN DECIDED, by `reevaluate`, which removed its durable record when
|
|
93
|
+
* it promoted it. Re-running `decide` on the way out would let a second, divergent answer hold a
|
|
94
|
+
* message whose record is gone — losing it — while still costing the host's hooks another call. The
|
|
95
|
+
* cap is still honoured, because that is the half a release genuinely changes: a held message
|
|
96
|
+
* occupies no accepted slot, and a delivered one does.
|
|
97
|
+
*/
|
|
98
|
+
reserveAccepted(receiverKey: SerializedRuntimeAddress, messageId: string): InboundVerdict;
|
|
99
|
+
/** WS-10 §13's 5-minute dialog expiry, applied on its own. Returns what it swept. */
|
|
100
|
+
sweepExpired(receiverKey: SerializedRuntimeAddress): Promise<HeldEntry[]>;
|
|
101
|
+
/** What is held for a receiver right now, durably. */
|
|
102
|
+
listHeld(receiverKey: SerializedRuntimeAddress): Promise<HeldMessageRecord[]>;
|
|
103
|
+
readonly caps: {
|
|
104
|
+
held: number;
|
|
105
|
+
accepted: number;
|
|
106
|
+
};
|
|
107
|
+
/** Exposed for the router's own counters; the mailbox is this module's, not the router's. */
|
|
108
|
+
readonly mailbox: Mailbox;
|
|
109
|
+
}
|
|
110
|
+
export declare function createInboundPolicy(deps: InboundPolicyDeps): InboundPolicy;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export { createRuntimeDirectory } from "../directory/directory.js";
|
|
2
|
+
export type { DirectorySnapshot, RuntimeDirectoryHandle, RuntimeDirectoryOptions } from "../directory/directory.js";
|
|
3
|
+
export type { RuntimeDirectoryRecoveryHooks, RuntimeDirectoryRetention } from "../directory/recovery.js";
|
|
4
|
+
export { entryToChildLike, entryToListedRuntimeObject, entryToListedRuntimeObjectList, isListableFrom, isLiveStatus, isResolvableFrom, mergeAdapterOwnedFields, owningSessionIdOf, parentAddressOf, sessionAddressOf } from "../directory/entries.js";
|
|
5
|
+
export { createGlobalMessaging, callerAddressOf, deriveMessageId } from "./router.js";
|
|
6
|
+
export type { GlobalMessagingContext, GlobalMessagingHandle, GlobalMessagingOptions, ReplyRequest } from "./router.js";
|
|
7
|
+
export { createWinterMessagingAdapter } from "./winter-adapter.js";
|
|
8
|
+
export type { WinterMessagingAdapter, WinterMessagingAdapterDeps } from "./winter-adapter.js";
|
|
9
|
+
export { createOfficialMessagingAdapter } from "./official-adapter.js";
|
|
10
|
+
export type { OfficialMessagingAdapter, OfficialMessagingAdapterDeps } from "./official-adapter.js";
|
|
11
|
+
export { createDispatchingAdapter } from "./dispatch.js";
|
|
12
|
+
export type { DispatchDeps, RouterMessagingAdapter } from "./dispatch.js";
|
|
13
|
+
export { createInboundPolicy } from "./inbound.js";
|
|
14
|
+
export type { InboundPolicy, InboundPolicyDeps, InboundPolicyHooks, InboundVerdict } from "./inbound.js";
|
|
15
|
+
export { createAttachedSessionRegistry } from "./sessions.js";
|
|
16
|
+
export type { AttachedOfficialSession, AttachedSession, AttachedSessionRegistry, AttachedWinterSession, LiveSessionStatus } from "./sessions.js";
|
|
17
|
+
export { renderAttributedTurn, renderOwnerQualifiedTurn, UnattributableSenderError } from "./attribution.js";
|
|
18
|
+
export { createMessagingToolHandlers, toolUseIdFromExtra, VENDOR_TOOL_USE_ID_META_KEY } from "./handlers.js";
|
|
19
|
+
export type { MessagingToolCaller, MessagingToolHandler, MessagingToolHandlers, MessagingToolResult } from "./handlers.js";
|
|
20
|
+
import { type RuntimeDirectoryHandle, type RuntimeDirectoryOptions } from "../directory/directory.js";
|
|
21
|
+
import type { SeamContext } from "../seams/context.js";
|
|
22
|
+
import { type GlobalMessagingHandle, type GlobalMessagingOptions } from "./router.js";
|
|
23
|
+
export interface RuntimeMessaging {
|
|
24
|
+
directory: RuntimeDirectoryHandle;
|
|
25
|
+
messaging: GlobalMessagingHandle;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The directory and the router, built together and wired to each other.
|
|
29
|
+
*
|
|
30
|
+
* THE ONE WIRE THAT IS EASY TO MISS: the directory's `ChildLike` view delivers through the ROUTER, so
|
|
31
|
+
* a caller that used `ChildLike.steer`/`resume` (the router core never does — see `entryToChildLike`)
|
|
32
|
+
* reaches the child's own runtime adapter rather than a stub. It is a late binding on purpose: the
|
|
33
|
+
* router does not exist yet when the directory is constructed, so the hook closes over a variable
|
|
34
|
+
* that is assigned one line later.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createRuntimeMessaging(context: SeamContext, options?: {
|
|
37
|
+
directory?: RuntimeDirectoryOptions;
|
|
38
|
+
messaging?: GlobalMessagingOptions;
|
|
39
|
+
}): RuntimeMessaging;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RuntimeDirectory } from "../seams/directory.js";
|
|
2
|
+
import type { RuntimeDirectoryEntry } from "../seams/directory-store.js";
|
|
3
|
+
import type { PermissionClassLabel } from "../seams/messaging-contract.js";
|
|
4
|
+
import type { RouterMessagingAdapter } from "./dispatch.js";
|
|
5
|
+
import type { AttachedOfficialSession, AttachedSessionRegistry } from "./sessions.js";
|
|
6
|
+
export interface OfficialMessagingAdapterDeps {
|
|
7
|
+
directory: RuntimeDirectory;
|
|
8
|
+
sessions: AttachedSessionRegistry<AttachedOfficialSession>;
|
|
9
|
+
/**
|
|
10
|
+
* WS-15 §6.2's "exited official Claude session → explicitly resume by `backendSessionId`,
|
|
11
|
+
* re-establish the adapter, then deliver".
|
|
12
|
+
*
|
|
13
|
+
* INJECTED, because everything that resume needs — WS-14 §2's Options template, §1's launch profile
|
|
14
|
+
* and observed config dir, §3's child environment, §12's credential fetch — belongs to the official
|
|
15
|
+
* ADAPTER lane, and duplicating any of it here would be a second, drifting copy of the branch's
|
|
16
|
+
* hardest-won configuration. This lane owns only the delivery half: it hands back a live handle, and
|
|
17
|
+
* this module pushes into it.
|
|
18
|
+
*
|
|
19
|
+
* Absent: an exited official session is `unavailable`, non-retryable, with the reason naming what is
|
|
20
|
+
* missing — never a silent `not_found`, because the session exists and is resumable by a host that
|
|
21
|
+
* wires this.
|
|
22
|
+
*/
|
|
23
|
+
resumeExited?: (entry: RuntimeDirectoryEntry) => Promise<AttachedOfficialSession | undefined>;
|
|
24
|
+
/**
|
|
25
|
+
* The receiver/sender permission class for an official object, when the host tracks one.
|
|
26
|
+
*
|
|
27
|
+
* The pinned runtime exposes no live permission mode to read, so the honest default is `unknown` —
|
|
28
|
+
* which WS-10 §13's matrix handles explicitly rather than by guessing (`prompts × unknown → accept`,
|
|
29
|
+
* `bypasses × unknown → hold`). A host that knows the mode it launched a session with can say so.
|
|
30
|
+
*/
|
|
31
|
+
permissionClass?: (entry: RuntimeDirectoryEntry) => Promise<PermissionClassLabel> | PermissionClassLabel;
|
|
32
|
+
}
|
|
33
|
+
export interface OfficialMessagingAdapter extends RouterMessagingAdapter {
|
|
34
|
+
readonly sessions: AttachedSessionRegistry<AttachedOfficialSession>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createOfficialMessagingAdapter(deps: OfficialMessagingAdapterDeps): OfficialMessagingAdapter;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { NotificationRecord, SendMessageResult } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
2
|
+
import type { RuntimeDirectoryHandle } from "../directory/directory.js";
|
|
3
|
+
import type { SeamContext } from "../seams/context.js";
|
|
4
|
+
import type { GlobalMessaging, SendMessageRequest } from "../seams/global-messaging.js";
|
|
5
|
+
import type { DeliveryOutcome, GlobalAgentMessage, RuntimeAddress, SerializedRuntimeAddress } from "../seams/messaging-contract.js";
|
|
6
|
+
import { type InboundPolicyHooks } from "./inbound.js";
|
|
7
|
+
import { type OfficialMessagingAdapter, type OfficialMessagingAdapterDeps } from "./official-adapter.js";
|
|
8
|
+
import { type AttachedOfficialSession, type AttachedWinterSession } from "./sessions.js";
|
|
9
|
+
import { type WinterMessagingAdapter, type WinterMessagingAdapterDeps } from "./winter-adapter.js";
|
|
10
|
+
/** The context this factory needs: the seam's, with the directory as the concrete handle. */
|
|
11
|
+
export interface GlobalMessagingContext extends SeamContext {
|
|
12
|
+
directory: RuntimeDirectoryHandle;
|
|
13
|
+
}
|
|
14
|
+
export interface GlobalMessagingOptions extends InboundPolicyHooks {
|
|
15
|
+
now?: () => number;
|
|
16
|
+
/** Passed to the Winter adapter this factory builds (cold-resume options, child resume context). */
|
|
17
|
+
winter?: Omit<WinterMessagingAdapterDeps, "peers" | "directory" | "sessions">;
|
|
18
|
+
/** Passed to the official adapter this factory builds (the resume collaborator, the class hook). */
|
|
19
|
+
official?: Omit<OfficialMessagingAdapterDeps, "directory" | "sessions">;
|
|
20
|
+
/**
|
|
21
|
+
* R-7b-4's one behavioural seam, forwarded to the core: how a THROW out of an adapter is classified.
|
|
22
|
+
*
|
|
23
|
+
* ABSENT MEANS EVERYTHING IS UNCERTAIN, which is the conservative reading and the core's own default
|
|
24
|
+
* — `refused` asserts the effect did NOT happen, and from this side "the call failed" and "the
|
|
25
|
+
* effect happened and then the call failed" are indistinguishable (WS-10 §12).
|
|
26
|
+
*/
|
|
27
|
+
classifyDeliveryError?: (error: unknown) => "refused" | "uncertain";
|
|
28
|
+
}
|
|
29
|
+
export interface ReplyRequest {
|
|
30
|
+
/** The envelope being replied to. Its `to` becomes the reply's `from`, and its `from` the target. */
|
|
31
|
+
original: GlobalAgentMessage;
|
|
32
|
+
body: string;
|
|
33
|
+
summary?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The seam, plus the doors a host and this package's own tool handlers need. */
|
|
36
|
+
export interface GlobalMessagingHandle extends GlobalMessaging {
|
|
37
|
+
readonly winterAdapter: WinterMessagingAdapter;
|
|
38
|
+
readonly officialAdapter: OfficialMessagingAdapter;
|
|
39
|
+
/** Register a live Winter session AND bridge its idle notices. Returns the detach function. */
|
|
40
|
+
attachWinterSession(address: SerializedRuntimeAddress, handle: AttachedWinterSession): () => void;
|
|
41
|
+
attachOfficialSession(address: SerializedRuntimeAddress, handle: AttachedOfficialSession): () => void;
|
|
42
|
+
/**
|
|
43
|
+
* `send()`, keeping the shared core's SUPPLEMENTARY fact about a combined call.
|
|
44
|
+
*
|
|
45
|
+
* The seam pins `send(): Promise<DeliveryOutcome>`, and the delivery outcome is rightly primary —
|
|
46
|
+
* but a call that carried both a message AND `notify_when_idle` has a second, separate result (was
|
|
47
|
+
* the subscription honoured?), and WS-10 §10.1's own result text allows for it. The core reports it
|
|
48
|
+
* beside the outcome rather than inventing an eleventh status; this door is where that survives, and
|
|
49
|
+
* `send()` is exactly `(await sendDetailed(...)).outcome`.
|
|
50
|
+
*/
|
|
51
|
+
sendDetailed(request: SendMessageRequest): Promise<SendMessageResult>;
|
|
52
|
+
/** WS-10 §13's re-evaluation door: sweep, re-decide, deliver what is now acceptable. */
|
|
53
|
+
releaseHeld(receiver: SerializedRuntimeAddress): Promise<DeliveryOutcome[]>;
|
|
54
|
+
/** Reply routing — the one path on which `hopCount` grows, and therefore the one the bound binds. */
|
|
55
|
+
reply(request: ReplyRequest): Promise<DeliveryOutcome>;
|
|
56
|
+
/** WS-06 §3.6's `ReadNotifications` page for one session's own queue. */
|
|
57
|
+
readNotifications(sessionId: string): {
|
|
58
|
+
notifications: NotificationRecord[];
|
|
59
|
+
remaining: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* "This target has gone idle" — WS-10 §14's firing edge, from whatever noticed it.
|
|
63
|
+
*
|
|
64
|
+
* Called by the Winter facet bridge for every live notice and every drained one; a host that detects
|
|
65
|
+
* idleness some other way calls it too. Returns how many notices were pushed.
|
|
66
|
+
*/
|
|
67
|
+
noteIdle(target: SerializedRuntimeAddress, notice?: {
|
|
68
|
+
notificationId?: string;
|
|
69
|
+
content?: string;
|
|
70
|
+
}): Promise<number>;
|
|
71
|
+
/** The caps and windows this router enforces, so a host can render them without re-deriving them. */
|
|
72
|
+
readonly bounds: {
|
|
73
|
+
heldCap: number;
|
|
74
|
+
acceptedCap: number;
|
|
75
|
+
holdExpiryMs: number;
|
|
76
|
+
idleSubscriptionMs: number;
|
|
77
|
+
messageTtlMs: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* WS-10 §12's message id: "derived/persisted from the sender session plus tool-call ID", so that "a
|
|
82
|
+
* retry with the same ID returns the stored outcome".
|
|
83
|
+
*
|
|
84
|
+
* DERIVED, NOT COUNTED, and that is the whole point: the shared core allocates `msg-1`, `msg-2` from
|
|
85
|
+
* an in-process counter, which is stable within one process and COLLIDES across a restart — two
|
|
86
|
+
* different messages would share an id, and the second would be answered with the first's stored
|
|
87
|
+
* outcome. Both components are percent-encoded so the pair can never be ambiguous.
|
|
88
|
+
*/
|
|
89
|
+
export declare function deriveMessageId(senderSessionId: string, toolUseId: string): string;
|
|
90
|
+
export declare function createGlobalMessaging(context: GlobalMessagingContext, options?: GlobalMessagingOptions): GlobalMessagingHandle;
|
|
91
|
+
/**
|
|
92
|
+
* The canonical address a `SendMessage`/`ListAgents` handler runs AS (see `handlers.ts`).
|
|
93
|
+
*
|
|
94
|
+
* The subpath's own `callerAddress` builds `agent:<parent>:<child>` for a child caller and
|
|
95
|
+
* `session:<id>` for a top-level one — the same rule the resolution fence reads, so the handler and
|
|
96
|
+
* the router can never disagree about who is asking.
|
|
97
|
+
*/
|
|
98
|
+
export declare function callerAddressOf(caller: {
|
|
99
|
+
sessionId: string;
|
|
100
|
+
agentId?: string;
|
|
101
|
+
}): RuntimeAddress;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { SessionMessagingFacet } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import type { ListedRuntimeObject, SerializedRuntimeAddress } from "../seams/messaging-contract.js";
|
|
3
|
+
/** The live status a handle can report, when it can. Same vocabulary as the directory's own. */
|
|
4
|
+
export type LiveSessionStatus = ListedRuntimeObject["status"];
|
|
5
|
+
export interface AttachedSession {
|
|
6
|
+
/**
|
|
7
|
+
* Push one turn into this session's input stream.
|
|
8
|
+
*
|
|
9
|
+
* The text is ALREADY RENDERED (attributed, escaped) by the time it gets here — a handle is a pipe,
|
|
10
|
+
* not a policy. Absent on a Winter handle that only carries a facet: the facet's own `deliver` is
|
|
11
|
+
* the runtime-side push, and it renders on the far side.
|
|
12
|
+
*/
|
|
13
|
+
push?(text: string): void | Promise<void>;
|
|
14
|
+
/** The live status, when the host tracks one. Absent falls back to the durable row's `status`. */
|
|
15
|
+
status?(): LiveSessionStatus;
|
|
16
|
+
}
|
|
17
|
+
export interface AttachedWinterSession extends AttachedSession {
|
|
18
|
+
/**
|
|
19
|
+
* `Query.messaging` (R-7b-4). The ONLY door into a spawned Winter session's children — a child
|
|
20
|
+
* engine has no facet surface of its own, so `steer_child`/`resume_child` on the PARENT's facet is
|
|
21
|
+
* how a child is reached, never a session address (Task 0 fix r2, concern 4).
|
|
22
|
+
*/
|
|
23
|
+
readonly messaging?: SessionMessagingFacet;
|
|
24
|
+
}
|
|
25
|
+
export interface AttachedOfficialSession extends AttachedSession {
|
|
26
|
+
/**
|
|
27
|
+
* REQUIRED on this branch, because there is no alternative. The pinned official SDK exposes no
|
|
28
|
+
* messaging surface of any kind (its `Query` declares none), so an input-stream push is the whole
|
|
29
|
+
* mechanism — for the session itself and, owner-qualified, for its children.
|
|
30
|
+
*/
|
|
31
|
+
push(text: string): void | Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface AttachedSessionRegistry<T extends AttachedSession> {
|
|
34
|
+
/** Register a handle for a canonical address. Returns the detach function. */
|
|
35
|
+
attach(address: SerializedRuntimeAddress, handle: T): () => void;
|
|
36
|
+
detach(address: SerializedRuntimeAddress): void;
|
|
37
|
+
get(address: SerializedRuntimeAddress): T | undefined;
|
|
38
|
+
addresses(): SerializedRuntimeAddress[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A registry, and deliberately nothing more: no liveness inference, no reaping, no ordering.
|
|
42
|
+
*
|
|
43
|
+
* ATTACHING IS THE HOST'S CALL, not something this package can discover. `RuntimeSdk.query()` is
|
|
44
|
+
* where a session is created, and until that door routes to both branches (spine-owned, Task 6) a
|
|
45
|
+
* host attaches what it launched. `attach` returning its own detach — rather than the caller
|
|
46
|
+
* remembering the address — is what keeps a handle from outliving the session in the one case that
|
|
47
|
+
* matters, a re-launch of the same address: the OLD detach then removes only the handle it added.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createAttachedSessionRegistry<T extends AttachedSession>(): AttachedSessionRegistry<T>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Options } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import type { RuntimeSdkPeers } from "../sdk.js";
|
|
3
|
+
import type { RuntimeDirectory } from "../seams/directory.js";
|
|
4
|
+
import type { RuntimeDirectoryEntry } from "../seams/directory-store.js";
|
|
5
|
+
import type { PermissionClassLabel } from "../seams/messaging-contract.js";
|
|
6
|
+
import type { RouterMessagingAdapter } from "./dispatch.js";
|
|
7
|
+
import type { ChildSelectionInput } from "../selection/runtime-selection.js";
|
|
8
|
+
import type { AttachedSessionRegistry, AttachedWinterSession } from "./sessions.js";
|
|
9
|
+
export interface WinterMessagingAdapterDeps {
|
|
10
|
+
peers: RuntimeSdkPeers;
|
|
11
|
+
directory: RuntimeDirectory;
|
|
12
|
+
sessions: AttachedSessionRegistry<AttachedWinterSession>;
|
|
13
|
+
/**
|
|
14
|
+
* The `Options` a COLD RESUME is opened with, beside the `resume` the adapter supplies itself.
|
|
15
|
+
*
|
|
16
|
+
* A resume needs a cwd, a model, a permission mode — session facts the messaging layer does not
|
|
17
|
+
* hold and must not invent. Absent means an empty options object, which is a legitimate resume (the
|
|
18
|
+
* transcript carries the session's own configuration) and is what the hermetic tests use.
|
|
19
|
+
*/
|
|
20
|
+
resumeOptions?: (entry: RuntimeDirectoryEntry) => Options | Promise<Options>;
|
|
21
|
+
/**
|
|
22
|
+
* WS-13c §8 / WS-10's Phase 6.6 amendment: the catalogue a RESUMED CHILD re-resolves against.
|
|
23
|
+
*
|
|
24
|
+
* `resumeChildSelection` answers "is the row this child is RECORDED on still servable?" — and a
|
|
25
|
+
* refusal is `{ status: "unavailable", retryable: false, reason: "child-provider-unavailable: …" }`
|
|
26
|
+
* WITH NO GENERATION STARTED, which is why the check runs before the parent's facet is touched.
|
|
27
|
+
* Absent: nothing re-resolves, and the child's own persisted record stands — which is the correct
|
|
28
|
+
* fallback rather than a silent skip, because WS-13c §8 makes that record authoritative anyway; the
|
|
29
|
+
* check only detects DRIFT, and a host with no catalogue has nothing to detect it against.
|
|
30
|
+
*/
|
|
31
|
+
childResumeContext?: (entry: RuntimeDirectoryEntry) => Omit<ChildSelectionInput, "slot" | "model" | "provider"> | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* The permission class of a session this process holds no FACET for — the host's own declaration.
|
|
34
|
+
*
|
|
35
|
+
* A live facet answers from the session's live permission mode and always wins. This is the fallback
|
|
36
|
+
* for a session the router drives through a plain input-stream writer, or one it has a record of and
|
|
37
|
+
* no handle at all: without it every such receiver is `unknown`, and since review r1's D2 made an
|
|
38
|
+
* unknown receiver FAIL CLOSED (its mail is held until the class is known), a host would have no way
|
|
39
|
+
* to say what it launched the session with.
|
|
40
|
+
*/
|
|
41
|
+
permissionClass?: (entry: RuntimeDirectoryEntry) => Promise<PermissionClassLabel> | PermissionClassLabel;
|
|
42
|
+
}
|
|
43
|
+
/** The adapter plus the one thing the router attaches to it: the live-session registry. */
|
|
44
|
+
export interface WinterMessagingAdapter extends RouterMessagingAdapter {
|
|
45
|
+
readonly sessions: AttachedSessionRegistry<AttachedWinterSession>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createWinterMessagingAdapter(deps: WinterMessagingAdapterDeps): WinterMessagingAdapter;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** WS-10 §10.1's bounds. `to`'s own limit is the subpath's; this is the copy the SCHEMA advertises. */
|
|
2
|
+
export declare const SEND_MESSAGE_TO_MAX = 300;
|
|
3
|
+
export declare const SEND_MESSAGE_SUMMARY_MAX = 200;
|
|
4
|
+
/** WS-10 §10.2's bound on both reserved `ListAgents` fields. */
|
|
5
|
+
export declare const LIST_AGENTS_FIELD_MAX = 256;
|
|
6
|
+
export declare const NATIVE_SEND_MESSAGE_SCHEMA: {
|
|
7
|
+
readonly type: "object";
|
|
8
|
+
readonly properties: {
|
|
9
|
+
readonly to: {
|
|
10
|
+
readonly type: "string";
|
|
11
|
+
readonly maxLength: 300;
|
|
12
|
+
readonly description: "no newline, no \"*\" broadcast";
|
|
13
|
+
};
|
|
14
|
+
readonly message: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly description: "required; defaults \"\" for pure idle subscription";
|
|
17
|
+
};
|
|
18
|
+
readonly summary: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly maxLength: 200;
|
|
21
|
+
};
|
|
22
|
+
readonly notify_when_idle: {
|
|
23
|
+
readonly type: "boolean";
|
|
24
|
+
readonly description: "one-shot; main conversation -> same-machine session only";
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
readonly required: readonly ["to", "message"];
|
|
28
|
+
};
|
|
29
|
+
export declare const NATIVE_LIST_AGENTS_SCHEMA: {
|
|
30
|
+
readonly type: "object";
|
|
31
|
+
readonly properties: {
|
|
32
|
+
readonly channel: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly maxLength: 256;
|
|
35
|
+
readonly description: "reserved";
|
|
36
|
+
};
|
|
37
|
+
readonly q: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
readonly maxLength: 256;
|
|
40
|
+
readonly description: "reserved";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/** WS-10 §10.2: "`ListAgents` output is EXACTLY `{ listing: string }`." */
|
|
45
|
+
export declare const NATIVE_LIST_AGENTS_OUTPUT_SCHEMA: {
|
|
46
|
+
readonly type: "object";
|
|
47
|
+
readonly properties: {
|
|
48
|
+
readonly listing: {
|
|
49
|
+
readonly type: "string";
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
readonly required: readonly ["listing"];
|
|
53
|
+
};
|
|
54
|
+
/** The native `SendMessage` arguments (WS-10 §10.1), after validation. */
|
|
55
|
+
export interface NativeSendMessageArgs {
|
|
56
|
+
to: string;
|
|
57
|
+
message: string;
|
|
58
|
+
summary?: string;
|
|
59
|
+
notify_when_idle?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** The native `ListAgents` arguments (WS-10 §10.2). Both fields are reserved in the pinned build. */
|
|
62
|
+
export interface NativeListAgentsArgs {
|
|
63
|
+
channel?: string;
|
|
64
|
+
q?: string;
|
|
65
|
+
}
|
|
66
|
+
export type NativeArgsResult<T> = {
|
|
67
|
+
ok: true;
|
|
68
|
+
args: T;
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
reason: string;
|
|
72
|
+
};
|
|
73
|
+
/** Accepts the native `SendMessage` arguments EXACTLY — no more, no less. */
|
|
74
|
+
export declare function acceptNativeSendMessageArgs(input: unknown): NativeArgsResult<NativeSendMessageArgs>;
|
|
75
|
+
/** The same treatment for `ListAgents`: two reserved optional fields, both capped, nothing else. */
|
|
76
|
+
export declare function acceptNativeListAgentsArgs(input: unknown): NativeArgsResult<NativeListAgentsArgs>;
|