@davidorex/pi-jit-agents 0.14.6 → 0.28.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 (96) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +2 -2
  3. package/dist/agent-spec.d.ts.map +1 -1
  4. package/dist/agent-spec.js +69 -5
  5. package/dist/agent-spec.js.map +1 -1
  6. package/dist/budget-enforcer.d.ts +43 -0
  7. package/dist/budget-enforcer.d.ts.map +1 -0
  8. package/dist/budget-enforcer.js +172 -0
  9. package/dist/budget-enforcer.js.map +1 -0
  10. package/dist/compile.d.ts +39 -0
  11. package/dist/compile.d.ts.map +1 -1
  12. package/dist/compile.js +363 -24
  13. package/dist/compile.js.map +1 -1
  14. package/dist/dispatch-inline.d.ts +25 -0
  15. package/dist/dispatch-inline.d.ts.map +1 -0
  16. package/dist/dispatch-inline.js +41 -0
  17. package/dist/dispatch-inline.js.map +1 -0
  18. package/dist/field-path.d.ts +32 -0
  19. package/dist/field-path.d.ts.map +1 -0
  20. package/dist/field-path.js +48 -0
  21. package/dist/field-path.js.map +1 -0
  22. package/dist/index.d.ts +11 -4
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/jit-runtime.d.ts +14 -3
  27. package/dist/jit-runtime.d.ts.map +1 -1
  28. package/dist/jit-runtime.js +41 -5
  29. package/dist/jit-runtime.js.map +1 -1
  30. package/dist/markers.d.ts +28 -0
  31. package/dist/markers.d.ts.map +1 -0
  32. package/dist/markers.js +36 -0
  33. package/dist/markers.js.map +1 -0
  34. package/dist/renderer-registry.d.ts +60 -0
  35. package/dist/renderer-registry.d.ts.map +1 -0
  36. package/dist/renderer-registry.js +141 -0
  37. package/dist/renderer-registry.js.map +1 -0
  38. package/dist/template.d.ts +15 -0
  39. package/dist/template.d.ts.map +1 -1
  40. package/dist/template.js +29 -3
  41. package/dist/template.js.map +1 -1
  42. package/dist/trace-redactor.d.ts +1 -1
  43. package/dist/trace-redactor.js +1 -1
  44. package/dist/trace-writer.js +2 -2
  45. package/dist/types.d.ts +86 -4
  46. package/dist/types.d.ts.map +1 -1
  47. package/package.json +11 -4
  48. package/schemas/agent-trace.schema.json +30 -2
  49. package/templates/analyzers/base-analyzer.md +9 -0
  50. package/templates/analyzers/patterns-task.md +11 -0
  51. package/templates/analyzers/patterns.md +15 -0
  52. package/templates/analyzers/quality-task.md +11 -0
  53. package/templates/analyzers/quality.md +15 -0
  54. package/templates/analyzers/structure-task.md +17 -0
  55. package/templates/analyzers/structure.md +15 -0
  56. package/templates/architecture-designer/task.md +117 -0
  57. package/templates/architecture-inferrer/task.md +61 -0
  58. package/templates/audit-finding-verifier/task.md +59 -0
  59. package/templates/audit-fixer/task.md +67 -0
  60. package/templates/audit-results-router/task.md +37 -0
  61. package/templates/decomposer/task.md +56 -0
  62. package/templates/explorer/system.md +3 -0
  63. package/templates/explorer/task.md +9 -0
  64. package/templates/gap-identifier/task.md +103 -0
  65. package/templates/gap-resolution-assessor/task.md +48 -0
  66. package/templates/handoff-writer/task.md +101 -0
  67. package/templates/investigator/task.md +30 -0
  68. package/templates/items/architecture.md +33 -0
  69. package/templates/items/conformance.md +26 -0
  70. package/templates/items/conventions.md +19 -0
  71. package/templates/items/decisions.md +47 -0
  72. package/templates/items/domain.md +21 -0
  73. package/templates/items/features.md +63 -0
  74. package/templates/items/framework-gaps.md +43 -0
  75. package/templates/items/issues.md +21 -0
  76. package/templates/items/layer-plans.md +50 -0
  77. package/templates/items/project.md +48 -0
  78. package/templates/items/requirements.md +20 -0
  79. package/templates/items/research.md +57 -0
  80. package/templates/items/spec-reviews.md +51 -0
  81. package/templates/items/tasks.md +29 -0
  82. package/templates/phase-author/task.md +82 -0
  83. package/templates/plan-creator/task.md +143 -0
  84. package/templates/plan-decomposer/task.md +46 -0
  85. package/templates/project-definer/task.md +67 -0
  86. package/templates/project-inferrer/task.md +56 -0
  87. package/templates/requirements-gatherer/task.md +90 -0
  88. package/templates/researcher/task.md +26 -0
  89. package/templates/shared/macros.md +169 -0
  90. package/templates/shared/render-helpers.md +106 -0
  91. package/templates/spec-implementer/task.md +53 -0
  92. package/templates/synthesizer/system.md +3 -0
  93. package/templates/synthesizer/task.md +38 -0
  94. package/templates/task-verifier/task.md +44 -0
  95. package/templates/task-worker/task.md +52 -0
  96. package/templates/verifier/task.md +57 -0
