twining-mcp 1.8.2 → 1.11.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 (65) hide show
  1. package/README.md +15 -5
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +20 -5
  4. package/dist/config.js.map +1 -1
  5. package/dist/dashboard/public/app.js +3 -1
  6. package/dist/engine/blackboard.d.ts +4 -0
  7. package/dist/engine/blackboard.d.ts.map +1 -1
  8. package/dist/engine/blackboard.js +9 -0
  9. package/dist/engine/blackboard.js.map +1 -1
  10. package/dist/engine/context-assembler.d.ts +14 -1
  11. package/dist/engine/context-assembler.d.ts.map +1 -1
  12. package/dist/engine/context-assembler.js +222 -42
  13. package/dist/engine/context-assembler.js.map +1 -1
  14. package/dist/engine/decisions.d.ts +1 -1
  15. package/dist/engine/decisions.d.ts.map +1 -1
  16. package/dist/engine/decisions.js +26 -42
  17. package/dist/engine/decisions.js.map +1 -1
  18. package/dist/engine/graph-auto-populator.d.ts +61 -0
  19. package/dist/engine/graph-auto-populator.d.ts.map +1 -0
  20. package/dist/engine/graph-auto-populator.js +283 -0
  21. package/dist/engine/graph-auto-populator.js.map +1 -0
  22. package/dist/engine/verify.d.ts.map +1 -1
  23. package/dist/engine/verify.js +34 -0
  24. package/dist/engine/verify.js.map +1 -1
  25. package/dist/instructions.d.ts +1 -1
  26. package/dist/instructions.d.ts.map +1 -1
  27. package/dist/instructions.js +1 -1
  28. package/dist/server.d.ts.map +1 -1
  29. package/dist/server.js +23 -9
  30. package/dist/server.js.map +1 -1
  31. package/dist/tools/blackboard-tools.d.ts +1 -1
  32. package/dist/tools/blackboard-tools.d.ts.map +1 -1
  33. package/dist/tools/blackboard-tools.js +39 -38
  34. package/dist/tools/blackboard-tools.js.map +1 -1
  35. package/dist/tools/context-tools.d.ts +2 -2
  36. package/dist/tools/context-tools.d.ts.map +1 -1
  37. package/dist/tools/context-tools.js +30 -27
  38. package/dist/tools/context-tools.js.map +1 -1
  39. package/dist/tools/coordination-tools.d.ts +2 -1
  40. package/dist/tools/coordination-tools.d.ts.map +1 -1
  41. package/dist/tools/coordination-tools.js +160 -142
  42. package/dist/tools/coordination-tools.js.map +1 -1
  43. package/dist/tools/decision-tools.d.ts +1 -1
  44. package/dist/tools/decision-tools.d.ts.map +1 -1
  45. package/dist/tools/decision-tools.js +118 -113
  46. package/dist/tools/decision-tools.js.map +1 -1
  47. package/dist/tools/export-tools.d.ts +1 -1
  48. package/dist/tools/export-tools.d.ts.map +1 -1
  49. package/dist/tools/export-tools.js +4 -2
  50. package/dist/tools/export-tools.js.map +1 -1
  51. package/dist/tools/graph-tools.d.ts +1 -1
  52. package/dist/tools/graph-tools.d.ts.map +1 -1
  53. package/dist/tools/graph-tools.js +54 -52
  54. package/dist/tools/graph-tools.js.map +1 -1
  55. package/dist/tools/lifecycle-tools.d.ts.map +1 -1
  56. package/dist/tools/lifecycle-tools.js +34 -32
  57. package/dist/tools/lifecycle-tools.js.map +1 -1
  58. package/dist/tools/verify-tools.d.ts +1 -1
  59. package/dist/tools/verify-tools.d.ts.map +1 -1
  60. package/dist/tools/verify-tools.js +4 -1
  61. package/dist/tools/verify-tools.js.map +1 -1
  62. package/dist/utils/types.d.ts +14 -2
  63. package/dist/utils/types.d.ts.map +1 -1
  64. package/package.json +10 -2
  65. package/src/dashboard/public/app.js +3 -1
package/README.md CHANGED
@@ -80,7 +80,7 @@ Agents self-select into work by reading the blackboard. No central bottleneck. N
80
80
  /plugin install twining@twining-marketplace
