@hydraharness/harness-tool-subagent-report 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,64 @@
1
+ # @hydraharness/harness-tool-subagent-report
2
+
3
+ The optional child-scoped `report` tool is a thin adapter over `ctx.subagents.reportFrom()`. It gives every continuable in-process child a return channel to the Agent that started it, and installs the prompt section that instructs the child to use it. The package registers a continuable-child setup contribution instead of a global tool, so the tool and its guidance exist only inside those children. Roots, one-shot subagents, remote subagent providers, sibling scopes, and agentless tool execution never present or execute it. Installing this package grants only that child-scoped capability; the parent-to-child direction remains the independent [`@hydraharness/harness-tool-subagent-control`](../tool-subagent-control/README.md), and continuable mode depends on neither package.
4
+
5
+ The child-scoped `tool:report` prompt section instructs the child to call `report` once before finishing, with a self-contained answer, and earlier whenever a partial finding changes what the parent should do next. The instruction is guidance, not enforcement: the mechanism still accepts zero or many calls in one turn, and no runtime path rejects a child that never reports. A successful call neither concludes the turn, settles the Activation, nor prevents later parent follow-ups, and finishing a turn never reports automatically. The tool accepts no recipient: `exec.agent` is the sender's exact live Agent and the authority credential, and the service derives the sole recipient from that child's durable `parentSession`. Success returns the stable `MessageId` of the parent-accepted message, not a read receipt, an inbox-occurrence id, a parent-log acknowledgement, a turn-completion receipt, or a persistence flush. A parent absent from the registry fails the call with `direct parent is not live; report was not delivered` — registry presence governs parent resolution, and a registered parent already in host-owned disposal still accepts while its log admits appends. The service performs no injection, parent cold resume, or offline mailbox write; the durable child transcript remains the recovery source, and a failed tool call does not prove non-delivery (a later `tools/post-execute` veto can fail a call whose report was already accepted).
6
+
7
+ `reportDelivery` selects parent scheduling for every accepted report. `next-step` (the default) uses `parent.steer()`: a running parent receives the report at its nearest safe step boundary, while an idle parent starts a turn. Reports accepted in sequence share the next-step FIFO, including the later manager-authored settlement notice, so the parent cannot observe settlement before an earlier report; reports waiting together enter one claimed batch. `quiet` uses `parent.inject()`, adding the same next-step context without waking a parked parent. This is deployment scheduling policy, so the model-facing schema cannot select or override it per call.
8
+
9
+ Scope-local registration deliberately survives the child's global `toolFilter`, so a delegation allow-list cannot remove the only return channel. A deployment that requires a child with no return channel omits this package.
10
+
11
+ The contribution body is exported as `installReportTool(childCtx, ctx, delivery)` so inspection consumers can install `report` and its guidance into a minted child scope, and returns the one disposer revoking both. The generated tool catalog uses that path because the global registry cannot expose a scope-local schema. Production composition still enters through `apply()`; the subagent seam's contribution registry remains private.
12
+
13
+ ## Model Experience
14
+
15
+ ### Tool schema
16
+
17
+ #### What the model sees
18
+
19
+ The generated [`report` schema](../../../docs/tool-catalog.md#hydraharness-tool-subagent-report): one required `output` string. Its description states that the child must report once before finishing, that reporting reaches only the Agent that started the child, and that it does not end the turn. It carries no recipient or delivery-mode parameter. The separate `tool:report` prompt section repeats the obligation outside the schema, where a child that ignores tool descriptions still reads it.
20
+
21
+ #### Token effect
22
+
23
+ Fixed schema and prompt-section cost per continuable-child request, and none in any other Agent's requests.
24
+
25
+ #### KV Cache effect
26
+
27
+ Prefix-stable within a child; neither the schema nor the section changes at runtime. Removing the package revokes both from resident children, which changes their next request prefix.
28
+
29
+ ### Report result
30
+
31
+ #### What the model sees
32
+
33
+ `report accepted by the agent that started you as message <messageId>` on acceptance; the canonical output carries the stable `messageId`. A failure from an unauthorized sender, an unavailable parent, or a closing lifecycle is an errored result. The description says a failed call may still have arrived because a later `tools/post-execute` failure can replace the result after `reportFrom()` accepted the message.
34
+
35
+ #### Token effect
36
+
37
+ One short acknowledgement per call in the reporting child. The reported content is additionally billed to the parent: next-step delivery joins the next request in an open parent turn or starts a turn for an idle parent, while quiet delivery waits for another input to wake the parent.
38
+
39
+ #### KV Cache effect
40
+
41
+ Append-only in the child. In the parent, the framed report follows existing history and preserves the reusable prefix.
42
+
43
+ ### Parent-visible report
44
+
45
+ #### What the model sees
46
+
47
+ One user-role parent message framed as `Background subagent <child-id> reported:` followed by the child's exact `output`, with a durable source `{ kind: 'subagent-report', senderSessionId: <child-id> }` that names the child.
48
+
49
+ #### Token effect
50
+
51
+ The child's complete `output` plus the one-line frame, uncapped by this package.
52
+
53
+ #### KV Cache effect
54
+
55
+ Append-only; the report follows the parent's reusable request prefix. Next-step delivery wakes the parent and may extend its open turn, while quiet delivery does not wake it.
56
+
57
+ ## Known Limitations and Deferred Work
58
+
59
+ - **A parent whose host-owned disposal already started can still accept** — `AgentHandle.dispose()` cancels, awaits quiescence, and only then unwinds the scope and leaves the registry; it exposes no signal for "disposal started." A report accepted in that window is appended to the parent's transcript, but that parent will not act on it in this process. A continuation-manager-owned parent rejects forest teardown through the manager's admission boundary.
60
+ - **Acceptance is weaker than durable delivery** — there is no durable mailbox, idempotency key, delivery receipt, retry protocol, or exactly-once claim. A process failure after one side recorded acceptance leaves the outcome ambiguous, and an external retry may duplicate the report.
61
+ - **A staged quiet report is not immediately reconstructable** — acceptance returns its stable `MessageId`, but the parent Session reconstructs the framed content only after pending context reaches its ordinary log boundary.
62
+ - **Granting waits for the next Activation; revocation is immediate** — installing this package after a child becomes resident grants `report` and its guidance only on that child's next Activation, while removing the package revokes both from resident children immediately.
63
+ - **Nested reporting reaches exactly one edge upward** — a grandchild reports to its direct child parent, never to the top-level coordinator, which must explicitly report a derived update later.
64
+ - **No rate limiting** — the default `next-step` mode can amplify model work when nested children report frequently, although reports waiting together share one step; a deployment that accepts unread reports over that amplification selects `quiet`.
package/lib/index.js ADDED
@@ -0,0 +1,98 @@
1
+ import z from "@hydraharness/schemastery";
2
+ import { defineTool } from "@hydraharness/harness-tools";
3
+ //#region lib/types/index.js
4
+ /**
5
+ * The child-scoped `report` tool and its usage guidance, installed into every
6
+ * continuable in-process child's unpublished context. Roots, one-shot children,
7
+ * remote providers, and agentless executions never see the registration.
8
+ *
9
+ * @module @hydraharness/harness-tool-subagent-report
10
+ */
11
+ const name = "tool-subagent-report";
12
+ const inject = [
13
+ "subagents",
14
+ "tools",
15
+ "systemPrompt"
16
+ ];
17
+ /** Guidance order after every per-tool section a continuable child can carry. */
18
+ const REPORT_SECTION_ORDER = 117;
19
+ const Config = z.object({ reportDelivery: z.union(["quiet", "next-step"]).default("next-step") });
20
+ /**
21
+ * Install `report` and its usage guidance into one continuable child's scope.
22
+ * Both registrations are owned by that scope and are therefore invisible to the
23
+ * child's parent and siblings.
24
+ * @param childCtx - child-scoped context receiving the tool and the guidance.
25
+ * @param ctx - service context used for delivery.
26
+ * @param delivery - resolved deployment scheduling policy.
27
+ * @returns disposer that attempts both child registrations before reporting cleanup failures.
28
+ */
29
+ function installReportTool(childCtx, ctx, delivery) {
30
+ const disposeSection = childCtx.systemPrompt.section({
31
+ name: "tool:report",
32
+ order: REPORT_SECTION_ORDER,
33
+ text: "Deliver your result with the report tool before you finish: call it once with a self-contained answer. The agent that started you shares your workspace but does not automatically receive your transcript, tool output, or reasoning, so a closing remark such as \"done\" leaves it nothing it can use. Report earlier as well whenever a partial finding changes what that agent should do next; reporting never ends your turn."
34
+ });
35
+ let disposeTool;
36
+ try {
37
+ disposeTool = childCtx.tools.register(defineTool({
38
+ name: "report",
39
+ description: "Report selected content to the agent that started you. Call this once before you finish, with a self-contained final result, and earlier for progress or findings that change what that agent does next. That agent shares your workspace but does not automatically receive your transcript, tool output, or reasoning, so finishing your work is not itself a result. Reporting does not end your turn or finish your work, and only your direct parent receives it. A failed call may still have arrived, so do not blindly repeat it.",
40
+ parameters: { output: {
41
+ type: "string",
42
+ required: true,
43
+ description: "Actionable content for your parent; summarize conclusions and reference relevant shared paths."
44
+ } },
45
+ output: {
46
+ schema: {
47
+ type: "object",
48
+ additionalProperties: false,
49
+ properties: { messageId: {
50
+ type: "string",
51
+ required: true
52
+ } }
53
+ },
54
+ render: (_args, value) => [{
55
+ type: "text",
56
+ text: `report accepted by the agent that started you as message ${value.messageId}`
57
+ }]
58
+ },
59
+ async execute(args, exec) {
60
+ const content = [{
61
+ type: "text",
62
+ text: args.output
63
+ }];
64
+ return { messageId: await ctx.subagents.reportFrom(exec.agent, content, {
65
+ delivery,
66
+ signal: exec.signal
67
+ }) };
68
+ }
69
+ }));
70
+ } catch (error) {
71
+ try {
72
+ disposeSection();
73
+ } catch (rollbackError) {
74
+ throw new AggregateError([error, rollbackError], "failed to register the report tool and roll back its prompt guidance");
75
+ }
76
+ throw error;
77
+ }
78
+ return () => {
79
+ const failures = [];
80
+ for (const dispose of [disposeTool, disposeSection]) try {
81
+ dispose();
82
+ } catch (error) {
83
+ failures.push(error);
84
+ }
85
+ if (failures.length > 0) throw new AggregateError(failures, "failed to revoke report tool and prompt registrations");
86
+ };
87
+ }
88
+ /**
89
+ * Register the continuable-child contribution.
90
+ * @param ctx - context carrying tools, the system prompt, and the subagent service.
91
+ * @param config - deployment scheduling policy.
92
+ */
93
+ function apply(ctx, config = {}) {
94
+ const { reportDelivery } = Config(config);
95
+ ctx.subagents.registerContinuableSetup((childCtx) => installReportTool(childCtx, ctx, reportDelivery));
96
+ }
97
+ //#endregion
98
+ export { Config, apply, inject, installReportTool, name };
@@ -0,0 +1,23 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hydraharness/harness-tool-subagent-report`.
4
+ * @module @hydraharness/harness-tool-subagent-report/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hydraharness/harness-tool-subagent-report";
7
+ /** Cordis companion plugin name. */
8
+ const name = "tool-subagent-report-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: this adapter has no independent lifecycle stream;
13
+ * sender authorization and delivery relations belong to the subagent service.
14
+ */
15
+ const install = () => {};
16
+ /**
17
+ * Register this package's invariant companion.
18
+ * @param ctx - context carrying the invariant service.
19
+ * @returns the registration disposer after setup succeeds.
20
+ */
21
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
+ //#endregion
23
+ export { apply, inject, name };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The child-scoped `report` tool and its usage guidance, installed into every
3
+ * continuable in-process child's unpublished context. Roots, one-shot children,
4
+ * remote providers, and agentless executions never see the registration.
5
+ *
6
+ * @module @hydraharness/harness-tool-subagent-report
7
+ */
8
+ import type { Context } from '@hydraharness/cordis';
9
+ import z from '@hydraharness/schemastery';
10
+ import type { SubagentReportDelivery } from '@hydraharness/harness-subagent';
11
+ export declare const name = "tool-subagent-report";
12
+ export declare const inject: string[];
13
+ /** Config: how accepted reports are scheduled on the parent. */
14
+ export interface Config {
15
+ /**
16
+ * Parent scheduling (default `next-step`). `next-step` wakes the parent and
17
+ * enters at its nearest step boundary; `quiet` adds the same context without
18
+ * waking, so a parked parent waits for another waking input.
19
+ */
20
+ reportDelivery?: SubagentReportDelivery;
21
+ }
22
+ export declare const Config: z<Config>;
23
+ /**
24
+ * Install `report` and its usage guidance into one continuable child's scope.
25
+ * Both registrations are owned by that scope and are therefore invisible to the
26
+ * child's parent and siblings.
27
+ * @param childCtx - child-scoped context receiving the tool and the guidance.
28
+ * @param ctx - service context used for delivery.
29
+ * @param delivery - resolved deployment scheduling policy.
30
+ * @returns disposer that attempts both child registrations before reporting cleanup failures.
31
+ */
32
+ export declare function installReportTool(childCtx: Context, ctx: Context, delivery: SubagentReportDelivery): () => void;
33
+ /**
34
+ * Register the continuable-child contribution.
35
+ * @param ctx - context carrying tools, the system prompt, and the subagent service.
36
+ * @param config - deployment scheduling policy.
37
+ */
38
+ export declare function apply(ctx: Context, config?: Config): void;
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hydraharness/harness-tool-subagent-report`.
3
+ * @module @hydraharness/harness-tool-subagent-report/invariant
4
+ */
5
+ import type { Context } from '@hydraharness/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "tool-subagent-report-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - context carrying the invariant service.
13
+ * @returns the registration disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@hydraharness/harness-tool-subagent-report",
3
+ "description": "Child-scoped report tool over ctx.subagents continuations",
4
+ "hydra": {
5
+ "plugin": {
6
+ "application": "Let a child agent report its progress or findings to its parent."
7
+ }
8
+ },
9
+ "version": "0.1.1-rc.6",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/MaiHongPhong1902/Hydra-Harness.git",
16
+ "directory": "packages/subagent/tool-subagent-report"
17
+ },
18
+ "type": "module",
19
+ "main": "lib/index.js",
20
+ "types": "lib/types/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./lib/types/index.d.ts",
24
+ "default": "./lib/index.js"
25
+ },
26
+ "./invariant": {
27
+ "types": "./lib/types/invariant.d.ts",
28
+ "default": "./lib/invariant.js"
29
+ },
30
+ "./src/*": "./src/*",
31
+ "./package.json": "./package.json"
32
+ },
33
+ "files": [
34
+ "lib/index.js",
35
+ "lib/invariant.js",
36
+ "lib/types/**/*.d.ts"
37
+ ],
38
+ "license": "MIT",
39
+ "peerDependencies": {
40
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
41
+ "@hydraharness/harness-llm": "^0.1.1-rc.6",
42
+ "@hydraharness/harness-subagent": "^0.1.1-rc.6",
43
+ "@hydraharness/harness-system-prompt": "^0.1.1-rc.6",
44
+ "@hydraharness/cordis": "^4.0.2",
45
+ "@hydraharness/harness-tools": "^0.1.1-rc.6"
46
+ },
47
+ "dependencies": {
48
+ "@hydraharness/schemastery": "^3.18.2"
49
+ },
50
+ "devDependencies": {
51
+ "@hydraharness/harness-agent": "^0.1.1-rc.6",
52
+ "@hydraharness/harness-agent-loop": "^0.1.1-rc.6",
53
+ "@hydraharness/harness-agent-loop-testkit": "^0.1.1-rc.6",
54
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
55
+ "@hydraharness/harness-llm": "^0.1.1-rc.6",
56
+ "@hydraharness/harness-session": "^0.1.1-rc.6",
57
+ "@hydraharness/harness-session-persistence": "^0.1.1-rc.6",
58
+ "@hydraharness/harness-subagent": "^0.1.1-rc.6",
59
+ "@hydraharness/harness-subagent-spawn-in-process": "^0.1.1-rc.6",
60
+ "@hydraharness/harness-session-persistence-jsonl": "^0.1.1-rc.6",
61
+ "@hydraharness/harness-system-prompt": "^0.1.1-rc.6",
62
+ "@hydraharness/harness-tool-subagent-control": "^0.1.1-rc.6",
63
+ "@hydraharness/harness-tools": "^0.1.1-rc.6",
64
+ "@hydraharness/cordis": "^4.0.2"
65
+ }
66
+ }