@yanlinglabs/winter-agent-sdk 0.0.38 → 0.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/{index-51ysrfm8.js → index-3zhkc4nb.js} +183 -8
- package/dist/index.d.ts +3 -2
- package/dist/index.js +100 -7
- package/dist/messaging/host.d.ts +42 -0
- package/dist/messaging/index.d.ts +3 -0
- package/dist/messaging/index.js +23 -1
- package/dist/messaging/router.d.ts +20 -2
- package/dist/options.d.ts +61 -2
- package/dist/protocol/config.d.ts +112 -0
- package/dist/tools/index.js +17 -6
- package/dist/tools/messaging-handlers.d.ts +2 -0
- package/dist/tools/port.d.ts +10 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -176,6 +176,34 @@ All three report the icon of each site they name to the HOST only, as `winter_si
|
|
|
176
176
|
|
|
177
177
|
Subagents inherit both.
|
|
178
178
|
|
|
179
|
+
### Messaging a host's other sessions (0.0.39)
|
|
180
|
+
|
|
181
|
+
**`Options.hostMessaging`** — `{ send, list }`, for a host that runs many sessions (one process or one
|
|
182
|
+
Worker each) and wants `SendMessage` and `ListAgents` to reach the others. With it set, `query()` puts
|
|
183
|
+
`hostMessaging: true` on the wire and answers two runtime → host control requests,
|
|
184
|
+
`host_message_send` and `host_message_list` (`HOST_MESSAGE_SEND_SUBTYPE` / `HOST_MESSAGE_LIST_SUBTYPE`),
|
|
185
|
+
the same way for a spawned `winter` process and an embedded Worker:
|
|
186
|
+
|
|
187
|
+
- `SendMessage` resolves `to` in-process first — the session's own subagents, then the in-process
|
|
188
|
+
peers. Only a `not_found` there goes to `send`, with the model's raw `to`, the message, its summary,
|
|
189
|
+
`notifyWhenIdle`, the runtime's message id and (for a subagent's call) `fromAgentId`. The host's
|
|
190
|
+
answer (`delivered`, `queued`, `resumed_and_delivered`, `refused`, `not_found`, `unavailable`,
|
|
191
|
+
`delivery_uncertain`, plus an optional `notify` fact and a one-sentence `note`) is the tool's
|
|
192
|
+
result under the runtime's message id, so a retry of the same tool call is answered from the
|
|
193
|
+
ledger and never asks the host twice. A throwing or malformed `send` is `delivery_uncertain`.
|
|
194
|
+
- `ListAgents` lists the subagents, then the sessions `list` returns as `session` rows, ending with
|
|
195
|
+
a count of the ones the host left out (`omitted`) — a listing is never silently cut. A failing
|
|
196
|
+
`list` lists nothing more.
|
|
197
|
+
- `TaskStop` with a `task_id` that names no task of this session goes to the optional `stop`
|
|
198
|
+
(`host_session_stop`): the host interrupts that session's running turn and answers `stopped`,
|
|
199
|
+
`not_running`, `refused`, `not_found` or `unavailable`.
|
|
200
|
+
- The calling tool's abort signal travels with the request: an interrupted call cancels it
|
|
201
|
+
(`control_cancel_request`), and the handler's own `signal` aborts so the host can skip the delivery.
|
|
202
|
+
|
|
203
|
+
The handler is per session and never told who is sending: it knows its caller by construction. The
|
|
204
|
+
router core reaches the same seam through `MessagingRuntimeDeps.hostMessaging` (a `HostMessagingPort`
|
|
205
|
+
per owning session, on the `/messaging` subpath). Without the option, nothing changes.
|
|
206
|
+
|
|
179
207
|
### The 0.0.16 background-default change
|
|
180
208
|
|
|
181
209
|
Before 0.0.16, an Agent tool call with no `run_in_background` ran in the **foreground** (this call
|
|
@@ -419,6 +419,138 @@ function facetNotificationKey2(sessionId) {
|
|
|
419
419
|
function isReservedNotificationKey2(key) {
|
|
420
420
|
return key.startsWith(RESERVED_NOTIFICATION_KEY_PREFIX2);
|
|
421
421
|
}
|
|
422
|
+
// src/messaging/host.ts
|
|
423
|
+
var SEND_STATUSES = new Set(["delivered", "queued", "resumed_and_delivered", "refused", "not_found", "unavailable", "delivery_uncertain"]);
|
|
424
|
+
var NEEDS_REASON = new Set(["refused", "not_found", "unavailable", "delivery_uncertain"]);
|
|
425
|
+
var LISTED_STATUSES = new Set(["starting", "running", "idle", "exited", "unavailable", "archived"]);
|
|
426
|
+
var HOST_MESSAGE_NOTE_MAX2 = 500;
|
|
427
|
+
var HOST_MESSAGE_LIST_MAX2 = 200;
|
|
428
|
+
function isRecord(v) {
|
|
429
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
430
|
+
}
|
|
431
|
+
function isHostMessageSendRequest2(payload) {
|
|
432
|
+
if (!isRecord(payload))
|
|
433
|
+
return false;
|
|
434
|
+
return typeof payload.to === "string" && typeof payload.message === "string" && typeof payload.messageId === "string" && (payload.summary === undefined || typeof payload.summary === "string") && (payload.notifyWhenIdle === undefined || typeof payload.notifyWhenIdle === "boolean") && (payload.fromAgentId === undefined || typeof payload.fromAgentId === "string");
|
|
435
|
+
}
|
|
436
|
+
function isHostMessageListRequest2(payload) {
|
|
437
|
+
if (payload === undefined)
|
|
438
|
+
return true;
|
|
439
|
+
return isRecord(payload) && (payload.fromAgentId === undefined || typeof payload.fromAgentId === "string");
|
|
440
|
+
}
|
|
441
|
+
function isHostSessionStopRequest2(payload) {
|
|
442
|
+
return isRecord(payload) && typeof payload.id === "string" && (payload.fromAgentId === undefined || typeof payload.fromAgentId === "string");
|
|
443
|
+
}
|
|
444
|
+
var STOP_STATUSES = new Set(["stopped", "not_running", "refused", "not_found", "unavailable"]);
|
|
445
|
+
var STOP_NEEDS_REASON = new Set(["refused", "not_found", "unavailable"]);
|
|
446
|
+
function isHostSessionStopAnswer2(value) {
|
|
447
|
+
if (!isRecord(value))
|
|
448
|
+
return false;
|
|
449
|
+
if (typeof value.status !== "string" || !STOP_STATUSES.has(value.status))
|
|
450
|
+
return false;
|
|
451
|
+
if (value.reason !== undefined && typeof value.reason !== "string")
|
|
452
|
+
return false;
|
|
453
|
+
if (STOP_NEEDS_REASON.has(value.status) && (typeof value.reason !== "string" || value.reason.length === 0))
|
|
454
|
+
return false;
|
|
455
|
+
if (value.note !== undefined && typeof value.note !== "string")
|
|
456
|
+
return false;
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
function isHostMessageSendAnswer2(value) {
|
|
460
|
+
if (!isRecord(value))
|
|
461
|
+
return false;
|
|
462
|
+
if (typeof value.status !== "string" || !SEND_STATUSES.has(value.status))
|
|
463
|
+
return false;
|
|
464
|
+
if (value.reason !== undefined && typeof value.reason !== "string")
|
|
465
|
+
return false;
|
|
466
|
+
if (NEEDS_REASON.has(value.status) && (typeof value.reason !== "string" || value.reason.length === 0))
|
|
467
|
+
return false;
|
|
468
|
+
if (value.retryable !== undefined && typeof value.retryable !== "boolean")
|
|
469
|
+
return false;
|
|
470
|
+
if (value.note !== undefined && typeof value.note !== "string")
|
|
471
|
+
return false;
|
|
472
|
+
if (value.notify !== undefined) {
|
|
473
|
+
if (!isRecord(value.notify))
|
|
474
|
+
return false;
|
|
475
|
+
if (value.notify.subscribed !== undefined && value.notify.subscribed !== true)
|
|
476
|
+
return false;
|
|
477
|
+
if (value.notify.refused !== undefined && typeof value.notify.refused !== "string")
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
function isHostReachableSession(value) {
|
|
483
|
+
if (!isRecord(value))
|
|
484
|
+
return false;
|
|
485
|
+
if (typeof value.address !== "string" || !validateToField2(value.address).ok)
|
|
486
|
+
return false;
|
|
487
|
+
if (value.name !== undefined && (typeof value.name !== "string" || value.name.includes(`
|
|
488
|
+
`)))
|
|
489
|
+
return false;
|
|
490
|
+
if (typeof value.status !== "string" || !LISTED_STATUSES.has(value.status))
|
|
491
|
+
return false;
|
|
492
|
+
if (typeof value.mode !== "string")
|
|
493
|
+
return false;
|
|
494
|
+
if (value.cwd !== undefined && typeof value.cwd !== "string")
|
|
495
|
+
return false;
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
function normaliseHostMessageListAnswer2(value) {
|
|
499
|
+
if (!isRecord(value) || !Array.isArray(value.sessions))
|
|
500
|
+
return;
|
|
501
|
+
const valid = value.sessions.filter(isHostReachableSession);
|
|
502
|
+
const sessions = valid.slice(0, HOST_MESSAGE_LIST_MAX2);
|
|
503
|
+
const hostOmitted = typeof value.omitted === "number" && Number.isInteger(value.omitted) && value.omitted > 0 ? value.omitted : 0;
|
|
504
|
+
const omitted = hostOmitted + (valid.length - sessions.length);
|
|
505
|
+
return {
|
|
506
|
+
...omitted > 0 ? { omitted } : {},
|
|
507
|
+
sessions: sessions.map((s) => ({
|
|
508
|
+
address: s.address,
|
|
509
|
+
...s.name !== undefined ? { name: s.name } : {},
|
|
510
|
+
status: s.status,
|
|
511
|
+
mode: s.mode,
|
|
512
|
+
...s.cwd !== undefined ? { cwd: s.cwd } : {}
|
|
513
|
+
}))
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function hostSessionToListed2(row) {
|
|
517
|
+
const reachable = row.status !== "archived" && row.status !== "unavailable";
|
|
518
|
+
return {
|
|
519
|
+
address: row.address,
|
|
520
|
+
...row.name !== undefined ? { name: row.name } : {},
|
|
521
|
+
objectKind: "session",
|
|
522
|
+
runtimeKind: "winter-agent",
|
|
523
|
+
status: row.status,
|
|
524
|
+
mode: row.mode,
|
|
525
|
+
...row.cwd !== undefined ? { cwd: row.cwd } : {},
|
|
526
|
+
capabilities: { message: reachable, resume: reachable, notifyWhenIdle: false, reply: reachable }
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function hostAnswerToOutcome2(messageId, answer) {
|
|
530
|
+
const reason = answer.reason ?? answer.status;
|
|
531
|
+
switch (answer.status) {
|
|
532
|
+
case "delivered":
|
|
533
|
+
case "queued":
|
|
534
|
+
case "resumed_and_delivered":
|
|
535
|
+
return { status: answer.status, messageId };
|
|
536
|
+
case "refused":
|
|
537
|
+
return { status: "refused", messageId, reason };
|
|
538
|
+
case "not_found":
|
|
539
|
+
return { status: "not_found", messageId, reason };
|
|
540
|
+
case "unavailable":
|
|
541
|
+
return { status: "unavailable", messageId, retryable: answer.retryable === true, reason };
|
|
542
|
+
case "delivery_uncertain":
|
|
543
|
+
return { status: "delivery_uncertain", messageId, deliveryMayHaveOccurred: true, reason };
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function boundedHostNote2(note) {
|
|
547
|
+
if (note === undefined)
|
|
548
|
+
return;
|
|
549
|
+
const trimmed = note.trim();
|
|
550
|
+
if (trimmed.length === 0)
|
|
551
|
+
return;
|
|
552
|
+
return trimmed.length > HOST_MESSAGE_NOTE_MAX2 ? `${trimmed.slice(0, HOST_MESSAGE_NOTE_MAX2 - 1)}…` : trimmed;
|
|
553
|
+
}
|
|
422
554
|
// src/messaging/router.ts
|
|
423
555
|
var MAX_TRACKED_MESSAGE_IDS2 = 1e4;
|
|
424
556
|
function rememberBounded2(map, key, value, cap = MAX_TRACKED_MESSAGE_IDS2) {
|
|
@@ -484,15 +616,15 @@ var SUCCESS_CLASS_STATUSES = new Set(["delivered", "queued", "resumed_and_delive
|
|
|
484
616
|
function outcomeReason(outcome) {
|
|
485
617
|
return "reason" in outcome ? outcome.reason : outcome.status;
|
|
486
618
|
}
|
|
487
|
-
async function sendMessage2(deps, caller, input) {
|
|
619
|
+
async function sendMessage2(deps, caller, input, opts = {}) {
|
|
488
620
|
const now = deps.now();
|
|
489
621
|
const messageId = deps.seam.allocateMessageId(caller.sessionId, caller.toolUseId);
|
|
490
622
|
const existing = deps.seam.lookupOutcome(messageId);
|
|
491
623
|
if (existing !== undefined)
|
|
492
624
|
return { outcome: existing };
|
|
493
|
-
function settle(outcome, notify) {
|
|
625
|
+
function settle(outcome, notify, note) {
|
|
494
626
|
deps.seam.recordOutcome(messageId, outcome);
|
|
495
|
-
return notify !== undefined ? {
|
|
627
|
+
return { outcome, ...notify !== undefined ? { notify } : {}, ...note !== undefined ? { note } : {} };
|
|
496
628
|
}
|
|
497
629
|
const from = callerAddress2(caller);
|
|
498
630
|
const fromKey = serializeRuntimeAddress2(from);
|
|
@@ -512,8 +644,12 @@ async function sendMessage2(deps, caller, input) {
|
|
|
512
644
|
const reachable = await deps.adapter.listReachable({ parent: buildSessionAddress2(caller.sessionId) });
|
|
513
645
|
const peers = reachable.filter((r) => r.objectKind === "session");
|
|
514
646
|
const resolved = resolveTarget2({ to: input.to, callerParentSessionId: caller.sessionId, children: deps.seam.children(), peers });
|
|
515
|
-
if (resolved.kind === "not_found")
|
|
516
|
-
|
|
647
|
+
if (resolved.kind === "not_found") {
|
|
648
|
+
const host = deps.hostMessaging?.(caller.sessionId);
|
|
649
|
+
if (host === undefined)
|
|
650
|
+
return settle(notFound2(messageId, resolved.message));
|
|
651
|
+
return settle(...await askHost(host, messageId, caller, input, opts.signal));
|
|
652
|
+
}
|
|
517
653
|
if (resolved.kind === "stale")
|
|
518
654
|
return settle(refused2(messageId, resolved.message));
|
|
519
655
|
if (resolved.kind === "ambiguous")
|
|
@@ -560,6 +696,25 @@ async function sendMessage2(deps, caller, input) {
|
|
|
560
696
|
const idleOutcome = await deps.adapter.subscribeIdle(to, { messageId });
|
|
561
697
|
return settle(bodyOutcome, idleOutcome.status === "subscribed" ? { subscribed: true } : { refused: outcomeReason(idleOutcome) });
|
|
562
698
|
}
|
|
699
|
+
async function askHost(host, messageId, caller, input, signal) {
|
|
700
|
+
let answer;
|
|
701
|
+
try {
|
|
702
|
+
answer = await host.send({
|
|
703
|
+
to: input.to,
|
|
704
|
+
message: input.message,
|
|
705
|
+
...input.summary !== undefined ? { summary: input.summary } : {},
|
|
706
|
+
...input.notify_when_idle !== undefined ? { notifyWhenIdle: input.notify_when_idle } : {},
|
|
707
|
+
messageId,
|
|
708
|
+
...caller.agentId !== undefined ? { fromAgentId: caller.agentId } : {}
|
|
709
|
+
}, signal !== undefined ? { signal } : {});
|
|
710
|
+
} catch (err) {
|
|
711
|
+
return [deliveryUncertain2(messageId, `the host did not answer the delivery: ${describeThrow(err)}`)];
|
|
712
|
+
}
|
|
713
|
+
if (!isHostMessageSendAnswer2(answer))
|
|
714
|
+
return [deliveryUncertain2(messageId, "the host answered the delivery with a malformed outcome")];
|
|
715
|
+
const notify = answer.notify === undefined ? undefined : { ...answer.notify.subscribed === true ? { subscribed: true } : {}, ...answer.notify.refused !== undefined ? { refused: answer.notify.refused } : {} };
|
|
716
|
+
return [hostAnswerToOutcome2(messageId, answer), notify, boundedHostNote2(answer.note)];
|
|
717
|
+
}
|
|
563
718
|
function describeThrow(err) {
|
|
564
719
|
return err instanceof Error ? err.message : String(err);
|
|
565
720
|
}
|
|
@@ -593,12 +748,32 @@ function formatListing(rows) {
|
|
|
593
748
|
}).join(`
|
|
594
749
|
`);
|
|
595
750
|
}
|
|
596
|
-
|
|
751
|
+
function omittedLine(omitted) {
|
|
752
|
+
return `(${omitted} more reachable session${omitted === 1 ? "" : "s"} not listed)`;
|
|
753
|
+
}
|
|
754
|
+
async function listAgents2(deps, caller, _input, opts = {}) {
|
|
597
755
|
const selfAddr = buildSessionAddress2(caller.sessionId);
|
|
598
756
|
const selfKey = serializeRuntimeAddress2(selfAddr);
|
|
599
757
|
const reachable = await deps.adapter.listReachable({ parent: selfAddr });
|
|
600
758
|
const rows = reachable.filter((r) => r.address !== selfKey);
|
|
601
|
-
|
|
759
|
+
const host = deps.hostMessaging?.(caller.sessionId);
|
|
760
|
+
let omitted = 0;
|
|
761
|
+
if (host !== undefined) {
|
|
762
|
+
const answer = normaliseHostMessageListAnswer2(await host.list({}, opts.signal !== undefined ? { signal: opts.signal } : {}).catch(() => {
|
|
763
|
+
return;
|
|
764
|
+
}));
|
|
765
|
+
omitted = answer?.omitted ?? 0;
|
|
766
|
+
const seen = new Set(rows.map((r) => r.address));
|
|
767
|
+
for (const row of answer?.sessions ?? []) {
|
|
768
|
+
if (seen.has(row.address) || row.address === selfKey)
|
|
769
|
+
continue;
|
|
770
|
+
seen.add(row.address);
|
|
771
|
+
rows.push(hostSessionToListed2(row));
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const listing = omitted > 0 ? `${formatListing(rows)}
|
|
775
|
+
${omittedLine(omitted)}` : formatListing(rows);
|
|
776
|
+
return { listing, rows, ...omitted > 0 ? { omitted } : {} };
|
|
602
777
|
}
|
|
603
778
|
function readNotifications2(deps, caller) {
|
|
604
779
|
return deps.notifications.drain(caller.sessionId);
|
|
@@ -611,4 +786,4 @@ function createMessagingRouter2(deps) {
|
|
|
611
786
|
readNotifications: (caller) => readNotifications2(deps, caller)
|
|
612
787
|
};
|
|
613
788
|
}
|
|
614
|
-
export { serializeRuntimeAddress2, createFakeMessagingRouterSeam2, REFERENCE_RUNTIME_KIND2, validateToField2, buildSessionAddress2, buildChildAddress2, parseRuntimeAddress2, sameAddress2, MAX_GLOBAL_MESSAGE_SIZE2, DEFAULT_MESSAGE_TTL_MS2, MAX_HOP_COUNT2, RAPID_REPEAT_WINDOW_MS2, HELD_INBOX_CAP2, ACCEPTED_QUEUE_CAP2, DEFAULT_HOLD_EXPIRY_MS2, NOTIFY_IDLE_EXPIRY_MS2, messageExceedsMaxSize2, hopCountExceeded2, delivered2, queued2, resumedAndDelivered2, held2, subscribed2, deliveryUncertain2, refused2, ambiguous2, notFound2, unavailable2, createLoopGuard2, childToListedRuntimeObject2, resolveTarget2, classifyPermissionMode2, mapFromModeToPermissionClass2, defaultInboundResult2, resolveInboundDecision2, createMailbox2, buildDefaultHoldEntry2, buildExplicitHoldEntry2, isIdleSubscribeSenderAllowed2, isIdleSubscribeTargetAllowed2, createNotificationQueue2, createIdleSubscriptionStore2, AGENT_MESSAGE_TAG2, escapeAttributionText2, escapeAttributionAttribute2, RESERVED_NOTIFICATION_KEY_PREFIX2, facetNotificationKey2, isReservedNotificationKey2, MAX_TRACKED_MESSAGE_IDS2, rememberBounded2, createMessagingRouterSeam2, createSubscriberDirectory2, callerAddress2, sendMessage2, formatListing, listAgents2, readNotifications2, createMessagingRouter2 };
|
|
789
|
+
export { serializeRuntimeAddress2, createFakeMessagingRouterSeam2, REFERENCE_RUNTIME_KIND2, validateToField2, buildSessionAddress2, buildChildAddress2, parseRuntimeAddress2, sameAddress2, HOST_MESSAGE_NOTE_MAX2, HOST_MESSAGE_LIST_MAX2, isHostMessageSendRequest2, isHostMessageListRequest2, isHostSessionStopRequest2, isHostSessionStopAnswer2, isHostMessageSendAnswer2, normaliseHostMessageListAnswer2, hostSessionToListed2, hostAnswerToOutcome2, boundedHostNote2, MAX_GLOBAL_MESSAGE_SIZE2, DEFAULT_MESSAGE_TTL_MS2, MAX_HOP_COUNT2, RAPID_REPEAT_WINDOW_MS2, HELD_INBOX_CAP2, ACCEPTED_QUEUE_CAP2, DEFAULT_HOLD_EXPIRY_MS2, NOTIFY_IDLE_EXPIRY_MS2, messageExceedsMaxSize2, hopCountExceeded2, delivered2, queued2, resumedAndDelivered2, held2, subscribed2, deliveryUncertain2, refused2, ambiguous2, notFound2, unavailable2, createLoopGuard2, childToListedRuntimeObject2, resolveTarget2, classifyPermissionMode2, mapFromModeToPermissionClass2, defaultInboundResult2, resolveInboundDecision2, createMailbox2, buildDefaultHoldEntry2, buildExplicitHoldEntry2, isIdleSubscribeSenderAllowed2, isIdleSubscribeTargetAllowed2, createNotificationQueue2, createIdleSubscriptionStore2, AGENT_MESSAGE_TAG2, escapeAttributionText2, escapeAttributionAttribute2, RESERVED_NOTIFICATION_KEY_PREFIX2, facetNotificationKey2, isReservedNotificationKey2, MAX_TRACKED_MESSAGE_IDS2, rememberBounded2, createMessagingRouterSeam2, createSubscriberDirectory2, callerAddress2, sendMessage2, formatListing, omittedLine, listAgents2, readNotifications2, createMessagingRouter2 };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export type { Query, SdkMessage, SessionMessagingFacet } from "./query.js";
|
|
|
3
3
|
export type { QueryInternal, ControlRequestHandler, ControlRequestHandlerResult } from "./query.js";
|
|
4
4
|
export type { Options } from "./options.js";
|
|
5
5
|
export { SYSTEM_PROMPT_DYNAMIC_BOUNDARY, DEFAULT_CONTEXT_WINDOW_TOKENS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_PLANS_DIRECTORY, DEFAULT_OUTPUT_STYLE } from "./options.js";
|
|
6
|
-
export { DEFAULT_PROVIDER_STALL_TIMEOUT_MS, DEFAULT_KEYCHAIN_SERVICE, TEST_KEYCHAIN_ENV, TEST_KEYCHAIN_MEMORY, MCP_OAUTH_REFRESH_SUBTYPE, CREDENTIAL_RESOLVE_SUBTYPE } from "./options.js";
|
|
6
|
+
export { DEFAULT_PROVIDER_STALL_TIMEOUT_MS, DEFAULT_KEYCHAIN_SERVICE, TEST_KEYCHAIN_ENV, TEST_KEYCHAIN_MEMORY, MCP_OAUTH_REFRESH_SUBTYPE, CREDENTIAL_RESOLVE_SUBTYPE, HOST_MESSAGE_SEND_SUBTYPE, HOST_MESSAGE_LIST_SUBTYPE, HOST_SESSION_STOP_SUBTYPE } from "./options.js";
|
|
7
|
+
export type { HostMessagingHandler } from "./options.js";
|
|
7
8
|
export { WEB_TOOLS_DEFAULTS, resolveWebToolsConfig } from "./options.js";
|
|
8
9
|
export type { ResolvedWebToolsConfig } from "./options.js";
|
|
9
10
|
export type { WebToolsConfig, WebSearchConfig, WebFetchConfig, WebPrivateAddressPolicy, AutoMemoryConfig } from "./protocol/config.js";
|
|
@@ -22,7 +23,7 @@ export type { SpawnedRuntimeProcess, SpawnRuntimeOptions, SpawnClaudeCodeProcess
|
|
|
22
23
|
export type { RuntimeConfig, RuntimeHooksConfig, RuntimeHookMatcherGroup, SandboxSettingsConfig } from "./protocol/config.js";
|
|
23
24
|
export type { McpServerConfigForProcessTransport, McpVersionNegotiation, AgentMcpServerSpec, RuntimeAgentDefinition } from "./protocol/config.js";
|
|
24
25
|
export type { McpOAuthConfig, McpOAuthSecretRef, McpOAuthRefreshRequest, McpOAuthRefreshAnswer } from "./protocol/config.js";
|
|
25
|
-
export type { CredentialResolveRequest, CredentialResolveAnswer } from "./protocol/config.js";
|
|
26
|
+
export type { CredentialResolveRequest, CredentialResolveAnswer, HostMessageSendRequest, HostMessageSendAnswer, HostMessageListRequest, HostMessageListAnswer, HostReachableSession, HostSessionStopRequest, HostSessionStopAnswer } from "./protocol/config.js";
|
|
26
27
|
export { encodeFrame, decodeFrame, splitFrames, ProtocolError } from "./protocol/codec.js";
|
|
27
28
|
export { PROTOCOL_VERSION } from "./protocol/frames.js";
|
|
28
29
|
export { SDK_VERSION } from "./version.js";
|
package/dist/index.js
CHANGED
|
@@ -16,10 +16,16 @@ import {
|
|
|
16
16
|
WinterCompatibilitySessionStore2
|
|
17
17
|
} from "./index-0xks1t4r.js";
|
|
18
18
|
import {
|
|
19
|
+
isHostMessageSendRequest2,
|
|
20
|
+
isHostMessageListRequest2,
|
|
21
|
+
isHostSessionStopRequest2,
|
|
22
|
+
isHostSessionStopAnswer2,
|
|
23
|
+
isHostMessageSendAnswer2,
|
|
24
|
+
normaliseHostMessageListAnswer2,
|
|
19
25
|
deliveryUncertain2,
|
|
20
26
|
refused2,
|
|
21
27
|
unavailable2
|
|
22
|
-
} from "./index-
|
|
28
|
+
} from "./index-3zhkc4nb.js";
|
|
23
29
|
|
|
24
30
|
// src/query.ts
|
|
25
31
|
import { randomUUID } from "node:crypto";
|
|
@@ -70,6 +76,9 @@ var TEST_KEYCHAIN_ENV = "WINTER_TEST_KEYCHAIN";
|
|
|
70
76
|
var TEST_KEYCHAIN_MEMORY = "memory";
|
|
71
77
|
var MCP_OAUTH_REFRESH_SUBTYPE = "mcp_oauth_refresh";
|
|
72
78
|
var CREDENTIAL_RESOLVE_SUBTYPE = "credential_resolve";
|
|
79
|
+
var HOST_MESSAGE_SEND_SUBTYPE = "host_message_send";
|
|
80
|
+
var HOST_MESSAGE_LIST_SUBTYPE = "host_message_list";
|
|
81
|
+
var HOST_SESSION_STOP_SUBTYPE = "host_session_stop";
|
|
73
82
|
var WEB_TOOLS_DEFAULTS = {
|
|
74
83
|
searchEnabled: true,
|
|
75
84
|
maxSearchesPerCall: 8,
|
|
@@ -420,13 +429,15 @@ function toWireMcpServers(servers) {
|
|
|
420
429
|
name: cfg.name,
|
|
421
430
|
...cfg.timeout !== undefined ? { timeout: cfg.timeout } : {},
|
|
422
431
|
...isWinterMcpServerInstance(cfg.instance) ? { tools: cfg.instance.listTools() } : {},
|
|
423
|
-
...cfg.toolNames !== undefined ? { toolNames: { ...cfg.toolNames } } : {}
|
|
432
|
+
...cfg.toolNames !== undefined ? { toolNames: { ...cfg.toolNames } } : {},
|
|
433
|
+
...cfg.toolLanes !== undefined ? { toolLanes: { ...cfg.toolLanes } } : {}
|
|
424
434
|
} : cfg;
|
|
425
435
|
}
|
|
426
436
|
return out;
|
|
427
437
|
}
|
|
438
|
+
var ANSWERED_AFTER_CANCEL = new Set(["sdk_mcp_call"]);
|
|
428
439
|
function makeSdkMcpCallHandler(mcpServers) {
|
|
429
|
-
return async (payload) => {
|
|
440
|
+
return async (payload, handlerCtx) => {
|
|
430
441
|
const req = payload;
|
|
431
442
|
const cfg = req.server !== undefined ? mcpServers[req.server] : undefined;
|
|
432
443
|
if (!cfg || cfg.type !== "sdk") {
|
|
@@ -442,7 +453,7 @@ function makeSdkMcpCallHandler(mcpServers) {
|
|
|
442
453
|
};
|
|
443
454
|
}
|
|
444
455
|
try {
|
|
445
|
-
const result = await cfg.instance.callTool(req.tool ?? "", req.arguments ?? {});
|
|
456
|
+
const result = await cfg.instance.callTool(req.tool ?? "", req.arguments ?? {}, ...handlerCtx !== undefined ? [{ signal: handlerCtx.signal }] : []);
|
|
446
457
|
return { ok: true, payload: result };
|
|
447
458
|
} catch (err) {
|
|
448
459
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -551,6 +562,79 @@ function makeCredentialResolveHandler(onCredentialResolve, abortController) {
|
|
|
551
562
|
}
|
|
552
563
|
};
|
|
553
564
|
}
|
|
565
|
+
function abortableFor(abortController, handlerCtx) {
|
|
566
|
+
const controller = new AbortController;
|
|
567
|
+
const onAbort = () => controller.abort();
|
|
568
|
+
if (abortController?.signal.aborted === true || handlerCtx?.signal.aborted === true)
|
|
569
|
+
controller.abort();
|
|
570
|
+
abortController?.signal.addEventListener("abort", onAbort, { once: true });
|
|
571
|
+
handlerCtx?.signal.addEventListener("abort", onAbort, { once: true });
|
|
572
|
+
return {
|
|
573
|
+
controller,
|
|
574
|
+
dispose: () => {
|
|
575
|
+
abortController?.signal.removeEventListener("abort", onAbort);
|
|
576
|
+
handlerCtx?.signal.removeEventListener("abort", onAbort);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function makeHostMessageSendHandler(host, abortController) {
|
|
581
|
+
return async (payload, handlerCtx) => {
|
|
582
|
+
if (!isHostMessageSendRequest2(payload))
|
|
583
|
+
return { ok: false, error: { code: "invalid_payload", message: "host_message_send expects { to, message, messageId, summary?, notifyWhenIdle?, fromAgentId? }" } };
|
|
584
|
+
const { controller, dispose } = abortableFor(abortController, handlerCtx);
|
|
585
|
+
const request = {
|
|
586
|
+
to: payload.to,
|
|
587
|
+
message: payload.message,
|
|
588
|
+
messageId: payload.messageId,
|
|
589
|
+
...payload.summary !== undefined ? { summary: payload.summary } : {},
|
|
590
|
+
...payload.notifyWhenIdle !== undefined ? { notifyWhenIdle: payload.notifyWhenIdle } : {},
|
|
591
|
+
...payload.fromAgentId !== undefined ? { fromAgentId: payload.fromAgentId } : {}
|
|
592
|
+
};
|
|
593
|
+
try {
|
|
594
|
+
const answer = await host.send(request, { signal: controller.signal });
|
|
595
|
+
return { ok: true, payload: isHostMessageSendAnswer2(answer) ? answer : { status: "delivery_uncertain", reason: "the host's message handler returned a malformed answer" } };
|
|
596
|
+
} catch (err) {
|
|
597
|
+
console.error(`winter: hostMessaging.send threw (${err instanceof Error ? err.name : "error"}) -- answering delivery_uncertain`);
|
|
598
|
+
return { ok: true, payload: { status: "delivery_uncertain", reason: "the host's message handler failed" } };
|
|
599
|
+
} finally {
|
|
600
|
+
dispose();
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function makeHostMessageListHandler(host, abortController) {
|
|
605
|
+
return async (payload, handlerCtx) => {
|
|
606
|
+
if (!isHostMessageListRequest2(payload))
|
|
607
|
+
return { ok: false, error: { code: "invalid_payload", message: "host_message_list expects { fromAgentId? }" } };
|
|
608
|
+
const { controller, dispose } = abortableFor(abortController, handlerCtx);
|
|
609
|
+
try {
|
|
610
|
+
const answer = await host.list({ ...payload?.fromAgentId !== undefined ? { fromAgentId: payload.fromAgentId } : {} }, { signal: controller.signal });
|
|
611
|
+
return { ok: true, payload: normaliseHostMessageListAnswer2(answer) ?? { sessions: [] } };
|
|
612
|
+
} catch (err) {
|
|
613
|
+
console.error(`winter: hostMessaging.list threw (${err instanceof Error ? err.name : "error"}) -- answering an empty listing`);
|
|
614
|
+
return { ok: true, payload: { sessions: [] } };
|
|
615
|
+
} finally {
|
|
616
|
+
dispose();
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function makeHostSessionStopHandler(host, abortController) {
|
|
621
|
+
return async (payload, handlerCtx) => {
|
|
622
|
+
if (!isHostSessionStopRequest2(payload))
|
|
623
|
+
return { ok: false, error: { code: "invalid_payload", message: "host_session_stop expects { id, fromAgentId? }" } };
|
|
624
|
+
if (host.stop === undefined)
|
|
625
|
+
return { ok: true, payload: { status: "not_found", reason: `no task or session "${payload.id}" is known` } };
|
|
626
|
+
const { controller, dispose } = abortableFor(abortController, handlerCtx);
|
|
627
|
+
try {
|
|
628
|
+
const answer = await host.stop({ id: payload.id, ...payload.fromAgentId !== undefined ? { fromAgentId: payload.fromAgentId } : {} }, { signal: controller.signal });
|
|
629
|
+
return { ok: true, payload: isHostSessionStopAnswer2(answer) ? answer : { status: "unavailable", reason: "the host's stop handler returned a malformed answer" } };
|
|
630
|
+
} catch (err) {
|
|
631
|
+
console.error(`winter: hostMessaging.stop threw (${err instanceof Error ? err.name : "error"}) -- answering unavailable`);
|
|
632
|
+
return { ok: true, payload: { status: "unavailable", reason: "the host's stop handler failed" } };
|
|
633
|
+
} finally {
|
|
634
|
+
dispose();
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
554
638
|
function buildRuntimeHooksConfig(hooks) {
|
|
555
639
|
if (!hooks)
|
|
556
640
|
return;
|
|
@@ -735,6 +819,7 @@ function query(args) {
|
|
|
735
819
|
...options.maxOutputTokens !== undefined ? { maxOutputTokens: options.maxOutputTokens } : {},
|
|
736
820
|
...brand.keychainService !== WINTER_BRAND2.keychainService || options.keychainService !== undefined ? { keychainService: brand.keychainService } : {},
|
|
737
821
|
...options.onCredentialResolve !== undefined ? { hostCredentials: true } : {},
|
|
822
|
+
...options.hostMessaging !== undefined ? { hostMessaging: true } : {},
|
|
738
823
|
...options.autoClassifier !== undefined ? { autoClassifier: options.autoClassifier } : {},
|
|
739
824
|
...options.advisor !== undefined ? { advisor: options.advisor } : {},
|
|
740
825
|
...options.web !== undefined ? { web: options.web } : {},
|
|
@@ -812,7 +897,7 @@ function query(args) {
|
|
|
812
897
|
incomingRequestAborts.set(cf.requestId, cancel);
|
|
813
898
|
try {
|
|
814
899
|
const result = await handler(cf.payload, { signal: cancel.signal });
|
|
815
|
-
if (cancel.signal.aborted)
|
|
900
|
+
if (cancel.signal.aborted && !ANSWERED_AFTER_CANCEL.has(cf.subtype))
|
|
816
901
|
return;
|
|
817
902
|
if (result.ok) {
|
|
818
903
|
writeControlResponse({
|
|
@@ -825,7 +910,7 @@ function query(args) {
|
|
|
825
910
|
writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: result.error });
|
|
826
911
|
}
|
|
827
912
|
} catch (err) {
|
|
828
|
-
if (cancel.signal.aborted)
|
|
913
|
+
if (cancel.signal.aborted && !ANSWERED_AFTER_CANCEL.has(cf.subtype))
|
|
829
914
|
return;
|
|
830
915
|
const message = err instanceof Error ? err.message : String(err);
|
|
831
916
|
writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: { code: "handler_threw", message } });
|
|
@@ -897,6 +982,11 @@ function query(args) {
|
|
|
897
982
|
if (options.onCredentialResolve) {
|
|
898
983
|
controlRequestHandlers.set(CREDENTIAL_RESOLVE_SUBTYPE, makeCredentialResolveHandler(options.onCredentialResolve, options.abortController));
|
|
899
984
|
}
|
|
985
|
+
if (options.hostMessaging) {
|
|
986
|
+
controlRequestHandlers.set(HOST_MESSAGE_SEND_SUBTYPE, makeHostMessageSendHandler(options.hostMessaging, options.abortController));
|
|
987
|
+
controlRequestHandlers.set(HOST_MESSAGE_LIST_SUBTYPE, makeHostMessageListHandler(options.hostMessaging, options.abortController));
|
|
988
|
+
controlRequestHandlers.set(HOST_SESSION_STOP_SUBTYPE, makeHostSessionStopHandler(options.hostMessaging, options.abortController));
|
|
989
|
+
}
|
|
900
990
|
if (options.onMcpOAuthRefresh) {
|
|
901
991
|
controlRequestHandlers.set(MCP_OAUTH_REFRESH_SUBTYPE, makeMcpOAuthRefreshHandler(options.onMcpOAuthRefresh, options.abortController));
|
|
902
992
|
}
|
|
@@ -1180,7 +1270,7 @@ function withTestKeychainRedirect(env) {
|
|
|
1180
1270
|
return { ...env, [TEST_KEYCHAIN_ENV]: redirect };
|
|
1181
1271
|
}
|
|
1182
1272
|
// src/version.ts
|
|
1183
|
-
var SDK_VERSION = "0.0.
|
|
1273
|
+
var SDK_VERSION = "0.0.40";
|
|
1184
1274
|
// src/paths/project-key.ts
|
|
1185
1275
|
var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
|
|
1186
1276
|
var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -2356,6 +2446,9 @@ export {
|
|
|
2356
2446
|
ESCALATING_PERMISSION_MODES,
|
|
2357
2447
|
FIRST_PARTY_ORIGINATORS2 as FIRST_PARTY_ORIGINATORS,
|
|
2358
2448
|
HOOK_EVENTS,
|
|
2449
|
+
HOST_MESSAGE_LIST_SUBTYPE,
|
|
2450
|
+
HOST_MESSAGE_SEND_SUBTYPE,
|
|
2451
|
+
HOST_SESSION_STOP_SUBTYPE,
|
|
2359
2452
|
InvalidBrandError,
|
|
2360
2453
|
MCP_OAUTH_REFRESH_SUBTYPE,
|
|
2361
2454
|
MESSAGING_CONTROL_SUBTYPES,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { HostMessageListAnswer, HostMessageListRequest, HostMessageSendAnswer, HostMessageSendRequest, HostReachableSession, HostSessionStopAnswer, HostSessionStopRequest } from "../protocol/config.js";
|
|
2
|
+
import type { DeliveryOutcome, ListedRuntimeObject } from "./adapter.js";
|
|
3
|
+
/**
|
|
4
|
+
* What the router core (and `TaskStop`) need from a host: `Options.hostMessaging`, already bound to one
|
|
5
|
+
* session. `signal` is the calling tool's own: an interrupted sender cancels the request, and the host
|
|
6
|
+
* is told (its handler's `signal` aborts) so it can avoid delivering.
|
|
7
|
+
*/
|
|
8
|
+
export interface HostMessagingPort {
|
|
9
|
+
send(request: HostMessageSendRequest, opts?: {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}): Promise<HostMessageSendAnswer>;
|
|
12
|
+
list(request: HostMessageListRequest, opts?: {
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}): Promise<HostMessageListAnswer>;
|
|
15
|
+
stop(request: HostSessionStopRequest, opts?: {
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}): Promise<HostSessionStopAnswer>;
|
|
18
|
+
}
|
|
19
|
+
/** A host's sentence for the model is bounded: it is rendered into a tool result. */
|
|
20
|
+
export declare const HOST_MESSAGE_NOTE_MAX = 500;
|
|
21
|
+
/** A host listing is bounded: it is rendered into a tool result, one line per row. */
|
|
22
|
+
export declare const HOST_MESSAGE_LIST_MAX = 200;
|
|
23
|
+
/** The request shape, checked on the WRAPPER side before the host's callback sees it. */
|
|
24
|
+
export declare function isHostMessageSendRequest(payload: unknown): payload is HostMessageSendRequest;
|
|
25
|
+
export declare function isHostMessageListRequest(payload: unknown): payload is HostMessageListRequest;
|
|
26
|
+
export declare function isHostSessionStopRequest(payload: unknown): payload is HostSessionStopRequest;
|
|
27
|
+
/** A well-formed stop answer: a known status, a non-empty reason wherever one is required. */
|
|
28
|
+
export declare function isHostSessionStopAnswer(value: unknown): value is HostSessionStopAnswer;
|
|
29
|
+
/** A well-formed send answer: a known status, a reason wherever one is required, the optional facts typed. */
|
|
30
|
+
export declare function isHostMessageSendAnswer(value: unknown): value is HostMessageSendAnswer;
|
|
31
|
+
/**
|
|
32
|
+
* A list answer, normalised: every malformed row is DROPPED (one bad row must not hide the others, and
|
|
33
|
+
* an unaddressable row would advertise a target the model cannot name), and the listing is capped.
|
|
34
|
+
* `undefined` for an answer that is not a listing at all.
|
|
35
|
+
*/
|
|
36
|
+
export declare function normaliseHostMessageListAnswer(value: unknown): HostMessageListAnswer | undefined;
|
|
37
|
+
/** A host's session row as a ListAgents row: always a `session` of the Winter runtime, never resumable "for free" or idle-notifiable unless the host says so through SendMessage itself. */
|
|
38
|
+
export declare function hostSessionToListed(row: HostReachableSession): ListedRuntimeObject;
|
|
39
|
+
/** The host's answer as the router's outcome, under the RUNTIME's message id. Assumes `isHostMessageSendAnswer`. */
|
|
40
|
+
export declare function hostAnswerToOutcome(messageId: string, answer: HostMessageSendAnswer): DeliveryOutcome;
|
|
41
|
+
/** A host note, cut to its bound (it is model-visible). Empty and absent read the same. */
|
|
42
|
+
export declare function boundedHostNote(note: string | undefined): string | undefined;
|
|
@@ -11,5 +11,8 @@ export type { PermissionClassLabel, CrossSessionInbound, InboundDecisionParams,
|
|
|
11
11
|
export { isIdleSubscribeSenderAllowed, isIdleSubscribeTargetAllowed, createNotificationQueue, createIdleSubscriptionStore, } from "./idle.js";
|
|
12
12
|
export type { NotificationRecord, NotificationQueue, PendingIdleSubscription, IdleSubscriptionStore } from "./idle.js";
|
|
13
13
|
export { AGENT_MESSAGE_TAG, RESERVED_NOTIFICATION_KEY_PREFIX, escapeAttributionText, escapeAttributionAttribute, facetNotificationKey, isReservedNotificationKey } from "./attribution.js";
|
|
14
|
+
export { HOST_MESSAGE_NOTE_MAX, HOST_MESSAGE_LIST_MAX, isHostMessageSendRequest, isHostMessageListRequest, isHostMessageSendAnswer, isHostSessionStopRequest, isHostSessionStopAnswer, normaliseHostMessageListAnswer, hostSessionToListed, hostAnswerToOutcome, boundedHostNote, } from "./host.js";
|
|
15
|
+
export type { HostMessagingPort } from "./host.js";
|
|
16
|
+
export type { HostMessageSendRequest, HostMessageSendAnswer, HostMessageListRequest, HostMessageListAnswer, HostReachableSession, HostSessionStopRequest, HostSessionStopAnswer } from "../protocol/config.js";
|
|
14
17
|
export { MAX_TRACKED_MESSAGE_IDS, rememberBounded, createMessagingRouterSeam, createSubscriberDirectory, callerAddress, sendMessage, listAgents, readNotifications, createMessagingRouter, } from "./router.js";
|
|
15
18
|
export type { MessagingRouterSeamWithRoster, SubscriberDirectory, MessagingRuntimeDeps, CallerContext, SessionCallerContext, SendMessageInput, NotifyOutcome, SendMessageResult, ListAgentsInput, MessagingRouter, } from "./router.js";
|
package/dist/messaging/index.js
CHANGED
|
@@ -7,6 +7,17 @@ import {
|
|
|
7
7
|
buildChildAddress2,
|
|
8
8
|
parseRuntimeAddress2,
|
|
9
9
|
sameAddress2,
|
|
10
|
+
HOST_MESSAGE_NOTE_MAX2,
|
|
11
|
+
HOST_MESSAGE_LIST_MAX2,
|
|
12
|
+
isHostMessageSendRequest2,
|
|
13
|
+
isHostMessageListRequest2,
|
|
14
|
+
isHostSessionStopRequest2,
|
|
15
|
+
isHostSessionStopAnswer2,
|
|
16
|
+
isHostMessageSendAnswer2,
|
|
17
|
+
normaliseHostMessageListAnswer2,
|
|
18
|
+
hostSessionToListed2,
|
|
19
|
+
hostAnswerToOutcome2,
|
|
20
|
+
boundedHostNote2,
|
|
10
21
|
MAX_GLOBAL_MESSAGE_SIZE2,
|
|
11
22
|
DEFAULT_MESSAGE_TTL_MS2,
|
|
12
23
|
MAX_HOP_COUNT2,
|
|
@@ -56,13 +67,15 @@ import {
|
|
|
56
67
|
listAgents2,
|
|
57
68
|
readNotifications2,
|
|
58
69
|
createMessagingRouter2
|
|
59
|
-
} from "../index-
|
|
70
|
+
} from "../index-3zhkc4nb.js";
|
|
60
71
|
export {
|
|
61
72
|
ACCEPTED_QUEUE_CAP2 as ACCEPTED_QUEUE_CAP,
|
|
62
73
|
AGENT_MESSAGE_TAG2 as AGENT_MESSAGE_TAG,
|
|
63
74
|
DEFAULT_HOLD_EXPIRY_MS2 as DEFAULT_HOLD_EXPIRY_MS,
|
|
64
75
|
DEFAULT_MESSAGE_TTL_MS2 as DEFAULT_MESSAGE_TTL_MS,
|
|
65
76
|
HELD_INBOX_CAP2 as HELD_INBOX_CAP,
|
|
77
|
+
HOST_MESSAGE_LIST_MAX2 as HOST_MESSAGE_LIST_MAX,
|
|
78
|
+
HOST_MESSAGE_NOTE_MAX2 as HOST_MESSAGE_NOTE_MAX,
|
|
66
79
|
MAX_GLOBAL_MESSAGE_SIZE2 as MAX_GLOBAL_MESSAGE_SIZE,
|
|
67
80
|
MAX_HOP_COUNT2 as MAX_HOP_COUNT,
|
|
68
81
|
MAX_TRACKED_MESSAGE_IDS2 as MAX_TRACKED_MESSAGE_IDS,
|
|
@@ -71,6 +84,7 @@ export {
|
|
|
71
84
|
REFERENCE_RUNTIME_KIND2 as REFERENCE_RUNTIME_KIND,
|
|
72
85
|
RESERVED_NOTIFICATION_KEY_PREFIX2 as RESERVED_NOTIFICATION_KEY_PREFIX,
|
|
73
86
|
ambiguous2 as ambiguous,
|
|
87
|
+
boundedHostNote2 as boundedHostNote,
|
|
74
88
|
buildChildAddress2 as buildChildAddress,
|
|
75
89
|
buildDefaultHoldEntry2 as buildDefaultHoldEntry,
|
|
76
90
|
buildExplicitHoldEntry2 as buildExplicitHoldEntry,
|
|
@@ -94,12 +108,20 @@ export {
|
|
|
94
108
|
facetNotificationKey2 as facetNotificationKey,
|
|
95
109
|
held2 as held,
|
|
96
110
|
hopCountExceeded2 as hopCountExceeded,
|
|
111
|
+
hostAnswerToOutcome2 as hostAnswerToOutcome,
|
|
112
|
+
hostSessionToListed2 as hostSessionToListed,
|
|
113
|
+
isHostMessageListRequest2 as isHostMessageListRequest,
|
|
114
|
+
isHostMessageSendAnswer2 as isHostMessageSendAnswer,
|
|
115
|
+
isHostMessageSendRequest2 as isHostMessageSendRequest,
|
|
116
|
+
isHostSessionStopAnswer2 as isHostSessionStopAnswer,
|
|
117
|
+
isHostSessionStopRequest2 as isHostSessionStopRequest,
|
|
97
118
|
isIdleSubscribeSenderAllowed2 as isIdleSubscribeSenderAllowed,
|
|
98
119
|
isIdleSubscribeTargetAllowed2 as isIdleSubscribeTargetAllowed,
|
|
99
120
|
isReservedNotificationKey2 as isReservedNotificationKey,
|
|
100
121
|
listAgents2 as listAgents,
|
|
101
122
|
mapFromModeToPermissionClass2 as mapFromModeToPermissionClass,
|
|
102
123
|
messageExceedsMaxSize2 as messageExceedsMaxSize,
|
|
124
|
+
normaliseHostMessageListAnswer2 as normaliseHostMessageListAnswer,
|
|
103
125
|
notFound2 as notFound,
|
|
104
126
|
parseRuntimeAddress2 as parseRuntimeAddress,
|
|
105
127
|
queued2 as queued,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RuntimeAddress, type ListedRuntimeObject, type DeliveryOutcome, type RuntimeMessagingAdapter, type MessagingRouterSeam, type ChildLike } from "./adapter.js";
|
|
2
|
+
import { type HostMessagingPort } from "./host.js";
|
|
2
3
|
import type { NotificationQueue, NotificationRecord } from "./idle.js";
|
|
3
4
|
import { type LoopGuard } from "./outcomes.js";
|
|
4
5
|
export interface MessagingRouterSeamWithRoster extends MessagingRouterSeam {
|
|
@@ -34,6 +35,14 @@ export interface MessagingRuntimeDeps {
|
|
|
34
35
|
* "everything is uncertain", which is exactly the conservative reading.
|
|
35
36
|
*/
|
|
36
37
|
classifyDeliveryError?(err: unknown): "refused" | "uncertain";
|
|
38
|
+
/**
|
|
39
|
+
* Host messaging (`Options.hostMessaging`): the port to the HOST's other sessions for the OWNING
|
|
40
|
+
* session `owningSessionId` (a subagent's calls use its parent's, since they share the session id),
|
|
41
|
+
* or `undefined` when that session has none -- which is every session of a standalone SDK user, and
|
|
42
|
+
* every session of a host that composes this core itself (the router package). Consulted only after
|
|
43
|
+
* in-process resolution answered `not_found` (SendMessage) and to add rows (ListAgents).
|
|
44
|
+
*/
|
|
45
|
+
hostMessaging?(owningSessionId: string): HostMessagingPort | undefined;
|
|
37
46
|
}
|
|
38
47
|
export interface CallerContext {
|
|
39
48
|
sessionId: string;
|
|
@@ -57,8 +66,11 @@ export interface NotifyOutcome {
|
|
|
57
66
|
export interface SendMessageResult {
|
|
58
67
|
outcome: DeliveryOutcome;
|
|
59
68
|
notify?: NotifyOutcome;
|
|
69
|
+
note?: string;
|
|
60
70
|
}
|
|
61
|
-
export declare function sendMessage(deps: MessagingRuntimeDeps, caller: CallerContext, input: SendMessageInput
|
|
71
|
+
export declare function sendMessage(deps: MessagingRuntimeDeps, caller: CallerContext, input: SendMessageInput, opts?: {
|
|
72
|
+
signal?: AbortSignal;
|
|
73
|
+
}): Promise<SendMessageResult>;
|
|
62
74
|
export interface ListAgentsInput {
|
|
63
75
|
channel?: string;
|
|
64
76
|
q?: string;
|
|
@@ -76,9 +88,14 @@ export declare function formatListing(rows: readonly ListedRuntimeObject[]): str
|
|
|
76
88
|
export interface SessionCallerContext {
|
|
77
89
|
sessionId: string;
|
|
78
90
|
}
|
|
79
|
-
|
|
91
|
+
/** The line a listing ends with when the host left reachable sessions out (its cap), so nothing is silently cut. */
|
|
92
|
+
export declare function omittedLine(omitted: number): string;
|
|
93
|
+
export declare function listAgents(deps: MessagingRuntimeDeps, caller: SessionCallerContext, _input: ListAgentsInput, opts?: {
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
}): Promise<{
|
|
80
96
|
listing: string;
|
|
81
97
|
rows: ListedRuntimeObject[];
|
|
98
|
+
omitted?: number;
|
|
82
99
|
}>;
|
|
83
100
|
export declare function readNotifications(deps: MessagingRuntimeDeps, caller: SessionCallerContext): {
|
|
84
101
|
notifications: NotificationRecord[];
|
|
@@ -90,6 +107,7 @@ export interface MessagingRouter {
|
|
|
90
107
|
listAgents(caller: SessionCallerContext, input: ListAgentsInput): Promise<{
|
|
91
108
|
listing: string;
|
|
92
109
|
rows: ListedRuntimeObject[];
|
|
110
|
+
omitted?: number;
|
|
93
111
|
}>;
|
|
94
112
|
readNotifications(caller: SessionCallerContext): {
|
|
95
113
|
notifications: NotificationRecord[];
|
package/dist/options.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SpawnClaudeCodeProcess } from "./transport.js";
|
|
2
2
|
import type { PermissionMode, CanUseTool, HookEvent, HookCallbackMatcher } from "./permissions/types.js";
|
|
3
|
-
import type { SandboxSettingsConfig, McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, RuntimeAgentDefinition, SdkPluginConfig, SystemPromptOption, OutputFormat, SkillsOption, ProviderSelection, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig, WebToolsConfig, WebPrivateAddressPolicy, AutoMemoryConfig, CredentialRef, McpOAuthRefreshRequest, McpOAuthRefreshAnswer, CredentialResolveRequest, CredentialResolveAnswer } from "./protocol/config.js";
|
|
3
|
+
import type { SandboxSettingsConfig, McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, RuntimeAgentDefinition, SdkPluginConfig, SystemPromptOption, OutputFormat, SkillsOption, ProviderSelection, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig, WebToolsConfig, WebPrivateAddressPolicy, AutoMemoryConfig, CredentialRef, McpOAuthRefreshRequest, McpOAuthRefreshAnswer, CredentialResolveRequest, CredentialResolveAnswer, HostMessageSendRequest, HostMessageSendAnswer, HostMessageListRequest, HostMessageListAnswer, HostSessionStopRequest, HostSessionStopAnswer } from "./protocol/config.js";
|
|
4
4
|
import type { SettingSource } from "./settings/types.js";
|
|
5
5
|
import type { SessionStore } from "./store/session-store.js";
|
|
6
6
|
import { type BrandProfile } from "./brand.js";
|
|
@@ -45,6 +45,33 @@ export declare const TEST_KEYCHAIN_MEMORY = "memory";
|
|
|
45
45
|
export declare const MCP_OAUTH_REFRESH_SUBTYPE = "mcp_oauth_refresh";
|
|
46
46
|
/** WS-25 §7: the runtime -> host control subtype a host-brokered session resolves a Keychain credential with (`CredentialResolveRequest` -> `CredentialResolveAnswer`). */
|
|
47
47
|
export declare const CREDENTIAL_RESOLVE_SUBTYPE = "credential_resolve";
|
|
48
|
+
/** Host messaging: the runtime -> host control subtype `SendMessage` asks its host to deliver with (`HostMessageSendRequest` -> `HostMessageSendAnswer`). */
|
|
49
|
+
export declare const HOST_MESSAGE_SEND_SUBTYPE = "host_message_send";
|
|
50
|
+
/** Host messaging: the runtime -> host control subtype `ListAgents` asks its host what it can reach with (`HostMessageListRequest` -> `HostMessageListAnswer`). */
|
|
51
|
+
export declare const HOST_MESSAGE_LIST_SUBTYPE = "host_message_list";
|
|
52
|
+
/** Host messaging: the runtime -> host control subtype `TaskStop` asks its host to stop one of the host's sessions with (`HostSessionStopRequest` -> `HostSessionStopAnswer`). */
|
|
53
|
+
export declare const HOST_SESSION_STOP_SUBTYPE = "host_session_stop";
|
|
54
|
+
/**
|
|
55
|
+
* Host messaging, WINTER-ONLY: the handler a host that runs MANY sessions gives one session so its
|
|
56
|
+
* `SendMessage` and `ListAgents` reach the host's other sessions. See `Options.hostMessaging`.
|
|
57
|
+
*/
|
|
58
|
+
export interface HostMessagingHandler {
|
|
59
|
+
/** Deliver one message this session's `SendMessage` could not resolve in-process. Never throws for a policy answer -- refuse with a typed status. */
|
|
60
|
+
send(request: HostMessageSendRequest, options: {
|
|
61
|
+
signal: AbortSignal;
|
|
62
|
+
}): Promise<HostMessageSendAnswer>;
|
|
63
|
+
/** The sessions this session can reach through the host, for `ListAgents` (beside its own subagents). */
|
|
64
|
+
list(request: HostMessageListRequest, options: {
|
|
65
|
+
signal: AbortSignal;
|
|
66
|
+
}): Promise<HostMessageListAnswer>;
|
|
67
|
+
/**
|
|
68
|
+
* Stop (interrupt the running turn of) one of the host's sessions, for a `TaskStop` whose `task_id`
|
|
69
|
+
* names no task of this session. Optional: a host without it answers every such request `not_found`.
|
|
70
|
+
*/
|
|
71
|
+
stop?(request: HostSessionStopRequest, options: {
|
|
72
|
+
signal: AbortSignal;
|
|
73
|
+
}): Promise<HostSessionStopAnswer>;
|
|
74
|
+
}
|
|
48
75
|
/**
|
|
49
76
|
* Every web-tool default, spelled ONCE. A reader function resolves an absent field against this --
|
|
50
77
|
* no consumer writes a literal of its own.
|
|
@@ -101,7 +128,16 @@ export interface WinterMcpServerInstance {
|
|
|
101
128
|
};
|
|
102
129
|
_meta?: Record<string, unknown>;
|
|
103
130
|
}>;
|
|
104
|
-
|
|
131
|
+
/**
|
|
132
|
+
* `extra.signal` (SDK 0.0.40) is ABORTED when the runtime cancels the call (an interrupted turn sends
|
|
133
|
+
* `control_cancel_request`). A tool that holds an exclusive resource should stop promptly and then return:
|
|
134
|
+
* the runtime keeps the tool's concurrency lane held until this promise settles (or a bounded grace runs
|
|
135
|
+
* out), so the next call of that lane never overlaps a tool still running. Optional: an instance that
|
|
136
|
+
* ignores it behaves as before.
|
|
137
|
+
*/
|
|
138
|
+
callTool(name: string, args: Record<string, unknown>, extra?: {
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
}): Promise<{
|
|
105
141
|
content: unknown[];
|
|
106
142
|
isError?: boolean;
|
|
107
143
|
}>;
|
|
@@ -256,6 +292,29 @@ export interface Options {
|
|
|
256
292
|
onCredentialResolve?: (request: CredentialResolveRequest, options: {
|
|
257
293
|
signal: AbortSignal;
|
|
258
294
|
}) => Promise<CredentialResolveAnswer>;
|
|
295
|
+
/**
|
|
296
|
+
* Host messaging, WINTER-ONLY: this session's line to the host's OTHER sessions. When set, `query()`
|
|
297
|
+
* puts `hostMessaging: true` on the wire and answers the runtime's `host_message_send` /
|
|
298
|
+
* `host_message_list` control requests with it -- identically for a spawned `winter` process and an
|
|
299
|
+
* embedded Worker, since both speak the same frame stream to this wrapper.
|
|
300
|
+
*
|
|
301
|
+
* - `SendMessage` resolves `to` in-process first (this session's subagents, then the in-process peer
|
|
302
|
+
* directory). Only a `not_found` there is handed to `send`; a subagent, a self-target, a stale or an
|
|
303
|
+
* ambiguous name never reaches the host. The host's typed answer is the tool's result, under the
|
|
304
|
+
* runtime's own message id (so a retry of the same tool call short-circuits on the stored outcome).
|
|
305
|
+
* - `ListAgents` lists this session's subagents and then whatever `list` returns, as `session` rows
|
|
306
|
+
* (with the host's `omitted` count, so nothing is silently cut).
|
|
307
|
+
* - `TaskStop` with a `task_id` that names no task of this session is handed to `stop` (when given):
|
|
308
|
+
* the host interrupts that session's running turn.
|
|
309
|
+
*
|
|
310
|
+
* The handler is per session: it knows its caller by construction, and the request never names the
|
|
311
|
+
* sender (only `fromAgentId`, information about which subagent of THIS session asked). A throwing
|
|
312
|
+
* `send` answers `delivery_uncertain`; a throwing `list` lists nothing.
|
|
313
|
+
*
|
|
314
|
+
* Absent: `SendMessage` and `ListAgents` reach exactly what the session's own process holds, as before.
|
|
315
|
+
* Never serialized (a JS object of functions).
|
|
316
|
+
*/
|
|
317
|
+
hostMessaging?: HostMessagingHandler;
|
|
259
318
|
systemPrompt?: SystemPromptOption;
|
|
260
319
|
plugins?: SdkPluginConfig[];
|
|
261
320
|
skills?: SkillsOption;
|
|
@@ -231,6 +231,100 @@ export type CredentialResolveAnswer = {
|
|
|
231
231
|
ok: false;
|
|
232
232
|
reason: "not_found" | "not_allowed" | "stale" | "unavailable";
|
|
233
233
|
};
|
|
234
|
+
/**
|
|
235
|
+
* Host messaging, WINTER-ONLY: the runtime -> host `host_message_send` control request (subtype
|
|
236
|
+
* `HOST_MESSAGE_SEND_SUBTYPE`), sent only when `RuntimeConfig.hostMessaging` is set.
|
|
237
|
+
*
|
|
238
|
+
* WHY. A session's `SendMessage` resolves `to` against what its own process can reach: the session's
|
|
239
|
+
* subagents and the in-process peer directory, which holds the calling session alone. A HOST that runs
|
|
240
|
+
* many sessions (the Winter daemon: one process or one Worker per session) owns every other one, so the
|
|
241
|
+
* runtime asks it when -- and only when -- local resolution finds nothing (`not_found`). Subagents,
|
|
242
|
+
* self-target, a stale or ambiguous name are all still answered in-process and never reach the host.
|
|
243
|
+
*
|
|
244
|
+
* - `to` -- EXACTLY what the model wrote (a host session id, a `session:<id>` address from ListAgents,
|
|
245
|
+
* a name the host recognises). Resolution is the host's.
|
|
246
|
+
* - `messageId` -- the runtime's own id for this call, stable across a retry of the same tool call (the
|
|
247
|
+
* runtime's ledger short-circuits a retry before it ever asks again; the host MAY also dedupe on it).
|
|
248
|
+
* - `fromAgentId` -- set when one of this session's SUBAGENTS is the sender. Information only: the host
|
|
249
|
+
* knows which session is asking from which session's handler answered, and must never take the sender
|
|
250
|
+
* from the payload.
|
|
251
|
+
*/
|
|
252
|
+
export interface HostMessageSendRequest {
|
|
253
|
+
to: string;
|
|
254
|
+
message: string;
|
|
255
|
+
summary?: string;
|
|
256
|
+
notifyWhenIdle?: boolean;
|
|
257
|
+
messageId: string;
|
|
258
|
+
fromAgentId?: string;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The host's answer. `status` is one of the delivery outcomes a host can honestly report; the runtime
|
|
262
|
+
* stamps its own `messageId` on it and records it in its ledger.
|
|
263
|
+
*
|
|
264
|
+
* - `delivered` (the target is reading it now), `queued` (behind a turn already running),
|
|
265
|
+
* `resumed_and_delivered` (the target was not running and the host resumed it for this message);
|
|
266
|
+
* - `refused` / `not_found` -- `reason` required; `unavailable` -- `reason` required, `retryable`
|
|
267
|
+
* optional (default `false`); `delivery_uncertain` -- `reason` required (the effect may have happened).
|
|
268
|
+
*
|
|
269
|
+
* `notify` is the separate fact of whether `notifyWhenIdle` was honoured, rendered beside the outcome
|
|
270
|
+
* exactly like the in-process router's combined call. `note` is ONE short sentence for the model beside
|
|
271
|
+
* the outcome (e.g. whether it will be told when the target finishes) -- never a second outcome.
|
|
272
|
+
*/
|
|
273
|
+
export interface HostMessageSendAnswer {
|
|
274
|
+
status: "delivered" | "queued" | "resumed_and_delivered" | "refused" | "not_found" | "unavailable" | "delivery_uncertain";
|
|
275
|
+
reason?: string;
|
|
276
|
+
retryable?: boolean;
|
|
277
|
+
notify?: {
|
|
278
|
+
subscribed?: true;
|
|
279
|
+
refused?: string;
|
|
280
|
+
};
|
|
281
|
+
note?: string;
|
|
282
|
+
}
|
|
283
|
+
/** `host_message_list` (subtype `HOST_MESSAGE_LIST_SUBTYPE`): what this session can reach through its host, for `ListAgents`. */
|
|
284
|
+
export interface HostMessageListRequest {
|
|
285
|
+
/** As on `HostMessageSendRequest`: set when a subagent is asking. Information only. */
|
|
286
|
+
fromAgentId?: string;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* One session the host lists. `address` is what the model passes back as `to` (it must satisfy
|
|
290
|
+
* `SendMessage`'s own `to` rules: non-empty, at most 300 characters, no newline, no `*`); `name` is a
|
|
291
|
+
* display name (a title). The runtime renders each as an ordinary `session` row of ListAgents.
|
|
292
|
+
*/
|
|
293
|
+
export interface HostReachableSession {
|
|
294
|
+
address: string;
|
|
295
|
+
name?: string;
|
|
296
|
+
status: "starting" | "running" | "idle" | "exited" | "unavailable" | "archived";
|
|
297
|
+
/** The session's product mode (`code`, `dispatch`, …). */
|
|
298
|
+
mode: string;
|
|
299
|
+
cwd?: string;
|
|
300
|
+
}
|
|
301
|
+
export interface HostMessageListAnswer {
|
|
302
|
+
sessions: HostReachableSession[];
|
|
303
|
+
/** How many MORE reachable sessions the host did not list (its own cap). Rendered as a count, so a listing is never silently truncated. */
|
|
304
|
+
omitted?: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Host messaging: the runtime -> host `host_session_stop` control request (subtype
|
|
308
|
+
* `HOST_SESSION_STOP_SUBTYPE`). `TaskStop` sends it when its `task_id` names no task of this session (a
|
|
309
|
+
* background command, a subagent, a workflow) -- i.e. when the id may be one of the HOST's sessions.
|
|
310
|
+
* Stopping a session interrupts the turn it is running; it never deletes or archives anything.
|
|
311
|
+
* `fromAgentId` as on `HostMessageSendRequest`: information only.
|
|
312
|
+
*/
|
|
313
|
+
export interface HostSessionStopRequest {
|
|
314
|
+
id: string;
|
|
315
|
+
fromAgentId?: string;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* `stopped` (a running turn was interrupted) and `not_running` (nothing to stop) are ordinary answers;
|
|
319
|
+
* `refused`, `not_found` and `unavailable` carry a required `reason` and reach the model as errors.
|
|
320
|
+
*/
|
|
321
|
+
export interface HostSessionStopAnswer {
|
|
322
|
+
status: "stopped" | "not_running" | "refused" | "not_found" | "unavailable";
|
|
323
|
+
reason?: string;
|
|
324
|
+
/** One sentence for the model beside a `stopped` / `not_running` result — e.g. what happens to messages
|
|
325
|
+
* that were queued behind the interrupted turn. Rendered into TaskStop's `message`. */
|
|
326
|
+
note?: string;
|
|
327
|
+
}
|
|
234
328
|
export interface McpStdioServerConfig {
|
|
235
329
|
type?: "stdio";
|
|
236
330
|
command: string;
|
|
@@ -301,6 +395,17 @@ export interface McpSdkServerConfig {
|
|
|
301
395
|
* host owns it.
|
|
302
396
|
*/
|
|
303
397
|
toolNames?: Record<string, string>;
|
|
398
|
+
/**
|
|
399
|
+
* SDK 0.0.40, a Winter extension: CONCURRENCY LANES for some of this server's tools, keyed by the
|
|
400
|
+
* server's own tool name (`{ computer: "computer", browser: "browser" }`). A round's calls normally run
|
|
401
|
+
* one at a time unless the tool is read-only (`readOnlyHint: true`); a tool in a lane instead runs BESIDE
|
|
402
|
+
* the round's other calls, but never beside another call of the SAME lane -- calls of one lane run one at
|
|
403
|
+
* a time, in call order, across the round. Use it for a tool that holds one exclusive resource (a screen,
|
|
404
|
+
* a browser) yet is safe next to everything else. A call that is neither read-only nor in a lane is a
|
|
405
|
+
* barrier, exactly as before. A lane key is 1-64 characters of `[A-Za-z0-9_.:-]`; anything else is
|
|
406
|
+
* ignored. Only an in-process (`sdk`) server may declare lanes; the host vouches for its own tools.
|
|
407
|
+
*/
|
|
408
|
+
toolLanes?: Record<string, string>;
|
|
304
409
|
}
|
|
305
410
|
export type McpServerConfigForProcessTransport = McpStdioServerConfig | McpHttpServerConfig | McpSSEServerConfig | McpSdkServerConfig;
|
|
306
411
|
export type AgentMcpServerSpec = string | Record<string, McpServerConfigForProcessTransport>;
|
|
@@ -538,6 +643,13 @@ export interface RuntimeConfig {
|
|
|
538
643
|
* runtime's own Keychain store, as before (a standalone SDK user). A flag, never material.
|
|
539
644
|
*/
|
|
540
645
|
hostCredentials?: boolean;
|
|
646
|
+
/**
|
|
647
|
+
* Host messaging, WINTER-ONLY: `true` when the host answers `host_message_send` / `host_message_list`
|
|
648
|
+
* (`Options.hostMessaging`). `SendMessage` then asks the host to deliver whatever it cannot resolve
|
|
649
|
+
* in-process, and `ListAgents` adds the sessions the host lists. Absent: in-process only, as before.
|
|
650
|
+
* A flag, never a handler.
|
|
651
|
+
*/
|
|
652
|
+
hostMessaging?: boolean;
|
|
541
653
|
autoClassifier?: AutoClassifierConfig;
|
|
542
654
|
advisor?: AdvisorConfig;
|
|
543
655
|
/** The wire twin of `Options.web` -- see `WebToolsConfig`. Pure passthrough; absent means every default in `WEB_TOOLS_DEFAULTS`. */
|
package/dist/tools/index.js
CHANGED
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
callerAddress2,
|
|
4
4
|
sendMessage2,
|
|
5
5
|
formatListing,
|
|
6
|
+
omittedLine,
|
|
6
7
|
listAgents2
|
|
7
|
-
} from "../index-
|
|
8
|
+
} from "../index-3zhkc4nb.js";
|
|
8
9
|
import {
|
|
9
10
|
resolveWinterHome2,
|
|
10
11
|
WinterCompatibilitySessionStore2
|
|
@@ -220,12 +221,16 @@ function messagingToolPortFromRuntimeDeps(deps) {
|
|
|
220
221
|
message: request.body,
|
|
221
222
|
...request.summary === undefined ? {} : { summary: request.summary },
|
|
222
223
|
...request.notifyWhenIdle === undefined ? {} : { notify_when_idle: request.notifyWhenIdle }
|
|
223
|
-
});
|
|
224
|
+
}, request.signal !== undefined ? { signal: request.signal } : {});
|
|
224
225
|
},
|
|
225
226
|
async listReachable(scope) {
|
|
226
227
|
const { rows } = await listAgents2(deps, { sessionId: scope.from.parentWinterSessionId ?? scope.from.winterSessionId }, {});
|
|
227
228
|
return rows;
|
|
228
229
|
},
|
|
230
|
+
async listReachableDetailed(scope) {
|
|
231
|
+
const { rows, omitted } = await listAgents2(deps, { sessionId: scope.from.parentWinterSessionId ?? scope.from.winterSessionId }, {}, scope.signal !== undefined ? { signal: scope.signal } : {});
|
|
232
|
+
return { rows, ...omitted !== undefined ? { omitted } : {} };
|
|
233
|
+
},
|
|
229
234
|
readNotifications(sessionId) {
|
|
230
235
|
return deps.notifications.drain(sessionId);
|
|
231
236
|
}
|
|
@@ -274,9 +279,10 @@ function createMessagingToolHandlers(port, caller) {
|
|
|
274
279
|
body: accepted.args.message,
|
|
275
280
|
...summary === undefined ? {} : { summary },
|
|
276
281
|
...accepted.args.notify_when_idle === undefined ? {} : { notifyWhenIdle: accepted.args.notify_when_idle },
|
|
277
|
-
...who.toolUseId === undefined ? {} : { originToolCallId: who.toolUseId }
|
|
282
|
+
...who.toolUseId === undefined ? {} : { originToolCallId: who.toolUseId },
|
|
283
|
+
...who.signal === undefined ? {} : { signal: who.signal }
|
|
278
284
|
});
|
|
279
|
-
const payload = result.notify === undefined ?
|
|
285
|
+
const payload = { ...result.outcome, ...result.notify === undefined ? {} : { notify: result.notify }, ...result.note === undefined ? {} : { note: result.note } };
|
|
280
286
|
return text(JSON.stringify(payload), MODEL_FACING_FAILURES.has(result.outcome.status));
|
|
281
287
|
});
|
|
282
288
|
},
|
|
@@ -285,8 +291,13 @@ function createMessagingToolHandlers(port, caller) {
|
|
|
285
291
|
if (!accepted.ok)
|
|
286
292
|
return text(accepted.reason, true);
|
|
287
293
|
return guarded("ListAgents could not reach the messaging system", async () => {
|
|
288
|
-
const
|
|
289
|
-
|
|
294
|
+
const who = identity();
|
|
295
|
+
const from = callerAddress2(who);
|
|
296
|
+
const detailed = port.listReachableDetailed !== undefined ? await port.listReachableDetailed({ from, ...who.signal !== undefined ? { signal: who.signal } : {} }) : { rows: await port.listReachable({ from }) };
|
|
297
|
+
const rows = detailed.rows;
|
|
298
|
+
const omitted = detailed.omitted ?? 0;
|
|
299
|
+
return text(JSON.stringify({ listing: omitted > 0 ? `${formatListing(rows)}
|
|
300
|
+
${omittedLine(omitted)}` : formatListing(rows) }));
|
|
290
301
|
});
|
|
291
302
|
},
|
|
292
303
|
async readNotifications(rawArgs) {
|
|
@@ -15,6 +15,8 @@ export interface WinterToolCaller {
|
|
|
15
15
|
sessionId: string;
|
|
16
16
|
agentId?: string;
|
|
17
17
|
toolUseId?: string;
|
|
18
|
+
/** The calling tool's own abort signal, when the host binds the caller per call (host messaging cancels a pending delivery with it). */
|
|
19
|
+
signal?: AbortSignal;
|
|
18
20
|
}
|
|
19
21
|
/** The host-neutral tool result: one text body, plus whether the model should read it as a failure. */
|
|
20
22
|
export interface WinterToolResult {
|
package/dist/tools/port.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { callerAddress, type ListedRuntimeObject, type MessagingRuntimeDeps, type NotificationRecord, type RuntimeAddress, type SendMessageResult } from "../messaging/index.js";
|
|
2
2
|
export interface MessagingToolPort {
|
|
3
|
+
/** `signal`: the calling tool's own (host messaging cancels a pending host delivery with it). */
|
|
3
4
|
sendDetailed(request: {
|
|
4
5
|
from: RuntimeAddress;
|
|
5
6
|
to: string;
|
|
@@ -7,10 +8,19 @@ export interface MessagingToolPort {
|
|
|
7
8
|
summary?: string;
|
|
8
9
|
notifyWhenIdle?: boolean;
|
|
9
10
|
originToolCallId?: string;
|
|
11
|
+
signal?: AbortSignal;
|
|
10
12
|
}): Promise<SendMessageResult>;
|
|
11
13
|
listReachable(scope: {
|
|
12
14
|
from: RuntimeAddress;
|
|
13
15
|
}): Promise<ListedRuntimeObject[]>;
|
|
16
|
+
/** Optional: the rows plus how many reachable ones were NOT listed (a host's cap). Preferred by the ListAgents handler when present. */
|
|
17
|
+
listReachableDetailed?(scope: {
|
|
18
|
+
from: RuntimeAddress;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
rows: ListedRuntimeObject[];
|
|
22
|
+
omitted?: number;
|
|
23
|
+
}>;
|
|
14
24
|
readNotifications(sessionId: string): {
|
|
15
25
|
notifications: NotificationRecord[];
|
|
16
26
|
remaining: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanlinglabs/winter-agent-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@yanlinglabs/winter-provider-catalog": "0.0.
|
|
48
|
+
"@yanlinglabs/winter-provider-catalog": "0.0.40"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.
|
|
51
|
+
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.40"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.4.0",
|
|
55
|
-
"@yanlinglabs/winter-agent-runtime": "0.0.
|
|
56
|
-
"@yanlinglabs/winter-conformance": "0.0.
|
|
55
|
+
"@yanlinglabs/winter-agent-runtime": "0.0.40",
|
|
56
|
+
"@yanlinglabs/winter-conformance": "0.0.40"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {}
|
|
59
59
|
}
|