package/CHANGELOG.md CHANGED
@@ -1 +1,50 @@
1
1
  # Changelog
2
+
3
+ All notable changes to this package are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/). This package has had two public npm releases (`0.14.6`, `0.26.0`); the entries below are scoped to its published surface (`src/`, `schemas/`, `templates/`), and `[Unreleased]` holds changes since `0.26.0`.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.28.0] - 2026-06-03
8
+
9
+ ### Added
10
+ - Six whole-block delegator macros (`render_decisions`, `render_features`, `render_framework_gaps`, `render_layer_plans`, `render_research`, `render_spec_reviews`) in `shared/macros.md` as thin wrappers over per-item macros (`dce327d`)
11
+ - `array_key` column on the `CANONICAL_MACRO_NAMES` registry, surfacing block_kind ≠ data-key divergences (framework-gaps→gaps, layer-plans→plans, spec-reviews→reviews); reader call-sites use the `.macro_name` property (`0fb6149`)
12
+ - `TraceEntry` `extension_load_warning` entry kind (`8030c61`)
13
+ - Grant-clamp infrastructure: `CompiledAgent.tools` + `DispatchContext.parentGrant`; `spec.tools` threaded through `compileAgent`; `GrantViolationError` clamping child-tools to a subset of the parent grant at the `executeAgent` dispatch boundary (`dee01a5`, `4e9a866`, `a273ab9`)
14
+
15
+ ### Changed
16
+ - Relocated the agent-template tree `packages/pi-workflows/templates/` → `packages/pi-jit-agents/templates/` and added a `bundledTemplateDir()` export; consumer call-sites rewired (`d71b48e`)
17
+ - Neutralized `.project` references in agent-reachable code strings (`5024158`)
18
+ - Stripped development-history citations from `agent-trace.schema.json` (`2f3ef47`)
19
+
20
+ ## [0.26.0] - 2026-05-25
21
+
22
+ Covers `v0.14.6..v0.26.0` for the package's published surface.
23
+
24
+ ### Added
25
+ - `contextBlocks` widened to `(string | ContextBlockRef)[]` for per-item injection; object-form `contextBlocks` wired through `compileAgent`; multi-entry-same-name injection (`9f7eb42`, `4d1fd1d`, `a0c6467`)
26
+ - Renderer registry mapping block kinds to per-item Nunjucks macro refs; composition-primitive surface symmetry (three tools, Nunjucks global, macro wiring) (`38fb27e`, `5341b2b`)
27
+ - `x-prompt-budget` enforcement primitive + narrative-field annotations on six newer block schemas (`be7a168`)
28
+
29
+ ### Changed
30
+ - Context-block delimiters adopt the pi 0.75.x XML-tag convention (`d153877`)
31
+ - Migrated consumers to the pi-context `context` vocabulary; retired per-item-macro duplication via shared helpers + a canonical-name registry map (`187c094`, `161bf4e`, `188f351`, `80b6915`)
32
+ - Migrated pi-mono peer-deps `@mariozechner/*` → `@earendil-works/*` at `^0.74.0` (`e5ab8ad`)
33
+
34
+ ### Fixed
35
+ - `compile.ts` catches `BootstrapNotFoundError` at `projectDir` + `buildIdIndex` call sites (`ba58d78`)
36
+ - Empty-prefix guard + cross-block status-vocab check + name-based error catches (`4fb4d1e`)
37
+
38
+ ## [0.14.6] - 2026-04-28
39
+
40
+ Covers the package from its extraction (`239f718`, 2026-04-12) through its first public release.
41
+
42
+ ### Added
43
+ - Initial extraction of `@davidorex/pi-jit-agents`: agent spec compilation (`agent-spec`), agent-trace SDK, and the verdict/agent-trace/trace-config schemas (`239f718`)
44
+ - Monitor classify trace capture aligned to canonical pi (`1bc36ba`)
45
+
46
+ ### Changed
47
+ - Bumped pi-mono `0.63.1` → `0.70.2`; migrated TypeBox v0.34 → v1.x via a hybrid import strategy (`11a4069`)
48
+
49
+ ### Fixed
50
+ - Provider-aware `toolChoice` normalization at the execute boundary (`ce37772`)
package/README.md CHANGED
@@ -18,7 +18,7 @@ There is one concept of "agent" regardless of whether a workflow step or a monit
18
18
  The package owns four public surfaces:
