@burtson-labs/agent-core 1.6.47 → 1.6.49

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/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ export declare const createAgentRuntime: (options?: CreateAgentRuntimeOptions) =
14
14
  export * from './tools';
15
15
  export * from './graph';
16
16
  export * from './suggestions';
17
+ export * from './lessons';
18
+ export * from './spec';
17
19
  export * from './mcp';
18
20
  export { redactSecrets, redactSecretsString, BUILTIN_SECRET_PATTERNS, type SecretPattern, type RedactionResult } from './security/secretPatterns';
19
21
  export { TelemetryExporter, resolveTelemetryConfig, TTFT_BUCKETS, DURATION_BUCKETS, type TelemetryConfig } from './telemetry/otlpExporter';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC5B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,yBAA0B,SAAQ,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IAC/F,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,eAAO,MAAM,kBAAkB,GAAI,UAAS,yBAA8B,KAAG,YAO5E,CAAC;AAGF,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAExB,cAAc,eAAe,CAAC;AAK9B,cAAc,OAAO,CAAC;AAKtB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAInC,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC5B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,yBAA0B,SAAQ,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IAC/F,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,eAAO,MAAM,kBAAkB,GAAI,UAAS,yBAA8B,KAAG,YAO5E,CAAC;AAGF,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAExB,cAAc,eAAe,CAAC;AAE9B,cAAc,WAAW,CAAC;AAE1B,cAAc,QAAQ,CAAC;AAKvB,cAAc,OAAO,CAAC;AAKtB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAInC,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -43,6 +43,10 @@ __exportStar(require("./tools"), exports);
43
43
  __exportStar(require("./graph"), exports);
44
44
  // Next-prompt prediction (composer "what's next?" suggestions).
45
45
  __exportStar(require("./suggestions"), exports);
46
+ // Learning memory — distill durable repo lessons from turns (host stores them).
47
+ __exportStar(require("./lessons"), exports);
48
+ // Spec-driven development — spec markdown → graph plan (criteria = contracts).
49
+ __exportStar(require("./spec"), exports);
46
50
  // MCP — Model Context Protocol client (Phase 1: groundwork). See
47
51
  // docs/integration-playlist/mcp-roadmap.md. Off by default — hosts
