@deepseek-ai/dsh-headless 0.1.5-rc.2 → 0.1.6-alpha.2

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/startup.js CHANGED
@@ -1,10 +1,24 @@
1
- import { Command } from "commander";
1
+ import { t as boundJsonLine } from "./json-stream-Bh4BGGk9.js";
2
+ import { Command, CommanderError } from "commander";
2
3
  import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
4
+ //#region lib/types/startup-internals.js
5
+ /**
6
+ * Process facts the startup provider reads, kept out of the `./startup` entry
7
+ * so substituting them in tests adds no public package API.
8
+ * @module @deepseek-ai/dsh-headless/startup-internals
9
+ */
10
+ /** Process facts the provider reads; tests substitute them. */
11
+ const internals = {
12
+ stdinIsTty: () => process.stdin.isTTY,
13
+ stdout: process.stdout
14
+ };
15
+ //#endregion
3
16
  //#region lib/types/startup.js
4
17
  /**
5
- * The one-shot app's command-line provider: it parses the task positional and
6
- * `--help`, then publishes {@link HEADLESS_STARTUP_SERVICE}. The runner is an
7
- * ordinary consumer whose lazy config waits for that service.
18
+ * The one-shot app's command-line provider: it parses the task positional,
19
+ * `--session-id`, `--json`, and `--help`, then publishes
20
+ * {@link HEADLESS_STARTUP_SERVICE}. The runner is an ordinary consumer whose
21
+ * lazy config waits for that service.
8
22
  * @module @deepseek-ai/dsh-headless/startup
9
23
  */
10
24
  /** Stable Cordis plugin name. */
@@ -14,27 +28,64 @@ const inject = ["cmdlineArgs"];
14
28
  /** Service provided by this plugin and injected by the one-shot runner. */
15
29
  const HEADLESS_STARTUP_SERVICE = "headlessStartup";
16
30
  /**
17
- * This app's command: the task positional, its description, and its help text.
31
+ * This app's command: the task positional, its options, and its help text.
18
32
  * @returns a fresh program, so one process can parse more than once (tests).
19
33
  */