81
81
  ```
82
82
 
83
- Includes the MCP server, 7 skills, lifecycle hooks, coordinator subagent, and commands. Skills teach Claude when and how to use Twining — everything works automatically.
83
+ Includes the MCP server, 8 skills, lifecycle hooks, coordinator subagent, and commands. Skills teach Claude when and how to use Twining — everything works automatically.
84
84
 
85
85
  ### Team Auto-Install
86
86
 
@@ -166,6 +166,7 @@ A web dashboard starts automatically at `http://localhost:24282` — browse deci
166
166
  | `twining_trace` | Trace a decision's dependency chain upstream and downstream |
167
167
  | `twining_reconsider` | Flag a decision for reconsideration with impact analysis |
168
168
  | `twining_override` | Override a decision, optionally creating a replacement |
169
+ | `twining_promote` | Promote provisional decisions to active after validation |
169
170
  | `twining_search_decisions` | Search decisions by keyword or semantic similarity |
170
171
  | `twining_link_commit` | Link a git commit to a decision |
171
172
  | `twining_commits` | Find decisions by git commit |
@@ -178,6 +179,7 @@ A web dashboard starts automatically at `http://localhost:24282` — browse deci
178
179
  | `twining_read` | Read entries filtered by type, scope, or agent |
179
180
  | `twining_query` | Semantic search across all entries |
180
181
  | `twining_recent` | Get the latest entries |
182
+ | `twining_dismiss` | Remove resolved or false-positive entries by ID |
181
183
 
182
184
  ### Context Assembly
183
185
 
@@ -186,9 +188,6 @@ A web dashboard starts automatically at `http://localhost:24282` — browse deci
186
188
  | `twining_assemble` | Build tailored context for a task within a token budget |
187
189
  | `twining_summarize` | High-level summary of project state |
188
190
  | `twining_what_changed` | What changed since a given point in time |
189
- | `twining_status` | Health check — entry counts, warnings, agent status |
190
- | `twining_archive` | Move stale entries to archive |
191
- | `twining_export` | Export full state as markdown |
192
191
 
193
192
  ### Knowledge Graph
194
193
 
@@ -198,6 +197,7 @@ A web dashboard starts automatically at `http://localhost:24282` — browse deci
198
197
  | `twining_add_relation` | Add a relation between entities |
199
198
  | `twining_neighbors` | Traverse from an entity up to depth 3 |
200
199
  | `twining_graph_query` | Search by name or property |
200
+ | `twining_prune_graph` | Remove orphaned entities with no relations |
201
201
 
202
202
  Decisions auto-populate the graph: `twining_decide` creates file and function entities with `decided_by` relations for every affected file.
203
203
 
@@ -205,12 +205,22 @@ Decisions auto-populate the graph: `twining_decide` creates file and function en
205
205
 
206
206
  | Tool | What It Does |
207
207
  |------|-------------|
208
+ | `twining_register` | Register or update an agent in the coordination registry |
208
209
  | `twining_agents` | List agents with capabilities and liveness |
209
210
  | `twining_discover` | Find agents matching required capabilities |
210
211
  | `twining_delegate` | Post a delegation request with capability requirements |
211
212
  | `twining_handoff` | Hand off work with results and auto-assembled context |
212
213
  | `twining_acknowledge` | Acknowledge receipt of a handoff |
213
214
 
215
+ ### Verification & Lifecycle
216
+
217
+ | Tool | What It Does |
218
+ |------|-------------|
219
+ | `twining_verify` | Run verification checks — test coverage, warnings, assembly tracking |
220
+ | `twining_status` | Health check — entry counts, warnings, agent status |
221
+ | `twining_archive` | Move stale entries to archive |
222
+ | `twining_export` | Export full state as markdown |
223
+
214
224
  ## How It Works
215
225
 
216
226
  All state lives in `.twining/` as plain files — JSONL for the blackboard, JSON for decisions, graph, agents, and handoffs. Everything is `jq`-queryable, `grep`-able, and git-diffable. No database. No cloud. No accounts.
@@ -293,7 +303,7 @@ That's it — the PostHog project key is built into the source code. If you run
293
303
  ```bash
294
304
  npm install # Install dependencies
295
305
  npm run build # Build
296
- npm test # Run tests (570+ tests)
306
+ npm test # Run tests (600+ tests)
297
307
  npm run test:watch
298
308
  ```