48
52
  // that don't construct an McpClientPool get zero behavior change.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,yDAAsD;AACtD,+EAAiF;AAGjF,gDAA8B;AAC9B,gDAA8B;AAC9B,uCAAkD;AAAzC,0GAAA,iBAAiB,OAAA;AAE1B,+DAIqC;AADnC,oHAAA,iBAAiB,OAAA;AAGnB,6EAG4C;AAF1C,qIAAA,2BAA2B,OAAA;AAI7B,uDAUgC;AAT9B,4GAAA,YAAY,OAAA;AAeP,MAAM,kBAAkB,GAAG,CAAC,UAAqC,EAAE,EAAgB,EAAE;IAC1F,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,QAAQ,GAAG,gBAAgB,IAAI,IAAI,oDAA2B,EAAE,CAAC;IACvE,OAAO,IAAI,2BAAY,CAAC;QACtB,QAAQ;QACR,GAAI,IAA8C;KACnD,CAAC,CAAC;AACL,CAAC,CAAC;AAPW,QAAA,kBAAkB,sBAO7B;AAEF,cAAc;AACd,0CAAwB;AACxB,uEAAuE;AACvE,8EAA8E;AAC9E,0CAAwB;AACxB,gEAAgE;AAChE,gDAA8B;AAE9B,iEAAiE;AACjE,mEAAmE;AACnE,kEAAkE;AAClE,wCAAsB;AAEtB,wDAAwD;AACxD,mEAAmE;AACnE,4CAA4C;AAC5C,4DAMmC;AALjC,+GAAA,aAAa,OAAA;AACb,qHAAA,mBAAmB,OAAA;AACnB,yHAAA,uBAAuB,OAAA;AAKzB,iFAAiF;AACjF,iFAAiF;AACjF,yDAMkC;AALhC,iHAAA,iBAAiB,OAAA;AACjB,sHAAA,sBAAsB,OAAA;AACtB,4GAAA,YAAY,OAAA;AACZ,gHAAA,gBAAgB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,yDAAsD;AACtD,+EAAiF;AAGjF,gDAA8B;AAC9B,gDAA8B;AAC9B,uCAAkD;AAAzC,0GAAA,iBAAiB,OAAA;AAE1B,+DAIqC;AADnC,oHAAA,iBAAiB,OAAA;AAGnB,6EAG4C;AAF1C,qIAAA,2BAA2B,OAAA;AAI7B,uDAUgC;AAT9B,4GAAA,YAAY,OAAA;AAeP,MAAM,kBAAkB,GAAG,CAAC,UAAqC,EAAE,EAAgB,EAAE;IAC1F,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,QAAQ,GAAG,gBAAgB,IAAI,IAAI,oDAA2B,EAAE,CAAC;IACvE,OAAO,IAAI,2BAAY,CAAC;QACtB,QAAQ;QACR,GAAI,IAA8C;KACnD,CAAC,CAAC;AACL,CAAC,CAAC;AAPW,QAAA,kBAAkB,sBAO7B;AAEF,cAAc;AACd,0CAAwB;AACxB,uEAAuE;AACvE,8EAA8E;AAC9E,0CAAwB;AACxB,gEAAgE;AAChE,gDAA8B;AAC9B,gFAAgF;AAChF,4CAA0B;AAC1B,+EAA+E;AAC/E,yCAAuB;AAEvB,iEAAiE;AACjE,mEAAmE;AACnE,kEAAkE;AAClE,wCAAsB;AAEtB,wDAAwD;AACxD,mEAAmE;AACnE,4CAA4C;AAC5C,4DAMmC;AALjC,+GAAA,aAAa,OAAA;AACb,qHAAA,mBAAmB,OAAA;AACnB,yHAAA,uBAAuB,OAAA;AAKzB,iFAAiF;AACjF,iFAAiF;AACjF,yDAMkC;AALhC,iHAAA,iBAAiB,OAAA;AACjB,sHAAA,sBAAsB,OAAA;AACtB,4GAAA,YAAY,OAAA;AACZ,gHAAA,gBAAgB,OAAA"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Learning memory — the distiller half.
