dsh-session-bridge 0.3.0 → 0.3.2-alpha.1

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/lib/index.js CHANGED
@@ -4,7 +4,7 @@ import { homedir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { appendFileSync, mkdirSync } from "node:fs";
6
6
  import { randomUUID } from "node:crypto";
7
- //#region node_modules/.pnpm/@deepseek-ai+dsh-home-paths@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-in_7832b76c982cc1477c0eac59a4954baf/node_modules/@deepseek-ai/dsh-home-paths/lib/index.js
7
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-home-paths@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-home-paths/lib/index.js
8
8
  /**
9
9
  * Shared filesystem path helpers for DeepSeek Harness user data.
10
10
  *
@@ -2200,7 +2200,7 @@ var Service = class Service {
2200
2200
  }
2201
2201
  };
2202
2202
  //#endregion
2203
- //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.1.2-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
2203
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
2204
2204
  /** The one Remote failure class shared by owners, the Gateway, and consumers. */
2205
2205
  /**
2206
2206
  * One Remote call failure: a real Error carrying its stable code and typed
@@ -2345,7 +2345,7 @@ function validateName(subject, value) {
2345
2345
  if (!isTypertRemoteSegment(value)) throw new TypeError(`typert-protocol: ${subject} must contain only RPC endpoint segment characters`);
2346
2346
  }
2347
2347
  //#endregion
2348
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-i_486cdbb63f2ab3a8eaad87d2b6e5a4b7/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
2348
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
2349
2349
  /** Duplicate-install-safe JSON and immutable-value helpers. @module @deepseek-ai/dsh-util-values */
2350
2350
  /**
2351
2351
  * Mark an unreachable closed-union branch.
@@ -2557,7 +2557,7 @@ function deepFreeze(value) {
2557
2557
  return value;
2558
2558
  }
2559
2559
  //#endregion
2560
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-i_45952c52282327ee28c4189b1e05c80c/node_modules/@deepseek-ai/dsh-util-crypto/lib/index.js
2560
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-crypto@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-crypto/lib/index.js
2561
2561
  /**
2562
2562
  * Random v4 UUID, minted from `crypto.getRandomValues`.
2563
2563
  * @returns the UUID string.
@@ -2570,16 +2570,16 @@ function randomUUID$1() {
2570
2570
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
2571
2571
  }
2572
2572
  //#endregion
2573
- //#region node_modules/.pnpm/@deepseek-ai+dsh-brand@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-invaria_0a6acdb1edc9f1b3caaaafba5a0cd464/node_modules/@deepseek-ai/dsh-brand/lib/index.js
2573
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-brand@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-brand/lib/index.js
2574
2574
  /**
2575
- * Duplicate-install-safe nominal string helpers.
2575
+ * Duplicate-install-safe nominal primitive helpers.
2576
2576
  *
2577
- * A brand makes structurally-identical strings non-interchangeable at the type
2578
- * level: a `SessionId` cannot be passed where a `ToolCallId` is expected, even
2579
- * though both are plain strings at runtime. Comparison, logging, and
2580
- * serialization all behave as ordinary strings.
2577
+ * A brand makes structurally identical strings or numbers non-interchangeable
2578
+ * at the type level: a `SessionId` cannot be passed where a `ToolCallId` is
2579
+ * expected, and an event sequence cannot be passed as a log offset. Comparison,
2580
+ * logging, and serialization retain the underlying primitive behavior.
2581
2581
  *
2582
- * This package owns no concrete id and keeps no runtime identity or mutable
2582
+ * This package owns no concrete domain value and keeps no runtime identity or mutable
2583
2583
  * state, so independently installed copies produce interchangeable values.
2584
2584
  *
2585
2585
  * @module @deepseek-ai/dsh-brand
@@ -3187,11 +3187,19 @@ defineMethod("transform", [
3187
3187
  "preserve"
3188
3188
  ], ({ inner }, isInner) => inner.toString(isInner));
3189
3189
  //#endregion
3190
- //#region node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.1.2-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
3190
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-timeout@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-timeout/lib/index.js
3191
3191
  /** Largest delay Node schedules without clamping it to one millisecond. */
3192
3192
  const MAX_TIMER_DELAY_MS = 2147483647;
3193
3193
  //#endregion
3194
- //#region node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-invariant_cc3ae850d309c6dee6d11afd096d0893/node_modules/@deepseek-ai/dsh-llm/lib/index.js
3194
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-llm@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-llm/lib/index.js
3195
+ /**
3196
+ * Bound one `notice` summary to {@link CONTEXT_SUMMARY_MAX_CHARS}.
3197
+ * @param summary - the producer's one-line account, of any length.
3198
+ * @returns the account, ellipsized when it exceeds the bound.
3199
+ */
3200
+ function boundContextSummary(summary) {
3201
+ return summary.length <= 120 ? summary : `${summary.slice(0, 119)}…`;
3202
+ }
3195
3203
  /**
3196
3204
  * Detach and deep-freeze a message whose identity already exists.
3197
3205
  * @param message - complete message, including its stable identity.
@@ -3433,13 +3441,15 @@ function failureSnapshot(value) {
3433
3441
  const status = candidate.status;
3434
3442
  const providerRetryAfterMs = candidate.providerRetryAfterMs;
3435
3443
  const requestId = candidate.requestId;
3436
- if (typeof message !== "string" || message.length === 0 || typeof code !== "string" || code.length === 0 || status !== void 0 && (!Number.isInteger(status) || status < 100 || status > 599) || providerRetryAfterMs !== void 0 && (!Number.isFinite(providerRetryAfterMs) || providerRetryAfterMs <= 0) || requestId !== void 0 && (typeof requestId !== "string" || requestId.length === 0)) return void 0;
3444
+ const offloadImages = candidate.offloadImages;
3445
+ if (typeof message !== "string" || message.length === 0 || typeof code !== "string" || code.length === 0 || status !== void 0 && (!Number.isInteger(status) || status < 100 || status > 599) || providerRetryAfterMs !== void 0 && (!Number.isFinite(providerRetryAfterMs) || providerRetryAfterMs <= 0) || requestId !== void 0 && (typeof requestId !== "string" || requestId.length === 0) || offloadImages !== void 0 && (!Number.isSafeInteger(offloadImages) || offloadImages <= 0)) return void 0;
3437
3446
  return Object.freeze({
3438
3447
  message,
3439
3448
  code,
3440
3449
  ...status === void 0 ? {} : { status },
3441
3450
  ...providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs },
3442
- ...requestId === void 0 ? {} : { requestId }
3451
+ ...requestId === void 0 ? {} : { requestId },
3452
+ ...offloadImages === void 0 ? {} : { offloadImages }
3443
3453
  });
3444
3454
  } catch (_sdkFailureGetter) {
3445
3455
  return;
@@ -3457,6 +3467,9 @@ function errorMessage$1(error) {
3457
3467
  function harnessErrorCode(error) {
3458
3468
  return error instanceof HarnessError ? error.code : "UNKNOWN";
3459
3469
  }
3470
+ function quoted(value) {
3471
+ return JSON.stringify(value);
3472
+ }
3460
3473
  /**
3461
3474
  * Stable text shown to a model that cannot accept one durable image reference.
3462
3475
  * @param ref - durable normalized attachment omitted from the request.
@@ -3476,6 +3489,76 @@ function textOnlyImageText(ref) {
3476
3489
  function contentHasImage(content) {
3477
3490
  return content.some((block) => block.type === "image" || block.type === "tool-result" && contentHasImage(block.content));
3478
3491
  }
3492
+ /**
3493
+ * True when typed model content contains a file block, walking nested
3494
+ * tool-result content on the same recursion every file policy shares.
3495
+ * Reads current content on every call without retaining scan results.
3496
+ * @param content - typed model content blocks.
3497
+ * @returns whether any nested block is a file.
3498
+ */
3499
+ function contentHasFile(content) {
3500
+ for (const block of content) if (block.type === "file" || block.type === "tool-result" && contentHasFile(block.content)) return true;
3501
+ return false;
3502
+ }
3503
+ /**
3504
+ * Stable model-facing handle for one durable file reference: the address of
3505
+ * the verbatim stored copy and the instruction to read it on demand. This is
3506
+ * the only representation a provider ever receives for a file.
3507
+ * @param ref - durable verbatim file reference.
3508
+ * @param readonlyPath - execution-world path of the stored copy, when resolvable.
3509
+ * @returns deterministic handle text naming the file, its size, and its address.
3510
+ */
3511
+ function fileHandleText(ref, readonlyPath) {
3512
+ const digest = String(ref.attachmentId).slice(7, 15);
3513
+ const identity = `File ${quoted(ref.name)} (${ref.bytes} bytes, sha256:${digest})`;
3514
+ if (readonlyPath === void 0) return `[${identity} was uploaded, but the current execution environment cannot access a readable path. Report that limitation if its contents are needed; do not claim to have read it.]`;
3515
+ return `[${identity}: verbatim read-only copy saved at ${quoted(readonlyPath)}. Read that path with your file tools when its contents are needed; copy it to a writable location before modifying it. When delegating file work, include this saved path in the delegation prompt; only subagents sharing this execution environment can read it.]`;
3516
+ }
3517
+ /** Replace every file occurrence, including nested tool results, with handle text. */
3518
+ function replaceFilesWithHandles(blocks, resolvePath) {
3519
+ let next;
3520
+ for (const [index, block] of blocks.entries()) {
3521
+ if (block.type === "file") {
3522
+ next ??= blocks.slice(0, index);
3523
+ next.push({
3524
+ type: "text",
3525
+ text: fileHandleText(block.attachment, resolvePath(block.attachment))
3526
+ });
3527
+ continue;
3528
+ }
3529
+ if (block.type === "tool-result") {
3530
+ const content = replaceFilesWithHandles(block.content, resolvePath);
3531
+ if (content !== block.content) {
3532
+ next ??= blocks.slice(0, index);
3533
+ next.push({
3534
+ ...block,
3535
+ content
3536
+ });
3537
+ continue;
3538
+ }
3539
+ }
3540
+ next?.push(block);
3541
+ }
3542
+ return next ?? blocks;
3543
+ }
3544
+ /**
3545
+ * Project durable file history into deterministic handle text for every model
3546
+ * route. Unlike images, no provider receives file blocks natively, so this
3547
+ * projection is unconditional in request assembly.
3548
+ * @param messages - complete request history.
3549
+ * @param resolvePath - resolve one reference's current execution-world read path.
3550
+ * @returns the original list without files, otherwise shallow message copies with handle text.
3551
+ */
3552
+ function projectFilesToText(messages, resolvePath) {
3553
+ if (!messages.some((message) => contentHasFile(message.content))) return messages;
3554
+ return messages.map((message) => {
3555
+ const content = replaceFilesWithHandles(message.content, resolvePath);
3556
+ return content === message.content ? message : {
3557
+ ...message,
3558
+ content
3559
+ };
3560
+ });
3561
+ }
3479
3562
  /** Replace every image occurrence, including nested tool results, for a text-only model. */
3480
3563
  function replaceImagesForTextModel(blocks) {
3481
3564
  let next;
@@ -3605,7 +3688,8 @@ var LlmError = class extends HarnessError {
3605
3688
  code,
3606
3689
  ...options?.status === void 0 ? {} : { status: options.status },
3607
3690
  ...options?.providerRetryAfterMs === void 0 ? {} : { providerRetryAfterMs: options.providerRetryAfterMs },
3608
- ...options?.requestId === void 0 ? {} : { requestId: options.requestId }
3691
+ ...options?.requestId === void 0 ? {} : { requestId: options.requestId },
3692
+ ...options?.offloadImages === void 0 ? {} : { offloadImages: options.offloadImages }
3609
3693
  });
3610
3694
  }
