@archmax-ai/harness 0.4.1 → 0.5.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 (100) hide show
  1. package/README.md +8 -0
  2. package/dist/agent.d.ts +14 -0
  3. package/dist/agent.js.map +1 -1
  4. package/dist/assembly/compose.d.ts +12 -9
  5. package/dist/assembly/compose.js +69 -42
  6. package/dist/assembly/compose.js.map +1 -1
  7. package/dist/assembly/index.js +51 -11
  8. package/dist/assembly/index.js.map +1 -1
  9. package/dist/authoring-skill/archmax-harness/SKILL.md +2 -2
  10. package/dist/authoring-skill/archmax-harness/references/backend-integration.md +33 -11
  11. package/dist/authoring-skill/archmax-harness/references/hook-and-test-scripts.md +2 -1
  12. package/dist/authoring-skill/archmax-harness/references/workflow-schema.md +65 -8
  13. package/dist/authoring-skill/archmax-harness/references/workflow-yaml.md +1 -1
  14. package/dist/cli/bootstrap.d.ts +8 -1
  15. package/dist/cli/bootstrap.js +43 -3
  16. package/dist/cli/bootstrap.js.map +1 -1
  17. package/dist/cli/command.d.ts +3 -1
  18. package/dist/cli/command.js +1 -1
  19. package/dist/cli/command.js.map +1 -1
  20. package/dist/cli/turn.d.ts +3 -1
  21. package/dist/cli/turn.js +1 -0
  22. package/dist/cli/turn.js.map +1 -1
  23. package/dist/cli.js +9 -1
  24. package/dist/cli.js.map +1 -1
  25. package/dist/core/attachments.d.ts +64 -0
  26. package/dist/core/attachments.js +87 -0
  27. package/dist/core/attachments.js.map +1 -0
  28. package/dist/core/file-operations.d.ts +22 -0
  29. package/dist/core/file-operations.js +54 -0
  30. package/dist/core/file-operations.js.map +1 -1
  31. package/dist/core/path-mapping.js +2 -2
  32. package/dist/core/path-mapping.js.map +1 -1
  33. package/dist/core/platform-prompt.generated.js +1 -1
  34. package/dist/core/platform-prompt.generated.js.map +1 -1
  35. package/dist/core/prompt.js +3 -2
  36. package/dist/core/prompt.js.map +1 -1
  37. package/dist/core/session-zone.d.ts +1 -1
  38. package/dist/core/session-zone.js +2 -2
  39. package/dist/core/session-zone.js.map +1 -1
  40. package/dist/core/workspace-router.d.ts +5 -2
  41. package/dist/core/workspace-router.js +14 -9
  42. package/dist/core/workspace-router.js.map +1 -1
  43. package/dist/index.d.ts +7 -2
  44. package/dist/index.js +5 -2
  45. package/dist/index.js.map +1 -1
  46. package/dist/kernel/kernel.d.ts +9 -0
  47. package/dist/kernel/kernel.js +15 -0
  48. package/dist/kernel/kernel.js.map +1 -1
  49. package/dist/machine/delegation.d.ts +3 -3
  50. package/dist/machine/delegation.js +3 -3
  51. package/dist/machine/machine.d.ts +7 -0
  52. package/dist/machine/machine.js +14 -0
  53. package/dist/machine/machine.js.map +1 -1
  54. package/dist/machine/signature.d.ts +16 -5
  55. package/dist/machine/signature.js +25 -4
  56. package/dist/machine/signature.js.map +1 -1
  57. package/dist/machine/spec-schema.d.ts +10 -0
  58. package/dist/sessions/resume.d.ts +9 -0
  59. package/dist/sessions/resume.js +13 -0
  60. package/dist/sessions/resume.js.map +1 -1
  61. package/dist/workflow/control-tools.d.ts +12 -1
  62. package/dist/workflow/control-tools.js +33 -4
  63. package/dist/workflow/control-tools.js.map +1 -1
  64. package/dist/workflow/file-values.d.ts +44 -0
  65. package/dist/workflow/file-values.js +29 -0
  66. package/dist/workflow/file-values.js.map +1 -0
  67. package/dist/workflow/governance.d.ts +4 -2
  68. package/dist/workflow/governance.js +2 -27
  69. package/dist/workflow/governance.js.map +1 -1
  70. package/dist/workflow/middleware.d.ts +6 -1
  71. package/dist/workflow/middleware.js +25 -2
  72. package/dist/workflow/middleware.js.map +1 -1
  73. package/dist/workflow/parks.js +5 -1
  74. package/dist/workflow/parks.js.map +1 -1
  75. package/dist/workflow/returns-check.d.ts +3 -0
  76. package/dist/workflow/returns-check.js +1 -1
  77. package/dist/workflow/returns-check.js.map +1 -1
  78. package/dist/workflow/signature-checks.d.ts +47 -5
  79. package/dist/workflow/signature-checks.js +73 -9
  80. package/dist/workflow/signature-checks.js.map +1 -1
  81. package/dist/workflow/sub-workflow.d.ts +14 -2
  82. package/dist/workflow/sub-workflow.js +119 -24
  83. package/dist/workflow/sub-workflow.js.map +1 -1
  84. package/dist/workflow/tool-service.d.ts +4 -0
  85. package/dist/workflow/tool-service.js +5 -2
  86. package/dist/workflow/tool-service.js.map +1 -1
  87. package/dist/workflow/turn-boundary.d.ts +3 -0
  88. package/dist/workflow/turn-boundary.js +5 -2
  89. package/dist/workflow/turn-boundary.js.map +1 -1
  90. package/dist/workflow/workflow-tools.js +11 -0
  91. package/dist/workflow/workflow-tools.js.map +1 -1
  92. package/package.json +2 -2
  93. package/skills/archmax-harness/SKILL.md +2 -2
  94. package/skills/archmax-harness/references/backend-integration.md +33 -11
  95. package/skills/archmax-harness/references/hook-and-test-scripts.md +2 -1
  96. package/skills/archmax-harness/references/workflow-schema.md +65 -8
  97. package/skills/archmax-harness/references/workflow-yaml.md +1 -1
  98. package/dist/workflow/prompt-pruning.d.ts +0 -20
  99. package/dist/workflow/prompt-pruning.js +0 -83
  100. package/dist/workflow/prompt-pruning.js.map +0 -1
