@toddzheng024/dscode-bundle 0.1.0

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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/bin/apply_patch +4 -0
  5. package/bootstrap.mjs +21 -0
  6. package/cordis.patch.yml +756 -0
  7. package/package.json +327 -0
  8. package/plugins/auto-review/audit.mjs +20 -0
  9. package/plugins/auto-review/index.mjs +181 -0
  10. package/plugins/auto-review/policy.mjs +59 -0
  11. package/plugins/dscode/index.mjs +35 -0
  12. package/plugins/session-metrics/index.mjs +44 -0
  13. package/plugins/session-metrics/pricing.mjs +18 -0
  14. package/plugins/session-metrics/store.mjs +24 -0
  15. package/plugins/session-metrics/view.mjs +58 -0
  16. package/plugins/tui-tools/hooks.mjs +23 -0
  17. package/plugins/tui-tools/index.mjs +164 -0
  18. package/plugins/ultra/policy.mjs +11 -0
  19. package/presets/dscode/agent.cordis.yml +288 -0
  20. package/presets/dscode/preset.yml +3 -0
  21. package/vendor/bash/LICENSE +21 -0
  22. package/vendor/bash/index.js +449 -0
  23. package/vendor/bash/types/background.d.ts +19 -0
  24. package/vendor/bash/types/index.d.ts +22 -0
  25. package/vendor/bash/types/render.d.ts +38 -0
  26. package/vendor/deepseek/LICENSE +21 -0
  27. package/vendor/deepseek/index.js +2102 -0
  28. package/vendor/deepseek/types/adapter.d.ts +163 -0
  29. package/vendor/deepseek/types/file-id.d.ts +19 -0
  30. package/vendor/deepseek/types/file-store.d.ts +83 -0
  31. package/vendor/deepseek/types/files-api.d.ts +103 -0
  32. package/vendor/deepseek/types/image-tokens.d.ts +19 -0
  33. package/vendor/deepseek/types/index.d.ts +106 -0
  34. package/vendor/deepseek/types/request-pricing.d.ts +48 -0
  35. package/vendor/deepseek/types/serialize.d.ts +85 -0
  36. package/vendor/deepseek/types/sse.d.ts +24 -0
  37. package/vendor/deepseek/types/translate.d.ts +37 -0
  38. package/vendor/deepseek/types/types.d.ts +178 -0
  39. package/vendor/deepseek/types/upload-index.d.ts +68 -0
  40. package/vendor/persistent/LICENSE +21 -0
  41. package/vendor/persistent/index.js +386 -0
  42. package/vendor/persistent/types/index.d.ts +24 -0
  43. package/vendor/tui/LICENSE +21 -0
  44. package/vendor/tui/devtools-CdTl3MNy.mjs +3643 -0
  45. package/vendor/tui/index.mjs +39105 -0
  46. package/vendor/tui/invariant.mjs +21 -0
  47. package/vendor/tui/rolldown-runtime-CMFfr-1z.mjs +26 -0
  48. package/vendor/tui/startup.mjs +109 -0
  49. package/vendor/tui/theme-DCT8Y2xf.mjs +628 -0
  50. package/vendor/tui/types/app.d.ts +304 -0
  51. package/vendor/tui/types/approval.d.ts +59 -0
  52. package/vendor/tui/types/attachments.d.ts +52 -0
  53. package/vendor/tui/types/authorization-panel.d.ts +22 -0
  54. package/vendor/tui/types/authorization.d.ts +36 -0
  55. package/vendor/tui/types/commands.d.ts +52 -0
  56. package/vendor/tui/types/editor-keys.d.ts +105 -0
  57. package/vendor/tui/types/editor.d.ts +6 -0
  58. package/vendor/tui/types/fork.d.ts +8 -0
  59. package/vendor/tui/types/git-workflow.d.ts +32 -0
  60. package/vendor/tui/types/history.d.ts +97 -0
  61. package/vendor/tui/types/index.d.ts +124 -0
  62. package/vendor/tui/types/input-split.d.ts +54 -0
  63. package/vendor/tui/types/internals.d.ts +26 -0
  64. package/vendor/tui/types/invariant.d.ts +15 -0
  65. package/vendor/tui/types/kernel-panels.d.ts +167 -0
  66. package/vendor/tui/types/keyboard.d.ts +80 -0
  67. package/vendor/tui/types/mentions.d.ts +81 -0
  68. package/vendor/tui/types/model-capabilities.d.ts +82 -0
  69. package/vendor/tui/types/models.d.ts +119 -0
  70. package/vendor/tui/types/permissions.d.ts +27 -0
  71. package/vendor/tui/types/plugin-inventory.d.ts +11 -0
  72. package/vendor/tui/types/presets.d.ts +22 -0
  73. package/vendor/tui/types/provider-settings.d.ts +239 -0
  74. package/vendor/tui/types/questions.d.ts +54 -0
  75. package/vendor/tui/types/render/animations.d.ts +265 -0
  76. package/vendor/tui/types/render/editor.d.ts +162 -0
  77. package/vendor/tui/types/render/export.d.ts +9 -0
  78. package/vendor/tui/types/render/fuzzy.d.ts +21 -0
  79. package/vendor/tui/types/render/inspector.d.ts +36 -0
  80. package/vendor/tui/types/render/lines.d.ts +66 -0
  81. package/vendor/tui/types/render/markdown.d.ts +29 -0
  82. package/vendor/tui/types/render/projection.d.ts +461 -0
  83. package/vendor/tui/types/render/status.d.ts +196 -0
  84. package/vendor/tui/types/render/text.d.ts +58 -0
  85. package/vendor/tui/types/render/tool-detail.d.ts +94 -0
  86. package/vendor/tui/types/render/tool-preview.d.ts +28 -0
  87. package/vendor/tui/types/render/width.d.ts +29 -0
  88. package/vendor/tui/types/session-directory.d.ts +173 -0
  89. package/vendor/tui/types/session-switch.d.ts +17 -0
  90. package/vendor/tui/types/settings-file.d.ts +41 -0
  91. package/vendor/tui/types/skills.d.ts +47 -0
  92. package/vendor/tui/types/startup.d.ts +65 -0
  93. package/vendor/tui/types/store.d.ts +58 -0
  94. package/vendor/tui/types/subagents.d.ts +70 -0
  95. package/vendor/tui/types/theme-panel.d.ts +24 -0
  96. package/vendor/tui/types/theme.d.ts +215 -0
  97. package/vendor/tui/types/version.d.ts +18 -0
  98. package/vendor/tui/types/whale-glyph.d.ts +6 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,449 @@
