@yanlinglabs/winter-agent-sdk 0.0.1 → 0.0.3

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.
@@ -0,0 +1,119 @@
1
+ import type { PermissionMode } from "../permissions/types.js";
2
+ export type RuntimeKind = "claude-agent" | "winter-agent";
3
+ export type RuntimeObjectKind = "session" | "agent";
4
+ export interface RuntimeAddress {
5
+ objectKind: RuntimeObjectKind;
6
+ runtimeKind: RuntimeKind;
7
+ winterSessionId: string;
8
+ backendSessionId?: string;
9
+ parentWinterSessionId?: string;
10
+ childId?: string;
11
+ }
12
+ export declare function serializeRuntimeAddress(addr: RuntimeAddress): string;
13
+ export interface ListedRuntimeObject {
14
+ address: string;
15
+ name?: string;
16
+ objectKind: RuntimeObjectKind;
17
+ runtimeKind: RuntimeKind;
18
+ status: "starting" | "running" | "idle" | "exited" | "unavailable" | "archived";
19
+ mode: string;
20
+ cwd?: string;
21
+ capabilities: {
22
+ message: boolean;
23
+ resume: boolean;
24
+ notifyWhenIdle: boolean;
25
+ reply: boolean;
26
+ };
27
+ }
28
+ export type DeliveryOutcome = {
29
+ status: "delivered";
30
+ messageId: string;
31
+ } | {
32
+ status: "queued";
33
+ messageId: string;
34
+ } | {
35
+ status: "resumed_and_delivered";
36
+ messageId: string;
37
+ } | {
38
+ status: "held";
39
+ messageId: string;
40
+ reason: string;
41
+ } | {
42
+ status: "subscribed";
43
+ messageId: string;
44
+ } | {
45
+ status: "delivery_uncertain";
46
+ messageId: string;
47
+ deliveryMayHaveOccurred: true;
48
+ reason: string;
49
+ } | {
50
+ status: "refused";
51
+ messageId: string;
52
+ reason: string;
53
+ } | {
54
+ status: "ambiguous";
55
+ messageId: string;
56
+ candidates: ListedRuntimeObject[];
57
+ } | {
58
+ status: "not_found";
59
+ messageId: string;
60
+ reason: string;
61
+ } | {
62
+ status: "unavailable";
63
+ messageId: string;
64
+ retryable: boolean;
65
+ reason: string;
66
+ };
67
+ export interface GlobalAgentMessage {
68
+ messageId: string;
69
+ from: RuntimeAddress;
70
+ fromGeneration: number;
71
+ to: RuntimeAddress;
72
+ toGeneration: number;
73
+ body: string;
74
+ summary?: string;
75
+ notifyWhenIdle: boolean;
76
+ createdAt: number;
77
+ expiresAt: number;
78
+ hopCount: number;
79
+ originToolCallId?: string;
80
+ senderPermissionClass: "prompts" | "bypasses" | "unknown";
81
+ }
82
+ export type ChildLikeStatus = "running" | "completed" | "stopped" | "failed";
83
+ export interface ChildLikeRecord {
84
+ id: string;
85
+ parentSessionId: string;
86
+ name?: string;
87
+ permission: {
88
+ effectiveMode: PermissionMode;
89
+ };
90
+ }
91
+ export interface ChildLike {
92
+ readonly record: ChildLikeRecord;
93
+ status(): ChildLikeStatus;
94
+ steer(msg: GlobalAgentMessage): Promise<DeliveryOutcome>;
95
+ resume(msg: GlobalAgentMessage): Promise<DeliveryOutcome>;
96
+ }
97
+ export interface RuntimeMessagingAdapter {
98
+ listReachable(scope: {
99
+ parent?: RuntimeAddress;
100
+ }): Promise<ListedRuntimeObject[]>;
101
+ steerChild(addr: RuntimeAddress, msg: GlobalAgentMessage): Promise<DeliveryOutcome>;
102
+ resumeChild(addr: RuntimeAddress, msg: GlobalAgentMessage): Promise<DeliveryOutcome>;
103
+ deliverToSession(addr: RuntimeAddress, msg: GlobalAgentMessage): Promise<DeliveryOutcome>;
104
+ subscribeIdle(addr: RuntimeAddress, req: {
105
+ messageId: string;
106
+ }): Promise<DeliveryOutcome>;
107
+ senderPermissionClass(addr: RuntimeAddress): Promise<"prompts" | "bypasses" | "unknown">;
108
+ }
109
+ export interface MessagingRouterSeam {
110
+ allocateMessageId(senderSessionId: string, toolUseId: string): string;
111
+ recordOutcome(messageId: string, outcome: DeliveryOutcome): void;
112
+ lookupOutcome(messageId: string): DeliveryOutcome | undefined;
113
+ children(): ChildLike[];
114
+ }
115
+ export interface FakeMessagingRouterSeam extends MessagingRouterSeam {
116
+ readonly outcomes: Map<string, DeliveryOutcome>;
117
+ setChildren(children: ChildLike[]): void;
118
+ }
119
+ export declare function createFakeMessagingRouterSeam(): FakeMessagingRouterSeam;
@@ -0,0 +1,13 @@
1
+ import { type RuntimeAddress, type RuntimeKind } from "./adapter.js";
2
+ export declare const REFERENCE_RUNTIME_KIND: RuntimeKind;
3
+ export type ToFieldValidation = {
4
+ ok: true;
5
+ } | {
6
+ ok: false;
7
+ message: string;
8
+ };
9
+ export declare function validateToField(to: unknown): ToFieldValidation;
10
+ export declare function buildSessionAddress(winterSessionId: string): RuntimeAddress;
11
+ export declare function buildChildAddress(parentWinterSessionId: string, childId: string): RuntimeAddress;
12
+ export declare function parseRuntimeAddress(serialized: string): RuntimeAddress | undefined;
13
+ export declare function sameAddress(a: RuntimeAddress, b: RuntimeAddress): boolean;
@@ -0,0 +1,43 @@
1
+ /** The tag a rendered turn is wrapped in. One literal, so nothing spells it twice. */
2
+ export declare const AGENT_MESSAGE_TAG = "agent-message";
3
+ /**
4
+ * Fix r2 (N2): the escape that makes the attribution frame mean something.
5
+ *
6
+ * THE FINDING. A rendered turn is TEXT in the receiving session's input, and the runtime concatenates
7
+ * sender-chosen text into it. `body` and `summary` are model-authored on the `SendMessage` path
8
+ * (straight off the tool input), so a subagent could close the runtime's frame and open a second one
9
+ * naming an address it does not own with `sender-permission-class="bypasses"` — measured, delivered
10
+ * intact, and syntactically indistinguishable to the receiving model from the real one. The `from`
11
+ * field was never forgeable; the FRAME was, which made the label decorative.
12
+ *
13
+ * WHAT IS ESCAPED, and nothing more: the two sequences that can end or start a frame (`</tag` and
14
+ * `<tag`) and the double quote that can close an attribute value. A real message never contains the
15
+ * first two, and the quote is escaped only inside attribute values, so "a legitimate message arrives
16
+ * altered" costs a bare `"` in a summary and nothing else. The transformation is VISIBLE rather than
17
+ * silent (`&lt;` / `&quot;`), so a receiver reading a message that genuinely discusses this syntax
18
+ * still sees what was written.
19
+ *
20
+ * WHAT IT IS NOT. Lexical escaping makes the count of attributions in a turn honest; it does not make
21
+ * attribution structural. A protocol-level frame kind — where the receiver's DECODER carries the
22
+ * attribution and a body is inert data whatever it contains — is the real fix, and is recorded as a
23
+ * carry. Until then this is what keeps the label from being trivially imitable.
24
+ */
25
+ export declare function escapeAttributionText(value: string): string;
26
+ /** The same, plus the attribute-value quote — for anything interpolated INSIDE the opening tag. */
27
+ export declare function escapeAttributionAttribute(value: string): string;
28
+ /**
29
+ * Fix r2 (N5): the queue key the FACET files and drains notifications under, built structurally.
30
+ *
31
+ * DELIBERATELY NOT the bare session id. A session's own model drains `notifications.drain(sessionId)`
32
+ * through its `ReadNotifications` tool, and a drain REMOVES — so one key would have whichever side
33
+ * read first silently eat the other's notices.
34
+ *
35
+ * `RESERVED_NOTIFICATION_KEY_PREFIX` is what makes the namespace a rule rather than a coincidence of
36
+ * string concatenation: a session id that already carries the prefix would otherwise collide back
37
+ * into the model's bucket. Product ids are `s_<hex>` so it cannot happen today, and
38
+ * `isReservedNotificationKey` lets a caller assert it instead of assuming it.
39
+ */
40
+ export declare const RESERVED_NOTIFICATION_KEY_PREFIX = "host:";
41
+ export declare function facetNotificationKey(sessionId: string): string;
42
+ /** True for a key in the facet's reserved namespace — i.e. one the session's own model never drains. */
43
+ export declare function isReservedNotificationKey(key: string): boolean;
@@ -0,0 +1,54 @@
1
+ import type { RuntimeObjectKind } from "./adapter.js";
2
+ export declare function isIdleSubscribeSenderAllowed(sender: {
3
+ isChild: boolean;
4
+ }): boolean;
5
+ export declare function isIdleSubscribeTargetAllowed(target: {
6
+ objectKind: RuntimeObjectKind;
7
+ hasReliableIdleSignal: boolean;
8
+ }): boolean;
9
+ export interface NotificationRecord {
10
+ notification_id: string;
11
+ origin: string;
12
+ queued_at: string;
13
+ content: string;
14
+ }
15
+ export interface NotificationQueue {
16
+ push(ownerKey: string, rec: {
17
+ origin: string;
18
+ content: string;
19
+ queuedAtMs: number;
20
+ }): void;
21
+ /**
22
+ * PHASE 7B: observe every push, WITHOUT consuming it. Returns an unsubscribe.
23
+ *
24
+ * The queue is the DURABLE record a host drains; this is the live signal a host can act on
25
+ * immediately. They are deliberately the same notice, correlated by `notification_id`: a listener
26
+ * that never fires (a crashed host, a host that reconnects later) loses nothing, because the entry
27
+ * is still queued for the drain -- which is what makes WS-15 §6.4's restart recovery possible at
28
+ * all. A listener must therefore NOT drain in response; the host acknowledges by draining.
29
+ *
30
+ * OPTIONAL, so a host that supplies its own `NotificationQueue` implementation still satisfies this
31
+ * interface. Its absence degrades to "drain only", never to a dropped notice.
32
+ */
33
+ subscribe?(listener: (ownerKey: string, rec: NotificationRecord) => void): () => void;
34
+ drain(ownerKey: string, max?: number): {
35
+ notifications: NotificationRecord[];
36
+ remaining: number;
37
+ };
38
+ pendingCount(ownerKey: string): number;
39
+ }
40
+ export declare function createNotificationQueue(): NotificationQueue;
41
+ export interface PendingIdleSubscription {
42
+ messageId: string;
43
+ subscriberKey: string;
44
+ targetKey: string;
45
+ createdAt: number;
46
+ expiresAt: number;
47
+ }
48
+ export interface IdleSubscriptionStore {
49
+ subscribe(sub: Omit<PendingIdleSubscription, "createdAt" | "expiresAt">, now: number): void;
50
+ fireIdle(targetKey: string, now: number, computeReducedStatus: (subscriberKey: string) => boolean, queue: NotificationQueue, originLabel: string): number;
51
+ sweepExpired(now: number): void;
52
+ pendingCount(targetKey: string): number;
53
+ }
54
+ export declare function createIdleSubscriptionStore(): IdleSubscriptionStore;
@@ -0,0 +1,39 @@
1
+ import type { PermissionMode } from "../permissions/types.js";
2
+ export type PermissionClassLabel = "prompts" | "bypasses" | "unknown";
3
+ export type CrossSessionInbound = "accept" | "hold" | "refuse";
4
+ export declare function classifyPermissionMode(mode: PermissionMode, opts: {
5
+ bypassAvailable: boolean;
6
+ }): PermissionClassLabel;
7
+ export declare function mapFromModeToPermissionClass(fromMode: "bypass" | "prompting" | undefined): PermissionClassLabel;
8
+ export declare function defaultInboundResult(receiverClass: PermissionClassLabel, senderClass: PermissionClassLabel): "accept" | "hold";
9
+ export interface InboundDecisionParams {
10
+ authenticated: boolean;
11
+ explicitSetting?: CrossSessionInbound;
12
+ receiverClass: PermissionClassLabel;
13
+ senderClass: PermissionClassLabel;
14
+ }
15
+ export declare function resolveInboundDecision(params: InboundDecisionParams): CrossSessionInbound;
16
+ export interface HeldEntry {
17
+ messageId: string;
18
+ reason: string;
19
+ kind: "default" | "explicit";
20
+ heldAt: number;
21
+ expiresAt?: number;
22
+ }
23
+ export interface Mailbox {
24
+ hold(receiverKey: string, entry: HeldEntry): boolean;
25
+ accept(receiverKey: string): boolean;
26
+ releaseAccepted(receiverKey: string, n?: number): void;
27
+ heldCount(receiverKey: string): number;
28
+ acceptedCount(receiverKey: string): number;
29
+ listHeld(receiverKey: string): readonly HeldEntry[];
30
+ takeHeld(receiverKey: string, messageId: string): HeldEntry | undefined;
31
+ reevaluate(receiverKey: string, decide: (entry: HeldEntry) => CrossSessionInbound): Array<{
32
+ entry: HeldEntry;
33
+ next: CrossSessionInbound;
34
+ }>;
35
+ sweepExpired(receiverKey: string, now: number): HeldEntry[];
36
+ }
37
+ export declare function createMailbox(): Mailbox;
38
+ export declare function buildDefaultHoldEntry(messageId: string, reason: string, now: number): HeldEntry;
39
+ export declare function buildExplicitHoldEntry(messageId: string, reason: string, now: number): HeldEntry;
@@ -0,0 +1,15 @@
1
+ export { serializeRuntimeAddress, createFakeMessagingRouterSeam, } from "./adapter.js";
2
+ export type { RuntimeKind, RuntimeObjectKind, RuntimeAddress, ListedRuntimeObject, DeliveryOutcome, GlobalAgentMessage, RuntimeMessagingAdapter, MessagingRouterSeam, FakeMessagingRouterSeam, ChildLike, ChildLikeRecord, ChildLikeStatus, } from "./adapter.js";
3
+ export { REFERENCE_RUNTIME_KIND, validateToField, buildSessionAddress, buildChildAddress, parseRuntimeAddress, sameAddress, } from "./addressing.js";
4
+ export type { ToFieldValidation } from "./addressing.js";
5
+ export { MAX_GLOBAL_MESSAGE_SIZE, DEFAULT_MESSAGE_TTL_MS, MAX_HOP_COUNT, RAPID_REPEAT_WINDOW_MS, HELD_INBOX_CAP, ACCEPTED_QUEUE_CAP, DEFAULT_HOLD_EXPIRY_MS, NOTIFY_IDLE_EXPIRY_MS, messageExceedsMaxSize, hopCountExceeded, delivered, queued, resumedAndDelivered, held, subscribed, deliveryUncertain, refused, ambiguous, notFound, unavailable, createLoopGuard, } from "./outcomes.js";
6
+ export type { LoopGuard } from "./outcomes.js";
7
+ export { childToListedRuntimeObject, resolveTarget } from "./resolution.js";
8
+ export type { ResolutionInputs, ResolutionResult } from "./resolution.js";
9
+ export { classifyPermissionMode, mapFromModeToPermissionClass, defaultInboundResult, resolveInboundDecision, createMailbox, buildDefaultHoldEntry, buildExplicitHoldEntry, } from "./inbound.js";
10
+ export type { PermissionClassLabel, CrossSessionInbound, InboundDecisionParams, HeldEntry, Mailbox } from "./inbound.js";
11
+ export { isIdleSubscribeSenderAllowed, isIdleSubscribeTargetAllowed, createNotificationQueue, createIdleSubscriptionStore, } from "./idle.js";
12
+ export type { NotificationRecord, NotificationQueue, PendingIdleSubscription, IdleSubscriptionStore } from "./idle.js";
13
+ export { AGENT_MESSAGE_TAG, RESERVED_NOTIFICATION_KEY_PREFIX, escapeAttributionText, escapeAttributionAttribute, facetNotificationKey, isReservedNotificationKey } from "./attribution.js";
14
+ export { MAX_TRACKED_MESSAGE_IDS, rememberBounded, createMessagingRouterSeam, createSubscriberDirectory, callerAddress, sendMessage, listAgents, readNotifications, createMessagingRouter, } from "./router.js";
15
+ export type { MessagingRouterSeamWithRoster, SubscriberDirectory, MessagingRuntimeDeps, CallerContext, SessionCallerContext, SendMessageInput, NotifyOutcome, SendMessageResult, ListAgentsInput, MessagingRouter, } from "./router.js";
@@ -0,0 +1,118 @@
1
+ import {
2
+ serializeRuntimeAddress2,
3
+ createFakeMessagingRouterSeam2,
4
+ REFERENCE_RUNTIME_KIND2,
5
+ validateToField2,
6
+ buildSessionAddress2,
7
+ buildChildAddress2,
8
+ parseRuntimeAddress2,
9
+ sameAddress2,
10
+ MAX_GLOBAL_MESSAGE_SIZE2,
11
+ DEFAULT_MESSAGE_TTL_MS2,
12
+ MAX_HOP_COUNT2,
13
+ RAPID_REPEAT_WINDOW_MS2,
14
+ HELD_INBOX_CAP2,
15
+ ACCEPTED_QUEUE_CAP2,
16
+ DEFAULT_HOLD_EXPIRY_MS2,
17
+ NOTIFY_IDLE_EXPIRY_MS2,
18
+ messageExceedsMaxSize2,
19
+ hopCountExceeded2,
20
+ delivered2,
21
+ queued2,
22
+ resumedAndDelivered2,
23
+ held2,
24
+ subscribed2,
25
+ deliveryUncertain2,
26
+ refused2,
27
+ ambiguous2,
28
+ notFound2,
29
+ unavailable2,
30
+ createLoopGuard2,
31
+ childToListedRuntimeObject2,
32
+ resolveTarget2,
33
+ classifyPermissionMode2,
34
+ mapFromModeToPermissionClass2,
35
+ defaultInboundResult2,
36
+ resolveInboundDecision2,
37
+ createMailbox2,
38
+ buildDefaultHoldEntry2,
39
+ buildExplicitHoldEntry2,
40
+ isIdleSubscribeSenderAllowed2,
41
+ isIdleSubscribeTargetAllowed2,
42
+ createNotificationQueue2,
43
+ createIdleSubscriptionStore2,
44
+ AGENT_MESSAGE_TAG2,
45
+ escapeAttributionText2,
46
+ escapeAttributionAttribute2,
47
+ RESERVED_NOTIFICATION_KEY_PREFIX2,
48
+ facetNotificationKey2,
49
+ isReservedNotificationKey2,
50
+ MAX_TRACKED_MESSAGE_IDS2,
51
+ rememberBounded2,
52
+ createMessagingRouterSeam2,
53
+ createSubscriberDirectory2,
54
+ callerAddress2,
55
+ sendMessage2,
56
+ listAgents2,
57
+ readNotifications2,
58
+ createMessagingRouter2
59
+ } from "../index-51ysrfm8.js";
60
+ export {
61
+ ACCEPTED_QUEUE_CAP2 as ACCEPTED_QUEUE_CAP,
62
+ AGENT_MESSAGE_TAG2 as AGENT_MESSAGE_TAG,
63
+ DEFAULT_HOLD_EXPIRY_MS2 as DEFAULT_HOLD_EXPIRY_MS,
64
+ DEFAULT_MESSAGE_TTL_MS2 as DEFAULT_MESSAGE_TTL_MS,
65
+ HELD_INBOX_CAP2 as HELD_INBOX_CAP,
66
+ MAX_GLOBAL_MESSAGE_SIZE2 as MAX_GLOBAL_MESSAGE_SIZE,
67
+ MAX_HOP_COUNT2 as MAX_HOP_COUNT,
68
+ MAX_TRACKED_MESSAGE_IDS2 as MAX_TRACKED_MESSAGE_IDS,
69
+ NOTIFY_IDLE_EXPIRY_MS2 as NOTIFY_IDLE_EXPIRY_MS,
70
+ RAPID_REPEAT_WINDOW_MS2 as RAPID_REPEAT_WINDOW_MS,
71
+ REFERENCE_RUNTIME_KIND2 as REFERENCE_RUNTIME_KIND,
72
+ RESERVED_NOTIFICATION_KEY_PREFIX2 as RESERVED_NOTIFICATION_KEY_PREFIX,
73
+ ambiguous2 as ambiguous,
74
+ buildChildAddress2 as buildChildAddress,
75
+ buildDefaultHoldEntry2 as buildDefaultHoldEntry,
76
+ buildExplicitHoldEntry2 as buildExplicitHoldEntry,
77
+ buildSessionAddress2 as buildSessionAddress,
78
+ callerAddress2 as callerAddress,
79
+ childToListedRuntimeObject2 as childToListedRuntimeObject,
80
+ classifyPermissionMode2 as classifyPermissionMode,
81
+ createFakeMessagingRouterSeam2 as createFakeMessagingRouterSeam,
82
+ createIdleSubscriptionStore2 as createIdleSubscriptionStore,
83
+ createLoopGuard2 as createLoopGuard,
84
+ createMailbox2 as createMailbox,
85
+ createMessagingRouter2 as createMessagingRouter,
86
+ createMessagingRouterSeam2 as createMessagingRouterSeam,
87
+ createNotificationQueue2 as createNotificationQueue,
88
+ createSubscriberDirectory2 as createSubscriberDirectory,
89
+ defaultInboundResult2 as defaultInboundResult,
90
+ delivered2 as delivered,
91
+ deliveryUncertain2 as deliveryUncertain,
92
+ escapeAttributionAttribute2 as escapeAttributionAttribute,
93
+ escapeAttributionText2 as escapeAttributionText,
94
+ facetNotificationKey2 as facetNotificationKey,
95
+ held2 as held,
96
+ hopCountExceeded2 as hopCountExceeded,
97
+ isIdleSubscribeSenderAllowed2 as isIdleSubscribeSenderAllowed,
98
+ isIdleSubscribeTargetAllowed2 as isIdleSubscribeTargetAllowed,
99
+ isReservedNotificationKey2 as isReservedNotificationKey,
100
+ listAgents2 as listAgents,
101
+ mapFromModeToPermissionClass2 as mapFromModeToPermissionClass,
102
+ messageExceedsMaxSize2 as messageExceedsMaxSize,
103
+ notFound2 as notFound,
104
+ parseRuntimeAddress2 as parseRuntimeAddress,
105
+ queued2 as queued,
106
+ readNotifications2 as readNotifications,
107
+ refused2 as refused,
108
+ rememberBounded2 as rememberBounded,
109
+ resolveInboundDecision2 as resolveInboundDecision,
110
+ resolveTarget2 as resolveTarget,
111
+ resumedAndDelivered2 as resumedAndDelivered,
112
+ sameAddress2 as sameAddress,
113
+ sendMessage2 as sendMessage,
114
+ serializeRuntimeAddress2 as serializeRuntimeAddress,
115
+ subscribed2 as subscribed,
116
+ unavailable2 as unavailable,
117
+ validateToField2 as validateToField
118
+ };
@@ -0,0 +1,25 @@
1
+ import type { DeliveryOutcome, ListedRuntimeObject } from "./adapter.js";
2
+ export declare const MAX_GLOBAL_MESSAGE_SIZE = 1000000;
3
+ export declare const DEFAULT_MESSAGE_TTL_MS: number;
4
+ export declare const MAX_HOP_COUNT = 10;
5
+ export declare const RAPID_REPEAT_WINDOW_MS = 5000;
6
+ export declare const HELD_INBOX_CAP = 100;
7
+ export declare const ACCEPTED_QUEUE_CAP = 50;
8
+ export declare const DEFAULT_HOLD_EXPIRY_MS: number;
9
+ export declare const NOTIFY_IDLE_EXPIRY_MS: number;
10
+ export declare function messageExceedsMaxSize(body: string): boolean;
11
+ export declare function hopCountExceeded(hopCount: number): boolean;
12
+ export declare function delivered(messageId: string): DeliveryOutcome;
13
+ export declare function queued(messageId: string): DeliveryOutcome;
14
+ export declare function resumedAndDelivered(messageId: string): DeliveryOutcome;
15
+ export declare function held(messageId: string, reason: string): DeliveryOutcome;
16
+ export declare function subscribed(messageId: string): DeliveryOutcome;
17
+ export declare function deliveryUncertain(messageId: string, reason: string): DeliveryOutcome;
18
+ export declare function refused(messageId: string, reason: string): DeliveryOutcome;
19
+ export declare function ambiguous(messageId: string, candidates: ListedRuntimeObject[]): DeliveryOutcome;
20
+ export declare function notFound(messageId: string, reason: string): DeliveryOutcome;
21
+ export declare function unavailable(messageId: string, retryable: boolean, reason: string): DeliveryOutcome;
22
+ export interface LoopGuard {
23
+ check(from: string, to: string, body: string, now: number): "ok" | "duplicate";
24
+ }
25
+ export declare function createLoopGuard(): LoopGuard;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * WS-10 §11: the resolution algorithm -- rules 1-6, MUST, in order.
3
+ *
4
+ * RUNTIME KIND. A serialized address (`session:<id>` / `agent:<parent>:<child>`) carries NO runtime
5
+ * kind -- WS-10 §11 puts it in the directory record instead -- so `parseRuntimeAddress` can only ever
6
+ * stamp a default. Where a `ListedRuntimeObject` row is available, THAT row's declared `runtimeKind`
7
+ * is authoritative and this function carries it onto the resolved address; a child resolves under
8
+ * `winter-agent`, since a child of a Winter session is one by construction. A consumer resolving from
9
+ * its OWN directory (the router) must overlay `runtimeKind` the same way for any address it builds by
10
+ * hand. `sameAddress` is unaffected -- it compares serializations, which never carry the kind.
11
+ *
12
+ * Consumes the `ChildLike` boundary interface and the RuntimeAddress/ListedRuntimeObject/
13
+ * serializeRuntimeAddress shapes from adapter.ts, and nothing else: resolution is pure, so both the
14
+ * Winter runtime's in-process adapter and the router package's cross-runtime one get identical
15
+ * answers.
16
+ */
17
+ import { type RuntimeAddress, type ListedRuntimeObject, type ChildLike } from "./adapter.js";
18
+ export declare function childToListedRuntimeObject(parentSessionId: string, child: ChildLike): ListedRuntimeObject;
19
+ export interface ResolutionInputs {
20
+ to: string;
21
+ callerParentSessionId: string;
22
+ children: readonly ChildLike[];
23
+ peers: readonly ListedRuntimeObject[];
24
+ }
25
+ export type ResolutionResult = {
26
+ kind: "resolved";
27
+ address: RuntimeAddress;
28
+ child?: ChildLike;
29
+ } | {
30
+ kind: "ambiguous";
31
+ candidates: ListedRuntimeObject[];
32
+ } | {
33
+ kind: "stale";
34
+ message: string;
35
+ } | {
36
+ kind: "not_found";
37
+ message: string;
38
+ };
39
+ export declare function resolveTarget(input: ResolutionInputs): ResolutionResult;
@@ -0,0 +1,100 @@
1
+ import { type RuntimeAddress, type ListedRuntimeObject, type DeliveryOutcome, type RuntimeMessagingAdapter, type MessagingRouterSeam, type ChildLike } from "./adapter.js";
2
+ import type { NotificationQueue, NotificationRecord } from "./idle.js";
3
+ import { type LoopGuard } from "./outcomes.js";
4
+ export interface MessagingRouterSeamWithRoster extends MessagingRouterSeam {
5
+ addChildRosterSource(getChildren: () => readonly ChildLike[]): () => void;
6
+ }
7
+ export declare const MAX_TRACKED_MESSAGE_IDS = 10000;
8
+ export declare function rememberBounded<V>(map: Map<string, V>, key: string, value: V, cap?: number): void;
9
+ export declare function createMessagingRouterSeam(): MessagingRouterSeamWithRoster;
10
+ export interface SubscriberDirectory {
11
+ remember(messageId: string, subscriberSessionId: string): void;
12
+ lookup(messageId: string): string | undefined;
13
+ }
14
+ export declare function createSubscriberDirectory(): SubscriberDirectory;
15
+ export interface MessagingRuntimeDeps {
16
+ seam: MessagingRouterSeam;
17
+ adapter: RuntimeMessagingAdapter;
18
+ notifications: NotificationQueue;
19
+ loopGuard: LoopGuard;
20
+ subscribers: SubscriberDirectory;
21
+ now(): number;
22
+ /**
23
+ * How a THROW out of an adapter delivery call is classified (R-7b-4's one behavioural seam).
24
+ *
25
+ * WS-10 §12's crash-window semantics make `delivery_uncertain` the honest default for an
26
+ * unexplained throw: from here, "the call failed" and "the effect happened and then the call
27
+ * failed" are indistinguishable. But a POLICY refusal thrown by an adapter is neither -- it is a
28
+ * clean, side-effect-free "no", and reporting it as uncertain would be a lie in the safe-looking
29
+ * direction.
30
+ *
31
+ * The core cannot recognise those classes itself: they belong to whichever runtime the adapter
32
+ * drives (the Winter runtime's `ChildResumeModeIncomparableError` under RULING P4-D; the router
33
+ * package's own official-branch refusals). So the owner supplies the predicate. ABSENT means
34
+ * "everything is uncertain", which is exactly the conservative reading.
35
+ */
36
+ classifyDeliveryError?(err: unknown): "refused" | "uncertain";
37
+ }
38
+ export interface CallerContext {
39
+ sessionId: string;
40
+ agentId?: string;
41
+ toolUseId: string;
42
+ }
43
+ export declare function callerAddress(caller: {
44
+ sessionId: string;
45
+ agentId?: string;
46
+ }): RuntimeAddress;
47
+ export interface SendMessageInput {
48
+ to: string;
49
+ message: string;
50
+ summary?: string;
51
+ notify_when_idle?: boolean;
52
+ }
53
+ export interface NotifyOutcome {
54
+ subscribed?: true;
55
+ refused?: string;
56
+ }
57
+ export interface SendMessageResult {
58
+ outcome: DeliveryOutcome;
59
+ notify?: NotifyOutcome;
60
+ }
61
+ export declare function sendMessage(deps: MessagingRuntimeDeps, caller: CallerContext, input: SendMessageInput): Promise<SendMessageResult>;
62
+ export interface ListAgentsInput {
63
+ channel?: string;
64
+ q?: string;
65
+ }
66
+ /**
67
+ * WS-10 §10.2's line format for the one `listing` string.
68
+ *
69
+ * `export`ed for `../tools/messaging-handlers.ts` alone — NOT re-exported from this subpath's barrel,
70
+ * so it stays an internal seam rather than new published surface. The tool handler needs the SAME
71
+ * renderer this function already is: `listAgents` returns `{ listing, rows }` for a host that owns
72
+ * the deps, while the tool handler works through the address-centric port and has only rows, and a
73
+ * second formatter there would be a second answer to "what does the model see".
74
+ */
75
+ export declare function formatListing(rows: readonly ListedRuntimeObject[]): string;
76
+ export interface SessionCallerContext {
77
+ sessionId: string;
78
+ }
79
+ export declare function listAgents(deps: MessagingRuntimeDeps, caller: SessionCallerContext, _input: ListAgentsInput): Promise<{
80
+ listing: string;
81
+ rows: ListedRuntimeObject[];
82
+ }>;
83
+ export declare function readNotifications(deps: MessagingRuntimeDeps, caller: SessionCallerContext): {
84
+ notifications: NotificationRecord[];
85
+ remaining: number;
86
+ };
87
+ export type { RuntimeAddress, ListedRuntimeObject, DeliveryOutcome };
88
+ export interface MessagingRouter {
89
+ sendMessage(caller: CallerContext, input: SendMessageInput): Promise<SendMessageResult>;
90
+ listAgents(caller: SessionCallerContext, input: ListAgentsInput): Promise<{
91
+ listing: string;
92
+ rows: ListedRuntimeObject[];
93
+ }>;
94
+ readNotifications(caller: SessionCallerContext): {
95
+ notifications: NotificationRecord[];
96
+ remaining: number;
97
+ };
98
+ readonly deps: MessagingRuntimeDeps;
99
+ }
100
+ export declare function createMessagingRouter(deps: MessagingRuntimeDeps): MessagingRouter;
@@ -1 +1,3 @@
1
+ export declare const TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
2
+ export declare function isVendorCompliantProjectKey(key: string): boolean;
1
3
  export declare function transcriptProjectKey(absPath: string): string;