3611
3695
  };
@@ -3916,6 +4000,15 @@ var LlmError = class extends HarnessError {
3916
4000
  imageRequestPricing(provider, model) {
3917
4001
  return this.adapters.get(provider)?.adapter.imageRequestPricing(provider, model);
3918
4002
  }
4003
+ /**
4004
+ * Resolve the exact text one durable file occurrence contributes to every
4005
+ * provider request in the current execution environment.
4006
+ * @param ref - durable verbatim file reference from model history.
4007
+ * @returns the same deterministic handle text used at adapter dispatch.
4008
+ */
4009
+ fileRequestText(ref) {
4010
+ return fileHandleText(ref, this.fileReadPath(ref));
4011
+ }
3919
4012
  /** Detach typed adapter-owned modality metadata. */
3920
4013
  detachedModalities(modalities) {
3921
4014
  return modalities === void 0 ? void 0 : [...modalities];
@@ -3965,6 +4058,8 @@ var LlmError = class extends HarnessError {
3965
4058
  const context = resolved.context;
3966
4059
  if (context !== void 0 && (!Number.isInteger(context.contextWindow) || context.contextWindow <= 0)) throw new LlmError(`adapter returned invalid context metadata for provider "${provider}" model "${model}"`, "INVALID_MODEL_CONTEXT");
3967
4060
  const inputModalities = this.detachedModalities(resolved.inputModalities);
4061
+ const systemPromptUpdate = resolved.systemPromptUpdate;
4062
+ if (systemPromptUpdate !== void 0 && systemPromptUpdate !== "in-history") throw new LlmError(`adapter returned invalid system prompt update mode for provider "${provider}" model "${model}"`, "INVALID_MODEL_INFO");
3968
4063
  const defaultMaxTokens = resolved.defaultMaxTokens;
3969
4064
  if (defaultMaxTokens !== void 0 && (!Number.isSafeInteger(defaultMaxTokens) || defaultMaxTokens <= 0)) throw new LlmError(`adapter returned invalid default maxTokens for provider "${provider}" model "${model}"`, "INVALID_MODEL_MAX_TOKENS");
3970
4065
  const info = {
@@ -3974,7 +4069,8 @@ var LlmError = class extends HarnessError {
3974
4069
  ...resolved.description === void 0 ? {} : { description: resolved.description },
3975
4070
  ...inputModalities === void 0 ? {} : { inputModalities },
3976
4071
  ...context === void 0 ? {} : { context: { contextWindow: context.contextWindow } },
3977
- ...defaultMaxTokens === void 0 ? {} : { defaultMaxTokens }
4072
+ ...defaultMaxTokens === void 0 ? {} : { defaultMaxTokens },
4073
+ ...resolved.systemPromptUpdate === void 0 ? {} : { systemPromptUpdate: resolved.systemPromptUpdate }
3978
4074
  };
3979
4075
  const reasoning = resolved.reasoning;
3980
4076
  if (reasoning === void 0) return info;
@@ -4068,6 +4164,7 @@ var LlmError = class extends HarnessError {
4068
4164
  adapterDefaults,
4069
4165
  ...context === void 0 ? {} : { context },
4070
4166
  ...modelInfo.inputModalities === void 0 ? {} : { inputModalities: Object.freeze([...modelInfo.inputModalities]) },
4167
+ ...modelInfo.systemPromptUpdate === void 0 ? {} : { systemPromptUpdate: modelInfo.systemPromptUpdate },
4071
4168
  stream: (options) => {
4072
4169
  if (dispatched) throw new LlmError("a prepared LLM call can only be dispatched once", "INVALID_PREPARED_CALL");
4073
4170
  if (!callConfigEquals(options, resolvedConfig)) throw new LlmError("prepared LLM call config changed before adapter dispatch", "INVALID_PREPARED_CALL");
@@ -4109,6 +4206,20 @@ var LlmError = class extends HarnessError {
4109
4206
  return Object.isFrozen(options) ? deepFreeze(filtered) : filtered;
4110
4207
  }
4111
4208
  /**
4209
+ * Resolve the current execution-world read path of one durable file
4210
+ * reference through the mounted attachment and filesystem providers.
4211
+ */
4212
+ fileReadPath(ref) {
4213
+ let hostPath;
4214
+ try {
4215
+ hostPath = this.ctx.get("attachments")?.fileHostPath(ref);
4216
+ } catch {
4217
+ return;
4218
+ }
4219
+ if (hostPath === void 0) return void 0;
4220
+ return this.ctx.get("fs")?.processPathFromHostPath(hostPath);
4221
+ }
4222
+ /**
4112
4223
  * Final adapter boundary. Adapter selection, dispatch, iterator construction,
4113
4224
  * and iteration failures become one terminal failure chunk. Middleware and
4114
4225
  * downstream consumer failures remain thrown plugin or consumer errors.
@@ -4139,13 +4250,16 @@ var LlmError = class extends HarnessError {
4139
4250
  ...options,
4140
4251
  ...resolvedConfig
4141
4252
  };
4142
- const projectedOptions = modelInfo.inputModalities !== void 0 && !modelInfo.inputModalities.includes("image") && resolvedOptions.messages.some((message) => contentHasImage(message.content)) ? Object.isFrozen(resolvedOptions) ? deepFreeze({
4253
+ let projectedMessages = resolvedOptions.messages;
4254
+ if (projectedMessages.some((message) => contentHasFile(message.content))) projectedMessages = projectFilesToText(projectedMessages, (ref) => this.fileReadPath(ref));
4255
+ if (modelInfo.inputModalities !== void 0 && !modelInfo.inputModalities.includes("image") && projectedMessages.some((message) => contentHasImage(message.content))) projectedMessages = projectImagesForTextModel(projectedMessages);
4256
+ const projectedOptions = projectedMessages === resolvedOptions.messages ? resolvedOptions : Object.isFrozen(resolvedOptions) ? deepFreeze({
4143
4257
  ...resolvedOptions,
4144
- messages: projectImagesForTextModel(resolvedOptions.messages)
4258
+ messages: projectedMessages
4145
4259
  }) : {
4146
4260
  ...resolvedOptions,
4147
- messages: projectImagesForTextModel(resolvedOptions.messages)
4148
- } : resolvedOptions;
4261
+ messages: projectedMessages
4262
+ };
4149
4263
  iterator = dispatch(this.forAdapter(projectedOptions, adapter))[Symbol.asyncIterator]();
4150
4264
  } catch (error) {
4151
4265
  yield adapterFailureChunk(error, options.signal);
@@ -8966,7 +9080,7 @@ function superRefine(fn, params) {
8966
9080
  return /* @__PURE__ */ _superRefine(fn, params);
8967
9081
  }
8968
9082
  //#endregion
8969
- //#region node_modules/.pnpm/@deepseek-ai+dsh-storage@0.1.2-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-storage/lib/index.js
9083
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-storage@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-storage/lib/index.js
8970
9084
  /**
8971
9085
  * Backend-facing vocabulary of the storage hub: a backend owns one medium
8972
9086
  * (a file-tree root, a database file) and exposes operation groups over it.
@@ -8977,7 +9091,7 @@ function superRefine(fn, params) {
8977
9091
  /** Allowed format for unit and table names: safe as a file name and as a SQL identifier segment without escaping. */
8978
9092
  const UNIT_NAME_RE = /^[a-z][a-z0-9_]*$/;
8979
9093
  //#endregion
8980
- //#region node_modules/.pnpm/@deepseek-ai+dsh-storage-domain@0.1.2-rc.1_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-i_9f44d404e0b20c2f6e8599234336f27b/node_modules/@deepseek-ai/dsh-storage-domain/lib/index.js
9094
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-storage-domain@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2_@deepseek-ai+ds_404bb44176e435660710548cd6a4e0aa/node_modules/@deepseek-ai/dsh-storage-domain/lib/index.js
8981
9095
  /**
8982
9096
  * Domain declaration vocabulary. A spec object is the single source of a
8983
9097
  * domain's identity, layout, and record schemas: the owning package defines
@@ -9121,6 +9235,27 @@ function sessionEvents(session) {
9121
9235
  function asEventList(events) {
9122
9236
  return Array.isArray(events) ? events : [];
9123
9237
  }
9238
+ /**
9239
+ * 读取一个持久化(离线)会话的事件日志与头部元数据。兼容两代 persistence API:
9240
+ * - 旧版:`sessionPersistence.inspect(id)` 直接返回 { events, meta };
9241
+ * - dsh 0.1.5 之后:`inspect` 已删除,改用 `open(id, 'read')` → `handle.read()`
9242
+ * (+ `handle.header`),读句柄不抢写所有权,读后必须 close()。
9243
+ * 会话不存在时两个 API 都抛错,调用方按原语义处理。
9244
+ */
9245
+ async function inspectPersistedSession(ctx, id) {
9246
+ const persistence = ctx.sessionPersistence;
9247
+ if (typeof persistence.inspect === "function") return await persistence.inspect(id);
9248
+ const handle = await persistence.open(id, "read");
9249
+ try {
9250
+ const { events } = await handle.read();
9251
+ return {
9252
+ events,
9253
+ meta: handle.header
9254
+ };
9255
+ } finally {
9256
+ await handle.close();
9257
+ }
9258
+ }
9124
9259
  /** agent.followup / steer 接受的用户消息值。 */
9125
9260
  function userMessage(text) {
9126
9261
  return {
@@ -9252,12 +9387,14 @@ function latestSegmentSince(events, sinceSeq = 0) {
9252
9387
  return segs.length === 0 ? null : segs[segs.length - 1] ?? null;
9253
9388
  }
9254
9389
  /**
9255
- * 对一个 live 会话的事件日志折叠出其"实时思维链"
9256
- * - 优先聚合流式 assistant/chunk 事件里的 reasoning-delta(turn 中途即可见、增量,
9257
- * 这正是监控思维链并按规则提前终止所需的粒度);
9258
- * - 同时聚合同窗口的 text-delta 增量;
9259
- * - 若没有进行中的 chunk(会话空闲、或该 provider 不流式推理/无推理),返回 null,
9260
- * 调用方应回落为 foldMessages 里的已定型 message.reasoning。
9390
+ * 对一个 live 会话的事件日志折叠出其"最近推理/文本增量流"。兼容两代日志形状:
9391
+ * - dsh 0.1.5 之后:turn 内不再发布独立 chunk 事件,流式增量内嵌在已定型
9392
+ * assistant/message(或未定型 assistant/attempt)事件的 `stream` 记录里
9393
+ * (reasoning-chunks / text-chunks 批量段 + 原始 chunk:reasoning-delta / text-delta);
9394
+ * - 0.1.2-rc 之前:独立的 assistant/chunk 事件(turn 中途即可见、增量,
9395
+ * 这正是旧版监控思维链并按规则提前终止所需的粒度)。
9396
+ * 两者兼读、取最新一份;无任何增量(会话空闲、或该 provider 不流式推理/无推理)
9397
+ * 返回 null,调用方应回落为 foldMessages 里已定型的 message.reasoning。
9261
9398
  * 纯读、无副作用;事件形状不做任何假设,缺失/异常一律安全处理。
9262
9399
  */
9263
9400
  function liveReasoningSnapshot(events) {
@@ -9271,11 +9408,41 @@ function liveReasoningSnapshot(events) {
9271
9408
  let step = 0;
9272
9409
  let foundDelta = false;
9273
9410
  for (const event of list) {
9274
- if (event.type === "assistant/message") {
9275
- lastFinalizedSeq = event.seq;
9411
+ const type = event.type;
9412
+ if (type === "assistant/message" || type === "assistant/attempt") {
9413
+ const data = event.data;
9414
+ if (data !== null && data !== void 0 && typeof data === "object" && Array.isArray(data.stream)) {
9415
+ let r = "";
9416
+ let t = "";
9417
+ for (const raw of data.stream) {
9418
+ const record = raw;
9419
+ if (record === null || typeof record !== "object") continue;
9420
+ if (record.type === "reasoning-chunks" && Array.isArray(record.texts)) {
9421
+ for (const s of record.texts) if (typeof s === "string") r += s;
9422
+ } else if (record.type === "text-chunks" && Array.isArray(record.texts)) {
9423
+ for (const s of record.texts) if (typeof s === "string") t += s;
9424
+ } else if (record.type === "chunk") {
9425
+ const c = record.chunk;
9426
+ if (c !== null && typeof c === "object" && typeof c.type === "string" && typeof c.text === "string") {
9427
+ if (c.type === "reasoning-delta") r += c.text;
9428
+ else if (c.type === "text-delta") t += c.text;
9429
+ }
9430
+ }
9431
+ }
9432
+ if (r !== "" || t !== "") {
9433
+ if (typeof data.turn === "number") turn = data.turn;
9434
+ if (typeof data.step === "number") step = data.step;
9435
+ reasoning = r;
9436
+ text = t;
9437
+ seq = event.seq;
9438
+ time = event.time;
9439
+ foundDelta = true;
9440
+ }
9441
+ }
9442
+ if (type === "assistant/message") lastFinalizedSeq = event.seq;
9276
9443
  continue;
9277
9444
  }
9278
- if (event.type !== "assistant/chunk") continue;
9445
+ if (type !== "assistant/chunk") continue;
9279
9446
  if (event.seq <= lastFinalizedSeq) continue;
9280
9447
  const data = event.data;
9281
9448
  if (data === null || data === void 0 || typeof data !== "object") continue;
@@ -9511,7 +9678,7 @@ function statusSnapshot(ctx, agent) {
9511
9678
  lastTurn,
9512
9679
  lastActivityAt,
9513
9680
  stalledMs: lastActivityAt === null ? null : Date.now() - lastActivityAt,
9514
- pendingWork: inbox?.hasPending ?? false,
9681
+ pendingWork: inbox?.hasPending === true || (inbox?.nextTurn?.length ?? 0) + (inbox?.nextStep?.length ?? 0) > 0,
9515
9682
  nextTurnCount: inbox?.nextTurn?.length ?? 0,
9516
9683
  nextStepCount: inbox?.nextStep?.length ?? 0,
9517
9684
  ...lastAssistantText === void 0 ? {} : { lastAssistantText },
@@ -9874,7 +10041,7 @@ var SessionMonitor = class {
9874
10041
  }
9875
10042
  };
9876
10043
  //#endregion
9877
- //#region node_modules/.pnpm/@deepseek-ai+dsh-scope@0.1.2-alpha.2_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-invaria_51e6fefef6ddce4f991b3d02344a1a72/node_modules/@deepseek-ai/dsh-scope/lib/index.js
10044
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-scope@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-invaria_eaf7046112a9b60866fe7f69bc8f6f64/node_modules/@deepseek-ai/dsh-scope/lib/index.js
9878
10045
  /**
9879
10046
  * Shared insertion-ordered storage and effect ownership for scope-aware registries.
9880
10047
  *
@@ -10153,7 +10320,92 @@ function scopeTarget(base, key) {
10153
10320
  return carrier;
10154
10321
  }
10155
10322
  //#endregion
10156
- //#region node_modules/.pnpm/@deepseek-ai+dsh-tools@0.1.2-alpha.2_96d3de6f0f5da790676b5bfb20e4a981/node_modules/@deepseek-ai/dsh-tools/lib/index.js
10323
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-sandbox@0.1.6-alpha.1_@deepseek-ai+cordis@4.0.2_@deepseek-ai+dsh-llm@0_1d3a97f96d2766469566ca2b01247bfa/node_modules/@deepseek-ai/dsh-sandbox/lib/index.js
10324
+ /**
10325
+ * The escalation vocabulary and choreography shared by every sandbox-enforcing
10326
+ * tool family (`@deepseek-ai/dsh-tool-bash`, `@deepseek-ai/dsh-tool-fs`): the
10327
+ * strictly-wider ladder, the argument-pairing validation, the model-facing
10328
+ * denial/hint markers, and {@link approveEscalation} — the ordered fail-closed
10329
+ * sequence that resolves a `sandbox_permissions` request through a
10330
+ * user-approval channel BEFORE anything executes. One home keeps the two
10331
+ * families' approval ordering and verbatim error texts from drifting apart.
10332
+ *
10333
+ * The channel is a minimal STRUCTURAL function shape ({@link EscalationAsk}),
10334
+ * not the approval service type: the tool layer — which owns the agent, the
10335
+ * call id, and the tool name — closes over `ctx.approval.request(...)` and
10336
+ * hands the closure down, so this package never depends on the approval or
10337
+ * agent packages.
10338
+ *
10339
+ * @module dsh-sandbox/escalation
10340
+ */
10341
+ /**
10342
+ * The strictly-wider table: what a call whose effective mode is the key may
10343
+ * escalate TO. Checked at EXECUTION, never baked into a tool schema — the
10344
+ * schema's enum is {@link ESCALATION_TARGETS}, because schemas are
10345
+ * registry-global while the effective mode is per-call truth.
10346
+ */
10347
+ const WIDER_MODES = {
10348
+ "read-only": ["workspace-write", "danger-full-access"],
10349
+ "workspace-write": ["danger-full-access"]
10350
+ };
10351
+ /**
10352
+ * The closed escalation-target vocabulary — every mode a call could ever
10353
+ * escalate TO (`read-only` is the floor; nothing escalates to it). Advertised
10354
+ * whenever the mounted capability confines: cutting the enum down to the modes
10355
+ * wider than the composition's DEFAULT would strand a session whose effective
10356
+ * mode sits below it (a `danger-full-access` default would advertise nothing
10357
+ * while a narrower-switched session stays confined with no lever).
10358
+ */
10359
+ const ESCALATION_TARGETS = ["workspace-write", "danger-full-access"];
10360
+ /**
10361
+ * Validate the escalation argument pairing a tool schema cannot express:
10362
+ * `sandbox_permissions` and `justification` travel together — an approval
10363
+ * prompt without a reason, or a reason driving nothing, is a malformed ask —
10364
+ * and the justification must be a non-empty sentence.
10365
+ * @param sandboxPermissions - the raw `sandbox_permissions` argument, if given.
10366
+ * @param justification - the raw `justification` argument, if given.
10367
+ */
10368
+ function validateEscalationArgs(sandboxPermissions, justification) {
10369
+ if (sandboxPermissions !== void 0 && justification === void 0) throw new Error("invalid escalation: sandbox_permissions requires a justification");
10370
+ if (justification !== void 0 && sandboxPermissions === void 0) throw new Error("invalid escalation: justification is only valid together with sandbox_permissions");
10371
+ if (justification !== void 0 && justification.trim().length === 0) throw new Error("invalid justification: expected a non-empty sentence");
10372
+ }
10373
+ /**
10374
+ * Resolve a sandbox-escalation request BEFORE anything executes: check strict
10375
+ * widening against the call's effective mode, then resolve the approval
10376
+ * channel, then map every outcome — the ordered fail-closed sequence both
10377
+ * enforcing families share. Returns the granted mode to stamp onto exactly
10378
+ * this call; throws the distinct verbatim text for every other path (a
10379
+ * non-widening request, a missing approval service, an agent-less execution,
10380
+ * a rejection, a cancellation, an unanswerable ask) — the tool registry turns
10381
+ * the throw into the call's isError result, and nothing has run. A
10382
+ * non-widening request never prompts a human.
10383
+ * @param request - the escalation to judge (see {@link EscalationRequest}).
10384
+ * @param approval - the approval ingredients the tool holds (see {@link EscalationApproval}).
10385
+ * @returns the granted mode, consumed by the one call that asked.
10386
+ */
10387
+ async function approveEscalation(request, approval) {
10388
+ const { requestedMode: mode, effectiveMode, justification, subject } = request;
10389
+ if (!(WIDER_MODES[effectiveMode] ?? []).includes(mode)) throw new Error(`sandbox escalation to "${mode}" is not strictly wider than this call's current "${effectiveMode}" mode`);
10390
+ if (approval.approver === void 0) throw new Error(`sandbox escalation to "${mode}" requires approval, but no approval service is composed`);
10391
+ if (approval.agent === void 0) throw new Error(`sandbox escalation to "${mode}" requires approval, but the call has no agent to route it through`);
10392
+ const outcome = await approval.approver.request({
10393
+ agent: approval.agent,
10394
+ toolName: approval.toolName,
10395
+ callId: approval.callId,
10396
+ reason: `escalate sandbox to ${mode}: ${justification}`,
10397
+ ...approval.signal ? { signal: approval.signal } : {}
10398
+ });
10399
+ switch (outcome) {
10400
+ case "allowed-once": return mode;
10401
+ case "rejected": throw new Error(`the user rejected escalating this ${subject} to "${mode}"`);
10402
+ case "cancelled": throw new Error(`approval for escalating to "${mode}" was cancelled`);
10403
+ case "unavailable": throw new Error(`sandbox escalation to "${mode}" requires approval, but no approval channel is available`);
10404
+ default: return assertNever(outcome, "EscalationOutcome");
10405
+ }
10406
+ }
10407
+ //#endregion
10408
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-tools@0.1.6-alpha.1_e89197212305e1b19bd6df05221bc44b/node_modules/@deepseek-ai/dsh-tools/lib/index.js
10157
10409
  /**
10158
10410
  * Enforced JSON Schema subset shared by tool outputs, generated PTC mode
10159
10411
  * types, subagents, and workflows. The subset accepts any JSON root, an
@@ -11024,7 +11276,7 @@ const TYPESCRIPT_FLAVOR = {
11024
11276
  description: "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run.",
11025
11277
  codeDescription: "The program: the body of an async TypeScript function."
11026
11278
  };
11027
- /** Per-language `run_code` schema flavors (see {@link RunCodeFlavor}); one entry per {@link CodeSdkLanguage}. */
11279
+ /** Per-language `run_code` schema flavors (see {@link RunCodeFlavor}); one entry per {@link PtcSdkLanguage}. */
11028
11280
  const RUN_CODE_FLAVORS = {
11029
11281
  typescript: TYPESCRIPT_FLAVOR,
11030
11282
  python: {
@@ -11039,17 +11291,48 @@ const RUN_CODE_FLAVORS = {
11039
11291
  * can never drift.
11040
11292
  */
11041
11293
  const RUN_CODE_DESCRIPTION_PARAM_DESCRIPTION = "Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI). Examples: \"Count TODO markers across packages\"; \"Read failing test and its fixture\"; \"Rename config key in every cordis.yml\".";
11294
+ const RUN_CODE_CONTROLS = {
11295
+ timeoutMs: {
11296
+ type: "number",
11297
+ description: "Positive elapsed-time budget in milliseconds, capped by the deployment maximum."
11298
+ },
11299
+ sandbox_permissions: {
11300
+ type: "string",
11301
+ enum: [...ESCALATION_TARGETS],
11302
+ description: "Wider sandbox mode for this complete program execution; requires justification and approval."
11303
+ },
11304
+ justification: {
11305
+ type: "string",
11306
+ description: "Reason this complete program needs wider access, shown to the user for approval."
11307
+ }
11308
+ };
11309
+ function controlParameters(runtime) {
11310
+ if (runtime === void 0) return RUN_CODE_CONTROLS;
11311
+ return {
11312
+ ...runtime.timeout === void 0 ? {} : { timeoutMs: {
11313
+ ...RUN_CODE_CONTROLS.timeoutMs,
11314
+ description: `Positive elapsed-time budget in milliseconds, including nested tool and approval waits. Default ${runtime.timeout.defaultMs}; capped at ${runtime.timeout.maxMs}. Zero does not disable the deadline.`
11315
+ } },
11316
+ ...runtime.sandboxMode === void 0 ? {} : {
11317
+ sandbox_permissions: RUN_CODE_CONTROLS.sandbox_permissions,
11318
+ justification: RUN_CODE_CONTROLS.justification
11319
+ }
11320
+ };
11321
+ }
11322
+ function escalationGuidance(runtime) {
11323
+ return runtime?.sandboxMode === void 0 ? "" : " A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.";
11324
+ }
11042
11325
  /**
11043
11326
  * Resolve the {@link RunCodeFlavor} for the loaded runtime's language, read at
11044
11327
  * schema-emission time so the model-visible `run_code` schema always matches
11045
11328
  * the SDK section's language. `peekRuntime` returns `undefined` only when no
11046
11329
  * runtime is mounted, which reaches this function through definition readers
11047
11330
  * and `schemas()` — the doc-catalog harvest is the only shipped one, and none
11048
- * of them feeds a model, because `wireSchemas` calls `requireCodeRuntime`
11331
+ * of them feeds a model, because `wireSchemas` calls `requirePtcRuntime`
11049
11332
  * before projecting — so that path degrades to {@link TYPESCRIPT_FLAVOR}. A
11050
11333
  * mounted runtime whose language has no flavor entry fails loud, exactly as
11051
- * `requireCodeRuntime` rejects it at assembly. Keeping this table in step with
11052
- * `SDK_RENDERERS` is the compiler's job ({@link CodeSdkLanguage}); what this
11334
+ * `requirePtcRuntime` rejects it at assembly. Keeping this table in step with
11335
+ * `SDK_RENDERERS` is the compiler's job ({@link PtcSdkLanguage}); what this
11053
11336
  * guard owns is the runtime-supplied language neither table knows, which never
11054
11337
  * yields a wrong-language schema for a real runtime.
11055
11338
  */
@@ -11218,7 +11501,8 @@ function createRunCodeTool(registry, options) {
11218
11501
  type: "string",
11219
11502
  required: true,
11220
11503
  description: RUN_CODE_DESCRIPTION_PARAM_DESCRIPTION
11221
- }
11504
+ },
11505
+ ...RUN_CODE_CONTROLS
11222
11506
  },
11223
11507
  output: {
11224
11508
  schema: {
@@ -11230,12 +11514,37 @@ function createRunCodeTool(registry, options) {
11230
11514
  required: true,
11231
11515
  items: { type: "string" }
11232
11516
  },
11233
- result: { type: "json" }
11517
+ result: { type: "json" },
11518
+ sandbox: {
11519
+ type: "object",
11520
+ additionalProperties: false,
11521
+ properties: {
11522
+ mode: {
11523
+ type: "string",
11524
+ required: true,
11525
+ enum: [
11526
+ "read-only",
11527
+ "workspace-write",
11528
+ "danger-full-access"
11529
+ ]
11530
+ },
11531
+ denied: {
11532
+ type: "boolean",
11533
+ required: true
11534
+ },
11535
+ enforcement: {
11536
+ type: "string",
11537
+ enum: ["full", "partial"]
11538
+ }
11539
+ }
11540
+ }
11234
11541
  }
11235
11542
  },
11236
11543
  render: (_args, value) => {
11237
11544
  const rendered = value.result === void 0 ? "" : renderValue(value.result);
11238
11545
  const parts = [value.logs.join("\n"), rendered].filter((part) => part.length > 0);
11546
+ if (value.sandbox?.enforcement === "partial") parts.push("File sandbox enforcement is partial on this host.");
11547
+ if (value.sandbox?.denied) parts.push(`The ${value.sandbox.mode} file sandbox denied an operation.${escalationGuidance(peekRuntime())}`);
11239
11548
  return [{
11240
11549
  type: "text",
11241
11550
  text: parts.length > 0 ? parts.join("\n") : "(run_code completed with no output)"
@@ -11245,6 +11554,31 @@ function createRunCodeTool(registry, options) {
11245
11554
  async execute(args, exec) {
11246
11555
  if (args.description.trim().length === 0) throw new Error("invalid description: expected a non-empty string");
11247
11556
  const runtime = requireRuntime();
11557
+ validateEscalationArgs(args.sandbox_permissions, args.justification);
11558
+ if (args.timeoutMs !== void 0 && runtime.timeout === void 0) throw new Error("timeoutMs is not available for this PTC runtime");
11559
+ if (args.timeoutMs !== void 0 && (!Number.isFinite(args.timeoutMs) || args.timeoutMs <= 0)) throw new Error("invalid timeoutMs: expected a positive finite number");
11560
+ const standingPolicy = runtime.sandboxMode === void 0 ? void 0 : options.resolveSandboxPolicy(exec);
11561
+ let policy = standingPolicy;
11562
+ if (args.sandbox_permissions !== void 0 && args.justification !== void 0) {
11563
+ if (standingPolicy === void 0) throw new Error("sandbox_permissions is not available for this PTC runtime");
11564
+ const approvedMode = await approveEscalation({
11565
+ requestedMode: args.sandbox_permissions,
11566
+ justification: args.justification,
11567
+ effectiveMode: standingPolicy.mode,
11568
+ subject: "program"
11569
+ }, {
11570
+ approver: options.peekApprover(),
11571
+ agent: exec.agent,
11572
+ callId: exec.callId,
11573
+ toolName: RUN_CODE_NAME,
11574
+ signal: exec.signal
11575
+ });
11576
+ policy = {
11577
+ ...standingPolicy,
11578
+ mode: approvedMode
11579
+ };
11580
+ }
11581
+ exec.signal.throwIfAborted();
11248
11582
  const runController = new AbortController();
11249
11583
  const onOuterAbort = () => {
11250
11584
  runController.abort(exec.signal.reason);
@@ -11326,15 +11660,17 @@ function createRunCodeTool(registry, options) {
11326
11660
  while (logWork.size > 0) await Promise.allSettled([...logWork]);
11327
11661
  };
11328
11662
  const runOver = () => runController.signal.aborted;
11329
- const binding = (name) => async (rawArgs) => {
11663
+ const binding = (schema) => async (rawArgs) => {
11664
+ const { name } = schema;
11330
11665
  if (runOver()) throw new Error(`run_code run is over (${String(runController.signal.reason)}); ${name} not dispatched`);
11331
11666
  const normalized = jsonNormalizeArgs(rawArgs);
11332
11667
  const n = ++dispatches;
11333
- const subCallId = brandString(`${String(exec.callId)}:code:${n}`);
11668
+ const subCallId = brandString(`${String(exec.callId)}:ptc:${n}`);
11334
11669
  const input = {
11335
11670
  callId: subCallId,
11336
11671
  rootCallId: exec.rootCallId,
11337
11672
  name,
11673
+ schema,
11338
11674
  arguments: normalized.dispatched,
11339
11675
  ...exec.agent ? { agent: exec.agent } : {},
11340
11676
  parent: exec.token,
@@ -11362,13 +11698,14 @@ function createRunCodeTool(registry, options) {
11362
11698
  isError: result.isError,
11363
11699
  content: result.content
11364
11700
  });
11365
- agent.session.append("tool/code-dispatch", {
11701
+ agent.session.append("tool/ptc-dispatch", {
11366
11702
  rootCallId: exec.rootCallId,
11367
11703
  parentCallId: exec.callId,
11368
11704
  subCallId,
11369
11705
  name,
11370
11706
  arguments: normalized.logged,
11371
11707
  isError: result.isError,
11708
+ ...result.error?.info === void 0 ? {} : { error: result.error.info },
11372
11709
  content: logged
11373
11710
  });
11374
11711
  })().finally(() => {
@@ -11384,7 +11721,7 @@ function createRunCodeTool(registry, options) {
11384
11721
  reject(/* @__PURE__ */ new Error(`run_code run is over (${String(runController.signal.reason)}); ${name} tool call abandoned`));
11385
11722
  },
11386
11723
  async start() {
11387
- exec.agent?.session.append("tool/code-dispatch-start", {
11724
+ exec.agent?.session.append("tool/ptc-dispatch-start", {
11388
11725
  rootCallId: exec.rootCallId,
11389
11726
  parentCallId: exec.callId,
11390
11727
  subCallId,
@@ -11418,7 +11755,7 @@ function createRunCodeTool(registry, options) {
11418
11755
  content: result.content,
11419
11756
  source: {
11420
11757
  kind: "plugin",
11421
- plugin: "tools-code-mode"
11758
+ plugin: "tools-ptc"
11422
11759
  }
11423
11760
  }));
11424
11761
  for (const context of result.additionalContexts ?? []) exec.deferContext(context);
@@ -11439,13 +11776,13 @@ function createRunCodeTool(registry, options) {
11439
11776
  if (schema.name === "run_code") continue;
11440
11777
  Object.defineProperty(functions, schema.name, {
11441
11778
  enumerable: true,
11442
- value: binding(schema.name)
11779
+ value: binding(deepFreeze(schema))
11443
11780
  });
11444
11781
  }
11445
11782
  try {
11446
11783
  let result;
11447
11784
  try {
11448
- result = await runtime.run({
11785
+ result = await runtime.run(runtime.resolve({
11449
11786
  program: args.code,
11450
11787
  bindings: [{
11451
11788
  global: "tools",
@@ -11455,18 +11792,23 @@ function createRunCodeTool(registry, options) {
11455
11792
  memberNameProperty: "toolName"
11456
11793
  }
11457
11794
  }],
11458
- signal: runController.signal
11459
- });
11795
+ signal: runController.signal,
11796
+ ...exec.agent?.session.header.cwd !== void 0 ? { cwd: exec.agent.session.header.cwd } : {},
11797
+ ...policy !== void 0 ? { sandboxPolicy: policy } : {},
11798
+ ...args.timeoutMs !== void 0 ? { timeoutMs: args.timeoutMs } : {}
11799
+ }));
11460
11800
  } finally {
11461
11801
  runController.abort("run_code settled");
11462
11802
  await drainDispatches();
11463
11803
  }
11464
11804
  if (result.error) {
11465
11805
  const logsText = result.logs.length > 0 ? `\nCaptured output:\n${result.logs.join("\n")}` : "";
11466
- throw new CodeRunFailedError(`code run failed (${result.error.kind}): ${result.error.message}${logsText}`);
11806
+ const sandboxText = result.sandbox === void 0 ? "" : `\nFile sandbox: ${result.sandbox.mode}${result.sandbox.enforcement === void 0 ? "" : `; enforcement: ${result.sandbox.enforcement}`}${result.sandbox.denied ? "; operation denied" : ""}.`;
11807
+ throw new CodeRunFailedError(`code run failed (${result.error.kind}): ${result.error.message}${logsText}${sandboxText}${result.sandbox?.denied ? escalationGuidance(runtime) : ""}`);
11467
11808
  }
11468
11809
  return {
11469
11810
  logs: result.logs,
11811
+ ...result.sandbox === void 0 ? {} : { sandbox: result.sandbox },
11470
11812
  ...result.value !== void 0 ? { result: result.value } : {}
11471
11813
  };
11472
11814
  } finally {
@@ -11482,7 +11824,11 @@ function createRunCodeTool(registry, options) {
11482
11824
  });
11483
11825
  Object.defineProperty(definition, "description", {
11484
11826
  enumerable: true,
11485
- get: () => resolveFlavor(peekRuntime).description
11827
+ get: () => {
11828
+ const runtime = peekRuntime();
11829
+ const instructions = runtime?.executionInstructions;
11830
+ return resolveFlavor(peekRuntime).description + (instructions ? ` ${instructions}` : "") + (runtime === void 0 ? "" : " The working directory is the Session's current directory.") + escalationGuidance(runtime);
11831
+ }
11486
11832
  });
11487
11833
  Object.defineProperty(definition, "parameters", {
11488
11834
  enumerable: true,
@@ -11496,7 +11842,8 @@ function createRunCodeTool(registry, options) {
11496
11842
  type: "string",
11497
11843
  required: true,
11498
11844
  description: RUN_CODE_DESCRIPTION_PARAM_DESCRIPTION
11499
- }
11845
+ },
11846
+ ...controlParameters(peekRuntime())
11500
11847
  })
11501
11848
  });
11502
11849
  return definition;
@@ -11781,7 +12128,7 @@ function renderToolsSdk(schemas) {
11781
12128
  * PTC mode codegen — Python flavor. The pure projection from registered tool schemas to the
11782
12129
  * Python SDK text the model programs against under `runtime.language === 'python'`. Sibling of
11783
12130
  * {@link ./ts-types.ts | ts-types.ts}; the two files are two projections of the same registry
11784
- * store, keyed by the loaded {@link @deepseek-ai/dsh-code-runtime#CodeRuntime.language | code
12131
+ * store, keyed by the loaded {@link @deepseek-ai/dsh-ptc-runtime#PtcRuntime.language | PTC
11785
12132
  * runtime's language}.
11786
12133
  *
11787
12134
  * Under `mode: 'ptc'` the native tool schemas are omitted from the request, so this generated
@@ -12468,17 +12815,17 @@ function renderToolsSdkPy(schemas) {
12468
12815
  */
12469
12816
  /**
12470
12817
  * Language → SDK-section renderer. The registry looks up the loaded
12471
- * `ctx.codeRuntime.language` in this table when assembling the `tools:sdk`
12818
+ * `ctx.ptcRuntime.language` in this table when assembling the `tools:sdk`
12472
12819
  * section under a non-native mode; a runtime whose language is not a key
12473
12820
  * fails the assembly loudly (same idiom as `toolOrder` violations). Adding a
12474
- * new backend language is three parallel edits — a {@link CodeSdkLanguage}
12821
+ * new backend language is three parallel edits — a {@link PtcSdkLanguage}
12475
12822
  * member, an entry here, and a `RUN_CODE_FLAVORS` entry in `ptc.ts` for
12476
12823
  * its `run_code` schema strings — plus the renderer function this table points
12477
12824
  * at. The `satisfies` clause pins this table's key set to that union, which
12478
12825
  * the flavor table is checked against too, so any of the three left out is a
12479
12826
  * typecheck failure. What no check reaches is the prose that names the values
12480
- * instead of deriving them: the seam's `dsh-code-runtime` README pair, its
12481
- * `CodeRuntime.language` JSDoc, and `docs/subsystems/code-runtime.md`
12827
+ * instead of deriving them: the seam's `dsh-ptc-runtime` README pair, its
12828
+ * `PtcRuntime.language` JSDoc, and `docs/subsystems/ptc-runtime.md`
12482
12829
  * with its zh pair, plus this package's own README pair and the
12483
12830
  * {@link Config.mode} JSDoc.
12484
12831
  */
@@ -12717,12 +13064,13 @@ var ToolRuntime = class extends Service {
12717
13064
  return {
12718
13065
  name: "tools:sdk",
12719
13066
  order: this.ctx.systemPrompt.getSectionOrder("TOOLS_SDK"),
13067
+ interpolate: false,
12720
13068
  text: (context) => {
12721
13069
  const mode = this.modeFor(context.scope);
12722
13070
  if (mode === "native") return "";
12723
- const runtime = this.requireCodeRuntime(mode);
13071
+ const runtime = this.requirePtcRuntime(mode);
12724
13072
  const render = SDK_RENDERERS[runtime.language];
12725
- /* v8 ignore next -- requireCodeRuntime rejects an unknown language before this runs. */
13073
+ /* v8 ignore next -- requirePtcRuntime rejects an unknown language before this runs. */
12726
13074
  if (render === void 0) throw new Error(`dsh-tools: no SDK renderer for ${runtime.language}`);
12727
13075
  return render(this.sdkSchemas(context.scope));
12728
13076
  }
@@ -12751,10 +13099,16 @@ var ToolRuntime = class extends Service {
12751
13099
  * and only for scopes whose mode actually presents it.
12752
13100
  * @returns the shared transport definition.
12753
13101
  */
12754
- requireCodeTransport() {
13102
+ requirePtcTransport() {
12755
13103
  this.ptcTransport ??= createRunCodeTool(this, {
12756
- requireRuntime: () => this.requireCodeRuntime(this.defaultMode),
12757
- peekRuntime: () => this.ctx.get("codeRuntime"),
13104
+ requireRuntime: () => this.requirePtcRuntime(this.defaultMode),
13105
+ peekApprover: () => this.ctx.get("approval"),
13106
+ resolveSandboxPolicy: (exec) => {
13107
+ const policy = this.ctx.get("sandboxPolicy");
13108
+ if (policy === void 0) throw new Error("dsh-tools: confined PTC runtime requires sandboxPolicy");
13109
+ return policy.resolve(exec.agent === void 0 ? {} : { session: exec.agent.session });
13110
+ },
13111
+ peekRuntime: () => this.ctx.get("ptcRuntime"),
12758
13112
  maxParallel: this.maxParallelSubCalls,
12759
13113
  shapeDispatchLog: (dispatch) => this.shapeDispatchLog(dispatch)
12760
13114
  });
@@ -12799,7 +13153,7 @@ var ToolRuntime = class extends Service {
12799
13153
  schemas: [...view.visible.values()].map((definition) => this.schemaOf(definition, false)),
12800
13154
  knownNames: [...view.knownNames]
12801
13155
  };
12802
- this.requireCodeRuntime(mode);
13156
+ this.requirePtcRuntime(mode);
12803
13157
  const schemas = [...view.visible.values()].map((definition) => this.schemaOf(definition, false));
12804
13158
  if (mode === "ptc") return {
12805
13159
  schemas: schemas.filter((schema) => schema.name === RUN_CODE_NAME),
@@ -12811,10 +13165,10 @@ var ToolRuntime = class extends Service {
12811
13165
  };
12812
13166
  }
12813
13167
  /**
12814
- * Resolve the code runtime or throw the actionable misconfiguration error.
13168
+ * Resolve the PTC runtime or throw the actionable misconfiguration error.
12815
13169
  * Read at use time (assembly / run_code execution), NOT via static
12816
13170
  * `inject`: an inject entry would hold `ctx.tools` — and every tool plugin
12817
- * behind it — hostage to a code runtime existing even under `mode:
13171
+ * behind it — hostage to a PTC runtime existing even under `mode:
12818
13172
  * 'native'`.
12819
13173
  *
12820
13174
  * Assembly and `run_code` execution read separately, so the language is not
@@ -12822,12 +13176,11 @@ var ToolRuntime = class extends Service {
12822
13176
  * reads return the same flavor — but a reload that swapped in a second
12823
13177
  * language between them would hand a program written against one SDK to the
12824
13178
  * other. Binding it is deferred until a second backend ships (the first
12825
- * point it is testable); rationale in the
12826
- * [language-dispatch note](../../../../.agents/notes/implemented/feature/2026-07-31-ptc-language-dispatch.md).
13179
+ * point it is testable).
12827
13180
  */
12828
- requireCodeRuntime(mode) {
12829
- const runtime = this.ctx.get("codeRuntime");
12830
- if (!runtime) throw new Error(`dsh-tools: mode "${mode}" requires a code runtime — load a ctx.codeRuntime implementation (e.g. @deepseek-ai/dsh-code-runtime-worker-thread) or set tools mode to "native"`);
13181
+ requirePtcRuntime(mode) {
13182
+ const runtime = this.ctx.get("ptcRuntime");
13183
+ if (!runtime) throw new Error(`dsh-tools: mode "${mode}" requires a PTC runtime — load a ctx.ptcRuntime implementation (e.g. @deepseek-ai/dsh-ptc-runtime-node) or set tools mode to "native"`);
12831
13184
  if (!Object.hasOwn(SDK_RENDERERS, runtime.language)) {
12832
13185
  const known = Object.keys(SDK_RENDERERS).map((name) => JSON.stringify(name)).join(", ");
12833
13186
  throw new Error(`dsh-tools: no SDK renderer registered for runtime language ${JSON.stringify(runtime.language)} (known: ${known})`);
@@ -12909,9 +13262,9 @@ var ToolRuntime = class extends Service {
12909
13262
  * A restriction filters what a scope inherits — the global layer and every
12910
13263
  * ancestor layer on its chain — and never what its OWN layer registers.
12911
13264
  * That exemption is what a per-child capability filter has to keep intact:
12912
- * the delegation runtime registers a child's reporting and structured-output
12913
- * tools into the child's own layer, and a filter naming the capabilities the
12914
- * child may use must not strip the machinery it answers through.
13265
+ * the delegation runtime registers a child's structured-output tool into the
13266
+ * child's own layer, and a filter naming the capabilities the child may use
13267
+ * must not strip the machinery it answers through.
12915
13268
  *
12916
13269
  * Reading the exempt set as "the global layer" instead of "not mine" held
12917
13270
  * only while every model-facing tool sat in the host composition. Once
@@ -12941,7 +13294,7 @@ var ToolRuntime = class extends Service {
12941
13294
  knownNames.add(name);
12942
13295
  visible.set(name, definition);
12943
13296
  }
12944
- if (this.modeFor(scope) !== "native") visible.set(RUN_CODE_NAME, this.requireCodeTransport());
13297
+ if (this.modeFor(scope) !== "native") visible.set(RUN_CODE_NAME, this.requirePtcTransport());
12945
13298
  return {
12946
13299
  visible,
12947
13300
  knownNames,
@@ -13029,7 +13382,7 @@ var ToolRuntime = class extends Service {
13029
13382
  }
13030
13383
  /**
13031
13384
  * Run the `tools/ptc-dispatch-log` waterfall over one settled sub-dispatch
13032
- * and return the content the bridge should log on `tool/code-dispatch`.
13385
+ * and return the content the bridge should log on `tool/ptc-dispatch`.
13033
13386
  * Contained: when a listener throws, the method logs the original settled
13034
13387
  * content; that failure must not fail the dispatch or omit the settle event. Private:
13035
13388
  * the ONE consumer is the `run_code` bridge this registry constructs, which
@@ -13112,6 +13465,7 @@ var ToolRuntime = class extends Service {
13112
13465
  signal,
13113
13466
  ...agent !== void 0 ? { agent } : {},
13114
13467
  ...parent !== void 0 ? { parent } : {},
13468
+ ...exec.schema !== void 0 ? { schema: exec.schema } : {},
13115
13469
  deferContext(context) {
13116
13470
  deferredContexts.push(context);
13117
13471
  },
@@ -13194,7 +13548,13 @@ var ToolRuntime = class extends Service {
13194
13548
  exec,
13195
13549
  result: toolAbortedBeforeDispatchResult()
13196
13550
  });
13551
+ if (decision.kind === "cancel") return await next({
13552
+ kind: "post-result",
13553
+ exec,
13554
+ result: toolAbortedBeforeDispatchResult()
13555
+ });
13197
13556
  const denialReason = decision.kind === "allow" ? this.guardReason(exec) : decision.reason;
13557
+ const denialInfo = decision.kind === "deny" ? decision.info : void 0;
13198
13558
  if (denialReason !== void 0) return await next({
13199
13559
  kind: "post-result",
13200
13560
  exec,
@@ -13204,7 +13564,10 @@ var ToolRuntime = class extends Service {
13204
13564
  text: `Error: ${denialReason}`
13205
13565
  }],
13206
13566
  isError: true,
13207
- error: { message: denialReason }
13567
+ error: {
13568
+ message: denialReason,
13569
+ ...denialInfo === void 0 ? {} : { info: denialInfo }
13570
+ }
13208
13571
  })
13209
13572
  });
13210
13573
  if (this.callerCancelled(exec)) return await next({
@@ -13655,11 +14018,33 @@ function toolAbortedBeforeDispatchResult(prior) {
13655
14018
  };
13656
14019
  }
13657
14020
  //#endregion
13658
- //#region node_modules/.pnpm/@deepseek-ai+dsh-agent@0.1.2-alpha.2_82401c175458b799db42b9e8f7dd7700/node_modules/@deepseek-ai/dsh-agent/lib/index.js
14021
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-agent@0.1.6-alpha.1_ce40d13c69e7e1ce48818baba37665e1/node_modules/@deepseek-ai/dsh-agent/lib/index.js
13659
14022
  /**
13660
14023
  * Agent-scoped model selection shared by runtime entry points.
13661
14024
  * @module @deepseek-ai/dsh-agent/model-selection
13662
14025
  */
14026
+ function sameRoute(left, right) {
14027
+ return left.provider === right.provider && left.model === right.model;
14028
+ }
14029
+ function routeLabel(route, other) {
14030
+ return route.provider === other.provider ? route.model : `${route.provider}/${route.model}`;
14031
+ }
14032
+ function modelSwitchNotice(previous, selected) {
14033
+ const from = routeLabel(previous, selected);
14034
+ const to = routeLabel(selected, previous);
14035
+ return createUserMessage({
14036
+ content: [{
14037
+ type: "text",
14038
+ text: `[model changed: assistant turns above this point were generated by ${from}; the session continues with ${to}]`
14039
+ }],
14040
+ source: {
14041
+ kind: "plugin",
14042
+ plugin: "model-selection",
14043
+ form: "notice",
14044
+ summary: boundContextSummary(`${from} → ${to}`)
14045
+ }
14046
+ });
14047
+ }
13663
14048
  /**
13664
14049
  * Couple one mutable selection to Agent-scoped prompt assembly and request routing.
13665
14050
  * Prompt assembly snapshots the selected model before delegating, then applies
@@ -13668,9 +14053,15 @@ function toolAbortedBeforeDispatchResult(prior) {
13668
14053
  * surfaces. An absent selected effort clears any inherited effort, restoring
13669
14054
  * the selected model's provider/default behavior.
13670
14055
  *
14056
+ * A provider/model change appends a durable user-role notice to the next
14057
+ * admitted request. It compares the assembled selection with the latest
14058
+ * request header; effort-only changes and empty no-request decisions add no
14059
+ * notice. Failure before header persistence repeats the notice on the next
14060
+ * request.
14061
+ *
13671
14062
  * @param agentCtx - The selected Agent's scoped context.
13672
14063
  * @param selection - Mutable selection owned by the calling entry point.
13673
- * @returns Disposer for both scoped waterfall listeners.
14064
+ * @returns Disposer for all scoped waterfall listeners.
13674
14065
  */
13675
14066
  function installModelSelection(agentCtx, selection) {
13676
14067
  const disposeAssembly = agentCtx.on("system-prompt/assemble", async (_assembly, _context, next) => {
@@ -13699,9 +14090,22 @@ function installModelSelection(agentCtx, selection) {
13699
14090
  ...selected.reasoningEffort === void 0 ? {} : { reasoningEffort: selected.reasoningEffort }
13700
14091
  };
13701
14092
  });
14093
+ const disposeNotice = agentCtx.on("agent/pre-step", async ({ agent, messages, signal, step }, next) => {
14094
+ const decision = await next();
14095
+ if (decision.kind === "reject" || signal.aborted) return decision;
14096
+ if (decision.messages.length === 0 && (step === 1 || messages.length > 0)) return decision;
14097
+ const selected = selection.assembled;
14098
+ const previous = agent.session.requestHeader()?.config;
14099
+ if (selected === void 0 || previous === void 0 || sameRoute(selected, previous)) return decision;
14100
+ return {
14101
+ ...decision,
14102
+ messages: [...decision.messages, modelSwitchNotice(previous, selected)]
14103
+ };
14104
+ }, { prepend: true });
13702
14105
  return () => {
13703
14106
  disposeAssembly();
13704
14107
  disposeRequest();
14108
+ disposeNotice();
13705
14109
  };
13706
14110
  }
13707
14111
  //#endregion
@@ -13853,8 +14257,10 @@ function registerCreate(env) {
13853
14257
  ...model !== "" ? { model } : {},
13854
14258
  ...reasoningEffort === void 0 ? {} : { reasoningEffort }
13855
14259
  },
13856
- setup: (agentCtx) => {
13857
- if (caller !== void 0) env.ctx.agentPresets.composeFrom(agentCtx, caller.ctx);
14260
+ setup: async (agentCtx) => {
14261
+ const requestedPreset = typeof args.agentPreset === "string" && args.agentPreset.trim() !== "" ? args.agentPreset.trim() : void 0;
14262
+ if (requestedPreset !== void 0) await env.ctx.agentPresets.mount(agentCtx, requestedPreset);
14263
+ else if (caller !== void 0) env.ctx.agentPresets.composeFrom(agentCtx, caller.ctx);
13858
14264
  if (installModelSelection$1 !== void 0) {
13859
14265
  installModelSelection$1(agentCtx, selection);
13860
14266
  return;
@@ -14022,7 +14428,11 @@ function registerResume(env) {
14022
14428
  }
14023
14429
  let headers = [];
14024
14430
  try {
14025
- headers = await env.ctx.sessionPersistence.list();
14431
+ headers = (await env.ctx.sessionPersistence.list()).map((rec) => ({
14432
+ id: rec.header.id,
14433
+ ...rec.header.cwd === void 0 ? {} : { cwd: rec.header.cwd },
14434
+ createdAt: rec.header.createdAt
14435
+ }));
14026
14436
  } catch (error) {
14027
14437
  throw new Error("session persistence unavailable: " + (error instanceof Error ? error.message : String(error)));
14028
14438
  }
@@ -14185,7 +14595,7 @@ function registerSegments(env) {
14185
14595
  events = sessionEvents(agent.session);
14186
14596
  live = true;
14187
14597
  } else try {
14188
- events = (await env.ctx.sessionPersistence.inspect(args.sessionId)).events;
14598
+ events = (await inspectPersistedSession(env.ctx, args.sessionId)).events;
14189
14599
  live = false;
14190
14600
  } catch (error) {
14191
14601
  throw new Error(String(error));
@@ -14279,7 +14689,7 @@ function registerRead(env) {
14279
14689
  } else {
14280
14690
  let inspection;
14281
14691
  try {
14282
- inspection = await env.ctx.sessionPersistence.inspect(args.sessionId);
14692
+ inspection = await inspectPersistedSession(env.ctx, args.sessionId);
14283
14693
  } catch (error) {
14284
14694
  throw new Error("cannot read session " + JSON.stringify(args.sessionId) + ": " + (error instanceof Error ? error.message : String(error)));
14285
14695
  }
@@ -14403,7 +14813,8 @@ function registerFind(env) {
14403
14813
  }
14404
14814
  if (args.liveOnly !== true) try {
14405
14815
  const persistence = env.ctx.sessionPersistence;
14406
- for (const header of await persistence.list()) {
14816
+ for (const rec of await persistence.list()) {
14817
+ const header = rec.header;
14407
14818
  if (items.some((item) => item.sessionId === header.id)) continue;
14408
14819
  items.push({
14409
14820
  sessionId: header.id,
@@ -14439,13 +14850,12 @@ function registerFind(env) {
14439
14850
  const wsFilter = typeof args.workspaceId === "string" ? args.workspaceId.trim() : "";
14440
14851
  const cwdFilter = typeof args.cwd === "string" ? args.cwd.trim().toLowerCase() : "";
14441
14852
  if (titleFilter !== "" || query !== "" && items.some((item) => !item.live)) {
14442
- const persistence = env.ctx.sessionPersistence;
14443
14853
  let inspected = 0;
14444
14854
  for (const item of items) {
14445
14855
  if (inspected >= 30) break;
14446
14856
  if (item.live || item.title !== void 0) continue;
14447
14857
  try {
14448
- item.title = titleOf((await persistence.inspect(item.sessionId)).events);
14858
+ item.title = titleOf((await inspectPersistedSession(env.ctx, item.sessionId)).events);
14449
14859
  inspected += 1;
14450
14860
  } catch {
14451
14861
  inspected += 1;
@@ -14950,7 +15360,6 @@ function registerArchive(env) {
14950
15360
  async execute(args) {
14951
15361
  const items = env.ctx.workspaceRegistry.archivedSessionIds.map(String).map((id) => ({ sessionId: id }));
14952
15362
  if (args.resolveTitles === true && items.length > 0) {
14953
- const persistence = env.ctx.sessionPersistence;
14954
15363
  const registryTitles = /* @__PURE__ */ new Map();
14955
15364
  try {
14956
15365
  const records = await env.registry.all();
@@ -14962,7 +15371,7 @@ function registerArchive(env) {
14962
15371
  continue;
14963
15372
  }
14964
15373
  try {
14965
- item.title = titleOf((await persistence.inspect(item.sessionId)).events);
15374
+ item.title = titleOf((await inspectPersistedSession(env.ctx, item.sessionId)).events);
14966
15375
  } catch {}
14967
15376
  }
14968
15377
  }