@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.
@@ -0,0 +1,52 @@
1
+ import { type MessagingToolPort } from "./port.js";
2
+ /**
3
+ * WHO IS CALLING — bound at registration, never read out of the arguments.
4
+ *
5
+ * The standing MCP server is materialized per session (WS-14 §11), so the caller is known when the
6
+ * handler is built. Taking it from the ARGUMENTS instead would make the sender's identity something
7
+ * a model could write, and every fence in the messaging core — the owning-parent rule, the
8
+ * self-target refusal, WS-10 §13's sender class, WS-15 §6.2's dedupe key — is keyed on it.
9
+ *
10
+ * `toolUseId` is the second half of WS-10 §12's retry key, and it is OPTIONAL because a host that
11
+ * cannot supply one exists: it gets no dedupe, stated at the door rather than faked with a
12
+ * stable-looking key that would make two different messages one.
13
+ */
14
+ export interface WinterToolCaller {
15
+ sessionId: string;
16
+ agentId?: string;
17
+ toolUseId?: string;
18
+ }
19
+ /** The host-neutral tool result: one text body, plus whether the model should read it as a failure. */
20
+ export interface WinterToolResult {
21
+ text: string;
22
+ isError?: boolean;
23
+ }
24
+ export type WinterToolHandler = (args: unknown, extra?: unknown) => Promise<WinterToolResult>;
25
+ export interface MessagingToolHandlers {
26
+ sendMessage: WinterToolHandler;
27
+ listAgents: WinterToolHandler;
28
+ readNotifications: WinterToolHandler;
29
+ }
30
+ /**
31
+ * WS-10 §12's RETRY KEY, on the official branch — and it exists, which was not known until it was
32
+ * measured.
33
+ *
34
+ * §12 wants a message id derived from (sender session, TOOL-CALL id) so "a retry allocates the SAME
35
+ * id" and returns the stored outcome instead of starting a second turn. On the Winter branch the
36
+ * caller binds `toolUseId` at registration. On the official branch the handler is inside the
37
+ * vendor's in-process MCP server, where the only per-call channel is the second argument the vendor
38
+ * passes — and the reasonable expectation was that it carries MCP request context (a JSON-RPC
39
+ * request id, `_meta`) rather than an Anthropic-API `tool_use_id`, which is one layer up.
40
+ *
41
+ * THE PINNED RUNTIME BRIDGES THEM. Measured on 0.3.250: `extra._meta["claudecode/toolUseId"]` is the
42
+ * exact id the model emitted. So the official branch gets a real §12 key rather than depending on
43
+ * the rapid-repeat guard, and the vendor's own namespaced `_meta` name is read rather than guessed.
44
+ *
45
+ * A VENDOR-NAMESPACED KEY IS NEVER REBRANDED (WS-01 §5): `claudecode/toolUseId` is the vendor's name
46
+ * for the vendor's field, exactly like `CLAUDE_CONFIG_DIR`. It is read defensively — an absent or
47
+ * non-string value simply falls back to the bound caller's id — because a future pin may move it,
48
+ * and losing the key must degrade to today's behaviour rather than to a crash.
49
+ */
50
+ export declare const VENDOR_TOOL_USE_ID_META_KEY = "claudecode/toolUseId";
51
+ export declare function toolUseIdFromExtra(extra: unknown): string | undefined;
52
+ export declare function createMessagingToolHandlers(port: MessagingToolPort, caller: WinterToolCaller | (() => WinterToolCaller)): MessagingToolHandlers;
@@ -0,0 +1,39 @@
1
+ import { callerAddress, type ListedRuntimeObject, type MessagingRuntimeDeps, type NotificationRecord, type RuntimeAddress, type SendMessageResult } from "../messaging/index.js";
2
+ export interface MessagingToolPort {
3
+ sendDetailed(request: {
4
+ from: RuntimeAddress;
5
+ to: string;
6
+ body: string;
7
+ summary?: string;
8
+ notifyWhenIdle?: boolean;
9
+ originToolCallId?: string;
10
+ }): Promise<SendMessageResult>;
11
+ listReachable(scope: {
12
+ from: RuntimeAddress;
13
+ }): Promise<ListedRuntimeObject[]>;
14
+ readNotifications(sessionId: string): {
15
+ notifications: NotificationRecord[];
16
+ remaining: number;
17
+ };
18
+ }
19
+ /**
20
+ * The Winter-runtime side of the port: `MessagingRuntimeDeps` in, `MessagingToolPort` out.
21
+ *
22
+ * `listReachable` goes through the core's own `listAgents` rather than straight to
23
+ * `deps.adapter.listReachable`, because the core is where the self-exclusion rule lives (WS-10
24
+ * §10.2) — so the handlers never filter again (they are the layer least able to know the caller's
25
+ * real address).
26
+ *
27
+ * WHAT THAT FILTER ACTUALLY EXCLUDES, stated precisely rather than as "never yourself" (whole-branch
28
+ * fix wave). The `from` address is resolved to its OWNING SESSION before the core is asked, and the
29
+ * core drops the row matching that session address. For a top-level caller those are the same thing
30
+ * and the rule reads as written. For a CHILD caller (`agent:<parent>:<child>`) they are not: the
31
+ * scope collapses to `session:<parent>`, so the child's OWN `agent:` row can still appear in the
32
+ * listing it gets back. The router's handle filters the same way, by the address it was scoped with.
33
+ *
34
+ * Pre-existing on both branches and left alone here deliberately — this round changed no behaviour,
35
+ * and the fix belongs where the resolution happens, not in a re-filter bolted onto the port.
36
+ * Ledgered for the 0.0.4 patch wave.
37
+ */
38
+ export declare function messagingToolPortFromRuntimeDeps(deps: MessagingRuntimeDeps): MessagingToolPort;
39
+ export { callerAddress };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The self-describing shape a tool schema is stored and served as.
3
+ *
4
+ * Deliberately structural and permissive rather than a full JSON Schema type: neither host validates
5
+ * model input against these at all (the Winter registry's own type is explicitly "self-describing…
6
+ * not a validator" — the ACCEPTORS in `accept.ts` are what enforce the contract). What matters is
7
+ * that the advertised bytes are one object, in one place.
8
+ */
9
+ export interface JsonSchemaObject {
10
+ type: "object";
11
+ properties?: Record<string, unknown>;
12
+ required?: readonly string[];
13
+ additionalProperties?: boolean;
14
+ }
15
+ /** WS-10 §10.1's bounds. `to`'s own limit is the messaging subpath's; this is the copy the SCHEMA advertises. */
16
+ export declare const SEND_MESSAGE_TO_MAX = 300;
17
+ export declare const SEND_MESSAGE_SUMMARY_MAX = 200;
18
+ /** WS-10 §10.2's bound on both reserved `ListAgents` fields. */
19
+ export declare const LIST_AGENTS_FIELD_MAX = 256;
20
+ export declare const NATIVE_SEND_MESSAGE_SCHEMA: JsonSchemaObject;
21
+ export declare const NATIVE_LIST_AGENTS_SCHEMA: JsonSchemaObject;
22
+ /**
23
+ * WS-10 §10.2: "`ListAgents` output is EXACTLY `{ listing: string }`."
24
+ *
25
+ * "Exactly" is spelled `additionalProperties: false` here, which neither former copy said out loud
26
+ * (both merely listed the one property). It is the whole content of §10.2's sentence, and it is the
27
+ * half a reader of the schema alone would otherwise have to take on trust.
28
+ */
29
+ export declare const NATIVE_LIST_AGENTS_OUTPUT_SCHEMA: JsonSchemaObject;
30
+ /** WS-06 §3.6: the ordinary call is `{}` and nothing else. */
31
+ export declare const NATIVE_READ_NOTIFICATIONS_SCHEMA: JsonSchemaObject;
32
+ /**
33
+ * The drained PAGE, not one notification: the pinned vendor shape is an ARRAY per call plus what is
34
+ * left behind, so a model that drains knows whether to drain again.
35
+ */
36
+ export declare const NATIVE_READ_NOTIFICATIONS_OUTPUT_SCHEMA: JsonSchemaObject;
37
+ /**
38
+ * WS-06 §4: input is `{}` — the runtime forwards the session's own history; no model-supplied
39
+ * parameters.
40
+ *
41
+ * NO `additionalProperties` KEY, DELIBERATELY. Neither host validates model input against a JSON
42
+ * Schema, so the keyword would be decorative — and the posture is applied uniformly across the
43
+ * parameterless tools rather than declared on one and not the others. The ACCEPTOR is where "no
44
+ * more" is actually enforced (ruling P-4), and it is enforced for `read_notifications` too, whose
45
+ * schema does carry the keyword because its pinned vendor shape does.
46
+ */
47
+ export declare const NATIVE_ADVISOR_SCHEMA: JsonSchemaObject;
48
+ export declare const NATIVE_ADVISOR_OUTPUT_SCHEMA: JsonSchemaObject;
@@ -0,0 +1 @@
1
+ export declare const SDK_VERSION: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanlinglabs/winter-agent-sdk",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -25,6 +25,10 @@
25
25
  "./messaging": {
26
26
  "types": "./dist/messaging/index.d.ts",
27
27
  "default": "./dist/messaging/index.js"
28
+ },
29
+ "./tools": {
30
+ "types": "./dist/tools/index.d.ts",
31
+ "default": "./dist/tools/index.js"
28
32
  }
29
33
  },
30
34
  "files": [
@@ -41,15 +45,15 @@
41
45
  }
42
46
  },
43
47
  "dependencies": {
44
- "@yanlinglabs/winter-provider-catalog": "0.0.2"
48
+ "@yanlinglabs/winter-provider-catalog": "0.0.3"
45
49
  },
46
50
  "optionalDependencies": {
47
- "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.2"
51
+ "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.3"
48
52
  },
49
53
  "devDependencies": {
50
54
  "@types/node": "^26.4.0",
51
- "@yanlinglabs/winter-conformance": "0.0.2",
52
- "winter-agent-runtime": "0.0.2"
55
+ "@yanlinglabs/winter-conformance": "0.0.3",
56
+ "winter-agent-runtime": "0.0.3"
53
57
  },
54
58
  "scripts": {}
55
59
  }