19
19
 
20
20
  - `loadAgent(name, ctx) → AgentSpec` — resolves spec from discovery tiers, fully resolves all path fields to absolute
21
- - `compileAgent(spec, ctx) → CompiledAgent` — renders templates, injects `contextBlocks` from `.project/`, composes final prompts
21
+ - `compileAgent(spec, ctx) → CompiledAgent` — renders templates, injects `contextBlocks` from the substrate directory, composes final prompts
22
22
  - `executeAgent(compiled, dispatch) → JitAgentResult` — in-process LLM dispatch with phantom tool enforcement
23
23
  - `agentContract(spec) → AgentContract` — projection for introspection, no execution
24
24
 
@@ -28,7 +28,7 @@ Subprocess dispatch stays in pi-workflows. The package never reads from `.pi/`
28
28
 
29
29
  Agent specs are searched in this order:
30
30
 
31
- 1. `{cwd}/.project/agents/{name}.agent.yaml` — project-level overrides
31
+ 1. `{cwd}/<substrate-dir>/agents/{name}.agent.yaml` — project-level overrides
32
32
  2. `{userDir ?? ~/.pi/agent/agents/}/{name}.agent.yaml` — user-global overrides
33
33
  3. `{builtinDir}/{name}.agent.yaml` — consumer-supplied builtins
34
34
 
@@ -1 +1 @@
1
- {"version":3,"file":"agent-spec.d.ts","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAmCzD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CA6C1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAkB/E"}
1
+ {"version":3,"file":"agent-spec.d.ts","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAmB,WAAW,EAAE,MAAM,YAAY,CAAC;AAuI1E;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAiD1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAyB/E"}
@@ -7,6 +7,7 @@
7
7
  import fs from "node:fs";
8
8
  import os from "node:os";
9
9
  import path from "node:path";
10
+ import { tryResolveContextDir } from "@davidorex/pi-context/context-dir";
10
11
  import { parse as parseYaml } from "yaml";
11
12
  import { AgentNotFoundError, AgentParseError } from "./errors.js";
