@yanlinglabs/winter-agent-sdk 0.0.2 → 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.
@@ -56,7 +56,7 @@ import {
56
56
  listAgents2,
57
57
  readNotifications2,
58
58
  createMessagingRouter2
59
- } from "../index-h1tryj38.js";
59
+ } from "../index-51ysrfm8.js";
60
60
  export {
61
61
  ACCEPTED_QUEUE_CAP2 as ACCEPTED_QUEUE_CAP,
62
62
  AGENT_MESSAGE_TAG2 as AGENT_MESSAGE_TAG,
@@ -63,6 +63,16 @@ export interface ListAgentsInput {
63
63
  channel?: string;
64
64
  q?: string;
65
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;
66
76
  export interface SessionCallerContext {
67
77
  sessionId: string;
68
78
  }
@@ -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;
@@ -0,0 +1,39 @@
1
+ /** The native `SendMessage` arguments (WS-10 §10.1), after validation. */
2
+ export interface NativeSendMessageArgs {
3
+ to: string;
4
+ message: string;
5
+ summary?: string;
6
+ notify_when_idle?: boolean;
7
+ }
8
+ /** The native `ListAgents` arguments (WS-10 §10.2). Both fields are reserved in the pinned build. */
9
+ export interface NativeListAgentsArgs {
10
+ channel?: string;
11
+ q?: string;
12
+ }
13
+ export type NativeArgsResult<T> = {
14
+ ok: true;
15
+ args: T;
16
+ } | {
17
+ ok: false;
18
+ reason: string;
19
+ };
20
+ /** Accepts the native `SendMessage` arguments EXACTLY — no more, no less. */
21
+ export declare function acceptNativeSendMessageArgs(input: unknown): NativeArgsResult<NativeSendMessageArgs>;
22
+ /** The same treatment for `ListAgents`: two reserved optional fields, both capped, nothing else. */
23
+ export declare function acceptNativeListAgentsArgs(input: unknown): NativeArgsResult<NativeListAgentsArgs>;
24
+ /**
25
+ * `ReadNotifications` takes the empty object, and only the empty object (ruling P-4).
26
+ *
27
+ * The Winter runtime's executor used to accept stray fields on the reasoning that an inert extra is
28
+ * not a reason to fail an otherwise-harmless call. That reasoning is right about HARM and wrong
29
+ * about SCHEMAS: a model that got away with `{ limit: 5 }` here has been told, by the runtime's own
30
+ * silence, that a `limit` exists.
31
+ */
32
+ export declare function acceptNativeReadNotificationsArgs(input: unknown): NativeArgsResult<Record<string, never>>;
33
+ /**
34
+ * WS-10 §10.1: "summary?: derived from first message line when absent; truncated when overlong."
35
+ *
36
+ * NEVER a validation error — a computed value, or absent when there is nothing to derive from (the
37
+ * pure-idle-subscription case, whose message is empty by construction).
38
+ */
39
+ export declare function deriveSendMessageSummary(rawSummary: string | undefined, message: string): string | undefined;
@@ -0,0 +1,106 @@
1
+ import type { SessionKey, SessionStore } from "../store/session-store.js";
2
+ import type { WinterToolHandler } from "./messaging-handlers.js";
3
+ /**
4
+ * One raw entry of a session's transcript — deliberately narrower/speech-shaped (who said what) than
5
+ * any host's own provider message type, which additionally carries structured content blocks for
6
+ * wire purposes this assembler has no need of.
7
+ */
8
+ export interface TranscriptEntry {
9
+ role: "user" | "assistant" | "tool";
10
+ text: string;
11
+ }
12
+ /**
13
+ * `getEntries` may be SYNCHRONOUS or async. The Winter runtime's real source is a live in-memory
14
+ * array captured by reference from the round loop (synchronous, and it must stay that way — a
15
+ * snapshot taken at wiring time would be empty forever); a host reading a durable transcript off
16
+ * disk needs the promise. The union is what lets one factory serve both.
17
+ */
18
+ export interface TranscriptSource {
19
+ getEntries(): TranscriptEntry[] | Promise<TranscriptEntry[]>;
20
+ }
21
+ export interface AdvisorReviewerRequest {
22
+ messages: ReadonlyArray<{
23
+ role: "user" | "assistant" | "tool";
24
+ content: string;
25
+ }>;
26
+ }
27
+ export interface AdvisorReviewerTurn {
28
+ kind: string;
29
+ text?: string;
30
+ }
31
+ export interface AdvisorReviewer {
32
+ generate(input: AdvisorReviewerRequest): Promise<AdvisorReviewerTurn>;
33
+ }
34
+ /**
35
+ * Resolves BOTH the provider to call and the model id the result must report, TOGETHER: WS-06 §4's
36
+ * result shape pins `model: string` as required, but a provider interface has no `model` field to
37
+ * read back — whatever backs the reviewer capability must hand back the model id alongside the
38
+ * provider instance it resolved, in ONE seam, rather than two seams that could disagree.
39
+ */
40
+ export interface ResolvedReviewer {
41
+ provider: AdvisorReviewer;
42
+ model: string;
43
+ }
44
+ /** `undefined` is the "no reviewer resolvable" case — WS-06 §4's ordinary tool error, never a throw. */
45
+ export type ReviewerResolver = () => ResolvedReviewer | undefined;
46
+ export interface AdvisorToolDeps {
47
+ transcriptSource: TranscriptSource;
48
+ resolveReviewer: ReviewerResolver;
49
+ /** Injectable so a host can pin the truncation boundary without a multi-KB fixture transcript. */
50
+ maxChars?: number;
51
+ }
52
+ export declare const ADVISOR_DEFAULT_MAX_CHARS = 20000;
53
+ /**
54
+ * RULING R3-3: "the assembler enforces the enforceable floor now — provider-opaque state
55
+ * (encrypted_content, signatures, reasoning items) is NEVER included."
56
+ *
57
+ * `thinking` and `redacted_thinking` are in the list because a probe fed both to a scripted reviewer
58
+ * as literal transcript text and both reached the wire verbatim, payload included, while the
59
+ * original three were correctly stripped. WS-06 §4's constraint reads "provider-opaque state", not
60
+ * "these three keys", and `redacted_thinking.data` is opaque by name.
61
+ *
62
+ * THE COST IS REAL AND ACCEPTED: "thinking" is an ordinary English word, so a review line that
63
+ * merely USES it ("I was thinking about the schema") is dropped along with the ones that carry a
64
+ * key. That is this function's declared posture — drop the whole line, never partially redact, fail
65
+ * toward the reviewer seeing less — and the price is one line of context in an advisory channel
66
+ * against a class of leak the transcript has no other guard for.
67
+ */
68
+ export declare const OPAQUE_MARKERS: readonly ["encrypted_content", "reasoning_item", "signature", "thinking", "redacted_thinking"];
69
+ export declare function stripOpaqueMarkers(text: string): string;
70
+ /**
71
+ * Keeps the transcript TAIL (recent turns are what advice needs) and reports `truncated: true` only
72
+ * when something was actually clipped. Opaque-marker stripping runs PER ENTRY before truncation is
73
+ * measured, so a kept entry is always a whole, already-cleaned entry — truncation never bisects one,
74
+ * and a 4-KB `signature:` line never evicts the real conversation that came before it.
75
+ */
76
+ export declare function assembleReviewerMessages(entries: readonly TranscriptEntry[], maxChars?: number): {
77
+ messages: Array<{
78
+ role: "user" | "assistant" | "tool";
79
+ content: string;
80
+ }>;
81
+ truncated: boolean;
82
+ };
83
+ /**
84
+ * NO CACHE (WS-06 §4's closing line: "the advisor and the auto-mode classifier are separate routes
85
+ * and MUST NOT share a verdict cache"). Every call re-resolves the reviewer, re-assembles the
86
+ * transcript, and re-generates from scratch; nothing in this module is memoized, so there is nothing
87
+ * here that COULD be shared with a classifier's verdict cache even by accident.
88
+ */
89
+ export declare function createAdvisorToolHandler(deps: AdvisorToolDeps): WinterToolHandler;
90
+ /**
91
+ * A `TranscriptSource` over the DURABLE transcript of one session.
92
+ *
93
+ * For a host that does not hold the live turn array — the router package, or any consumer wiring the
94
+ * advisor outside an engine's own closure. The Winter runtime keeps its in-memory source (the live
95
+ * `messages` array, captured by reference), because a resumed session's file and its live turns are
96
+ * not the same conversation until the turn ends.
97
+ *
98
+ * Reads through the pinned `SessionStore` interface so a host may inject an in-memory one; the
99
+ * default is the filesystem store over the resolved Winter home. A missing transcript is NO ENTRIES,
100
+ * never a throw: the advisor's whole posture is that an unavailable reviewer context degrades to an
101
+ * ordinary tool error rather than blocking the turn.
102
+ */
103
+ export declare function transcriptSourceForSessionKey(key: SessionKey, opts?: {
104
+ store?: SessionStore;
105
+ winterHome?: string;
106
+ }): TranscriptSource;
@@ -0,0 +1,31 @@
1
+ import { type JsonSchemaObject } from "./schemas.js";
2
+ export interface WinterToolDefinition {
3
+ /** The BARE tool name: `send_message`, `list_agents`, `read_notifications`, `advisor`. */
4
+ readonly toolName: string;
5
+ /** The official SDK's built-in name for the same tool — the alias key a host binds under. */
6
+ readonly builtinName?: string;
7
+ readonly description: string;
8
+ /** Tool-Search terms, for a host that defers this tool. Advertised nowhere when it does not. */
9
+ readonly searchHint?: string;
10
+ readonly inputSchema: JsonSchemaObject;
11
+ readonly outputSchema?: JsonSchemaObject;
12
+ readonly annotations?: {
13
+ readOnlyHint?: boolean;
14
+ destructiveHint?: boolean;
15
+ openWorldHint?: boolean;
16
+ idempotentHint?: boolean;
17
+ title?: string;
18
+ };
19
+ /**
20
+ * What the CALL does, not where the descriptor came from. `advisor` is `"mcp"` because it sends
21
+ * conversation content to a provider in the session's own trust domain (the same class of egress
22
+ * as the worker model's own requests), even though it registers as a bare built-in name.
23
+ */
24
+ readonly permissionClass: "messaging" | "mcp";
25
+ }
26
+ export declare const SEND_MESSAGE_DEFINITION: WinterToolDefinition;
27
+ export declare const LIST_AGENTS_DEFINITION: WinterToolDefinition;
28
+ export declare const READ_NOTIFICATIONS_DEFINITION: WinterToolDefinition;
29
+ export declare const ADVISOR_DEFINITION: WinterToolDefinition;
30
+ /** The four, in the order a host registers them. */
31
+ export declare const WINTER_DEFAULT_TOOL_DEFINITIONS: readonly [typeof SEND_MESSAGE_DEFINITION, typeof LIST_AGENTS_DEFINITION, typeof READ_NOTIFICATIONS_DEFINITION, typeof ADVISOR_DEFINITION];
@@ -0,0 +1,14 @@
1
+ export { SEND_MESSAGE_TO_MAX, SEND_MESSAGE_SUMMARY_MAX, LIST_AGENTS_FIELD_MAX, NATIVE_SEND_MESSAGE_SCHEMA, NATIVE_LIST_AGENTS_SCHEMA, NATIVE_LIST_AGENTS_OUTPUT_SCHEMA, NATIVE_READ_NOTIFICATIONS_SCHEMA, NATIVE_READ_NOTIFICATIONS_OUTPUT_SCHEMA, NATIVE_ADVISOR_SCHEMA, NATIVE_ADVISOR_OUTPUT_SCHEMA, } from "./schemas.js";
2
+ export type { JsonSchemaObject } from "./schemas.js";
3
+ export { SEND_MESSAGE_DEFINITION, LIST_AGENTS_DEFINITION, READ_NOTIFICATIONS_DEFINITION, ADVISOR_DEFINITION, WINTER_DEFAULT_TOOL_DEFINITIONS } from "./definitions.js";
4
+ export type { WinterToolDefinition } from "./definitions.js";
5
+ export { acceptNativeSendMessageArgs, acceptNativeListAgentsArgs, acceptNativeReadNotificationsArgs, deriveSendMessageSummary } from "./accept.js";
6
+ export type { NativeSendMessageArgs, NativeListAgentsArgs, NativeArgsResult } from "./accept.js";
7
+ export { messagingToolPortFromRuntimeDeps, callerAddress } from "./port.js";
8
+ export type { MessagingToolPort } from "./port.js";
9
+ export { createMessagingToolHandlers, toolUseIdFromExtra, VENDOR_TOOL_USE_ID_META_KEY } from "./messaging-handlers.js";
10
+ export type { WinterToolCaller, WinterToolResult, WinterToolHandler, MessagingToolHandlers } from "./messaging-handlers.js";
11
+ export { ADVISOR_DEFAULT_MAX_CHARS, OPAQUE_MARKERS, stripOpaqueMarkers, assembleReviewerMessages, createAdvisorToolHandler, transcriptSourceForSessionKey } from "./advisor.js";
12
+ export type { TranscriptEntry, TranscriptSource, AdvisorReviewerRequest, AdvisorReviewerTurn, AdvisorReviewer, ResolvedReviewer, ReviewerResolver, AdvisorToolDeps } from "./advisor.js";
13
+ export type { RuntimeAddress, ListedRuntimeObject, SendMessageResult, NotificationRecord, MessagingRuntimeDeps } from "../messaging/index.js";
14
+ export type { SessionKey, SessionStore } from "../store/session-store.js";
@@ -0,0 +1,438 @@
1
+ import {
2
+ validateToField2,
3
+ callerAddress2,
4
+ sendMessage2,
5
+ formatListing,
6
+ listAgents2
7
+ } from "../index-51ysrfm8.js";
8
+ import {
9
+ resolveWinterHome2,
10
+ WinterCompatibilitySessionStore2
11
+ } from "../index-9e98bg1r.js";
12
+
13
+ // src/tools/schemas.ts
14
+ var SEND_MESSAGE_TO_MAX = 300;
15
+ var SEND_MESSAGE_SUMMARY_MAX = 200;
16
+ var LIST_AGENTS_FIELD_MAX = 256;
17
+ var NATIVE_SEND_MESSAGE_SCHEMA = {
18
+ type: "object",
19
+ properties: {
20
+ to: { type: "string", maxLength: SEND_MESSAGE_TO_MAX, description: 'no newline, no "*" broadcast' },
21
+ message: { type: "string", description: 'required; defaults "" for pure idle subscription' },
22
+ summary: { type: "string", maxLength: SEND_MESSAGE_SUMMARY_MAX },
23
+ notify_when_idle: { type: "boolean", description: "one-shot; main conversation -> same-machine session only" }
24
+ },
25
+ required: ["to", "message"]
26
+ };
27
+ var NATIVE_LIST_AGENTS_SCHEMA = {
28
+ type: "object",
29
+ properties: {
30
+ channel: { type: "string", maxLength: LIST_AGENTS_FIELD_MAX, description: "reserved" },
31
+ q: { type: "string", maxLength: LIST_AGENTS_FIELD_MAX, description: "reserved" }
32
+ }
33
+ };
34
+ var NATIVE_LIST_AGENTS_OUTPUT_SCHEMA = {
35
+ type: "object",
36
+ properties: { listing: { type: "string" } },
37
+ required: ["listing"],
38
+ additionalProperties: false
39
+ };
40
+ var NATIVE_READ_NOTIFICATIONS_SCHEMA = {
41
+ type: "object",
42
+ properties: {},
43
+ additionalProperties: false
44
+ };
45
+ var NATIVE_READ_NOTIFICATIONS_OUTPUT_SCHEMA = {
46
+ type: "object",
47
+ properties: {
48
+ notifications: {
49
+ type: "array",
50
+ items: {
51
+ type: "object",
52
+ properties: {
53
+ notification_id: { type: "string" },
54
+ origin: { type: "string" },
55
+ queued_at: { type: "string" },
56
+ content: { type: "string" }
57
+ }
58
+ }
59
+ },
60
+ remaining: { type: "number" }
61
+ }
62
+ };
63
+ var NATIVE_ADVISOR_SCHEMA = {
64
+ type: "object",
65
+ properties: {}
66
+ };
67
+ var NATIVE_ADVISOR_OUTPUT_SCHEMA = {
68
+ type: "object",
69
+ properties: {
70
+ advice: { type: "string" },
71
+ model: { type: "string" },
72
+ truncated: { type: "boolean" }
73
+ },
74
+ required: ["advice", "model"]
75
+ };
76
+ // src/tools/definitions.ts
77
+ var SEND_MESSAGE_DEFINITION = {
78
+ toolName: "send_message",
79
+ builtinName: "SendMessage",
80
+ description: "Resolves `to` against child registry, teammates, live peer registry; steers a running child, resumes an addressable completed/stopped child, wakes an idle live peer, queues for a running peer; never cold-resumes an arbitrary exited transcript.",
81
+ searchHint: "send message agent session peer child steer resume notify idle",
82
+ inputSchema: NATIVE_SEND_MESSAGE_SCHEMA,
83
+ permissionClass: "messaging"
84
+ };
85
+ var LIST_AGENTS_DEFINITION = {
86
+ toolName: "list_agents",
87
+ builtinName: "ListAgents",
88
+ description: "Names/refs, activity/status, addressing identity for children, teammates, eligible live peers; never an enumeration of exited transcripts.",
89
+ searchHint: "list agents sessions peers children roster reachable",
90
+ inputSchema: NATIVE_LIST_AGENTS_SCHEMA,
91
+ outputSchema: NATIVE_LIST_AGENTS_OUTPUT_SCHEMA,
92
+ permissionClass: "messaging"
93
+ };
94
+ var READ_NOTIFICATIONS_DEFINITION = {
95
+ toolName: "read_notifications",
96
+ builtinName: "ReadNotifications",
97
+ description: "Drains Winter's own global-messaging notification queue ([WS-10]).",
98
+ inputSchema: NATIVE_READ_NOTIFICATIONS_SCHEMA,
99
+ outputSchema: NATIVE_READ_NOTIFICATIONS_OUTPUT_SCHEMA,
100
+ permissionClass: "messaging"
101
+ };
102
+ var ADVISOR_DEFINITION = {
103
+ toolName: "advisor",
104
+ builtinName: "advisor",
105
+ description: "Consults a stronger reviewer model over this session's own conversation/tool history (provider-opaque state such as encrypted_content is never included). Reviewer unavailable/timeout -> ordinary tool error; never blocks the turn.",
106
+ inputSchema: NATIVE_ADVISOR_SCHEMA,
107
+ outputSchema: NATIVE_ADVISOR_OUTPUT_SCHEMA,
108
+ permissionClass: "mcp"
109
+ };
110
+ var WINTER_DEFAULT_TOOL_DEFINITIONS = [
111
+ SEND_MESSAGE_DEFINITION,
112
+ LIST_AGENTS_DEFINITION,
113
+ READ_NOTIFICATIONS_DEFINITION,
114
+ ADVISOR_DEFINITION
115
+ ];
116
+ // src/tools/accept.ts
117
+ var SEND_MESSAGE_FIELDS = new Set(Object.keys(NATIVE_SEND_MESSAGE_SCHEMA.properties ?? {}));
118
+ var LIST_AGENTS_FIELDS = new Set(Object.keys(NATIVE_LIST_AGENTS_SCHEMA.properties ?? {}));
119
+ function unknownFields(record, allowed) {
120
+ return Object.keys(record).filter((key) => !allowed.has(key));
121
+ }
122
+ function asRecord(input) {
123
+ return typeof input === "object" && input !== null && !Array.isArray(input) ? input : undefined;
124
+ }
125
+ function acceptNativeSendMessageArgs(input) {
126
+ const record = asRecord(input);
127
+ if (record === undefined)
128
+ return { ok: false, reason: "expected an object of SendMessage arguments" };
129
+ const extra = unknownFields(record, SEND_MESSAGE_FIELDS);
130
+ if (extra.length > 0)
131
+ return { ok: false, reason: `unknown argument(s): ${extra.join(", ")}` };
132
+ const to = record["to"];
133
+ const validated = validateToField2(to);
134
+ if (!validated.ok)
135
+ return { ok: false, reason: validated.message };
136
+ const message = record["message"];
137
+ if (typeof message !== "string")
138
+ return { ok: false, reason: "`message` is required and must be a string (an empty string is a pure idle subscription)" };
139
+ const notify = record["notify_when_idle"];
140
+ if (notify !== undefined && typeof notify !== "boolean")
141
+ return { ok: false, reason: "`notify_when_idle` must be a boolean" };
142
+ if (message.length === 0 && notify !== true) {
143
+ return { ok: false, reason: "`message` may only be empty when `notify_when_idle` is true (a pure idle subscription, WS-10 §10.1)" };
144
+ }
145
+ const summary = record["summary"];
146
+ if (summary !== undefined && typeof summary !== "string")
147
+ return { ok: false, reason: "`summary` must be a string" };
148
+ const capped = summary === undefined ? undefined : summary.slice(0, SEND_MESSAGE_SUMMARY_MAX);
149
+ return {
150
+ ok: true,
151
+ args: {
152
+ to,
153
+ message,
154
+ ...capped === undefined ? {} : { summary: capped },
155
+ ...notify === undefined ? {} : { notify_when_idle: notify }
156
+ }
157
+ };
158
+ }
159
+ function acceptNativeListAgentsArgs(input) {
160
+ if (input === undefined || input === null)
161
+ return { ok: true, args: {} };
162
+ const record = asRecord(input);
163
+ if (record === undefined)
164
+ return { ok: false, reason: "expected an object of ListAgents arguments" };
165
+ const extra = unknownFields(record, LIST_AGENTS_FIELDS);
166
+ if (extra.length > 0)
167
+ return { ok: false, reason: `unknown argument(s): ${extra.join(", ")}` };
168
+ for (const field of ["channel", "q"]) {
169
+ const value = record[field];
170
+ if (value !== undefined && (typeof value !== "string" || value.length > LIST_AGENTS_FIELD_MAX)) {
171
+ return { ok: false, reason: `\`${field}\` must be a string of at most ${LIST_AGENTS_FIELD_MAX} characters` };
172
+ }
173
+ }
174
+ return {
175
+ ok: true,
176
+ args: {
177
+ ...typeof record["channel"] === "string" ? { channel: record["channel"] } : {},
178
+ ...typeof record["q"] === "string" ? { q: record["q"] } : {}
179
+ }
180
+ };
181
+ }
182
+ function acceptNativeReadNotificationsArgs(input) {
183
+ if (input === undefined || input === null)
184
+ return { ok: true, args: {} };
185
+ const record = asRecord(input);
186
+ if (record === undefined)
187
+ return { ok: false, reason: "expected an empty object of ReadNotifications arguments" };
188
+ const extra = Object.keys(record);
189
+ if (extra.length > 0)
190
+ return { ok: false, reason: `unknown argument(s): ${extra.join(", ")} (ReadNotifications takes no arguments)` };
191
+ return { ok: true, args: {} };
192
+ }
193
+ function deriveSendMessageSummary(rawSummary, message) {
194
+ if (rawSummary !== undefined)
195
+ return rawSummary.slice(0, SEND_MESSAGE_SUMMARY_MAX);
196
+ const firstLine = (message.split(`
197
+ `)[0] ?? "").trim();
198
+ if (firstLine.length === 0)
199
+ return;
200
+ return firstLine.slice(0, SEND_MESSAGE_SUMMARY_MAX);
201
+ }
202
+ // src/tools/port.ts
203
+ var fallbackCounter = 0;
204
+ function fallbackToolUseId() {
205
+ return `no-tool-use-id-${++fallbackCounter}-${Date.now()}`;
206
+ }
207
+ function callerFromAddress(from, originToolCallId) {
208
+ return {
209
+ sessionId: from.parentWinterSessionId ?? from.winterSessionId,
210
+ ...from.objectKind === "agent" && from.childId !== undefined ? { agentId: from.childId } : {},
211
+ toolUseId: originToolCallId ?? fallbackToolUseId()
212
+ };
213
+ }
214
+ function messagingToolPortFromRuntimeDeps(deps) {
215
+ return {
216
+ async sendDetailed(request) {
217
+ const caller = callerFromAddress(request.from, request.originToolCallId);
218
+ return sendMessage2(deps, caller, {
219
+ to: request.to,
220
+ message: request.body,
221
+ ...request.summary === undefined ? {} : { summary: request.summary },
222
+ ...request.notifyWhenIdle === undefined ? {} : { notify_when_idle: request.notifyWhenIdle }
223
+ });
224
+ },
225
+ async listReachable(scope) {
226
+ const { rows } = await listAgents2(deps, { sessionId: scope.from.parentWinterSessionId ?? scope.from.winterSessionId }, {});
227
+ return rows;
228
+ },
229
+ readNotifications(sessionId) {
230
+ return deps.notifications.drain(sessionId);
231
+ }
232
+ };
233
+ }
234
+ // src/tools/messaging-handlers.ts
235
+ var VENDOR_TOOL_USE_ID_META_KEY = "claudecode/toolUseId";
236
+ function toolUseIdFromExtra(extra) {
237
+ if (typeof extra !== "object" || extra === null)
238
+ return;
239
+ const meta = extra._meta;
240
+ if (typeof meta !== "object" || meta === null)
241
+ return;
242
+ const id = meta[VENDOR_TOOL_USE_ID_META_KEY];
243
+ return typeof id === "string" && id.length > 0 ? id : undefined;
244
+ }
245
+ var MODEL_FACING_FAILURES = new Set(["refused", "ambiguous", "not_found", "unavailable"]);
246
+ function text(body, isError = false) {
247
+ return { text: body, ...isError ? { isError: true } : {} };
248
+ }
249
+ function describe(err) {
250
+ return err instanceof Error ? err.message : String(err);
251
+ }
252
+ async function guarded(what, run) {
253
+ try {
254
+ return await run();
255
+ } catch (err) {
256
+ return text(`Error: ${what}: ${describe(err)}`, true);
257
+ }
258
+ }
259
+ function createMessagingToolHandlers(port, caller) {
260
+ const identity = () => typeof caller === "function" ? caller() : caller;
261
+ return {
262
+ async sendMessage(rawArgs, extra) {
263
+ const accepted = acceptNativeSendMessageArgs(rawArgs);
264
+ if (!accepted.ok)
265
+ return text(accepted.reason, true);
266
+ const bound = identity();
267
+ const perCall = toolUseIdFromExtra(extra);
268
+ const who = perCall === undefined ? bound : { ...bound, toolUseId: perCall };
269
+ const summary = deriveSendMessageSummary(accepted.args.summary, accepted.args.message);
270
+ return guarded("SendMessage could not reach the messaging system", async () => {
271
+ const result = await port.sendDetailed({
272
+ from: callerAddress2(who),
273
+ to: accepted.args.to,
274
+ body: accepted.args.message,
275
+ ...summary === undefined ? {} : { summary },
276
+ ...accepted.args.notify_when_idle === undefined ? {} : { notifyWhenIdle: accepted.args.notify_when_idle },
277
+ ...who.toolUseId === undefined ? {} : { originToolCallId: who.toolUseId }
278
+ });
279
+ const payload = result.notify === undefined ? result.outcome : { ...result.outcome, notify: result.notify };
280
+ return text(JSON.stringify(payload), MODEL_FACING_FAILURES.has(result.outcome.status));
281
+ });
282
+ },
283
+ async listAgents(rawArgs) {
284
+ const accepted = acceptNativeListAgentsArgs(rawArgs);
285
+ if (!accepted.ok)
286
+ return text(accepted.reason, true);
287
+ return guarded("ListAgents could not reach the messaging system", async () => {
288
+ const rows = await port.listReachable({ from: callerAddress2(identity()) });
289
+ return text(JSON.stringify({ listing: formatListing(rows) }));
290
+ });
291
+ },
292
+ async readNotifications(rawArgs) {
293
+ const accepted = acceptNativeReadNotificationsArgs(rawArgs);
294
+ if (!accepted.ok)
295
+ return text(accepted.reason, true);
296
+ return guarded("ReadNotifications could not drain the notification queue", () => {
297
+ const { notifications, remaining } = port.readNotifications(identity().sessionId);
298
+ return text(JSON.stringify({ notifications, remaining }));
299
+ });
300
+ }
301
+ };
302
+ }
303
+ // src/tools/advisor.ts
304
+ var ADVISOR_DEFAULT_MAX_CHARS = 20000;
305
+ var OPAQUE_MARKERS = ["encrypted_content", "reasoning_item", "signature", "thinking", "redacted_thinking"];
306
+ function stripOpaqueMarkers(text) {
307
+ return text.split(`
308
+ `).filter((line) => !OPAQUE_MARKERS.some((marker) => line.toLowerCase().includes(marker))).join(`
309
+ `);
310
+ }
311
+ function assembleReviewerMessages(entries, maxChars = ADVISOR_DEFAULT_MAX_CHARS) {
312
+ const cleaned = entries.map((e) => ({ role: e.role, text: stripOpaqueMarkers(e.text) }));
313
+ const kept = [];
314
+ let total = 0;
315
+ let truncated = false;
316
+ for (let i = cleaned.length - 1;i >= 0; i--) {
317
+ const entry = cleaned[i];
318
+ if (!entry)
319
+ continue;
320
+ if (total + entry.text.length > maxChars) {
321
+ if (kept.length === 0) {
322
+ kept.unshift({ role: entry.role, text: entry.text.slice(Math.max(0, entry.text.length - maxChars)) });
323
+ }
324
+ truncated = true;
325
+ break;
326
+ }
327
+ kept.unshift(entry);
328
+ total += entry.text.length;
329
+ }
330
+ return { messages: kept.map((e) => ({ role: e.role, content: e.text })), truncated };
331
+ }
332
+ function error(body) {
333
+ return { text: body, isError: true };
334
+ }
335
+ function describe2(err) {
336
+ return err instanceof Error ? err.message : String(err);
337
+ }
338
+ function createAdvisorToolHandler(deps) {
339
+ return async () => {
340
+ let reviewer;
341
+ try {
342
+ reviewer = deps.resolveReviewer();
343
+ } catch (err) {
344
+ return error(`Error: advisor failed to resolve a reviewer model: ${describe2(err)}`);
345
+ }
346
+ if (!reviewer) {
347
+ return error(`Error: advisor is unavailable -- no reviewer model is resolvable in this session's provider catalog (WS-06 §4: "Reviewer unavailable/timeout -> ordinary tool error; never blocks the turn").`);
348
+ }
349
+ let entries;
350
+ try {
351
+ entries = await deps.transcriptSource.getEntries();
352
+ } catch (err) {
353
+ return error(`Error: advisor failed to assemble the session transcript: ${describe2(err)}`);
354
+ }
355
+ const { messages, truncated } = assembleReviewerMessages(entries, deps.maxChars ?? ADVISOR_DEFAULT_MAX_CHARS);
356
+ let turn;
357
+ try {
358
+ turn = await reviewer.provider.generate({ messages });
359
+ } catch (err) {
360
+ return error(`Error: advisor's reviewer model failed: ${describe2(err)}`);
361
+ }
362
+ if (turn.kind !== "text" || typeof turn.text !== "string") {
363
+ return error(`Error: advisor's reviewer model returned a non-text response (kind: "${turn.kind}"); advisor has no tool-execution loop to act on it.`);
364
+ }
365
+ return { text: JSON.stringify({ advice: turn.text, model: reviewer.model, ...truncated ? { truncated: true } : {} }) };
366
+ };
367
+ }
368
+ function entryText(content) {
369
+ if (typeof content === "string")
370
+ return content;
371
+ if (!Array.isArray(content))
372
+ return;
373
+ const parts = content.flatMap((block) => {
374
+ if (typeof block !== "object" || block === null)
375
+ return [];
376
+ const record = block;
377
+ return record.type === "text" && typeof record.text === "string" ? [record.text] : [];
378
+ });
379
+ return parts.length === 0 ? undefined : parts.join(`
380
+ `);
381
+ }
382
+ function toTranscriptEntry(entry) {
383
+ if (entry.type !== "user" && entry.type !== "assistant")
384
+ return;
385
+ const message = entry["message"];
386
+ if (typeof message !== "object" || message === null)
387
+ return;
388
+ const text = entryText(message.content);
389
+ if (text === undefined || text.length === 0)
390
+ return;
391
+ return { role: entry.type, text };
392
+ }
393
+ function transcriptSourceForSessionKey(key, opts = {}) {
394
+ const store = opts.store ?? new WinterCompatibilitySessionStore2({ winterHome: opts.winterHome ?? resolveWinterHome2() });
395
+ return {
396
+ async getEntries() {
397
+ const entries = await store.load(key);
398
+ if (entries === null)
399
+ return [];
400
+ return entries.flatMap((entry) => {
401
+ const mapped = toTranscriptEntry(entry);
402
+ return mapped === undefined ? [] : [mapped];
403
+ });
404
+ }
405
+ };
406
+ }
407
+ export {
408
+ ADVISOR_DEFAULT_MAX_CHARS,
409
+ ADVISOR_DEFINITION,
410
+ LIST_AGENTS_DEFINITION,
411
+ LIST_AGENTS_FIELD_MAX,
412
+ NATIVE_ADVISOR_OUTPUT_SCHEMA,
413
+ NATIVE_ADVISOR_SCHEMA,
414
+ NATIVE_LIST_AGENTS_OUTPUT_SCHEMA,
415
+ NATIVE_LIST_AGENTS_SCHEMA,
416
+ NATIVE_READ_NOTIFICATIONS_OUTPUT_SCHEMA,
417
+ NATIVE_READ_NOTIFICATIONS_SCHEMA,
418
+ NATIVE_SEND_MESSAGE_SCHEMA,
419
+ OPAQUE_MARKERS,
420
+ READ_NOTIFICATIONS_DEFINITION,
421
+ SEND_MESSAGE_DEFINITION,
422
+ SEND_MESSAGE_SUMMARY_MAX,
423
+ SEND_MESSAGE_TO_MAX,
424
+ VENDOR_TOOL_USE_ID_META_KEY,
425
+ WINTER_DEFAULT_TOOL_DEFINITIONS,
426
+ acceptNativeListAgentsArgs,
427
+ acceptNativeReadNotificationsArgs,
428
+ acceptNativeSendMessageArgs,
429
+ assembleReviewerMessages,
430
+ callerAddress2 as callerAddress,
431
+ createAdvisorToolHandler,
432
+ createMessagingToolHandlers,
433
+ deriveSendMessageSummary,
434
+ messagingToolPortFromRuntimeDeps,
435
+ stripOpaqueMarkers,
436
+ toolUseIdFromExtra,
437
+ transcriptSourceForSessionKey
438
+ };