1
+ // dscode-named-shell-v1
2
+ import z from "@deepseek-ai/schemastery";
3
+ import { isAbsolute, resolve } from "node:path";
4
+ import { TOOL_ABORTED, defineTool } from "@deepseek-ai/dsh-tools";
5
+ import { HarnessError } from "@deepseek-ai/dsh-llm";
6
+ import { ESCALATION_TARGETS, approveEscalation, canonicalPath, escalationHintMarker, sandboxDenialMarker, validateEscalationArgs } from "@deepseek-ai/dsh-sandbox";
7
+ import { DSH_ENV_PREFIX, parseExitStatus } from "@deepseek-ai/dsh-shell";
8
+ //#region lib/types/background.js
9
+ /**
10
+ * Generic-task adaptation for background bash process handles.
11
+ *
12
+ * @module @deepseek-ai/dsh-tool-bash/background
13
+ */
14
+ /**
15
+ * Map a settled background process onto the generic task-outcome vocabulary:
16
+ * `killed` stays `killed` (detail: the signal when one is known), everything
17
+ * else is `completed` with the exit code as detail. A nonzero command exit is
18
+ * reported, not failed, exactly like the foreground rendering.
19
+ * @param proc - the settled process handle.
20
+ * @returns the outcome for the `ctx.jobs` registration.
21
+ */
22
+ function processOutcome(proc) {
23
+ if (proc.status === "killed") return {
24
+ status: "killed",
25
+ detail: proc.signal !== null ? `signal: ${proc.signal}` : "killed before exit"
26
+ };
27
+ return {
28
+ status: "completed",
29
+ detail: `exit code: ${proc.exitCode ?? 0}`
30
+ };
31
+ }
32
+ //#endregion
33
+ //#region lib/types/render.js
34
+ /**
35
+ * Model-facing result rendering for the bash tool.
36
+ *
37
+ * @module @deepseek-ai/dsh-tool-bash/render
38
+ */
39
+ /** Append the truncation notice (with the full-output spill path) to a stream's text. */
40
+ function streamText(output) {
41
+ if (!output.truncated) return output.text;
42
+ return `${output.text}\n[output truncated; full output: ${output.spillPath ?? "(unavailable)"}]`;
43
+ }
44
+ /**
45
+ * Shape one finished run into the text the model sees: stdout, then a marked
46
+ * stderr section, then exit-status markers. Non-zero exits are reported, not
47
+ * errored — the model decides how to react; only infrastructure failures
48
+ * (spawn errors, aborts) surface as isError results.
49
+ * @param result - the completed foreground run from the executor.
50
+ * @param escalationModes - the escalation targets this composition advertises;
51
+ * non-empty adds the same-turn escalation hint after a denial marker
52
+ * (default `[]`: no hint).
53
+ * @returns the model-facing text: output body (or `(no output)`), then any timeout/signal/exit markers, each on its own line.
54
+ */
55
+ function renderResult(result, escalationModes = []) {
56
+ const out = streamText(result.stdout);
57
+ const err = streamText(result.stderr);
58
+ let body = out;
59
+ if (err.length > 0) {
60
+ if (body.length > 0 && !body.endsWith("\n")) body += "\n";
61
+ body += `[stderr]\n${err}`;
62
+ }
63
+ if (body.length === 0) body = "(no output)";
64
+ const markers = [];
65
+ if (result.sandbox?.denied) {
66
+ markers.push(sandboxDenialMarker(result.sandbox.mode));
67
+ if (escalationModes.length > 0) markers.push(escalationHintMarker("command"));
68
+ }
69
+ if (result.timedOut) markers.push(`[timed out after ${result.timeoutMs}ms]`);
70
+ if (result.signal !== null) markers.push(`[killed by signal: ${result.signal}]`);
71
+ else if (result.exitCode !== 0) markers.push(`[exit code: ${result.exitCode}]`);
72
+ if (markers.length === 0) return body;
73
+ if (!body.endsWith("\n")) body += "\n";
74
+ return body + markers.join("\n");
75
+ }
76
+ /**
77
+ * Shape one background-process read into the `job_output` delta the model
78
+ * sees: the incremental delta, plus the lossy-read notice (with full-stream
79
+ * spill paths) when in-memory truncation dropped unread bytes. Empty-delta
80
+ * rendering (`(no new output)`) is the generic job controller's job.
81
+ * @param read - one incremental read from the process handle.
82
+ * @param sandbox - settled sandbox facts, when this was a confined process.
83
+ * @param escalationModes - escalation targets advertised by this composition.
84
+ * @returns the delta text with any loss or sandbox notice appended.
85
+ */
86
+ function renderProcessRead(read, sandbox, escalationModes = []) {
87
+ const notices = [];
88
+ if (read.lossy) {
89
+ const paths = [read.stdoutSpillPath, read.stderrSpillPath].filter((path) => path !== void 0);
90
+ notices.push(`[some output was dropped from memory; full output: ${paths.length > 0 ? paths.join(", ") : "(unavailable)"}]`);
91
+ }
92
+ if (sandbox?.runnerFailed) notices.push(`[sandbox: the sandbox runner itself failed under ${sandbox.mode} mode — the command did not run; this is a sandbox problem, not a command failure]`);
93
+ else if (sandbox?.denied) {
94
+ notices.push(sandboxDenialMarker(sandbox.mode));
95
+ if (escalationModes.length > 0) notices.push(escalationHintMarker("command"));
96
+ }
97
+ if (notices.length === 0) return read.delta;
98
+ return `${read.delta}${read.delta.length > 0 && !read.delta.endsWith("\n") ? "\n" : ""}${notices.join("\n")}`;
99
+ }
100
+ //#endregion
101
+ //#region lib/types/index.js
102
+ /**
103
+ * Model-facing Consumer of the `ctx.shell` capability seam. Background calls
104
+ * register process handles with `ctx.jobs`; their work uses job cancellation
105
+ * rather than the tool-call signal after an id is returned.
106
+ *
107
+ * TODO(permissions): deployment policy belongs in `tools/pre-execute` and
108
+ * sandboxing executors; see docs/architecture.md § Where new behavior goes.
109
+ * @module @deepseek-ai/dsh-tool-bash
110
+ */
111
+ const name = "tool-bash";
112
+ const inject = [
113
+ "tools",
114
+ "shell",
115
+ "systemPrompt",
116
+ "shellEnv"
117
+ ];
118
+ /** Runtime configuration schema for the bash tool plugin. */
119
+ const Config = z.object({ toolName: z.string().default("bash"), enableRunInBackground: z.boolean().default(true) });
120
+ function validateBashArgs(args) {
121
+ if (args.command.trim().length === 0) throw new Error("invalid command: expected a non-empty string");
122
+ if (args.description.trim().length === 0) throw new Error("invalid description: expected a non-empty string");
123
+ if (args.timeoutMs !== void 0 && (!Number.isFinite(args.timeoutMs) || args.timeoutMs <= 0)) throw new Error(`invalid timeoutMs: expected a positive number, got ${JSON.stringify(args.timeoutMs)}`);
124
+ validateEscalationArgs(args.sandbox_permissions, args.justification);
125
+ }
126
+ function bashDescription(backgroundEnabled, escalationModes) {
127
+ const background = backgroundEnabled ? "Set `run_in_background: true` for long-running commands: the call returns a job id immediately; read its output with `job_output` and stop it with `job_kill`." : "Background execution is not available; long-running commands must finish within the timeout.";
128
+ const base = `Execute a bash command (\`bash -c\`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass \`workdir\` instead of using \`cd\`. Non-zero exits are reported as \`[exit code: N]\`. Current harness environment facts are exposed through managed \`$${DSH_ENV_PREFIX}*\` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as \`[sandbox: file access denied under <mode> mode]\` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. ` + background;
129
+ if (escalationModes.length === 0) return base;
130
+ return base + " Attempting a command the sandbox may deny is safe and expected: run it and read the marker rather than assuming the denial. When a command is denied and a wider mode would let it succeed, escalate immediately in the same turn — the one sanctioned exception to a denial: retry the exact same command once with `sandbox_permissions` (the narrowest wider mode that suffices) plus a one-sentence `justification`. Do not detour through chat to ask permission first — the approval prompt raised by that retry is how the user consents. If the session states approval prompts are disabled, there is no exception: a denial is final — do not set `sandbox_permissions`. Never escalate speculatively: ground the request in a real denial — normally the one this command just hit; escalating up front is fine only when this session already denied the same access. A rejected escalation is final for that command — stop and explain, never work around it — but it does not forbid attempting or escalating other commands later.";
131
+ }
132
+ function presentBashCall(args) {
133
+ if (args.run_in_background === true) return {
134
+ card: "generic",
135
+ title: args.command,
136
+ kind: "execute",
137
+ rawInput: args.command,
138
+ content: [{
139
+ type: "text",
140
+ text: args.description
141
+ }]
142
+ };
143
+ return {
144
+ card: "terminal",
145
+ title: args.command,
146
+ description: args.description,
147
+ ...args.workdir !== void 0 ? { cwd: args.workdir } : {}
148
+ };
149
+ }
150
+ /**
151
+ * Present completed foreground output as a terminal; background acknowledgements
152
+ * and execution errors use generic fenced output without an exit-status pill.
153
+ */
154
+ function presentBashResult(args, result) {
155
+ const block = result.content.length === 1 ? result.content[0] : void 0;
156
+ if (block === void 0 || block.type !== "text") return void 0;
157
+ const raw = block.text;
158
+ if (typeof args === "object" && args !== null && args.run_in_background === true || result.isError) return {
159
+ card: "generic",
160
+ content: [{
161
+ type: "text",
162
+ text: `\`\`\`console\n${raw.replace(/\n+$/, "")}\n\`\`\``
163
+ }]
164
+ };
165
+ const { body, ...exit } = parseExitStatus(raw);
166
+ return {
167
+ card: "terminal",
168
+ output: body,
169
+ ...exit
170
+ };
171
+ }
172
+ /**
173
+ * Resolve an explicit workdir first, making a relative one session-workspace-relative;
174
+ * otherwise use the filesystem identity of the session cwd and leave executor
175
+ * defaulting as the fallback. A resolved sandbox-policy root wins so workdir
176
+ * and confinement use the exact same per-call identity.
177
+ */
178
+ function resolveWorkdir(modelWorkdir, exec, policyWorkspaceRoot) {
179
+ const headerCwd = exec.agent?.session.header.cwd;
180
+ const sessionCwd = policyWorkspaceRoot ?? (headerCwd === void 0 ? void 0 : canonicalPath(headerCwd));
181
+ if (modelWorkdir === void 0) return sessionCwd;
182
+ if (sessionCwd !== void 0 && !isAbsolute(modelWorkdir)) return resolve(sessionCwd, modelWorkdir);
183
+ return modelWorkdir;
184
+ }
185
+ /** Detach the executor DTO from readonly Service Definition types into plain JSON data. */
186
+ function canonicalBashResult(result) {
187
+ const output = (stream) => ({
188
+ text: stream.text,
189
+ truncated: stream.truncated,
190
+ ...stream.spillPath !== void 0 ? { spillPath: stream.spillPath } : {}
191
+ });
192
+ return {
193
+ exitCode: result.exitCode,
194
+ signal: result.signal,
195
+ timedOut: result.timedOut,
196
+ aborted: result.aborted,
197
+ timeoutMs: result.timeoutMs,
198
+ stdout: output(result.stdout),
199
+ stderr: output(result.stderr),
200
+ ...result.sandbox !== void 0 ? { sandbox: {
201
+ mode: result.sandbox.mode,
202
+ denied: result.sandbox.denied,
203
+ ...result.sandbox.enforcement !== void 0 ? { enforcement: result.sandbox.enforcement } : {},
204
+ ...result.sandbox.runnerFailed !== void 0 ? { runnerFailed: result.sandbox.runnerFailed } : {}
205
+ } } : {}
206
+ };
207
+ }
208
+ /** Canonical background-handle properties shared by the bash output union. */
209
+ const BACKGROUND_OUTPUT_PROPERTIES = {
210
+ kind: {
211
+ type: "string",
212
+ required: true,
213
+ const: "background"
214
+ },
215
+ jobId: {
216
+ type: "string",
217
+ required: true
218
+ }
219
+ };
220
+ function apply(ctx, config = {}) {
221
+ const backgroundEnabled = config.enableRunInBackground ?? true;
222
+ const defaultMode = ctx.shell.sandboxMode;
223
+ const escalationModes = defaultMode === void 0 ? [] : ESCALATION_TARGETS;
224
+ const sandboxPolicy = defaultMode === void 0 ? void 0 : ctx.get("sandboxPolicy");
225
+ if (defaultMode !== void 0 && sandboxPolicy === void 0) throw new Error("tool-bash: the mounted bash executor confines but ctx.sandboxPolicy is missing");
226
+ /** Resolve the complete standing policy for this call when a confining executor is mounted. */
227
+ const resolveSandboxPolicy = (exec) => sandboxPolicy?.resolve(exec.agent === void 0 ? {} : { session: exec.agent.session });
228
+ /**
229
+ * Resolve a sandbox-escalation request through `ctx.approval` BEFORE
230
+ * anything executes, delegating the shared fail-closed sequence (strict
231
+ * widening, channel resolution, outcome mapping) to
232
+ * {@link approveEscalation}. This tool contributes only the composition
233
+ * guard (the fields are unadvertised without a sandboxing executor, yet
234
+ * schema validation checks advertised keys only, so an unadvertised
235
+ * `sandbox_permissions` still reaches execute) and the approval
236
+ * ingredients. The shared policy resolver is required whenever the executor
237
+ * advertises confinement, so a split composition fails at tool-plugin load.
238
+ */
239
+ const approveBashEscalation = (mode, justification, exec, standingPolicy) => {
240
+ if (escalationModes.length === 0) throw new Error("sandbox_permissions is not available in this composition (no sandboxing executor to escalate)");
241
+ const effectiveMode = standingPolicy.mode;
242
+ return approveEscalation({
243
+ requestedMode: mode,
244
+ justification,
245
+ effectiveMode,
246
+ subject: "command"
247
+ }, {
248
+ approver: ctx.get("approval"),
249
+ agent: exec.agent,
250
+ callId: exec.callId,
251
+ toolName: config.toolName,
252
+ signal: exec.signal
253
+ });
254
+ };
255
+ ctx.systemPrompt.section({
256
+ name: "tool:" + config.toolName,
257
+ order: ctx.systemPrompt.getSectionOrder("TOOL_BASH"),
258
+ text: `Check the [exit code: N] marker on every ${config.toolName} result; investigate failures before moving on.`
259
+ });
260
+ ctx.tools.register(defineTool({
261
+ name: config.toolName,
262
+ description: bashDescription(backgroundEnabled, escalationModes),
263
+ parameters: {
264
+ command: {
265
+ type: "string",
266
+ required: true,
267
+ description: "The bash command to execute."
268
+ },
269
+ description: {
270
+ type: "string",
271
+ required: true,
272
+ description: "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."
273
+ },
274
+ timeoutMs: {
275
+ type: "number",
276
+ description: "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
277
+ },
278
+ workdir: {
279
+ type: "string",
280
+ description: "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
281
+ },
282
+ ...backgroundEnabled ? { run_in_background: {
283
+ type: "boolean",
284
+ description: "Run in the background and return a job id immediately (collect with job_output, stop with job_kill). No timeout applies."
285
+ } } : {},
286
+ ...escalationModes.length > 0 ? {
287
+ sandbox_permissions: {
288
+ type: "string",
289
+ enum: [...escalationModes],
290
+ description: "The wider sandbox mode this command needs. Only valid as a one-shot retry of a command the sandbox just denied; requires justification and user approval."
291
+ },
292
+ justification: {
293
+ type: "string",
294
+ description: "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access."
295
+ }
296
+ } : {}
297
+ },
298
+ output: {
299
+ schema: { oneOf: [{
300
+ type: "object",
301
+ additionalProperties: false,
302
+ properties: BACKGROUND_OUTPUT_PROPERTIES
303
+ }, {
304
+ type: "object",
305
+ additionalProperties: false,
306
+ properties: {
307
+ kind: {
308
+ type: "string",
309
+ required: true,
310
+ const: "foreground"
311
+ },
312
+ exitCode: {
313
+ required: true,
314
+ oneOf: [{ type: "integer" }, { type: "null" }]
315
+ },
316
+ signal: {
317
+ required: true,
318
+ oneOf: [{ type: "string" }, { type: "null" }]
319
+ },
320
+ timedOut: {
321
+ type: "boolean",
322
+ required: true
323
+ },
324
+ aborted: {
325
+ type: "boolean",
326
+ required: true
327
+ },
328
+ timeoutMs: {
329
+ type: "number",
330
+ required: true
331
+ },
332
+ stdout: {
333
+ type: "object",
334
+ additionalProperties: false,
335
+ required: true,
336
+ properties: {
337
+ text: {
338
+ type: "string",
339
+ required: true
340
+ },
341
+ truncated: {
342
+ type: "boolean",
343
+ required: true
344
+ },
345
+ spillPath: { type: "string" }
346
+ }
347
+ },
348
+ stderr: {
349
+ type: "object",
350
+ additionalProperties: false,
351
+ required: true,
352
+ properties: {
353
+ text: {
354
+ type: "string",
355
+ required: true
356
+ },
357
+ truncated: {
358
+ type: "boolean",
359
+ required: true
360
+ },
361
+ spillPath: { type: "string" }
362
+ }
363
+ },
364
+ sandbox: {
365
+ type: "object",
366
+ additionalProperties: false,
367
+ properties: {
368
+ mode: {
369
+ type: "string",
370
+ required: true
371
+ },
372
+ denied: {
373
+ type: "boolean",
374
+ required: true
375
+ },
376
+ enforcement: { type: "string" },
377
+ runnerFailed: { type: "boolean" }
378
+ }
379
+ }
380
+ }
381
+ }] },
382
+ render: (_args, value) => [{
383
+ type: "text",
384
+ text: value.kind === "background" ? `started background job ${value.jobId}` : renderResult(value, escalationModes)
385
+ }]
386
+ },
387
+ async execute(args, exec) {
388
+ validateBashArgs(args);
389
+ const standingPolicy = resolveSandboxPolicy(exec);
390
+ const approvedMode = args.sandbox_permissions !== void 0 && args.justification !== void 0 ? await approveBashEscalation(args.sandbox_permissions, args.justification, exec, standingPolicy) : void 0;
391
+ const policy = approvedMode === void 0 ? standingPolicy : {
392
+ ...standingPolicy,
393
+ mode: approvedMode
394
+ };
395
+ const workdir = resolveWorkdir(args.workdir, exec, standingPolicy?.workspaceRoot);
396
+ const dshEnv = ctx.shellEnv.collect(exec);
397
+ const request = {
398
+ command: args.command,
399
+ ...workdir !== void 0 ? { workdir } : {},
400
+ ...args.timeoutMs !== void 0 ? { timeoutMs: args.timeoutMs } : {},
401
+ dshEnv,
402
+ ...policy !== void 0 ? { sandboxPolicy: policy } : {}
403
+ };
404
+ if (args.run_in_background === true) {
405
+ if (!backgroundEnabled) throw new Error("run_in_background is disabled for this deployment (enableRunInBackground: false)");
406
+ const jobs = ctx.get("jobs");
407
+ if (jobs === void 0) throw new Error("background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs");
408
+ if (exec.signal.aborted) {
409
+ const error = new HarnessError("tool call aborted", TOOL_ABORTED);
410
+ error.name = "AbortError";
411
+ throw error;
412
+ }
413
+ return {
414
+ kind: "background",
415
+ jobId: jobs.start({
416
+ kind: "bash",
417
+ label: args.command,
418
+ ...exec.agent ? { owner: exec.agent } : {},
419
+ run: () => {
420
+ const proc = ctx.shell.start(ctx.shell.resolve(request));
421
+ return {
422
+ cancel: () => void proc.kill(),
423
+ done: proc.done.then(() => processOutcome(proc)),
424
+ readOutput: () => renderProcessRead(proc.readOutput(), proc.sandbox, escalationModes)
425
+ };
426
+ }
427
+ })
428
+ };
429
+ }
430
+ const result = await ctx.shell.run(ctx.shell.resolve({
431
+ ...request,
432
+ signal: exec.signal
433
+ }));
434
+ if (result.aborted) {
435
+ const error = new HarnessError("tool call aborted", TOOL_ABORTED);
436
+ error.name = "AbortError";
437
+ throw error;
438
+ }
439
+ return {
440
+ kind: "foreground",
441
+ ...canonicalBashResult(result)
442
+ };
443
+ },
444
+ presentCall: presentBashCall,
445
+ presentResult: presentBashResult
446
+ }));
447
+ }
448
+ //#endregion
449
+ export { Config, apply, inject, name };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Generic-task adaptation for background bash process handles.
3
+ *
4
+ * @module @deepseek-ai/dsh-tool-bash/background
5
+ */
6
+ import type { ShellProcess } from '@deepseek-ai/dsh-shell';
7
+ /**
8
+ * Map a settled background process onto the generic task-outcome vocabulary:
9
+ * `killed` stays `killed` (detail: the signal when one is known), everything
10
+ * else is `completed` with the exit code as detail. A nonzero command exit is
11
+ * reported, not failed, exactly like the foreground rendering.
12
+ * @param proc - the settled process handle.
13
+ * @returns the outcome for the `ctx.jobs` registration.
14
+ */
15
+ export declare function processOutcome(proc: ShellProcess): {
16
+ status: 'completed' | 'killed';
17
+ detail: string;
18
+ };
19
+ //# sourceMappingURL=background.d.ts.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Model-facing Consumer of the `ctx.shell` capability seam. Background calls
3
+ * register process handles with `ctx.jobs`; their work uses job cancellation
4
+ * rather than the tool-call signal after an id is returned.
5
+ *
6
+ * TODO(permissions): deployment policy belongs in `tools/pre-execute` and
7
+ * sandboxing executors; see docs/architecture.md § Where new behavior goes.
8
+ * @module @deepseek-ai/dsh-tool-bash
9
+ */
10
+ import type { Context } from '@deepseek-ai/cordis';
11
+ import z from '@deepseek-ai/schemastery';
12
+ export declare const name = "tool-bash";
13
+ export declare const inject: string[];
14
+ /** Configuration for the bash tool. */
15
+ export interface Config {
16
+ /** Expose `run_in_background` (default true); disabled calls are also rejected. */
17
+ enableRunInBackground?: boolean;
18
+ }
19
+ /** Runtime configuration schema for the bash tool plugin. */
20
+ export declare const Config: z<Config>;
21
+ export declare function apply(ctx: Context, config?: Config): void;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Model-facing result rendering for the bash tool.
3
+ *
4
+ * @module @deepseek-ai/dsh-tool-bash/render
5
+ */
6
+ import type { ShellProcessRead, ShellRunResult, ShellSandboxInfo } from '@deepseek-ai/dsh-shell';
7
+ import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
8
+ /**
9
+ * Shape one finished run into the text the model sees: stdout, then a marked
10
+ * stderr section, then exit-status markers. Non-zero exits are reported, not
11
+ * errored — the model decides how to react; only infrastructure failures
12
+ * (spawn errors, aborts) surface as isError results.
13
+ * @param result - the completed foreground run from the executor.
14
+ * @param escalationModes - the escalation targets this composition advertises;
15
+ * non-empty adds the same-turn escalation hint after a denial marker
16
+ * (default `[]`: no hint).
17
+ * @returns the model-facing text: output body (or `(no output)`), then any timeout/signal/exit markers, each on its own line.
18
+ */
19
+ export declare function renderResult(result: ShellRunResult, escalationModes?: readonly SandboxMode[]): string;
20
+ /**
21
+ * Shape one background-process read into the `job_output` delta the model
22
+ * sees: the incremental delta, plus the lossy-read notice (with full-stream
23
+ * spill paths) when in-memory truncation dropped unread bytes. Empty-delta
24
+ * rendering (`(no new output)`) is the generic job controller's job.
25
+ * @param read - one incremental read from the process handle.
26
+ * @param sandbox - settled sandbox facts, when this was a confined process.
27
+ * @param escalationModes - escalation targets advertised by this composition.
28
+ * @returns the delta text with any loss or sandbox notice appended.
29
+ */
30
+ export declare function renderProcessRead(read: ShellProcessRead, sandbox?: ShellSandboxInfo, escalationModes?: readonly SandboxMode[]): string;
31
+ /**
32
+ * The exit-status parse is the shared marker-contract half of the shell-tool
33
+ * rendering story, owned by `@deepseek-ai/dsh-shell` so `dsh-tool-pwsh` reuses
34
+ * it (its renderer emits the same markers). Re-exported here to keep
35
+ * `../src/render.ts` a single import root for bash-tool consumers.
36
+ */
37
+ export { parseExitStatus, type ParsedExitStatus } from '@deepseek-ai/dsh-shell';
38
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.