299
309
 
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,aA8C5B,CAAC;AA+BF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAc5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,aAqD5B,CAAC;AA+BF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CA0B5D"}
package/dist/config.js CHANGED
@@ -16,11 +16,11 @@ export const DEFAULT_CONFIG = {
16
16
  context_assembly: {
17
17
  default_max_tokens: 4000,
18
18
  priority_weights: {
19
- recency: 0.3,
20
- relevance: 0.3,
21
- decision_confidence: 0.2,
19
+ recency: 0.2,
20
+ relevance: 0.2,
21
+ decision_confidence: 0.15,
22
22
  warning_boost: 0.1,
23
- graph_connectivity: 0.1,
23
+ graph_reachability: 0.35,
24
24
  },
25
25
  },
26
26
  conflict_resolution: "human",
@@ -50,6 +50,13 @@ export const DEFAULT_CONFIG = {
50
50
  instructions: {
51
51
  auto_inject: true, // Include workflow instructions in MCP initialize response
52
52
  },
53
+ tools: {
54
+ mode: "full", // "full" or "lite" — lite registers only core tools
55
+ full_surface: false, // when false, hide 16 rarely-used tools to reduce context noise
56
+ },
57
+ graph: {
58
+ auto_populate: false, // when false, skip auto-graph-population from tool calls
59
+ },
53
60
  };
54
61
  /** Deep merge source into target, returning a new object */
55
62
  function deepMerge(target, source) {
@@ -87,6 +94,14 @@ export function loadConfig(twiningDir) {
87
94
  if (parsed === null || parsed === undefined || typeof parsed !== "object") {
88
95
  return { ...DEFAULT_CONFIG };
89
96
  }
90
- return deepMerge(DEFAULT_CONFIG, parsed);
97
+ const config = deepMerge(DEFAULT_CONFIG, parsed);
98
+ // Validate priority weights sum to 1.0
99
+ const weights = config.context_assembly.priority_weights;
100
+ const weightSum = Object.values(weights).reduce((a, b) => (a ?? 0) + (b ?? 0), 0);
101
+ if (Math.abs(weightSum - 1.0) > 0.01) {
102
+ console.error(`[twining] Warning: priority_weights sum to ${weightSum}, expected 1.0. Using defaults.`);
103
+ config.context_assembly.priority_weights = { ...DEFAULT_CONFIG.context_assembly.priority_weights };
104
+ }
105
+ return config;
91
106
  }
92
107
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -2238,7 +2238,9 @@ var ENTITY_COLORS = {
2238
2238
  concept: '#f59e0b',
2239
2239
  pattern: '#10b981',
2240
2240
  dependency: '#ef4444',
2241
- api_endpoint: '#ec4899'
2241
+ api_endpoint: '#ec4899',
2242
+ agent: '#14b8a6',
2243
+ commit: '#a855f7'
2242
2244
  };
2243
2245
 
2244
2246
  // Track which entity types are visible (null = all)
@@ -9,6 +9,7 @@ import type { Embedder } from "../embeddings/embedder.js";
9
9
  import type { IndexManager } from "../embeddings/index-manager.js";
10
10
  import type { SearchEngine, BlackboardSearchResult } from "../embeddings/search.js";
11
11
  import type { Archiver } from "./archiver.js";
12
+ import type { GraphAutoPopulator } from "./graph-auto-populator.js";
12
13
  export declare class BlackboardEngine {
13
14
  private readonly store;
14
15
  private readonly embedder;
@@ -16,9 +17,12 @@ export declare class BlackboardEngine {
16
17
  private readonly searchEngine;
17
18
  private archiver;
18
19
  private archiveThreshold;
20
+ private graphPopulator;
19
21
  constructor(store: BlackboardStore, embedder?: Embedder | null, indexManager?: IndexManager | null, searchEngine?: SearchEngine | null);
20
22
  /** Inject archiver for threshold-based auto-archiving (spec §6.1.3). */
21
23
  setArchiver(archiver: Archiver, config: TwiningConfig): void;
24
+ /** Inject graph auto-populator for relation extraction from posts. */
25
+ setGraphPopulator(populator: GraphAutoPopulator): void;
22
26
  /** Post a new blackboard entry with validation and defaults. */
23
27
  post(input: {
24
28
  entry_type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"blackboard.d.ts","sourceRoot":"","sources":["../../src/engine/blackboard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,KAAK,EAAE,eAAe,EAAa,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,gBAAgB,CAAuB;gBAG7C,KAAK,EAAE,eAAe,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,EAC1B,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,EAClC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI;IAQpC,wEAAwE;IACxE,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI;IAK5D,gEAAgE;IAC1D,IAAI,CAAC,KAAK,EAAE;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAiE9C,qDAAqD;IAC/C,IAAI,CAAC,OAAO,CAAC,EAAE;QACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,eAAe,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAUhE,oEAAoE;IAC9D,KAAK,CACT,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACnD,OAAO,CAAC;QACT,OAAO,EAAE,sBAAsB,EAAE,CAAC;QAClC,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IAcF,kEAAkE;IAC5D,MAAM,CACV,CAAC,CAAC,EAAE,MAAM,EACV,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC;QAAE,OAAO,EAAE,eAAe,EAAE,CAAA;KAAE,CAAC;IAK1C,oFAAoF;IAC9E,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAkBpF"}
1
+ {"version":3,"file":"blackboard.d.ts","sourceRoot":"","sources":["../../src/engine/blackboard.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ export class BlackboardEngine {
13
13
  searchEngine;
14
14
  archiver = null;
15
15
  archiveThreshold = null;
16
+ graphPopulator = null;
16
17
  constructor(store, embedder, indexManager, searchEngine) {
17
18
  this.store = store;
18
19
  this.embedder = embedder ?? null;
@@ -24,6 +25,10 @@ export class BlackboardEngine {
24
25
  this.archiver = archiver;
25
26
  this.archiveThreshold = config.archive.max_blackboard_entries_before_archive;
26
27
  }
28
+ /** Inject graph auto-populator for relation extraction from posts. */
29
+ setGraphPopulator(populator) {
30
+ this.graphPopulator = populator;
31
+ }
27
32
  /** Post a new blackboard entry with validation and defaults. */
28
33
  async post(input) {
29
34
  // Validate entry_type
@@ -50,6 +55,10 @@ export class BlackboardEngine {
50
55
  relates_to: input.relates_to,
51
56
  agent_id: input.agent_id ?? "main",
52
57
  });
58
+ // Auto-populate graph with scope/relation entities (best-effort)
59
+ if (this.graphPopulator) {
60
+ await this.graphPopulator.onPost(entry);
61
+ }
53
62
  // Generate embedding (Phase 2) — never let embedding failure prevent the post
54
63
  if (this.embedder && this.indexManager) {
55
64
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"blackboard.js","sourceRoot":"","sources":["../../src/engine/blackboard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blackboard.js","sourceRoot":"","sources":["../../src/engine/blackboard.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,19 @@ export declare class ContextAssembler {
30
30
  * Implements spec section 4.3 (twining_assemble).
31
31
  */
32
32
  assemble(task: string, scope: string, maxTokens?: number, agentId?: string): Promise<AssembledContext>;
33
+ /**
34
+ * Assemble context and include a status summary inline.
35
+ * Combines assemble + summarize into one call (P5.1).
36
+ */
37
+ assembleWithStatus(task: string, scope: string, maxTokens?: number, agentId?: string): Promise<{
38
+ context: AssembledContext;
39
+ status_summary: string;
40
+ }>;
41
+ /**
42
+ * Format assembled context as structured markdown for LLM consumption.
43
+ * Produces imperative sentences and numbered lists instead of raw JSON.
44
+ */
45
+ static formatForLLM(ctx: AssembledContext, statusSummary?: string): string;
33
46
  /**
34
47
  * High-level summary of project or scope state.
35
48
  * Implements spec section 4.3 (twining_summarize).
@@ -45,7 +58,7 @@ export declare class ContextAssembler {
45
58
  * Decisions whose affected_files/symbols have more graph relations
46
59
  * to the scope get a higher boost (0.0 to 1.0).
47
60
  */
48
- private computeGraphConnectivity;
61
+ private computeGraphReachability;
49
62
  /**
50
63
  * Populate related_entities from the knowledge graph.
51
64
  * Finds entities matching the scope and gets their immediate neighbors.
@@ -1 +1 @@
1
- {"version":3,"file":"context-assembler.d.ts","sourceRoot":"","sources":["../../src/engine/context-assembler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EACV,gBAAgB,EAGhB,eAAe,EACf,aAAa,EACb,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAkB3B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAE/C,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;gBAGvD,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,MAAM,EAAE,aAAa,EACrB,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,EAChC,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,EACtC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,EAClC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IAYhC,0EAA0E;IAC1E,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACG,QAAQ,CACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,CAAC;IAqW5B;;;OAGG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA8EzD;;;OAGG;IACG,WAAW,CACf,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;IAuD7B;;;;OAIG;YACW,wBAAwB;IA4DtC;;;;OAIG;YACW,kBAAkB;IA2ChC,qDAAqD;IACrD,OAAO,CAAC,YAAY;IAMpB,sDAAsD;IACtD,OAAO,CAAC,eAAe;CAYxB"}
1
+ {"version":3,"file":"context-assembler.d.ts","sourceRoot":"","sources":["../../src/engine/context-assembler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EACV,gBAAgB,EAGhB,eAAe,EACf,aAAa,EACb,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAkB3B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAE/C,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;gBAGvD,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,MAAM,EAAE,aAAa,EACrB,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,EAChC,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,EACtC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,EAClC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IAYhC,0EAA0E;IAC1E,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C;;;OAGG;IACG,QAAQ,CACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,CAAC;IAqX5B;;;OAGG;IACG,kBAAkB,CACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAajE;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM;IAmH1E;;;OAGG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA8EzD;;;OAGG;IACG,WAAW,CACf,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;IAuD7B;;;;OAIG;YACW,wBAAwB;IAsHtC;;;;OAIG;YACW,kBAAkB;IA2ChC,qDAAqD;IACrD,OAAO,CAAC,YAAY;IAMpB,sDAAsD;IACtD,OAAO,CAAC,eAAe;CAYxB"}
@@ -103,22 +103,29 @@ export class ContextAssembler {
103
103
  }
104
104
  }
105
105
  }
106
- // 5. Compute graph connectivity scores for decisions (spec §5.5)
107
- const connectivityScores = await this.computeGraphConnectivity(scope, mergedDecisionMap);
106
+ // 5. Compute graph reachability scores for decisions
107
+ const { scores: reachabilityScores, paths: reachabilityPaths } = await this.computeGraphReachability(scope, mergedDecisionMap);
108
108
  // 6. Score each item
109
109
  const scoredItems = [];
110
- const graphWeight = weights.graph_connectivity ?? 0;
110
+ const graphWeight = weights.graph_reachability ?? weights.graph_connectivity ?? 0;
111
+ // Adaptive weight fallback: if graph returns 0 for all candidates,
112
+ // redistribute graph weight proportionally to other signals
113
+ const maxGraphScore = Math.max(0, ...Array.from(reachabilityScores.values()));
114
+ const effectiveGraphWeight = maxGraphScore === 0 ? 0 : graphWeight;
115
+ const weightScale = maxGraphScore === 0 && graphWeight > 0
116
+ ? 1.0 / (1.0 - graphWeight)
117
+ : 1.0;
111
118
  for (const [id, decision] of mergedDecisionMap) {
112
119
  const recency = this.recencyScore(decision.timestamp, now);
113
120
  const relevance = decisionRelevance.get(id) ?? 0.5;
114
121
  const confidence = this.confidenceScore(decision.confidence);
115
122
  const warningBoost = 0;
116
- const connectivity = connectivityScores.get(id) ?? 0;
117
- const score = recency * weights.recency +
118
- relevance * weights.relevance +
119
- confidence * weights.decision_confidence +
120
- warningBoost * weights.warning_boost +
121
- connectivity * graphWeight;
123
+ const reachability = reachabilityScores.get(id) ?? 0;
124
+ const score = recency * weights.recency * weightScale +
125
+ relevance * weights.relevance * weightScale +
126
+ confidence * weights.decision_confidence * weightScale +
127
+ warningBoost * weights.warning_boost * weightScale +
128
+ reachability * effectiveGraphWeight;
122
129
  const text = `${decision.summary} ${decision.rationale} ${decision.confidence} ${decision.affected_files.join(", ")}`;
123
130
  scoredItems.push({
124
131
  type: "decision",
@@ -133,10 +140,10 @@ export class ContextAssembler {
133
140
  const relevance = entryRelevance.get(id) ?? 0.5;
134
141
  const confidence = 0.5; // Neutral for non-decisions
135
142
  const warningBoost = entry.entry_type === "warning" ? 1.0 : 0.0;
136
- const score = recency * weights.recency +
137
- relevance * weights.relevance +
138
- confidence * weights.decision_confidence +
139
- warningBoost * weights.warning_boost;
143
+ const score = recency * weights.recency * weightScale +
144
+ relevance * weights.relevance * weightScale +
145
+ confidence * weights.decision_confidence * weightScale +
146
+ warningBoost * weights.warning_boost * weightScale;
140
147
  const text = `${entry.summary} ${entry.detail}`;
141
148
  scoredItems.push({
142
149
  type: entry.entry_type,
@@ -196,13 +203,18 @@ export class ContextAssembler {
196
203
  continue;
197
204
  if (item.type === "decision") {
198
205
  const d = item.data;
199
- activeDecisionResults.push({
206
+ const decisionEntry = {
200
207
  id: d.id,
201
208
  summary: d.summary,
202
209
  rationale: d.rationale,
203
210
  confidence: d.confidence,
204
211
  affected_files: d.affected_files,
205
- });
212
+ };
213
+ const path = reachabilityPaths.get(d.id);
214
+ if (path) {
215
+ decisionEntry.relevance_path = path;
216
+ }
217
+ activeDecisionResults.push(decisionEntry);
206
218
  }
207
219
  else {
208
220
  const e = item.data;
@@ -300,7 +312,9 @@ export class ContextAssembler {
300
312
  if (this.handoffStore) {
301
313
  const handoffEntries = await this.handoffStore.list({ scope, limit: 5 });
302
314
  if (handoffEntries.length > 0) {
303
- result.recent_handoffs = handoffEntries.map((h) => ({
315
+ // Load full records to get individual results for detailed checklist
316
+ const fullHandoffs = await Promise.all(handoffEntries.map((h) => this.handoffStore.get(h.id)));
317
+ result.recent_handoffs = handoffEntries.map((h, i) => ({
304
318
  id: h.id,
305
319
  source_agent: h.source_agent,
306
320
  target_agent: h.target_agent ?? "",
@@ -309,6 +323,7 @@ export class ContextAssembler {
309
323
  result_status: h.result_status,
310
324
  acknowledged: h.acknowledged,
311
325
  created_at: h.created_at,
326
+ results: fullHandoffs[i]?.results,
312
327
  }));
313
328
  }
314
329
  }
@@ -340,6 +355,125 @@ export class ContextAssembler {
340
355
  this.assemblyLog.set(agentId ?? "main", result.assembled_at);
341
356
  return result;
342
357
  }
358
+ /**
359
+ * Assemble context and include a status summary inline.
360
+ * Combines assemble + summarize into one call (P5.1).
361
+ */
362
+ async assembleWithStatus(task, scope, maxTokens, agentId) {
363
+ const context = await this.assemble(task, scope, maxTokens, agentId);
364
+ // Status summary is best-effort — don't let it break assembly
365
+ let statusSummary = "";
366
+ try {
367
+ const summary = await this.summarize(scope);
368
+ statusSummary = summary.recent_activity_summary;
369
+ }
370
+ catch {
371
+ // Non-fatal: skip status summary if summarize fails
372
+ }
373
+ return { context, status_summary: statusSummary };
374
+ }
375
+ /**
376
+ * Format assembled context as structured markdown for LLM consumption.
377
+ * Produces imperative sentences and numbered lists instead of raw JSON.
378
+ */
379
+ static formatForLLM(ctx, statusSummary) {
380
+ // Short-circuit: if there's nothing useful to say, return minimal output
381
+ const hasContent = ctx.active_warnings.length > 0 ||
382
+ ctx.active_decisions.length > 0 ||
383
+ ctx.recent_findings.length > 0 ||
384
+ ctx.open_needs.length > 0 ||
385
+ ctx.recent_questions.length > 0 ||
386
+ (ctx.recent_handoffs && ctx.recent_handoffs.length > 0) ||
387
+ ctx.planning_state != null;
388
+ if (!hasContent) {
389
+ return `No prior context for scope: ${ctx.scope}`;
390
+ }
391
+ const sections = [];
392
+ // Header
393
+ sections.push(`## Before You Start (scope: ${ctx.scope})`);
394
+ // 1. Warnings FIRST — "what not to do" is the most actionable signal
395
+ if (ctx.active_warnings.length > 0) {
396
+ sections.push("\n### STOP — READ THESE WARNINGS");
397
+ for (const w of ctx.active_warnings) {
398
+ sections.push(`- **${w.summary}**${w.detail ? `\n ${w.detail}` : ""}`);
399
+ }
400
+ }
401
+ // 2. Continue from (handoffs) — second priority, provides continuation context
402
+ if (ctx.recent_handoffs && ctx.recent_handoffs.length > 0) {
403
+ sections.push("\n### CONTINUE FROM PREVIOUS WORK");
404
+ for (const h of ctx.recent_handoffs) {
405
+ const status = h.acknowledged ? "acknowledged" : "pending";
406
+ sections.push(`**${h.source_agent} → ${h.target_agent || "any"}** (${status}): ${h.summary}`);
407
+ // Detailed checklist of individual results
408
+ if (h.results && h.results.length > 0) {
409
+ for (const r of h.results) {
410
+ const icon = r.status === "completed" ? "[x]" : r.status === "blocked" ? "[BLOCKED]" : "[ ]";
411
+ sections.push(` - ${icon} ${r.description}${r.notes ? ` — ${r.notes}` : ""}`);
412
+ }
413
+ }
414
+ }
415
+ }
416
+ // 3. Decisions — imperative framing with files prominent
417
+ if (ctx.active_decisions.length > 0) {
418
+ sections.push("\n### DECISIONS TO RESPECT");
419
+ for (let i = 0; i < ctx.active_decisions.length; i++) {
420
+ const d = ctx.active_decisions[i];
421
+ const files = d.affected_files?.length > 0 ? `\n Files: ${d.affected_files.join(", ")}` : "";
422
+ sections.push(`${i + 1}. **${d.summary}** (${d.confidence})${files}`);
423
+ sections.push(` Why: ${d.rationale}`);
424
+ }
425
+ }
426
+ else {
427
+ sections.push("\nNo active decisions for this scope.");
428
+ }
429
+ // 4. Open needs — what still needs to be done
430
+ if (ctx.open_needs.length > 0) {
431
+ sections.push("\n### REMAINING WORK");
432
+ for (const n of ctx.open_needs) {
433
+ sections.push(`- [ ] ${n.summary}`);
434
+ }
435
+ }
436
+ // 5. Recent findings (brief, only if not redundant with decisions)
437
+ if (ctx.recent_findings.length > 0) {
438
+ // Filter out findings whose summary is substantially duplicated by a decision
439
+ const decisionSummaries = ctx.active_decisions.map((d) => d.summary.toLowerCase());
440
+ const uniqueFindings = ctx.recent_findings.filter((f) => {
441
+ const fLower = f.summary.toLowerCase();
442
+ return !decisionSummaries.some((ds) => ds.includes(fLower.slice(0, 30)) || fLower.includes(ds.slice(0, 30)));
443
+ });
444
+ if (uniqueFindings.length > 0) {
445
+ sections.push("\n### FINDINGS");
446
+ for (const f of uniqueFindings) {
447
+ sections.push(`- ${f.summary}`);
448
+ }
449
+ }
450
+ }
451
+ // 6. Quick reference section — compact metadata
452
+ const quickRef = ["\n---"];
453
+ // Status summary (P5.1)
454
+ if (statusSummary) {
455
+ quickRef.push(`Status: ${statusSummary}`);
456
+ }
457
+ // Questions
458
+ if (ctx.recent_questions.length > 0) {
459
+ quickRef.push(`Open questions: ${ctx.recent_questions.map((q) => q.summary).join("; ")}`);
460
+ }
461
+ // Planning state
462
+ if (ctx.planning_state) {
463
+ quickRef.push(`Planning: Phase ${ctx.planning_state.current_phase}, ${ctx.planning_state.progress}`);
464
+ if (ctx.planning_state.blockers.length > 0) {
465
+ quickRef.push(`Blockers: ${ctx.planning_state.blockers.join("; ")}`);
466
+ }
467
+ }
468
+ // Suggested agents
469
+ if (ctx.suggested_agents && ctx.suggested_agents.length > 0) {
470
+ quickRef.push(`Suggested agents: ${ctx.suggested_agents.map((a) => `${a.agent_id} (${a.capabilities.join(", ")})`).join("; ")}`);
471
+ }
472
+ if (quickRef.length > 1) {
473
+ sections.push(quickRef.join("\n"));
474
+ }
475
+ return sections.join("\n");
476
+ }
343
477
  /**
344
478
  * High-level summary of project or scope state.
345
479
  * Implements spec section 4.3 (twining_summarize).
@@ -443,45 +577,91 @@ export class ContextAssembler {
443
577
  * Decisions whose affected_files/symbols have more graph relations
444
578
  * to the scope get a higher boost (0.0 to 1.0).
445
579
  */
446
- async computeGraphConnectivity(scope, decisions) {
580
+ async computeGraphReachability(scope, decisions) {
447
581
  const scores = new Map();
582
+ const paths = new Map();
448
583
  if (!this.graphEngine)
449
- return scores;
584
+ return { scores, paths };
450
585
  try {
451
- // Find entities matching the scope
586
+ // 1. Find entities matching scope, filtered to scope prefix
452
587
  const { entities: scopeEntities } = await this.graphEngine.query(scope, undefined, 20);
453
- if (scopeEntities.length === 0)
454
- return scores;
455
- const scopeEntityIds = new Set(scopeEntities.map((e) => e.id));
456
- const scopeEntityNames = new Set(scopeEntities.map((e) => e.name));
457
- for (const [id, decision] of decisions) {
458
- let connectionCount = 0;
459
- const targets = [
460
- ...decision.affected_files,
461
- ...decision.affected_symbols,
462
- ];
463
- for (const target of targets) {
464
- const { entities: targetEntities } = await this.graphEngine.query(target, undefined, 3);
465
- for (const te of targetEntities) {
466
- const { neighbors } = await this.graphEngine.neighbors(te.id, 1);
467
- for (const n of neighbors) {
468
- if (scopeEntityIds.has(n.entity.id) ||
469
- scopeEntityNames.has(n.entity.name)) {
470
- connectionCount++;
588
+ const filteredScopeEntities = scopeEntities.filter((e) => e.name.startsWith(scope) || scope.startsWith(e.name));
589
+ if (filteredScopeEntities.length === 0)
590
+ return { scores, paths };
591
+ // Relation types to follow during BFS
592
+ const followTypes = [
593
+ "depends_on", "decided_by", "implements", "affects", "produces", "challenged",
594
+ ];
595
+ // Relation type scoring bonuses
596
+ const relationBonus = {
597
+ decided_by: 1.0,
598
+ affects: 1.0,
599
+ depends_on: 0.8,
600
+ implements: 0.8,
601
+ produces: 0.7,
602
+ challenged: 0.7,
603
+ supersedes: 0.6,
604
+ tested_by: 0.6,
605
+ related_to: 0.5,
606
+ };
607
+ // Path length scoring (indexed by hop count - 1)
608
+ const depthScore = [1.0, 0.7, 0.4];
609
+ // Build set of decision IDs for quick lookup
610
+ const decisionIds = new Set(decisions.keys());
611
+ // 2. For each scope entity, do typed BFS traversal (max depth 3)
612
+ for (const scopeEntity of filteredScopeEntities) {
613
+ try {
614
+ const { neighbors } = await this.graphEngine.neighbors(scopeEntity.id, 3, followTypes);
615
+ for (const neighbor of neighbors) {
616
+ // Check if this neighbor is a decision concept node
617
+ if (neighbor.entity.type === "concept" && decisionIds.has(neighbor.entity.name)) {
618
+ const decisionId = neighbor.entity.name;
619
+ // Determine hop depth from relation (approximate: use relation type)
620
+ // Since neighbors() returns flat results, we estimate depth from the BFS order
621
+ // The relation tells us the connection type
622
+ const relType = neighbor.relation.type;
623
+ const bonus = relationBonus[relType] ?? 0.5;
624
+ // Use depth 0 (1-hop) as default since neighbors are direct or via BFS
625
+ const pathScore = depthScore[0] * bonus;
626
+ // Keep max score across all paths
627
+ const existing = scores.get(decisionId) ?? 0;
628
+ if (pathScore > existing) {
629
+ scores.set(decisionId, pathScore);
630
+ paths.set(decisionId, `${scopeEntity.name} → ${relType} → ${neighbor.entity.name}`);
631
+ }
632
+ }
633
+ // Also check if the neighbor's name matches affected_files of any decision
634
+ for (const [decisionId, decision] of decisions) {
635
+ if (decision.affected_files.includes(neighbor.entity.name) ||
636
+ decision.affected_symbols.includes(neighbor.entity.name)) {
637
+ const relType = neighbor.relation.type;
638
+ const bonus = relationBonus[relType] ?? 0.5;
639
+ const pathScore = depthScore[1] * bonus; // 2-hop equivalent
640
+ const existing = scores.get(decisionId) ?? 0;
641
+ if (pathScore > existing) {
642
+ scores.set(decisionId, pathScore);
643
+ paths.set(decisionId, `${scopeEntity.name} → ${relType} → ${neighbor.entity.name} → decided_by → ${decisionId}`);
644
+ }
471
645
  }
472
646
  }
473
647
  }
474
648
  }
475
- // Normalize: cap at 1.0, use log scale to avoid domination
476
- scores.set(id, connectionCount > 0
477
- ? Math.min(1.0, Math.log2(connectionCount + 1) / 3)
478
- : 0);
649
+ catch {
650
+ // Skip entities that fail to traverse
651
+ }
652
+ }
653
+ // 3. Normalize scores to 0.0-1.0
654
+ const maxScore = Math.max(0, ...Array.from(scores.values()));
655
+ if (maxScore > 0 && maxScore !== 1.0) {
656
+ for (const [id, score] of scores) {
657
+ scores.set(id, score / maxScore);
658
+ }
479
659
  }
480
660
  }
481
661
  catch {
482
662
  // Graph errors should never break scoring
483
663
  }
484
- return scores;
664
+ return { scores, paths };
485
665
  }
486
666
  /**
487
667
  * Populate related_entities from the knowledge graph.