@@ -106,7 +106,7 @@ All optional (`CreateAgentParams`, `src/assembly/index.ts`):
106
106
  | `checkpointer` | `BaseCheckpointSaver` | Custom-adapter escape hatch; takes precedence over the session store for checkpoint persistence. Defaults to a durable `BackendCheckpointSaver` writing through the session store. Use `MemorySaver` for ephemeral runs. |
107
107
  | `middleware` | `AgentMiddleware[]` | Extra middleware appended after workflow instrumentation. |
108
108
  | `onEvent` | `WorkflowEventHandler` | Subscribe to all lifecycle diagnostics; suppresses console output when set. |
109
- | `promptCache` | `PromptCacheOptions` | `{ enabled?, ttl?: "5m" \| "1h" }`. Provider prompt caching for the stable prefix; on by default. Falls back to `settings.prompt_cache`, then `ARCHMAX_PROMPT_CACHE`/`_TTL`. |
109
+ | `promptCache` | `PromptCacheOptions` | `{ enabled?, ttl?: "5m" \| "1h" }`. Provider prompt caching for the stable prefix; on by default. Falls back to `settings.prompt_cache`, then `ARCHMAX_PROMPT_CACHE`/`_TTL`. Turning it off does not stop `ChatAnthropic` or `ChatBedrockConverse` caching: Deep Agents installs LangChain's caching middleware for them itself (5-minute lifetime). |
110
110
  | `pricing` | `PricingTable` | USD per 1M tokens, keyed by model id (`default` matches any): `{ input?, output?, cacheRead?, cacheWrite? }`. Looked up by the id the response reported, else the id the runtime was configured to run — so a keyed table prices a workload behind a proxy that echoes no model back. Input rate applies to the input tokens the cache did not serve. Makes `costUsd` appear on usage events, run metadata, and the CLI footer. Falls back to `ARCHMAX_PRICE_*`. Unpriced → tokens only. |
111
111
 