3
+ *
4
+ * After a turn, the host may make ONE cheap model call asking: did anything
5
+ * DURABLE and REPO-GENERAL happen here worth remembering for future tasks?
6
+ * ("this project uses pnpm, not npm"; "tests for src/api live in test/api";
7
+ * "the build fails unless you run codegen first"). Most turns yield nothing —
8
+ * the point is to accrue a SMALL set of high-signal, reusable facts about a
9
+ * specific repo, not a diary. Those facts are stored (see host-kit's lesson
10
+ * store) and injected on future turns so the agent gets better at THIS repo
11
+ * over time.
12
+ *
13
+ * This module is the shared, host-agnostic core: build the (small) prompt,
14
+ * parse the reply into a lesson or nothing. Pure + fully unit-testable, same
15
+ * split as the planner and next-prompt suggestions. The host owns the model
16
+ * call, the persistence, and the opt-in gate (off by default — it's an extra
17
+ * call per turn, and a store the model writes to deserves a deliberate opt-in).
18
+ */
19
+ export interface LessonTurn {
20
+ /** The user's prompt for the turn. */
21
+ prompt: string;
22
+ /** The assistant's final response. */
23
+ assistantResponse: string;
24
+ /** Tool names used this turn, in order (optional but sharpens distillation). */
25
+ toolsUsed?: string[];
26
+ /** Whether the turn succeeded, if the host knows (a failure often teaches
27
+ * the most durable lesson — "X doesn't work here, do Y"). */
28
+ outcome?: 'ok' | 'error';
29
+ }
30
+ /** The sentinel a well-behaved model returns when nothing is worth storing. */
31
+ export declare const NO_LESSON = "NONE";
32
+ export declare function buildLessonPrompt(turn: LessonTurn): string;
33
+ /**
34
+ * Parse the distiller reply into a lesson, or null when nothing was learned.
35
+ * Never throws. Filters NONE, empties, over-long/paragraph replies, and the
36
+ * "here's a lesson:" preamble the model sometimes adds.
37
+ */
38
+ export declare function parseLesson(text: string): string | null;
39
+ /** Normalize a lesson for dedup comparison (lowercase, collapse whitespace,
40
+ * drop trailing punctuation). Two lessons that normalize equal are dupes. */
41
+ export declare function normalizeLesson(lesson: string): string;
42
+ //# sourceMappingURL=lessons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessons.d.ts","sourceRoot":"","sources":["../src/lessons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;kEAC8D;IAC9D,OAAO,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;CAC1B;AAED,+EAA+E;AAC/E,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAwB1D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAevD;AAED;8EAC8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Learning memory — the distiller half.
4
+ *
5
+ * After a turn, the host may make ONE cheap model call asking: did anything
6
+ * DURABLE and REPO-GENERAL happen here worth remembering for future tasks?
7
+ * ("this project uses pnpm, not npm"; "tests for src/api live in test/api";
8
+ * "the build fails unless you run codegen first"). Most turns yield nothing —
9
+ * the point is to accrue a SMALL set of high-signal, reusable facts about a
10
+ * specific repo, not a diary. Those facts are stored (see host-kit's lesson
11
+ * store) and injected on future turns so the agent gets better at THIS repo
12
+ * over time.
13
+ *
14
+ * This module is the shared, host-agnostic core: build the (small) prompt,
15
+ * parse the reply into a lesson or nothing. Pure + fully unit-testable, same
16
+ * split as the planner and next-prompt suggestions. The host owns the model
17
+ * call, the persistence, and the opt-in gate (off by default — it's an extra
18
+ * call per turn, and a store the model writes to deserves a deliberate opt-in).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.NO_LESSON = void 0;
22
+ exports.buildLessonPrompt = buildLessonPrompt;
23
+ exports.parseLesson = parseLesson;
24
+ exports.normalizeLesson = normalizeLesson;
25
+ /** The sentinel a well-behaved model returns when nothing is worth storing. */
26
+ exports.NO_LESSON = 'NONE';
27
+ function buildLessonPrompt(turn) {
28
+ const tools = turn.toolsUsed?.length ? `\nTools used: ${turn.toolsUsed.join(', ')}` : '';
29
+ const outcome = turn.outcome ? `\nOutcome: ${turn.outcome}` : '';
30
+ const clip = (s, n) => (s.length > n ? s.slice(0, n) + '…' : s);
31
+ return [
32
+ 'You maintain a small memory of DURABLE facts about THIS specific repository, to help future coding tasks.',
33
+ 'Look at the turn below and decide: did it reveal a fact that is (a) specific to this repo, (b) still true next week, and (c) genuinely useful for a future task?',
34
+ '',
35
+ 'Good lessons (store these):',
36
+ '- "This project uses pnpm; npm install breaks the workspace."',
37
+ '- "Tests for src/api/* live in test/api/* and must be updated alongside."',
38
+ '- "The build needs `pnpm codegen` run before `pnpm build`."',
39
+ '',
40
+ 'NOT lessons (never store these):',
41
+ '- Anything about this ONE task ("added a clamp function") — that\'s history, not a durable fact.',
42
+ '- Generic programming advice true of any repo.',
43
+ '- Anything you are not confident is actually true of this repo.',
44
+ '',
45
+ `Reply with ONE short sentence (the lesson, max ~140 chars) OR exactly "${exports.NO_LESSON}" if nothing durable was learned. Most turns are "${exports.NO_LESSON}". No preamble, no quotes, no markdown.`,
46
+ '',
47
+ '--- turn ---',
48
+ `User: ${clip(turn.prompt, 500)}`,
49
+ `Assistant: ${clip(turn.assistantResponse, 800)}${tools}${outcome}`,
50
+ ].join('\n');
51
+ }
52
+ /**
53
+ * Parse the distiller reply into a lesson, or null when nothing was learned.
54
+ * Never throws. Filters NONE, empties, over-long/paragraph replies, and the
55
+ * "here's a lesson:" preamble the model sometimes adds.
56
+ */
57
+ function parseLesson(text) {
58
+ let s = (text ?? '').trim();
59
+ if (!s)
60
+ return null;
61
+ // Strip a leading "lesson:" / "fact:" label if present.
62
+ s = s.replace(/^\s*(lesson|fact|answer)\s*:\s*/i, '').trim();
63
+ // Strip surrounding quotes/backticks/markdown bullet.
64
+ s = s.replace(/^[-*]\s+/, '').replace(/^["'`]+|["'`]+$/g, '').trim();
65
+ if (!s)
66
+ return null;
67
+ // Explicit "nothing to learn" in any casing/punctuation.
68
+ if (/^none[.!]?$/i.test(s))
69
+ return null;
70
+ // A model that ignored instructions and wrote a paragraph isn't a lesson.
71
+ if (s.length > 200)
72
+ return null;
73
+ // Refuse suspiciously generic non-facts.
74
+ if (/^(no|n\/a|nothing|not applicable|no lesson)\b/i.test(s))
75
+ return null;
76
+ return s;
77
+ }
78
+ /** Normalize a lesson for dedup comparison (lowercase, collapse whitespace,
79
+ * drop trailing punctuation). Two lessons that normalize equal are dupes. */
80
+ function normalizeLesson(lesson) {
81
+ return lesson.toLowerCase().replace(/\s+/g, ' ').replace(/[.!]+$/g, '').trim();
82
+ }
83
+ //# sourceMappingURL=lessons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessons.js","sourceRoot":"","sources":["../src/lessons.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAiBH,8CAwBC;AAOD,kCAeC;AAID,0CAEC;AAvDD,+EAA+E;AAClE,QAAA,SAAS,GAAG,MAAM,CAAC;AAEhC,SAAgB,iBAAiB,CAAC,IAAgB;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,OAAO;QACL,2GAA2G;QAC3G,kKAAkK;QAClK,EAAE;QACF,6BAA6B;QAC7B,+DAA+D;QAC/D,2EAA2E;QAC3E,6DAA6D;QAC7D,EAAE;QACF,kCAAkC;QAClC,kGAAkG;QAClG,gDAAgD;QAChD,iEAAiE;QACjE,EAAE;QACF,0EAA0E,iBAAS,qDAAqD,iBAAS,yCAAyC;QAC1L,EAAE;QACF,cAAc;QACd,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;QACjC,cAAc,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,GAAG,KAAK,GAAG,OAAO,EAAE;KACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,wDAAwD;IACxD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,sDAAsD;IACtD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,yDAAyD;IACzD,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,0EAA0E;IAC1E,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,IAAI,CAAC;IAChC,yCAAyC;IACzC,IAAI,gDAAgD,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1E,OAAO,CAAC,CAAC;AACX,CAAC;AAED;8EAC8E;AAC9E,SAAgB,eAAe,CAAC,MAAc;IAC5C,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACjF,CAAC"}
package/dist/spec.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Spec-driven development — the shared core.
3
+ *
4
+ * A spec is a durable, human-written markdown artifact: a goal plus explicit
5
+ * acceptance criteria. It's the most leveraged thing a person can produce when
6
+ * an agent writes the code. This module turns that spec into a GRAPH PLAN:
7
+ * the planner (Phase 9) proposes nodes that implement the spec, and the
8
+ * acceptance criteria become the run's verification — the same completion-
9
+ * contract / verification-node machinery the graph runtime already enforces.
10
+ *
11
+ * So spec-driven dev here is a COMPOSITION, not a new engine: spec → planner →
12
+ * graph → contracts-as-acceptance-criteria. This file is pure (parse + prompt
13
+ * building), mirroring the planner/suggestions/lessons split; the host reads
14
+ * the file, runs the graph, and checks the criteria.
15
+ */
16
+ export interface SpecDoc {
17
+ /** Title (the `# heading`). */
18
+ title: string;
19
+ /** The goal paragraph(s) under `## Goal`. */
20
+ goal: string;
21
+ /** Acceptance criteria (bullets/checkboxes under `## Acceptance criteria`).
22
+ * These become the run's verification targets. */
23
+ criteria: string[];
24
+ /** Files/paths named under `## Context` (bias the plan toward real work). */
25
+ context: string[];
26
+ /** Hard constraints under `## Constraints` ("don't touch the public API"). */
27
+ constraints: string[];
28
+ }
29
+ /** A blank spec to scaffold with `bandit spec new`. */
30
+ export declare function specTemplate(title: string): string;
31
+ /**
32
+ * Parse a structured markdown spec. Lenient: a bare goal with no headings is
33
+ * treated as the goal; missing sections yield empty arrays. Checkbox markers
34
+ * (`- [ ]` / `- [x]`) and plain bullets both count as criteria.
35
+ */
36
+ export declare function parseSpec(markdown: string): SpecDoc;
37
+ export interface SpecValidation {
38
+ ok: boolean;
39
+ errors: string[];
40
+ }
41
+ /** A spec is runnable only if it has a goal and at least one criterion. */
42
+ export declare function validateSpec(spec: SpecDoc): SpecValidation;
43
+ /**
44
+ * Build the planner prompt for a spec. It asks for the SAME GraphProposal JSON
45
+ * the planner already emits (so parseGraphProposal validates it), but seeded
46
+ * with the spec: implementation nodes for the work, then a final verification
47
+ * node whose job is to check every acceptance criterion. The host maps that
48
+ * final node to a completion contract, so "did we meet the spec?" is enforced
49
+ * by the graph runtime, not asserted in prose.
50
+ */
51
+ export declare function buildSpecPlanPrompt(spec: SpecDoc, maxNodes?: number): string;
52
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,OAAO;IACtB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb;uDACmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmBlD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAkDnD;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAK1D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,SAAI,GAAG,MAAM,CAyBvE"}
package/dist/spec.js ADDED
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Spec-driven development — the shared core.
4
+ *
5
+ * A spec is a durable, human-written markdown artifact: a goal plus explicit
6
+ * acceptance criteria. It's the most leveraged thing a person can produce when
7
+ * an agent writes the code. This module turns that spec into a GRAPH PLAN:
8
+ * the planner (Phase 9) proposes nodes that implement the spec, and the
9
+ * acceptance criteria become the run's verification — the same completion-
10
+ * contract / verification-node machinery the graph runtime already enforces.
11
+ *
12
+ * So spec-driven dev here is a COMPOSITION, not a new engine: spec → planner →
13
+ * graph → contracts-as-acceptance-criteria. This file is pure (parse + prompt
14
+ * building), mirroring the planner/suggestions/lessons split; the host reads
15
+ * the file, runs the graph, and checks the criteria.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.specTemplate = specTemplate;
19
+ exports.parseSpec = parseSpec;
20
+ exports.validateSpec = validateSpec;
21
+ exports.buildSpecPlanPrompt = buildSpecPlanPrompt;
22
+ /** A blank spec to scaffold with `bandit spec new`. */
23
+ function specTemplate(title) {
24
+ const t = title.trim() || 'Untitled feature';
25
+ return [
26
+ `# ${t}`,
27
+ '',
28
+ '## Goal',
29
+ '<One short paragraph: what should be true when this is done, and why.>',
30
+ '',
31
+ '## Acceptance criteria',
32
+ '- [ ] <A concrete, checkable outcome — e.g. "GET /health returns 200 with {status:\'ok\'}">',
33
+ '- [ ] <Another — keep each one independently verifiable>',
34
+ '',
35
+ '## Context',
36
+ '- <path/to/relevant/file.ts>',
37
+ '',
38
+ '## Constraints',
39
+ '- <e.g. "Do not change the public API in src/index.ts">',
40
+ ''
41
+ ].join('\n');
42
+ }
43
+ /**
44
+ * Parse a structured markdown spec. Lenient: a bare goal with no headings is
45
+ * treated as the goal; missing sections yield empty arrays. Checkbox markers
46
+ * (`- [ ]` / `- [x]`) and plain bullets both count as criteria.
47
+ */
48
+ function parseSpec(markdown) {
49
+ const lines = markdown.replace(/\r\n/g, '\n').split('\n');
50
+ let title = '';
51
+ const sections = new Map();
52
+ let current = 'goal'; // text before the first heading is the goal
53
+ for (const line of lines) {
54
+ const h1 = /^#\s+(.+)$/.exec(line);
55
+ const h2 = /^##\s+(.+)$/.exec(line);
56
+ if (h1 && !title) {
57
+ title = h1[1].trim();
58
+ continue;
59
+ }
60
+ if (h2) {
61
+ current = h2[1].trim().toLowerCase();
62
+ if (!sections.has(current))
63
+ sections.set(current, []);
64
+ continue;
65
+ }
66
+ const bucket = sections.get(current) ?? [];
67
+ bucket.push(line);
68
+ sections.set(current, bucket);
69
+ }
70
+ const bullets = (key) => (sections.get(key) ?? [])
71
+ .map((l) => l.trim())
72
+ .map((l) => l.replace(/^[-*]\s+(\[[ xX]\]\s*)?/, '')) // strip bullet + optional checkbox
73
+ .filter((l) => l.length > 0 && !/^<.*>$/.test(l)); // drop empties + template placeholders
74
+ const paragraph = (key) => (sections.get(key) ?? [])
75
+ .map((l) => l.trim())
76
+ .filter((l) => l.length > 0 && !/^<.*>$/.test(l))
77
+ .join(' ')
78
+ .trim();
79
+ // Accept a few heading aliases people actually type.
80
+ const criteria = bullets('acceptance criteria').length
81
+ ? bullets('acceptance criteria')
82
+ : bullets('criteria').length
83
+ ? bullets('criteria')
84
+ : bullets('requirements');
85
+ return {
86
+ title: title || 'Untitled spec',
87
+ goal: paragraph('goal'),
88
+ criteria,
89
+ context: bullets('context'),
90
+ constraints: bullets('constraints')
91
+ };
92
+ }
93
+ /** A spec is runnable only if it has a goal and at least one criterion. */
94
+ function validateSpec(spec) {
95
+ const errors = [];
96
+ if (!spec.goal.trim())
97
+ errors.push('spec has no ## Goal');
98
+ if (spec.criteria.length === 0)
99
+ errors.push('spec has no ## Acceptance criteria (need at least one checkable outcome)');
100
+ return { ok: errors.length === 0, errors };
101
+ }
102
+ /**
103
+ * Build the planner prompt for a spec. It asks for the SAME GraphProposal JSON
104
+ * the planner already emits (so parseGraphProposal validates it), but seeded
105
+ * with the spec: implementation nodes for the work, then a final verification
106
+ * node whose job is to check every acceptance criterion. The host maps that
107
+ * final node to a completion contract, so "did we meet the spec?" is enforced
108
+ * by the graph runtime, not asserted in prose.
109
+ */
110
+ function buildSpecPlanPrompt(spec, maxNodes = 6) {
111
+ const criteria = spec.criteria.map((c, i) => ` ${i + 1}. ${c}`).join('\n');
112
+ const context = spec.context.length ? `\nRelevant files:\n${spec.context.map((c) => ` - ${c}`).join('\n')}` : '';
113
+ const constraints = spec.constraints.length ? `\nConstraints (must honor):\n${spec.constraints.map((c) => ` - ${c}`).join('\n')}` : '';
114
+ return [
115
+ `You are planning how to implement a SPEC as a dependency graph. Output ONE JSON object in a \`\`\`json fence and nothing else.`,
116
+ '',
117
+ `Spec: ${spec.title}`,
118
+ `Goal: ${spec.goal}`,
119
+ '',
120
+ 'Acceptance criteria (the definition of done):',
121
+ criteria,
122
+ context,
123
+ constraints,
124
+ '',
125
+ 'JSON shape (same as the planner):',
126
+ '{"kind":"graph","reason":"one sentence","nodes":[{"id":"kebab-case","label":"short","prompt":"self-contained instruction","dependsOn":["other-id"],"readOnly":false}]}',
127
+ '',
128
+ 'Rules:',
129
+ `- Always "kind":"graph". Use 2-${maxNodes} nodes.`,
130
+ '- Early nodes do the implementation work, split by concern; ids kebab-case + unique; dependsOn lists only earlier ids; no cycles.',
131
+ '- The LAST node must be a verification node that depends on the others and checks EVERY acceptance criterion is satisfied, reporting per-criterion pass/fail. Give it id "verify-spec".',
132
+ '- Each node prompt is self-contained (its reader sees only that prompt + its dependencies\' results). Reference the specific criteria a node addresses.',
133
+ '- Mark investigation/verification nodes "readOnly": true.'
134
+ ].filter((l) => l !== '').join('\n');
135
+ }
136
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burtson-labs/agent-core",
3
- "version": "1.6.47",
3
+ "version": "1.6.49",
4
4
  "author": {
5
5
  "name": "Burtson Labs",
6
6
  "email": "team@burtson.ai",