@yanlinglabs/winter-agent-sdk 0.0.39 → 0.0.41

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/dist/index.js CHANGED
@@ -429,13 +429,16 @@ function toWireMcpServers(servers) {
429
429
  name: cfg.name,
430
430
  ...cfg.timeout !== undefined ? { timeout: cfg.timeout } : {},
431
431
  ...isWinterMcpServerInstance(cfg.instance) ? { tools: cfg.instance.listTools() } : {},
432
- ...cfg.toolNames !== undefined ? { toolNames: { ...cfg.toolNames } } : {}
432
+ ...cfg.toolNames !== undefined ? { toolNames: { ...cfg.toolNames } } : {},
433
+ ...cfg.toolLanes !== undefined ? { toolLanes: { ...cfg.toolLanes } } : {},
434
+ ...Array.isArray(cfg.concurrentTools) ? { concurrentTools: [...cfg.concurrentTools] } : {}
433
435
  } : cfg;
434
436
  }
435
437
  return out;
436
438
  }
439
+ var ANSWERED_AFTER_CANCEL = new Set(["sdk_mcp_call"]);
437
440
  function makeSdkMcpCallHandler(mcpServers) {
438
- return async (payload) => {
441
+ return async (payload, handlerCtx) => {
439
442
  const req = payload;
440
443
  const cfg = req.server !== undefined ? mcpServers[req.server] : undefined;
441
444
  if (!cfg || cfg.type !== "sdk") {
@@ -451,7 +454,7 @@ function makeSdkMcpCallHandler(mcpServers) {
451
454
  };
452
455
  }
453
456
  try {
454
- const result = await cfg.instance.callTool(req.tool ?? "", req.arguments ?? {});
457
+ const result = await cfg.instance.callTool(req.tool ?? "", req.arguments ?? {}, ...handlerCtx !== undefined ? [{ signal: handlerCtx.signal }] : []);
455
458
  return { ok: true, payload: result };
456
459
  } catch (err) {
457
460
  const message = err instanceof Error ? err.message : String(err);
@@ -895,7 +898,7 @@ function query(args) {
895
898
  incomingRequestAborts.set(cf.requestId, cancel);
896
899
  try {
897
900
  const result = await handler(cf.payload, { signal: cancel.signal });
898
- if (cancel.signal.aborted)
901
+ if (cancel.signal.aborted && !ANSWERED_AFTER_CANCEL.has(cf.subtype))
899
902
  return;
900
903
  if (result.ok) {
901
904
  writeControlResponse({
@@ -908,7 +911,7 @@ function query(args) {
908
911
  writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: result.error });
909
912
  }
910
913
  } catch (err) {
911
- if (cancel.signal.aborted)
914
+ if (cancel.signal.aborted && !ANSWERED_AFTER_CANCEL.has(cf.subtype))
912
915
  return;
913
916
  const message = err instanceof Error ? err.message : String(err);
914
917
  writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: { code: "handler_threw", message } });
@@ -1268,7 +1271,7 @@ function withTestKeychainRedirect(env) {
1268
1271
  return { ...env, [TEST_KEYCHAIN_ENV]: redirect };
1269
1272
  }
1270
1273
  // src/version.ts
1271
- var SDK_VERSION = "0.0.39";
1274
+ var SDK_VERSION = "0.0.41";
1272
1275
  // src/paths/project-key.ts
1273
1276
  var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
1274
1277
  var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
package/dist/options.d.ts CHANGED
@@ -128,7 +128,16 @@ export interface WinterMcpServerInstance {
128
128
  };
129
129
  _meta?: Record<string, unknown>;
130
130
  }>;
131
- callTool(name: string, args: Record<string, unknown>): Promise<{
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<{
132
141
  content: unknown[];
133
142
  isError?: boolean;
134
143
  }>;
@@ -395,6 +395,34 @@ export interface McpSdkServerConfig {
395
395
  * host owns it.
396
396
  */
397
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>;
409
+ /**
410
+ * SDK 0.0.41, a Winter extension: some of this server's tools, by the server's own tool name, that are
411
+ * SAFE TO RUN CONCURRENTLY without being read-only (`["session_spawn"]`). A round's calls normally run
412
+ * one at a time unless the tool is read-only (`readOnlyHint: true`); a tool listed here runs BESIDE the
413
+ * round's other concurrent calls (up to 10 at once), exactly like a read-only one -- several calls of it
414
+ * in one round run at the same time (claude runs several `Agent` calls of a round at once; this is the
415
+ * same thing for a host tool that starts work elsewhere).
416
+ *
417
+ * It is a SCHEDULING statement only. It never makes the tool read-only anywhere else: the tool's
418
+ * `annotations` are not touched, plan mode, permission evaluation, the hook input's `winter_mcp_server`
419
+ * and `canUseTool`'s `mcpServer` read the server's own `readOnlyHint` exactly as before. Use it only for a
420
+ * tool whose concurrent calls cannot interfere with each other (each call's work is its own); a tool
421
+ * that holds one exclusive resource belongs in a lane (`toolLanes`) instead. A tool named in BOTH is in
422
+ * its lane (the stricter answer). An entry that is not a string, or names no tool of this server, is
423
+ * ignored. Only an in-process (`sdk`) server may declare it; the host vouches for its own tools.
424
+ */
425
+ concurrentTools?: string[];
398
426
  }
399
427
  export type McpServerConfigForProcessTransport = McpStdioServerConfig | McpHttpServerConfig | McpSSEServerConfig | McpSdkServerConfig;
400
428
  export type AgentMcpServerSpec = string | Record<string, McpServerConfigForProcessTransport>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanlinglabs/winter-agent-sdk",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
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.39"
48
+ "@yanlinglabs/winter-provider-catalog": "0.0.41"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.39"
51
+ "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.41"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^26.4.0",
55
- "@yanlinglabs/winter-agent-runtime": "0.0.39",
56
- "@yanlinglabs/winter-conformance": "0.0.39"
55
+ "@yanlinglabs/winter-conformance": "0.0.41",
56
+ "@yanlinglabs/winter-agent-runtime": "0.0.41"
57
57
  },
58
58
  "scripts": {}
59
59
  }