112
112
  Fails closed: throws `WorkflowLoadError` if the named workflow's `workflow.yaml`
@@ -333,9 +333,11 @@ turn ended in — the position, variables, transcript and files are already ther
333
333
  message.
334
334
 
335
335
  The runtime does not reposition a later turn, so nothing is injected into the
336
- transcript to explain a jump. When a turn needs a path the current state cannot
337
- reach, the agent calls `archmax_reset` to return to the state the conversation began
338
- in (available in every state). To continue *mid-state* instead — inside the state
336
+ transcript to explain a jump. When the current state can serve the new message, the
337
+ agent answers there without a reset. That includes a terminal state that is also the
338
+ state the conversation began in, such as a one-state chat workflow. When a turn needs
339
+ a path the current state cannot reach, the agent calls `archmax_reset` to return to
340
+ the state the conversation began in (available in every state). To continue *mid-state* instead — inside the state
339
341
  that stopped, with the arrival in its transcript — the turn must park rather than
340
342
  finish.
341
343
 
@@ -378,7 +380,7 @@ const outcome = await agent.workflow!.send(sessionId, { message: "Refund the ord
378
380
 
379
381
  - `signatureJsonSchema(entries)` → `{ type: "object", properties, required }`:
380
382
  one property per entry in declaration order, every name required,
381
- `date`/`date-time` as `{ type: "string", format }`, an untyped entry as `{}`,
383
+ `date`/`date-time`/`file` as `{ type: "string", format }`, an untyped entry as `{}`,
382
384
  an entry's `description` carried over. It adds no `additionalProperties`; set
383
385
  it for your own ingress.
384
386
  - `signatureValueIssues(entries, values)` → one issue per missing name and per
@@ -387,6 +389,19 @@ const outcome = await agent.workflow!.send(sessionId, { message: "Refund the ord
387
389
  `"4"` to `4` before it validates or seeds.
388
390
  - A trigger's `description` is written for the caller. Use it as the MCP tool
389
391
  or operation description. The session's own model never sees it.
392
+ - **A `file` entry** (`format: "file"` in the schema) takes a workspace path. To
393
+ hand a session an uploaded file, pass it on the turn rather than writing into
394
+ the store yourself:
395
+ `send(sessionId, { message, files: { invoice: { name: "invoice.pdf", content: bytes } } })`.
396
+ The runtime places the bytes in the session's `scratchpad/attachments/` (a taken
397
+ name gets `-1`, `-2`, …), seeds `invoice`, locked, with the placed path, and the
398
+ turn boundary then checks that the file is there and readable. `send` throws
399
+ `InvalidTurnFileError`, before anything is written, for a bad variable or file
400
+ name, a variable also given in `variables`, a session parked at a human state, or
401
+ bytes the store cannot hold exactly (a non-text file needs a store with
402
+ `uploadFiles`). `signatureValueIssues` checks only a `file` value's shape, so a
403
+ start form validates the path the upload will produce, or skips the entry and
404
+ lets `files` supply it.
390
405
  - A host that reads specs itself must read `requires`/`returns` through
391
406
  `normalizeSignature` (both spellings → `{ name, type?, description? }[]`),
392
407
  never as `string[]`: an entry may be an object. Hosts must be on the release
@@ -618,6 +633,10 @@ and a delete itself. Deep Agents is handed the workspace without `delete`, so it
618
633
  own recursive `delete` tool never exists: `remove_file` is the one way to delete
619
634
  a file.
620
635
 
636
+ A store's `grep` receives the call's match cap as its fourth argument,
637
+ `maxCount` (the agent's `max_count`, else Deep Agents' 1,000). A store may stop
638
+ there and return `truncated: true`; the workspace caps one that ignores it.
639
+
621
640
  ### Host tools
622
641
 
623
642
  A host tool built with `toolsFromMap` reaches the workspace the way the
@@ -704,7 +723,7 @@ real). A bespoke target that intercepts by other means sets
704
723
  `capabilities.toolMocks` itself. Mockless cases run against any
705
724
  workflow-governed target.
706
725
 
707
- **Sub-workflows inherit the assembly.** When a workflow delegates (by advancing
726
+ **Sub-workflows inherit the assembly.** When a workflow delegates (by calling
708
727
  an `archmax_workflow_<slug>` tool), the child runtime is
709
728
  composed from the *same* resolved inputs as the parent — model and
710
729
  `modelFactory`, `backend` and `mounts`, `sessionStore` and `checkpointer`, host
@@ -717,11 +736,14 @@ never delegated to is never loaded, and one delegated from many places is
717
736
  composed once. A child whose spec is broken surfaces as a *dispatch* failure, not
718
737
  an assembly failure — a parent still assembles.
719
738
 
720
- The child runs on the parent's **session id** (which is what gives it the same
721
- run zone) under a nested checkpoint namespace, so `sessions.list()` reports one
722
- session and the parent session's metrics include the child's tokens.
723
- `capabilities.subWorkflows` is stamped `true` for a governed assembly and `false`
724
- for the plain one.
739
+ The child runs as a **separate session**, `<parent>~<state>:<workflow>:<n>`, in
740
+ the same session store, with its own checkpoints, its own token accounting and its
741
+ own file space: its `scratchpad/` is not its caller's, so it can neither read nor
742
+ overwrite a file of its caller's. `sessions.list()` reports it beside its caller
743
+ with `parentSessionId` naming the caller, and `sessions.delete` removes it by its
744
+ own id. The only files that cross are the runtime's copies of declared `file` arguments and
745
+ `file` returns. `capabilities.subWorkflows` is stamped `true` for a governed
746
+ assembly and `false` for the plain one.
725
747
 
726
748
  Omit both options and behavior is identical to `archmax test`. Cases run
727
749
  sequentially (`tests.maxConcurrency` above 1 is rejected, not silently
@@ -322,7 +322,8 @@ state fails that call closed with `parked`.
322
322
  Scripts still may not call `archmax_advance` (moving the state machine is the model's governed
323
323
  decision) or `archmax_set_variables`. Concurrent calls succeed or fail **independently**, so a script
324
324
  can act on what worked — `Promise.allSettled` is the honest shape when partial success is
325
- meaningful. Give each sub-run its own output path: they share one run zone.
325
+ meaningful. Each sub-run is its own session with its own `scratchpad/`, so concurrent sub-runs
326
+ never collide on a path; a `file` return comes back as a copy under a name no other file holds.
326
327
 
327
328
  ### Worked example: a `before` gate
328
329
 
@@ -557,7 +557,9 @@ states:
557
557
  - **`type`** is one of `string`, `integer` (no fractional part), `number`
558
558
  (finite), `boolean`, `date` (RFC 3339 `YYYY-MM-DD`, a real calendar day),
559
559
  `date-time` (RFC 3339 with a mandatory `Z`/`±hh:mm` offset), `object` (not an
560
- array) or `array`. Nothing is coerced (`"4"` is not an `integer`) and a typed
560
+ array), `array` or `file` (a path from the workspace root, such as
561
+ `scratchpad/attachments/invoice.pdf` or `skills/billing/assets/template.pdf`; see
562
+ [Passing files](#passing-files)). Nothing is coerced (`"4"` is not an `integer`) and a typed
561
563
  entry never takes `null`. It is held **at the start** (a mistyped seed is
562
564
  refused before any model call), **at the write** (`archmax_set_variables` of a
563
565
  mistyped typed return of the current trigger is a correctable refusal,
@@ -565,6 +567,17 @@ states:
565
567
  run — in practice a script or hook write). There are no enums, item or
566
568
  property schemas, ranges or optional inputs. An unknown `type`, an unknown
567
569
  entry key (`default:`) or an empty `description` fails load.
570
+ - **A `file` value must name a readable file.** The pure rule checks only the
571
+ path's shape (a host runs it in a browser). At the start, at the write, at
572
+ completion and at a delegation call the runtime also requires a **file** (not a
573
+ folder) at the path that the session may read **on runtime authority** — the
574
+ rules a hook is bound by: zone rules (`checkpoints/`, `artifacts/`, `_specs/`
575
+ never) and every workflow-wide `forbid_always`, inherited ones included; the
576
+ state's allow list and mount/skill grants do not narrow it. So tell the agent to
577
+ write the file *before* it sets a `file` return: `archmax_set_variables` refuses
578
+ a path with nothing there. A host hands a file in with `TurnInput.files` (CLI:
579
+ `archmax run … --file invoice=./invoice.pdf`), which places it under
580
+ `scratchpad/attachments/` and seeds the path.
568
581
  - **When to type.** Type an entry when something outside the session reads the
569
582
  contract: a delegating model that must build the argument, a host publishing an
570
583
  MCP tool or a start form. Leave it bare when any value will do. Typing a
@@ -1015,7 +1028,8 @@ the host at assembly (`variables`, every seed locked), the agent mid-run
1015
1028
 
1016
1029
  `trigger` holds the current turn's trigger id. `title` holds a short one-line label
1017
1030
  naming the task the run is doing; the platform system prompt asks the agent to set
1018
- one as its first activity and update it when the task changes significantly, so a
1031
+ one as its first activity, and to rename it only when the whole conversation has
1032
+ moved to a different subject (a follow-up on the same subject keeps it), so a
1019
1033
  host can name a run instead of showing a session id. A sub-workflow's child is not
1020
1034
  asked — its title names a session nothing lists and is never returned — so its
1021
1035
  prompt leaves the step out; do not write "set the title" into a child's state
@@ -1340,7 +1354,7 @@ enrich:
1340
1354
  delegation may happen is decided by the caller's `tools.allow` entry. A target
1341
1355
  with no `manual` entry has no state to start in and fails closed. `session:` and
1342
1356
  `message:` on that trigger are host-facing keys a delegation ignores: a sub-run
1343
- shares its parent's session, and carries no message. `sub-workflow` is no longer
1357
+ is its own session, derived from its caller's, and carries no message. `sub-workflow` is no longer
1344
1358
  a trigger id — load and `validate` report it with the rename.
1345
1359
  - **Declare the child's signature** on that trigger — the contract, enforced both
1346
1360
  ways and at both ingresses:
@@ -1400,14 +1414,17 @@ enrich:
1400
1414
  state's work. A workspace overriding the platform prompt
1401
1415
  (`.platform/system/GRAPH_STATE.md`) keeps that by wrapping what children skip
1402
1416
  in `<!-- top-level-only -->` … `<!-- /top-level-only -->` lines.
1403
- - **Same agent, isolated context, shared session.** Same model, backend, host
1417
+ - **Same agent, isolated context, own session.** Same model, backend, host
1404
1418
  tools; its **own** grading rubrics, from its own spec; a fresh transcript; the child's *own* per-state
1405
- `tools.allow`. It shares the run's session, so it writes `scratchpad/…` and
1406
- `scratchpad/…` exactly as the parent does and the parent reads them afterwards.
1419
+ `tools.allow`. It is a separate session (`<parent>~<state>:<workflow>:<n>`)
1420
+ with its own checkpoints and its own `scratchpad/`: a path it reads or writes
1421
+ resolves inside its own session, so it can neither see nor overwrite a file of
1422
+ its caller's, and concurrent sub-runs cannot collide on a path.
1407
1423
  - **Nothing crosses implicitly.** Down: only the arguments the call passed —
1408
1424
  the calling run's own variables are not copied. Up: only the result message
1409
- and the files. Everything the child sets is discarded when it finishes.
1410
- - **Give each concurrent sub-run its own output path** — they share one run zone.
1425
+ and the declared returns. Everything else the child sets or writes stays in its
1426
+ session. The only files that cross are runtime-made copies of `file` arguments
1427
+ and `file` returns (see "Passing files" below).
1411
1428
  - **Bounds** — the dispatcher's `bounds: { maxDepth: 3, maxConcurrent: 4 }`. No longer a spec key: the strict schema rejects `settings.sub_workflows` by name.
1412
1429
  Excess concurrency queues; depth and cycles are refused.
1413
1430
  - **Params are the sub-run's initial variables** — same construction, same name
@@ -1435,6 +1452,46 @@ parent reading a field the child does not return is an **error**; a state whose
1435
1452
  still carry it), and a fan-out is exempt from that warning because its
1436
1453
  entries' keys are not knowable statically.
1437
1454
 
1455
+ ### Passing files
1456
+
1457
+ A sub-run's `scratchpad/` is not its caller's, so a bare path string returned by a
1458
+ child (`returns: [result_file]`, untyped) names a file the caller **cannot
1459
+ read**. Type the entry `file` instead, and the runtime copies the file across:
1460
+
1461
+ ```yaml
1462
+ # the child
1463
+ triggers:
1464
+ manual:
1465
+ requires: [{ name: invoice, type: file }]
1466
+ returns: [{ name: report, type: file }]
1467
+ ```
1468
+
1469
+ - **Down.** The caller passes any path in its workspace (`scratchpad/inbox/a.pdf`,
1470
+ `skills/billing/assets/template.pdf`). Before the child starts, the runtime
1471
+ copies the exact bytes into the **child's** `scratchpad/attachments/` (same name;
1472
+ two arguments with one name take `-1`, `-2`) and seeds the child with that path,
1473
+ so its opening message shows it. The child edits its own copy and never the
1474
+ caller's file. The argument is a `read` path argument of the delegation tool, so
1475
+ a `paths:` guard on the allow entry and the zone and mount rules bind it. An
1476
+ argument naming nothing is refused `invalid-param` before the child runs. A
1477
+ failed call's record names the caller's path.
1478
+ - **Up.** On completion, each `file` return the child set is copied into the
1479
+ **caller's** `scratchpad/attachments/` and the result names that path. A taken
1480
+ name gets the lowest free `-1`, `-2`, … before the extension (`report-1.pdf`), so
1481
+ repeated or parallel calls never overwrite each other or the caller's files. A
1482
+ return whose file is gone at completion fails the call `invalid-return`.
1483
+ - **No laundering.** A `file` value must name a file the session may read on
1484
+ runtime authority, and a child inherits every ancestor's `forbid_always`. So a
1485
+ child cannot hand back a file its caller's workflow denies: its agent's write
1486
+ is refused, and a script's write is rejected at completion.
1487
+ - **Instructions to write.** Tell the child to write the file *first* and then set
1488
+ the return to its path. Tell the caller the returned path is under
1489
+ `scratchpad/attachments/`. Name files for what they hold (`<order_id>.json`):
1490
+ the name survives the copy.
1491
+ - **Mocks and cases.** A mocked `file` return names a file in the case's own
1492
+ session (seed it with `workspace:`), and it arrives copied into
1493
+ `scratchpad/attachments/` like a real one. Assert the copy's path.
1494
+
1438
1495
  ### A parked run may speak, never act
1439
1496
 
1440
1497
  A park does not end the conversation. Two moments get a **reply-only turn** — one
@@ -50,7 +50,7 @@ states:
50
50
  - { name: due, type: date, description: The day it is due. } # typed: held at the boundary
51
51
  returns: [result_file] # variables the run guarantees on completion (a caller's result);
52
52
  # short of them: a child returns what it set + a `note`; a top-level run is rejected
53
- # entry types: string integer number boolean date date-time object array
53
+ # entry types: string integer number boolean date date-time object array file
54
54
  instructions: >-
55
55
  Record the decision in scratchpad/refund.json, then archmax_advance to
56
56
  review; never close the case yourself.
@@ -1,20 +0,0 @@
1
- /**
2
- * Pruning of upstream Deep Agents prompt guidance for built-in tools an
3
- * assembly withholds: guidance for a tool the model cannot call only teaches it
4
- * to try.
5
- */
6
- /** The upstream headings that document `tool`'s guidance (empty when unknown). */
7
- export declare function upstreamSectionsFor(tool: string): readonly string[];
8
- export interface PruneResult {
9
- /** The prompt text with matched sections removed. */
10
- text: string;
11
- /** Headings that were found and removed. */
12
- removed: string[];
13
- /**
14
- * Tools whose guidance was found nowhere. A partial match is a successful
15
- * prune (upstream may carry only some of a tool's sections).
16
- */
17
- missingTools?: string[];
18
- }
19
- /** Prune the upstream guidance for every tool in `undisclosed`; tools with no known sections are ignored. */
20
- export declare function pruneUndisclosedToolSections(text: string, undisclosed: readonly string[]): PruneResult;
@@ -1,83 +0,0 @@
1
- /**
2
- * Pruning of upstream Deep Agents prompt guidance for built-in tools an
3
- * assembly withholds: guidance for a tool the model cannot call only teaches it
4
- * to try.
5
- */
6
- /**
7
- * Upstream section headings owned by a built-in tool's guidance.
8
- *
9
- * `task`'s guidance is two sections, and both must be named: the spawner
10
- * overview carries the bulk of it, and the notes section follows. A partial
11
- * match counts as a successful prune, so listing only one of them silently ships
12
- * the other.
13
- */
14
- const SECTIONS_BY_TOOL = {
15
- task: ["## `task` (subagent spawner)", "## Important Task Tool Usage Notes to Remember"],
16
- };
17
- /** The upstream headings that document `tool`'s guidance (empty when unknown). */
18
- export function upstreamSectionsFor(tool) {
19
- return SECTIONS_BY_TOOL[tool] ?? [];
20
- }
21
- /** A section spans its heading line to the next heading of the same or higher level. */
22
- function sectionEnd(lines, start, level) {
23
- for (let i = start + 1; i < lines.length; i++) {
24
- const match = /^(#{1,6})\s/.exec(lines[i]);
25
- if (match && match[1].length <= level)
26
- return i;
27
- }
28
- return lines.length;
29
- }
30
- function headingLevel(heading) {
31
- return /^(#{1,6})\s/.exec(heading)?.[1].length ?? 2;
32
- }
33
- /**
34
- * Put a target heading on its own line when composition glued it to the end of
35
- * the previous one.
36
- *
37
- * Upstream concatenates its prompt fragments without a separating newline, so a
38
- * section can arrive as `…within files## \`task\` (subagent spawner)`. Matching is
39
- * line-anchored (a `##` mid-sentence is prose, not a heading), so without this
40
- * the section is silently kept and billed on every call. Narrow by construction:
41
- * only a heading we were asked to remove is ever split out.
42
- */
43
- function unglueHeadings(text, headings) {
44
- let out = text;
45
- for (const heading of headings) {
46
- let at = out.indexOf(heading);
47
- while (at > 0) {
48
- if (out[at - 1] !== "\n") {
49
- out = `${out.slice(0, at)}\n${out.slice(at)}`;
50
- at += 1;
51
- }
52
- at = out.indexOf(heading, at + heading.length);
53
- }
54
- }
55
- return out;
56
- }
57
- /** Remove each heading's section. Matching is exact on the trimmed heading line. */
58
- function pruneSections(text, headings) {
59
- if (!text || headings.length === 0)
60
- return { text, removed: [] };
61
- const removed = [];
62
- let lines = unglueHeadings(text, headings).split("\n");
63
- for (const heading of headings) {
64
- const index = lines.findIndex((line) => line.trim() === heading);
65
- if (index === -1)
66
- continue;
67
- const end = sectionEnd(lines, index, headingLevel(heading));
68
- lines = [...lines.slice(0, index), ...lines.slice(end)];
69
- removed.push(heading);
70
- }
71
- // Collapse the blank runs left behind so the result is byte-stable however many sections were cut.
72
- return { text: lines.join("\n").replace(/\n{3,}/g, "\n\n").trim(), removed };
73
- }
74
- /** Prune the upstream guidance for every tool in `undisclosed`; tools with no known sections are ignored. */
75
- export function pruneUndisclosedToolSections(text, undisclosed) {
76
- const result = pruneSections(text, undisclosed.flatMap((tool) => [...upstreamSectionsFor(tool)]));
77
- const missingTools = undisclosed.filter((tool) => {
78
- const owned = upstreamSectionsFor(tool);
79
- return owned.length > 0 && !owned.some((heading) => result.removed.includes(heading));
80
- });
81
- return { ...result, ...(missingTools.length > 0 ? { missingTools } : {}) };
82
- }
83
- //# sourceMappingURL=prompt-pruning.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompt-pruning.js","sourceRoot":"","sources":["../../src/workflow/prompt-pruning.ts"],"names":[],"mappings":"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"}