@hydraharness/harness-headless 0.1.1-rc.6

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/LICENSE ADDED
@@ -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.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # `@hydraharness/harness-headless`
2
+
3
+ The hydra one-shot bundle. [`cordis.patch.yml`](cordis.patch.yml) rides directly over [`@hydraharness/harness-base`](../base/README.md): it supplies the coding persona and tool mode, disables HMR, mounts Code Mode's worker as a core execution capability, and inserts this package's `headless-runner` plugin (config `{task}`, resolved from the injected `headlessStartup` provider). It mounts no Host, HTTP server, Web runtime, or browser plugin.
4
+
5
+ After the Loader settles, the runner reads the shared [`ctx.agentDefaultModel`](../../core/agent-default-model/README.md), creates one fresh persisted Agent through `ctx.agents`, submits the task as an ordinary user message, and waits for quiescence. It flushes the Session before folding the owned durable event interval, writes the last non-empty assistant text to stdout, and requests exit through the launcher-provided `ctx.appExit` host hook ([`@hydraharness/harness-cmdline`](../../boot/cmdline/README.md)) (final `turn/end` completed → 0, otherwise 1). A terminal `error` reason also writes its code and message to stderr; successful runs keep stderr empty. The process opens no listening port. The task text is this app's command line: the ordinary `headless-startup` provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`@hydraharness/harness-cmdline`](../../boot/cmdline/README.md)), reads the positional argument of `hydra --profile headless "task"`, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config. A missing or whitespace-only task is rejected before the runner activates.
6
+
7
+ ## Model Experience
8
+
9
+ None, as the runner submits the task as an ordinary user message; prompts and tools belong to the base and headless bundle rows.
10
+
11
+ #### KV Cache effect
12
+
13
+ None; the runner adds nothing to the request prefix.
14
+
15
+ ## Known Limitations and Deferred Work
16
+
17
+ - **One submitted task only** — the runner has no interactive follow-up surface; it waits through any work the Agent completes before returning to idle and prints the last non-empty assistant message in that interval.
18
+ - **`ctx.appExit` is launcher-owned** — booting the headless profile outside the `hydra` launcher fails loud at activation until the host provides the exit request.
@@ -0,0 +1,35 @@
1
+ # The hydra-headless bundle patch: one-shot task mode directly over hydra-base.
2
+ # It mounts no Host, HTTP server, Web runtime, or browser plugin. An ordinary
3
+ # provider plugin injects `cmdlineArgs`, parses the task positional
4
+ # (`hydra --profile headless "<task>"`) and this app's --help, then the direct
5
+ # driver creates an Agent through the core registry and prints its durable result.
6
+
7
+ - id: system-prompt
8
+ config:
9
+ persona: >-
10
+ You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
11
+
12
+ # The shared module-reload HMR row stays off; the launcher's watch-only
13
+ # fallback still keeps the user patch layers live until the run exits.
14
+ - id: hmr
15
+ disabled: true
16
+
17
+ - id: tools
18
+ config:
19
+ # Keep the same temporary process-wide Code Mode opt-in as the Web surface.
20
+ mode: !!js process.env.HYDRA_TOOLS_MODE
21
+
22
+ - insert:
23
+ # Code Mode is a core execution capability, not a Web component.
24
+ - id: code-runtime
25
+ name: '@hydraharness/harness-code-runtime-worker-thread'
26
+
27
+ - id: headless-startup
28
+ name: '@hydraharness/harness-headless/startup'
29
+
30
+ # Reads its task from the ordinary headlessStartup provider.
31
+ - id: headless-runner
32
+ name: '@hydraharness/harness-headless'
33
+ inject: [headlessStartup]
34
+ config:
35
+ task: !!js ctx.headlessStartup.task
package/lib/index.js ADDED
@@ -0,0 +1,118 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import z from "@hydraharness/schemastery";
3
+ import { installModelSelection } from "@hydraharness/harness-agent";
4
+ import { createUserMessage } from "@hydraharness/harness-llm";
5
+ import { SessionId } from "@hydraharness/harness-session";
6
+ //#region lib/types/index.js
7
+ /**
8
+ * @hydraharness/harness-headless — one-shot direct Agent driver. The bundle patch
9
+ * rides over @hydraharness/harness-base without Host, HTTP, or browser plugins; this runner
10
+ * creates one Agent through the core registry, drives the task to quiescence,
11
+ * flushes its Session, prints the final assistant text, and exits.
12
+ *
13
+ * @module @hydraharness/harness-headless
14
+ */
15
+ /** Stable Cordis plugin name. */
16
+ const name = "headless-runner";
17
+ /** Core services required before the one-shot turn can start. */
18
+ const inject = [
19
+ "agentDefaultModel",
20
+ "agents",
21
+ "sessions"
22
+ ];
23
+ const Config = z.object({ task: z.string().required() });
24
+ /** The process streams the runner writes to; tests substitute captures. */
25
+ const internals = {
26
+ stdout: process.stdout,
27
+ stderr: process.stderr
28
+ };
29
+ /** Aggregate the last assistant text and turn outcome in one owned interval. */
30
+ function summarize(events, firstSeq) {
31
+ let started = false;
32
+ let text = "";
33
+ let reason;
34
+ for (const event of events) {
35
+ if (event.seq < firstSeq) continue;
36
+ if (event.type === "turn/start") {
37
+ started = true;
38
+ continue;
39
+ }
40
+ if (!started) continue;
41
+ if (event.type === "assistant/message") {
42
+ const joined = event.data.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
43
+ if (joined !== "") text = joined;
44
+ }
45
+ if (event.type === "turn/end") reason = event.data.reason;
46
+ }
47
+ return {
48
+ text,
49
+ reason
50
+ };
51
+ }
52
+ /** Report an unexpected direct-driver failure and request a failing exit. */
53
+ function fail(io, error) {
54
+ io.stderr.write(`hydra: ${error instanceof Error ? error.message : String(error)}\n`);
55
+ io.exit(1);
56
+ }
57
+ /**
58
+ * Run one task through a freshly created Agent and request process exit.
59
+ * @param ctx - plugin context carrying the Agent, default model, Session, and launcher IO services.
60
+ * @param task - one-shot task text.
61
+ * @param io - process-facing effects.
62
+ */
63
+ async function run(ctx, task, io) {
64
+ await ctx.get("loader")?.await();
65
+ const agents = ctx.get("agents");
66
+ const defaultModel = ctx.get("agentDefaultModel");
67
+ const sessions = ctx.get("sessions");
68
+ if (agents === void 0 || defaultModel === void 0 || sessions === void 0) return;
69
+ const selection = defaultModel.currentSelection();
70
+ const { agent } = await agents.create({
71
+ sessionId: SessionId(`session-${randomUUID()}`),
72
+ meta: { cwd: process.cwd() },
73
+ agentOptions: {
74
+ provider: selection.provider,
75
+ model: selection.model
76
+ },
77
+ setup: (agentCtx) => {
78
+ installModelSelection(agentCtx, {
79
+ current: selection,
80
+ assembled: void 0
81
+ });
82
+ }
83
+ });
84
+ await agent.whenIdle();
85
+ const firstSeq = agent.session.seq;
86
+ agent.followup(createUserMessage({
87
+ content: [{
88
+ type: "text",
89
+ text: task
90
+ }],
91
+ source: { kind: "user" }
92
+ }));
93
+ await agent.whenIdle();
94
+ await sessions.flush(agent.session);
95
+ const outcome = summarize(agent.session.events, firstSeq);
96
+ io.stdout.write(outcome.text + "\n");
97
+ if (outcome.reason?.kind === "error") io.stderr.write(`hydra: ${outcome.reason.error.code}: ${outcome.reason.error.message}\n`);
98
+ io.exit(outcome.reason?.kind === "completed" ? 0 : 1);
99
+ }
100
+ /**
101
+ * Mount the one-shot direct driver.
102
+ * @param ctx - plugin context carrying core services and the launcher-provided exit request.
103
+ * @param config - validated task config.
104
+ */
105
+ function apply(ctx, config) {
106
+ const exit = ctx.get("appExit");
107
+ if (exit === void 0) throw new Error("headless-runner: the launcher must provide ctx.appExit before the tree mounts");
108
+ const io = {
109
+ stdout: internals.stdout,
110
+ stderr: internals.stderr,
111
+ exit
112
+ };
113
+ run(ctx, config.task, io).catch((error) => {
114
+ fail(io, error);
115
+ });
116
+ }
117
+ //#endregion
118
+ export { Config, apply, inject, internals, name };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hydraharness/harness-headless`.
4
+ * @module @hydraharness/harness-headless/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hydraharness/harness-headless";
7
+ /** Cordis companion plugin name. */
8
+ const name = "headless-invariant";
9
+ /** Service required before the companion can register. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: the runner is a one-shot driver over the API carrier
13
+ * whose observable contract (final text on stdout, exit code by turn-end
14
+ * reason) is process-level and owned by the launcher e2e; it registers
15
+ * nothing and holds no mutable relation to audit inside the tree.
16
+ */
17
+ const install = () => {};
18
+ /**
19
+ * Register this package's invariant companion.
20
+ * @param ctx - Cordis context carrying the invariant service.
21
+ * @returns the installed registration's disposer after setup succeeds.
22
+ */
23
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
+ //#endregion
25
+ export { apply, inject, name };
package/lib/startup.js ADDED
@@ -0,0 +1,42 @@
1
+ import { Command } from "commander";
2
+ import { parseCmdline } from "@hydraharness/harness-cmdline";
3
+ //#region lib/types/startup.js
4
+ /**
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.
8
+ * @module @hydraharness/harness-headless/startup
9
+ */
10
+ /** Stable Cordis plugin name. */
11
+ const name = "headless-startup";
12
+ /** Services required before the task can be resolved. */
13
+ const inject = ["cmdlineArgs"];
14
+ /** Service provided by this plugin and injected by the one-shot runner. */
15
+ const HEADLESS_STARTUP_SERVICE = "headlessStartup";
16
+ /**
17
+ * This app's command: the task positional, its description, and its help text.
18
+ * @returns a fresh program, so one process can parse more than once (tests).
19
+ */
20
+ function headlessCommand() {
21
+ return new Command().name("hydra --profile headless").description("Answer one task, 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", `
22
+ Examples:
23
+ hydra --profile headless "run the tests" answer one task and exit
24
+ `);
25
+ }
26
+ /**
27
+ * 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.
30
+ * @param ctx - plugin context carrying the command line.
31
+ */
32
+ function apply(ctx) {
33
+ const program = headlessCommand();
34
+ program.action(() => {
35
+ const task = program.args.join(" ");
36
+ if (task.trim() === "") program.error("error: a task is required, for example: hydra --profile headless \"run the tests\"");
37
+ ctx.provide(HEADLESS_STARTUP_SERVICE, { task });
38
+ });
39
+ parseCmdline(ctx, program);
40
+ }
41
+ //#endregion
42
+ export { HEADLESS_STARTUP_SERVICE, apply, inject, name };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @hydraharness/harness-headless — one-shot direct Agent driver. The bundle patch
3
+ * rides over @hydraharness/harness-base without Host, HTTP, or browser plugins; this runner
4
+ * creates one Agent through the core registry, drives the task to quiescence,
5
+ * flushes its Session, prints the final assistant text, and exits.
6
+ *
7
+ * @module @hydraharness/harness-headless
8
+ */
9
+ import type { Context } from '@hydraharness/cordis';
10
+ import z from '@hydraharness/schemastery';
11
+ /** Stable Cordis plugin name. */
12
+ export declare const name = "headless-runner";
13
+ /** Core services required before the one-shot turn can start. */
14
+ export declare const inject: string[];
15
+ /** Plugin config: the task resolved from this app's injected provider service. */
16
+ export interface Config {
17
+ /** The prompt text for the single run. */
18
+ task: string;
19
+ }
20
+ export declare const Config: z<Config>;
21
+ /** Process-facing effects of one run: output streams plus the launcher's bounded exit request. */
22
+ interface HeadlessIo {
23
+ stdout: {
24
+ write(chunk: string): unknown;
25
+ };
26
+ stderr: {
27
+ write(chunk: string): unknown;
28
+ };
29
+ /** Request process exit with `code` after the tree disposes. */
30
+ exit(code: number): void;
31
+ }
32
+ /** The process streams the runner writes to; tests substitute captures. */
33
+ export declare const internals: {
34
+ stdout: HeadlessIo['stdout'];
35
+ stderr: HeadlessIo['stderr'];
36
+ };
37
+ /**
38
+ * Mount the one-shot direct driver.
39
+ * @param ctx - plugin context carrying core services and the launcher-provided exit request.
40
+ * @param config - validated task config.
41
+ */
42
+ export declare function apply(ctx: Context, config: Config): void;
43
+ export {};
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hydraharness/harness-headless`.
3
+ * @module @hydraharness/harness-headless/invariant
4
+ */
5
+ import type { Context } from '@hydraharness/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "headless-invariant";
8
+ /** Service required before the companion can register. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1,26 @@
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.
5
+ * @module @hydraharness/harness-headless/startup
6
+ */
7
+ import type { Context } from '@hydraharness/cordis';
8
+ /** Stable Cordis plugin name. */
9
+ export declare const name = "headless-startup";
10
+ /** Services required before the task can be resolved. */
11
+ export declare const inject: string[];
12
+ /** Service provided by this plugin and injected by the one-shot runner. */
13
+ export declare const HEADLESS_STARTUP_SERVICE = "headlessStartup";
14
+ /** What the runner row reads from {@link HEADLESS_STARTUP_SERVICE}. */
15
+ export interface HeadlessStartupValues {
16
+ /** The task text this invocation asked for. */
17
+ task: string;
18
+ }
19
+ /**
20
+ * 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.
23
+ * @param ctx - plugin context carrying the command line.
24
+ */
25
+ export declare function apply(ctx: Context): void;
26
+ //# sourceMappingURL=startup.d.ts.map
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@hydraharness/harness-headless",
3
+ "description": "The hydra one-shot bundle: a direct core Agent/Session runner over hydra-base with no Host, HTTP, or browser layer",
4
+ "version": "0.1.1-rc.6",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/MaiHongPhong1902/Hydra-Harness.git",
11
+ "directory": "packages/bundle/headless"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./startup": {
22
+ "types": "./lib/types/startup.d.ts",
23
+ "default": "./lib/startup.js"
24
+ },
25
+ "./invariant": {
26
+ "types": "./lib/types/invariant.d.ts",
27
+ "default": "./lib/invariant.js"
28
+ },
29
+ "./cordis.patch.yml": "./cordis.patch.yml",
30
+ "./src/*": "./src/*",
31
+ "./package.json": "./package.json"
32
+ },
33
+ "files": [
34
+ "lib/index.js",
35
+ "lib/invariant.js",
36
+ "lib/startup.js",
37
+ "cordis.patch.yml",
38
+ "lib/types/**/*.d.ts"
39
+ ],
40
+ "license": "MIT",
41
+ "hydra": {
42
+ "plugin": {
43
+ "application": "Run a single task from the command line and exit after the final answer."
44
+ },
45
+ "bundle": {
46
+ "patch": "./cordis.patch.yml"
47
+ }
48
+ },
49
+ "dependencies": {
50
+ "commander": "^15.0.0",
51
+ "@hydraharness/harness-cmdline": "^0.1.1-rc.6",
52
+ "@hydraharness/schemastery": "^3.18.2",
53
+ "@hydraharness/harness-code-runtime-worker-thread": "^0.1.1-rc.6"
54
+ },
55
+ "peerDependencies": {
56
+ "@hydraharness/cordis-plugin-loader": "^1.0.3",
57
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
58
+ "@hydraharness/harness-agent": "^0.1.1-rc.6",
59
+ "@hydraharness/harness-agent-default-model": "^0.1.1-rc.6",
60
+ "@hydraharness/harness-session": "^0.1.1-rc.6",
61
+ "@hydraharness/cordis": "^4.0.2",
62
+ "@hydraharness/harness-llm": "^0.1.1-rc.6"
63
+ },
64
+ "devDependencies": {
65
+ "@hydraharness/cordis-plugin-loader": "^1.0.3",
66
+ "@hydraharness/harness-agent-default-model": "^0.1.1-rc.6",
67
+ "@hydraharness/harness-agent": "^0.1.1-rc.6",
68
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
69
+ "@hydraharness/harness-llm": "^0.1.1-rc.6",
70
+ "@hydraharness/cordis": "^4.0.2",
71
+ "@hydraharness/harness-session": "^0.1.1-rc.6"
72
+ }
73
+ }