20
34
  function headlessCommand() {
21
- return new Command().name("dsh --profile headless").description("Answer one task, stream reasoning to stderr, print the final assistant message, and exit.").helpOption("-h, --help", "show this help").argument("[task...]", "the task text; multiple words are joined by spaces").addHelpText("after", `
35
+ return new Command().name("dsh --profile headless").description("Answer one task and exit; the answer goes to stdout and diagnostics to stderr.").helpOption("-h, --help", "show this help").option("--json", "write newline-delimited run events to stdout instead of the final message").option("--session-id <id>", "adopt the persisted Session with this id; an unknown id is an error").argument("[task...]", "the task text; multiple words are joined by spaces, and `-` reads stdin").addHelpText("after", `
22
36
  Examples:
23
- dsh --profile headless "run the tests" answer one task and exit
37
+ dsh --profile headless "run the tests" answer one task and exit
38
+ echo "run the tests" | dsh --profile headless read the task from stdin
39
+ dsh --profile headless --json "run the tests" emit machine-readable run events
40
+ dsh --profile headless --session-id session-… "continue" resume an existing Session
24
41
  `);
25
42
  }
26
43
  /**
44
+ * Whether the raw invocation asks for the machine-readable stream. The scan
45
+ * stops at `--` and skips a `--session-id` value, so a literal `--json` used as
46
+ * an option value or a positional never installs the JSON error override.
47
+ * @param argv - the invocation's raw arguments.
48
+ * @returns whether `--json` is a real flag of this invocation.
49
+ */
50
+ function jsonRequested(argv) {
51
+ for (let index = 0; index < argv.length; index += 1) {
52
+ const argument = argv[index];
53
+ if (argument === "--") return false;
54
+ if (argument === "--json") return true;
55
+ if (argument === "--session-id") index += 1;
56
+ }
57
+ return false;
58
+ }
59
+ /**
27
60
  * Parse and provide the one-shot task as an ordinary Cordis service. The
28
- * command's action publishes the task; a missing or whitespace-only task is a
29
- * usage error, so on rejection (and on `--help`) nothing is provided.
61
+ * command's action publishes the task; a missing task on an interactive stdin
62
+ * is a usage error, so on rejection (and on `--help`) nothing is provided.
30
63
  * @param ctx - plugin context carrying the command line.
31
64
  */
32
65
  function apply(ctx) {
33
66
  const program = headlessCommand();
67
+ if (jsonRequested(ctx.get("cmdlineArgs")?.get() ?? [])) program.error = (message, errorOptions) => {
68
+ const payload = boundJsonLine({
69
+ type: "error",
70
+ message: message.replace(/^error: /, "")
71
+ });
72
+ internals.stdout.write(`${payload}\n`);
73
+ throw new CommanderError(1, errorOptions?.code ?? "commander.error", message);
74
+ };
34
75
  program.action(() => {
35
- const task = program.args.join(" ");
36
- if (task.trim() === "") program.error("error: a task is required, for example: dsh --profile headless \"run the tests\"");
37
- ctx.provide(HEADLESS_STARTUP_SERVICE, { task });
76
+ if (program.args.length > 1 && program.args.includes("-")) program.error("error: `-` must be the only task argument");
77
+ const joined = program.args.join(" ");
78
+ if (program.args.length > 0 && joined.trim() === "") program.error("error: a task is required, for example: dsh --profile headless \"run the tests\"");
79
+ const task = program.args.length === 0 ? void 0 : joined;
80
+ if (task === void 0 && internals.stdinIsTty()) program.error("error: a task is required, for example: dsh --profile headless \"run the tests\"");
81
+ const options = program.opts();
82
+ const sessionId = options.sessionId;
83
+ if (sessionId !== void 0 && sessionId.trim() === "") program.error("error: --session-id requires a non-empty session id");
84
+ ctx.provide(HEADLESS_STARTUP_SERVICE, {
85
+ task,
86
+ sessionId,
87
+ json: options.json === true
88
+ });
38
89
  });
39
90
  parseCmdline(ctx, program);
40
91
  }
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * @deepseek-ai/dsh-headless — one-shot direct Agent driver. The bundle patch
3
3
  * rides over dsh-base without Host, HTTP, or browser plugins; this runner
4
- * creates one Agent through the core registry, drives the task to quiescence,
5
- * streams provider reasoning to stderr, flushes its Session, prints the final
6
- * assistant text to stdout, and exits.
4
+ * creates one Agent through the core registry (or adopts the exact Session a
5
+ * `--session-id` names), drives the task to quiescence, streams provider
6
+ * reasoning to stderr, flushes its Session, prints the final assistant text to
7
+ * stdout, and exits. With `--json` it projects the run as newline-delimited
8
+ * events instead of the final text.
7
9
  *
8
10
  * @module @deepseek-ai/dsh-headless
9
11
  */
@@ -13,33 +15,20 @@ import z from '@deepseek-ai/schemastery';
13
15
  export declare const name = "headless-runner";
14
16
  /** Core services required before the one-shot turn can start. */
15
17
  export declare const inject: string[];
16
- /** Plugin config: the task resolved from this app's injected provider service. */
18
+ /** Plugin config: the task and run options resolved from this app's injected provider service. */
17
19
  export interface Config {
18
- /** The prompt text for the single run. */
19
- task: string;
20
+ /** The prompt text for the single run; absent when the task arrives on stdin. */
21
+ task?: string;
22
+ /** Exact Session identity to adopt; absent for a fresh random identity. An id with no stored Session fails. */
23
+ sessionId?: string;
24
+ /** Whether stdout carries the machine-readable event stream instead of final text. */
25
+ json?: boolean;
20
26
  }
21
27
  export declare const Config: z<Config>;
22
- /** Process-facing effects of one run: output streams plus the launcher's bounded exit request. */
23
- interface HeadlessIo {
24
- stdout: {
25
- write(chunk: string): unknown;
26
- };
27
- stderr: {
28
- write(chunk: string): unknown;
29
- };
30
- /** Request process exit with `code` after the tree disposes. */
31
- exit(code: number): void;
32
- }
33
- /** The process streams the runner writes to; tests substitute captures. */
34
- export declare const internals: {
35
- stdout: HeadlessIo['stdout'];
36
- stderr: HeadlessIo['stderr'];
37
- };
38
28
  /**
39
29
  * Mount the one-shot direct driver.
40
30
  * @param ctx - plugin context carrying core services and the launcher-provided exit request.
41
- * @param config - validated task config.
31
+ * @param config - validated task and run options.
42
32
  */
43
33
  export declare function apply(ctx: Context, config: Config): void;
44
- export {};
45
34
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `--json` run projection: a bounded, ordered event stream derived from one
3
+ * Agent's durable Session events. Every projected event is a commit point:
4
+ * text and reasoning come from committed `assistant/message` content, never
5
+ * from a live attempt that may still be retried or discarded, so the stream
6
+ * never carries content the durable log does not contain.
7
+ * @module @deepseek-ai/dsh-headless/json-stream
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ import type { Agent } from '@deepseek-ai/dsh-agent';
11
+ /** Default per-string and per-key cap applied to every bounded projected payload. */
12
+ export declare const MAX_STRING_BYTES: number;
13
+ /** Default cap on one projected event's serialized bytes, newline included; the terminal `final` is exempt. */
14
+ export declare const MAX_EVENT_BYTES: number;
15
+ /** The stdout sink a projection writes newline-delimited events to. */
16
+ export interface JsonSink {
17
+ /** Write one chunk of the event stream. */
18
+ write(chunk: string): unknown;
19
+ }
20
+ /** Tunables for {@link projectJsonRun}; every field defaults. */
21
+ export interface JsonProjectionOptions {
22
+ /** Working directory reported by the opening `session` event. */
23
+ cwd?: string;
24
+ /** Per-string and per-key byte cap; longer values are truncated and flagged. */
25
+ maxStringBytes?: number;
26
+ }
27
+ /** The live handle of one `--json` projection. */
28
+ export interface JsonProjection {
29
+ /** Write the terminal `final` event carrying the run's answer text. */
30
+ finish(text: string): void;
31
+ /** Stop observing the Session. */
32
+ dispose(): void;
33
+ }
34
+ /**
35
+ * Serialize one projected payload under both limits: every string and key is
36
+ * capped at `maxStringBytes`, and the serialized line at `maxEventBytes`. The
37
+ * line cap reserves the newline the writer appends, so the complete record
38
+ * stays within it. When the line is still too long, scalar fields survive and
39
+ * structured fields are dropped; when even those are too long, only `type` and
40
+ * `truncated` remain.
41
+ * @param event - the event payload to serialize.
42
+ * @param maxStringBytes - per-string and per-key byte cap.
43
+ * @param maxEventBytes - cap on the serialized line plus its trailing newline.
44
+ * @returns the bounded JSON line, without a trailing newline.
45
+ */
46
+ export declare function boundJsonLine(event: Record<string, unknown>, maxStringBytes?: number, maxEventBytes?: number): string;
47
+ /**
48
+ * Project one Agent's run as newline-delimited JSON on `sink`.
49
+ *
50
+ * The opening `session` event is written before the subscription starts, so a
51
+ * caller must invoke this before submitting the task. Text and reasoning are
52
+ * emitted only when the step's `assistant/message` commits them, and the
53
+ * terminal `final` event carries the same lossless answer the default mode
54
+ * prints (it is deliberately not bounded).
55
+ * @param ctx - plugin context carrying the live Session feed.
56
+ * @param agent - the exact Agent whose events belong to this invocation.
57
+ * @param sink - stdout sink receiving one JSON object per line.
58
+ * @param options - projection tunables.
59
+ * @returns the projection handle that finishes or disposes the stream.
60
+ */
61
+ export declare function projectJsonRun(ctx: Context, agent: Agent, sink: JsonSink, options?: JsonProjectionOptions): JsonProjection;
62
+ //# sourceMappingURL=json-stream.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Process streams the runner reads and writes, kept out of the package entry so
3
+ * substituting them in tests adds no public package API. The shape matches the
4
+ * runner's own IO carrier structurally.
5
+ * @module @deepseek-ai/dsh-headless/runner-internals
6
+ */
7
+ /** The process streams the runner reads and writes; tests substitute captures. */
8
+ export declare const internals: {
9
+ stdout: {
10
+ write(chunk: string): unknown;
11
+ };
12
+ stderr: {
13
+ write(chunk: string): unknown;
14
+ };
15
+ readStdin: () => Promise<string>;
16
+ };
17
+ //# sourceMappingURL=runner-internals.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Process facts the startup provider reads, kept out of the `./startup` entry
3
+ * so substituting them in tests adds no public package API.
4
+ * @module @deepseek-ai/dsh-headless/startup-internals
5
+ */
6
+ /** Process facts the provider reads; tests substitute them. */
7
+ export declare const internals: {
8
+ stdinIsTty: () => boolean;
9
+ stdout: {
10
+ write(chunk: string): unknown;
11
+ };
12
+ };
13
+ //# sourceMappingURL=startup-internals.d.ts.map
@@ -1,7 +1,8 @@
1
1
  /**
2
- * The one-shot app's command-line provider: it parses the task positional and
3
- * `--help`, then publishes {@link HEADLESS_STARTUP_SERVICE}. The runner is an
4
- * ordinary consumer whose lazy config waits for that service.
2
+ * The one-shot app's command-line provider: it parses the task positional,
3
+ * `--session-id`, `--json`, and `--help`, then publishes
4
+ * {@link HEADLESS_STARTUP_SERVICE}. The runner is an ordinary consumer whose
5
+ * lazy config waits for that service.
5
6
  * @module @deepseek-ai/dsh-headless/startup
6
7
  */
7
8
  import type { Context } from '@deepseek-ai/cordis';
@@ -13,13 +14,17 @@ export declare const inject: string[];
13
14
  export declare const HEADLESS_STARTUP_SERVICE = "headlessStartup";
14
15
  /** What the runner row reads from {@link HEADLESS_STARTUP_SERVICE}. */
15
16
  export interface HeadlessStartupValues {
16
- /** The task text this invocation asked for. */
17
- task: string;
17
+ /** The task text this invocation asked for; absent when the runner reads stdin. */
18
+ task: string | undefined;
19
+ /** Exact Session identity to adopt; absent for a fresh random identity. */
20
+ sessionId: string | undefined;
21
+ /** Whether stdout carries the machine-readable event stream instead of final text. */
22
+ json: boolean;
18
23
  }
19
24
  /**
20
25
  * Parse and provide the one-shot task as an ordinary Cordis service. The
21
- * command's action publishes the task; a missing or whitespace-only task is a
22
- * usage error, so on rejection (and on `--help`) nothing is provided.
26
+ * command's action publishes the task; a missing task on an interactive stdin
27
+ * is a usage error, so on rejection (and on `--help`) nothing is provided.
23
28
  * @param ctx - plugin context carrying the command line.
24
29
  */
25
30
  export declare function apply(ctx: Context): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-headless",
3
3
  "description": "The dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layer",
4
- "version": "0.1.5-rc.2",
4
+ "version": "0.1.6-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,6 +30,7 @@
30
30
  "lib/index.js",
31
31
  "lib/startup.js",
32
32
  "cordis.patch.yml",
33
+ "lib/json-stream-*.js",
33
34
  "lib/types/**/*.d.ts"
34
35
  ],
35
36
  "license": "MIT",
@@ -40,28 +41,31 @@
40
41
  },
41
42
  "dependencies": {
42
43
  "commander": "^15.0.0",
43
- "@deepseek-ai/dsh-brand": "^0.1.5-rc.2",
44
- "@deepseek-ai/dsh-cmdline": "^0.1.5-rc.2",
45
- "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.5-rc.2",
46
- "@deepseek-ai/dsh-util-values": "^0.1.5-rc.2",
44
+ "@deepseek-ai/dsh-brand": "^0.1.6-alpha.2",
45
+ "@deepseek-ai/dsh-cmdline": "^0.1.6-alpha.2",
46
+ "@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2",
47
47
  "@deepseek-ai/schemastery": "^3.18.2"
48
48
  },
49
49
  "peerDependencies": {
50
+ "@deepseek-ai/cordis": "^4.0.2",
50
51
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
51
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
52
- "@deepseek-ai/dsh-agent-default-model": "^0.1.5-rc.2",
53
- "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
54
- "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
55
- "@deepseek-ai/cordis": "^4.0.2"
52
+ "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
53
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.6-alpha.2",
54
+ "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
55
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
56
+ "@deepseek-ai/dsh-session-query": "^0.1.6-alpha.2",
57
+ "@deepseek-ai/dsh-fs": "^0.1.6-alpha.2"
56
58
  },
57
59
  "devDependencies": {
58
- "@deepseek-ai/cordis": "^4.0.2",
59
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
60
- "@deepseek-ai/dsh-agent-default-model": "^0.1.5-rc.2",
61
- "@deepseek-ai/dsh-agent-loop": "^0.1.5-rc.2",
62
- "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.5-rc.2",
63
- "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
64
60
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
65
- "@deepseek-ai/dsh-session": "^0.1.5-rc.2"
61
+ "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
62
+ "@deepseek-ai/cordis": "^4.0.2",
63
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.6-alpha.2",
64
+ "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.6-alpha.2",
65
+ "@deepseek-ai/dsh-agent-loop": "^0.1.6-alpha.2",
66
+ "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
67
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
68
+ "@deepseek-ai/dsh-session-query": "^0.1.6-alpha.2",
69
+ "@deepseek-ai/dsh-fs": "^0.1.6-alpha.2"
66
70
  }
67
71
  }