12
13
  /**
@@ -43,6 +44,60 @@ function resolveSpecPath(value, specDir) {
43
44
  return value;
44
45
  return path.resolve(specDir, value);
45
46
  }
47
+ /**
48
+ * Validate and normalise a single `contextBlocks` entry.
49
+ *
50
+ * Accepts:
51
+ * - a non-empty string (whole-block reference, the established surface), or
52
+ * - an object with a required string `name` and optional `item` (string),
53
+ * `focus` (record of string→string), and `depth` (non-negative number).
54
+ *
55
+ * Throws `AgentParseError` with a descriptive message naming the offending
56
+ * entry's index and the failing constraint. Plan 4 (Wave 2) consumes the
57
+ * resulting union shape; this helper does not assign any runtime semantics.
58
+ */
59
+ function parseContextBlockEntry(entry, index, agentName, filePath) {
60
+ if (typeof entry === "string") {
61
+ if (entry.length === 0) {
62
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}]: bare-string entry must be non-empty`));
63
+ }
64
+ return entry;
65
+ }
66
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
67
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}]: entry must be a string or an object with at least { name: string }; got ${Array.isArray(entry) ? "array" : entry === null ? "null" : typeof entry}`));
68
+ }
69
+ const obj = entry;
70
+ if (typeof obj.name !== "string" || obj.name.length === 0) {
71
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}]: object form requires a non-empty string \`name\` field`));
72
+ }
73
+ const out = { name: obj.name };
74
+ if (obj.item !== undefined) {
75
+ if (typeof obj.item !== "string" || obj.item.length === 0) {
76
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}].item: when present must be a non-empty string`));
77
+ }
78
+ out.item = obj.item;
79
+ }
80
+ if (obj.focus !== undefined) {
81
+ if (obj.focus === null || typeof obj.focus !== "object" || Array.isArray(obj.focus)) {
82
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}].focus: when present must be an object mapping string keys to string values`));
83
+ }
84
+ const focus = {};
85
+ for (const [k, v] of Object.entries(obj.focus)) {
86
+ if (typeof v !== "string") {
87
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}].focus.${k}: value must be a string; got ${typeof v}`));
88
+ }
89
+ focus[k] = v;
90
+ }
91
+ out.focus = focus;
92
+ }
93
+ if (obj.depth !== undefined) {
94
+ if (typeof obj.depth !== "number" || !Number.isFinite(obj.depth) || obj.depth < 0 || !Number.isInteger(obj.depth)) {
95
+ throw new AgentParseError(agentName, filePath, new Error(`contextBlocks[${index}].depth: when present must be a non-negative integer; got ${String(obj.depth)}`));
96
+ }
97
+ out.depth = obj.depth;
98
+ }
99
+ return out;
100
+ }
46
101
  /**
47
102
  * Parse a YAML agent spec file into a fully-resolved AgentSpec.
48
103
  *
@@ -89,7 +144,9 @@ export function parseAgentYaml(filePath) {
89
144
  inputSchema: spec.input,
90
145
  outputFormat: spec.output?.format,
91
146
  outputSchema: resolveSpecPath(spec.output?.schema, specDir),
92
- contextBlocks: Array.isArray(spec.contextBlocks) ? spec.contextBlocks : undefined,
147
+ contextBlocks: Array.isArray(spec.contextBlocks)
148
+ ? spec.contextBlocks.map((entry, index) => parseContextBlockEntry(entry, index, spec.name || name, filePath))
149
+ : undefined,
93
150
  loadedFrom: specDir,
94
151
  };
95
152
  }
@@ -108,11 +165,18 @@ export function parseAgentYaml(filePath) {
108
165
  */
109
166
  export function createAgentLoader(ctx) {
110
167
  const userTier = ctx.userDir ?? path.join(os.homedir(), ".pi", "agent", "agents");
168
+ // Resolve the project tier once (FGAP-074 C3): pointer-less repos degrade by
169
+ // omitting the project-tier search path so the loader still searches the
170
+ // user/builtin tiers and ultimately throws its normal AgentNotFoundError
171
+ // (NOT BootstrapNotFoundError). `agentsDir(cwd)` was `<contextDir>/agents`,
172
+ // so the inline equivalent is `path.join(base, "agents", ...)`.
173
+ const base = tryResolveContextDir(ctx.cwd);
111
174
  return (name) => {
112
- const searchPaths = [
113
- path.join(ctx.cwd, ".project", "agents", `${name}.agent.yaml`),
114
- path.join(userTier, `${name}.agent.yaml`),
115
- ];
175
+ const searchPaths = [];
176
+ if (base !== null) {
177
+ searchPaths.push(path.join(base, "agents", `${name}.agent.yaml`));
178
+ }
179
+ searchPaths.push(path.join(userTier, `${name}.agent.yaml`));
116
180
  if (ctx.builtinDir) {
117
181
  searchPaths.push(path.join(ctx.builtinDir, `${name}.agent.yaml`));
118
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-spec.js","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-spec.js","sourceRoot":"","sources":["../src/agent-spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Render-time prompt-budget enforcement primitive.
3
+ *
4
+ * Reads `x-prompt-budget` annotations from a JSON Schema field and, when the
5
+ * rendered output exceeds the declared budget, returns tail-truncated output
6
+ * plus a structured warning. Annotation absence is pass-through; malformed
7
+ * annotations throw with the field path named in the error.
8
+ *
9
+ * Plan 5 of the per-item-macros work ships this primitive plus the schema
10
+ * annotations themselves (`.project/schemas/*.schema.json`). Per-item macros
11
+ * (Plans 6 and 7) consume the primitive to gate field rendering.
12
+ *
13
+ * Token measurement is intentionally a coarse approximation: whitespace +
14
+ * Unicode-punctuation split, not a tokenizer-accurate count tied to any
15
+ * specific model. The aim is a render-time signal, not a billing surface.
16
+ */
17
+ export interface PromptBudget {
18
+ tokens?: number;
19
+ words?: number;
20
+ }
21
+ export interface BudgetWarning {
22
+ field: string;
23
+ budget: PromptBudget;
24
+ actual: {
25
+ tokens: number;
26
+ words: number;
27
+ };
28
+ truncated: boolean;
29
+ }
30
+ export interface BudgetResult {
31
+ output: string;
32
+ warning: BudgetWarning | null;
33
+ }
34
+ /**
35
+ * If the schema field at `fieldPath` declares `x-prompt-budget`, measure
36
+ * `rendered` and return tail-truncated output + warning when over budget.
37
+ * If the field has no annotation, return `{ output: rendered, warning: null }`.
38
+ *
39
+ * Empty `rendered` short-circuits to pass-through regardless of annotation —
40
+ * there is nothing to measure or truncate.
41
+ */
42
+ export declare function enforceBudget(rendered: string, schema: object, fieldPath: string): BudgetResult;
43
+ //# sourceMappingURL=budget-enforcer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget-enforcer.d.ts","sourceRoot":"","sources":["../src/budget-enforcer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,YAAY;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC9B;AA6HD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAkC/F"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Render-time prompt-budget enforcement primitive.
3
+ *
4
+ * Reads `x-prompt-budget` annotations from a JSON Schema field and, when the
5
+ * rendered output exceeds the declared budget, returns tail-truncated output
6
+ * plus a structured warning. Annotation absence is pass-through; malformed
7
+ * annotations throw with the field path named in the error.
8
+ *
9
+ * Plan 5 of the per-item-macros work ships this primitive plus the schema
10
+ * annotations themselves (`.project/schemas/*.schema.json`). Per-item macros
11
+ * (Plans 6 and 7) consume the primitive to gate field rendering.
12
+ *
13
+ * Token measurement is intentionally a coarse approximation: whitespace +
14
+ * Unicode-punctuation split, not a tokenizer-accurate count tied to any
15
+ * specific model. The aim is a render-time signal, not a billing surface.
16
+ */
17
+ const TRUNCATION_MARKER = "[…truncated to budget]";
18
+ /**
19
+ * Approximate token count: split on Unicode whitespace + punctuation, drop empties.
20
+ * Not tokenizer-accurate; meant as a render-time proxy for prompt-size signal.
21
+ */
22
+ function countTokens(text) {
23
+ if (text === "")
24
+ return 0;
25
+ return text.split(/[\s\p{P}]+/u).filter(Boolean).length;
26
+ }
27
+ /**
28
+ * Word count: whitespace split, drop empties.
29
+ */
30
+ function countWords(text) {
31
+ if (text === "")
32
+ return 0;
33
+ return text.split(/\s+/).filter(Boolean).length;
34
+ }
35
+ /**
36
+ * Resolve a JSON-pointer-style path against a schema object.
37
+ *
38
+ * Path interpretation:
39
+ * - Leading "/" is optional; treated as the root delimiter.
40
+ * - Segments are split on "/" with no JSON Pointer escape handling
41
+ * (we do not encounter "~" or "/" inside schema keys in practice).
42
+ * - Each segment is looked up as a property key on the current node.
43
+ * - Returns null if any segment is missing.
44
+ *
45
+ * Example: "/properties/research/items/properties/findings_summary"
46
+ */
47
+ function resolveSchemaField(schema, fieldPath) {
48
+ const trimmed = fieldPath.startsWith("/") ? fieldPath.slice(1) : fieldPath;
49
+ if (trimmed === "")
50
+ return schema;
51
+ const segments = trimmed.split("/");
52
+ let cursor = schema;
53
+ for (const segment of segments) {
54
+ if (cursor === null || typeof cursor !== "object")
55
+ return null;
56
+ const next = cursor[segment];
57
+ if (next === undefined)
58
+ return null;
59
+ cursor = next;
60
+ }
61
+ if (cursor === null || typeof cursor !== "object")
62
+ return null;
63
+ return cursor;
64
+ }
65
+ /**
66
+ * Validate a raw `x-prompt-budget` annotation into a typed PromptBudget.
67
+ * Throws AgentBudgetError-style Error naming the field on malformed input.
68
+ */
69
+ function parseBudget(raw, fieldPath) {
70
+ if (raw === null || typeof raw !== "object") {
71
+ throw new Error(`x-prompt-budget at ${fieldPath} must be an object, got ${typeof raw}`);
72
+ }
73
+ const obj = raw;
74
+ const out = {};
75
+ if ("tokens" in obj) {
76
+ const t = obj.tokens;
77
+ if (typeof t !== "number" || !Number.isFinite(t) || t < 0 || !Number.isInteger(t)) {
78
+ throw new Error(`x-prompt-budget at ${fieldPath}: tokens must be a non-negative integer, got ${JSON.stringify(t)}`);
79
+ }
80
+ out.tokens = t;
81
+ }
82
+ if ("words" in obj) {
83
+ const w = obj.words;
84
+ if (typeof w !== "number" || !Number.isFinite(w) || w < 0 || !Number.isInteger(w)) {
85
+ throw new Error(`x-prompt-budget at ${fieldPath}: words must be a non-negative integer, got ${JSON.stringify(w)}`);
86
+ }
87
+ out.words = w;
88
+ }
89
+ if (out.tokens === undefined && out.words === undefined) {
90
+ throw new Error(`x-prompt-budget at ${fieldPath} declares neither tokens nor words; at least one must be present`);
91
+ }
92
+ return out;
93
+ }
94
+ /**
95
+ * Tail-truncate `text` so that it fits both `maxTokens` and `maxWords` (when set).
96
+ * Strategy: binary-search the smallest character-prefix length whose token+word
97
+ * counts (with the marker appended) satisfy both budgets. The marker itself
98
+ * adds a small fixed cost that is included in the count.
99
+ *
100
+ * If `text` already fits, returns `text` unchanged with `truncated: false`.
101
+ */
102
+ function tailTruncate(text, budget) {
103
+ const fitsBudget = (s) => {
104
+ if (budget.tokens !== undefined && countTokens(s) > budget.tokens)
105
+ return false;
106
+ if (budget.words !== undefined && countWords(s) > budget.words)
107
+ return false;
108
+ return true;
109
+ };
110
+ if (fitsBudget(text)) {
111
+ return { output: text, truncated: false };
112
+ }
113
+ // If even the marker alone is over budget, return just the marker (best effort).
114
+ if (!fitsBudget(TRUNCATION_MARKER)) {
115
+ return { output: TRUNCATION_MARKER, truncated: true };
116
+ }
117
+ // Binary-search the largest prefix length such that prefix + " " + marker fits.
118
+ let lo = 0;
119
+ let hi = text.length;
120
+ let best = 0;
121
+ while (lo <= hi) {
122
+ const mid = (lo + hi) >> 1;
123
+ const candidate = `${text.slice(0, mid).trimEnd()} ${TRUNCATION_MARKER}`;
124
+ if (fitsBudget(candidate)) {
125
+ best = mid;
126
+ lo = mid + 1;
127
+ }
128
+ else {
129
+ hi = mid - 1;
130
+ }
131
+ }
132
+ const output = `${text.slice(0, best).trimEnd()} ${TRUNCATION_MARKER}`;
133
+ return { output, truncated: true };
134
+ }
135
+ /**
136
+ * If the schema field at `fieldPath` declares `x-prompt-budget`, measure
137
+ * `rendered` and return tail-truncated output + warning when over budget.
138
+ * If the field has no annotation, return `{ output: rendered, warning: null }`.
139
+ *
140
+ * Empty `rendered` short-circuits to pass-through regardless of annotation —
141
+ * there is nothing to measure or truncate.
142
+ */
143
+ export function enforceBudget(rendered, schema, fieldPath) {
144
+ if (rendered === "") {
145
+ return { output: "", warning: null };
146
+ }
147
+ const fieldNode = resolveSchemaField(schema, fieldPath);
148
+ if (fieldNode === null) {
149
+ return { output: rendered, warning: null };
150
+ }
151
+ const annotation = fieldNode["x-prompt-budget"];
152
+ if (annotation === undefined) {
153
+ return { output: rendered, warning: null };
154
+ }
155
+ const budget = parseBudget(annotation, fieldPath);
156
+ const actualTokens = countTokens(rendered);
157
+ const actualWords = countWords(rendered);
158
+ const overTokens = budget.tokens !== undefined && actualTokens > budget.tokens;
159
+ const overWords = budget.words !== undefined && actualWords > budget.words;
160
+ if (!overTokens && !overWords) {
161
+ return { output: rendered, warning: null };
162
+ }
163
+ const { output, truncated } = tailTruncate(rendered, budget);
164
+ const warning = {
165
+ field: fieldPath,
166
+ budget,
167
+ actual: { tokens: actualTokens, words: actualWords },
168
+ truncated,
169
+ };
170
+ return { output, warning };
171
+ }
172
+ //# sourceMappingURL=budget-enforcer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget-enforcer.js","sourceRoot":"","sources":["../src/budget-enforcer.ts"],"names":[],"mappings":"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"}
package/dist/compile.d.ts CHANGED
@@ -1,4 +1,43 @@
1
+ import { type ItemLocation } from "@davidorex/pi-context";
2
+ import type nunjucks from "nunjucks";
3
+ import { type BudgetWarning } from "./budget-enforcer.js";
4
+ import type { RendererRegistry } from "./renderer-registry.js";
1
5
  import type { AgentSpec, CompileContext, CompiledAgent } from "./types.js";
6
+ /**
7
+ * Register the composition-time Nunjucks globals on a template environment.
8
+ *
9
+ * Three globals are registered, scoped to the closure-captured state passed in:
10
+ *
11
+ * - `resolve(id)` — looks up an item by ID via the lazy idIndex; returns
12
+ * ItemLocation or null. Used by macros to dispatch
13
+ * cross-block references.
14
+ * - `render_recursive(loc, depth)` — renders an item via the registered
15
+ * per-item macro (resolved through the renderer registry).
16
+ * Cycle-detected via the visited set.
17
+ * - `enforceBudget(rendered, blockName, fieldPathOrShorthand)` — measures
18
+ * rendered text against the field's `x-prompt-budget`
19
+ * annotation, returning truncated output when over budget;
20
+ * pass-through when annotation absent. Truncation warnings
21
+ * are appended to `warningsCollector` (closure-captured by
22
+ * the caller) so callers can surface them after compile
23
+ * returns.
24
+ *
25
+ * Used by both `compileAgent` (this module) and `renderItemById` in pi-workflows
26
+ * — the single source of truth for the composition-globals contract. Adding
27
+ * a new global, changing a signature, or adjusting a fallback means editing
28
+ * here and the contract propagates to every caller.
29
+ *
30
+ * The visited-set lifetime is one call to this function — sequential calls on
31
+ * the same env get isolated cycle scopes. addGlobal overwrites prior bindings,
32
+ * which is intentional (each composition pass owns its own scope).
33
+ */
34
+ export declare function registerCompositionGlobals(opts: {
35
+ env: nunjucks.Environment;
36
+ cwd: string;
37
+ rendererRegistry: RendererRegistry | undefined;
38
+ getIdIndex: () => Map<string, ItemLocation>;
39
+ warningsCollector: BudgetWarning[];
40
+ }): void;
2
41
  /**
3
42
  * Compile an AgentSpec into a CompiledAgent.
4
43
  *
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8B3E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa,CAiEhF"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAcA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGxE,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,sBAAsB,CAAC;AAKzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAE5F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,QAAQ,CAAC,WAAW,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC/C,UAAU,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC5C,iBAAiB,EAAE,aAAa,EAAE,CAAC;CACnC,GAAG,IAAI,CA+EP;AA8DD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,cAAc,GAAG,aAAa,CAiXhF"}