@kognai/orchestrator-core 0.1.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 (144) hide show
  1. package/README.md +44 -0
  2. package/dist/index.d.ts +63 -0
  3. package/dist/index.js +175 -0
  4. package/dist/lib/aar-middleware.d.ts +6 -0
  5. package/dist/lib/aar-middleware.js +70 -0
  6. package/dist/lib/aar-types.d.ts +34 -0
  7. package/dist/lib/aar-types.js +4 -0
  8. package/dist/lib/acp-engine.d.ts +68 -0
  9. package/dist/lib/acp-engine.js +123 -0
  10. package/dist/lib/acp.d.ts +61 -0
  11. package/dist/lib/acp.js +425 -0
  12. package/dist/lib/agent-registry.d.ts +50 -0
  13. package/dist/lib/agent-registry.js +137 -0
  14. package/dist/lib/anthropic-direct.d.ts +27 -0
  15. package/dist/lib/anthropic-direct.js +109 -0
  16. package/dist/lib/asmr-extractor.d.ts +40 -0
  17. package/dist/lib/asmr-extractor.js +151 -0
  18. package/dist/lib/asmr-retrieval.d.ts +76 -0
  19. package/dist/lib/asmr-retrieval.js +311 -0
  20. package/dist/lib/asmr.d.ts +8 -0
  21. package/dist/lib/asmr.js +24 -0
  22. package/dist/lib/brainx-client.d.ts +72 -0
  23. package/dist/lib/brainx-client.js +200 -0
  24. package/dist/lib/brainx-embed.d.ts +14 -0
  25. package/dist/lib/brainx-embed.js +139 -0
  26. package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
  27. package/dist/lib/brainx-swarm-bridge.js +242 -0
  28. package/dist/lib/byterover-client.d.ts +19 -0
  29. package/dist/lib/byterover-client.js +59 -0
  30. package/dist/lib/ceo-wallet.d.ts +37 -0
  31. package/dist/lib/ceo-wallet.js +176 -0
  32. package/dist/lib/chomsky-gate.d.ts +24 -0
  33. package/dist/lib/chomsky-gate.js +178 -0
  34. package/dist/lib/chomsky-runner.d.ts +29 -0
  35. package/dist/lib/chomsky-runner.js +157 -0
  36. package/dist/lib/citizen-score-contract.d.ts +72 -0
  37. package/dist/lib/citizen-score-contract.js +16 -0
  38. package/dist/lib/citizen-score-registry.d.ts +25 -0
  39. package/dist/lib/citizen-score-registry.js +65 -0
  40. package/dist/lib/citizenship.d.ts +103 -0
  41. package/dist/lib/citizenship.js +272 -0
  42. package/dist/lib/clawrouter-client.d.ts +37 -0
  43. package/dist/lib/clawrouter-client.js +148 -0
  44. package/dist/lib/code-asset-crystalliser.d.ts +41 -0
  45. package/dist/lib/code-asset-crystalliser.js +181 -0
  46. package/dist/lib/code-failure-logger.d.ts +27 -0
  47. package/dist/lib/code-failure-logger.js +42 -0
  48. package/dist/lib/cto-approval-gate.d.ts +45 -0
  49. package/dist/lib/cto-approval-gate.js +478 -0
  50. package/dist/lib/cto-gate-types.d.ts +28 -0
  51. package/dist/lib/cto-gate-types.js +8 -0
  52. package/dist/lib/decomposer-feedback.d.ts +54 -0
  53. package/dist/lib/decomposer-feedback.js +115 -0
  54. package/dist/lib/emotional-safety-gate.d.ts +48 -0
  55. package/dist/lib/emotional-safety-gate.js +97 -0
  56. package/dist/lib/engine-paths.d.ts +13 -0
  57. package/dist/lib/engine-paths.js +32 -0
  58. package/dist/lib/event-bus-listener.d.ts +8 -0
  59. package/dist/lib/event-bus-listener.js +144 -0
  60. package/dist/lib/event-bus-publisher.d.ts +25 -0
  61. package/dist/lib/event-bus-publisher.js +188 -0
  62. package/dist/lib/event-bus-types.d.ts +73 -0
  63. package/dist/lib/event-bus-types.js +23 -0
  64. package/dist/lib/failure-library.d.ts +178 -0
  65. package/dist/lib/failure-library.js +349 -0
  66. package/dist/lib/ksl/error-log.d.ts +28 -0
  67. package/dist/lib/ksl/error-log.js +43 -0
  68. package/dist/lib/ksl/index.d.ts +9 -0
  69. package/dist/lib/ksl/index.js +25 -0
  70. package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
  71. package/dist/lib/ksl/orchestrator-tap.js +85 -0
  72. package/dist/lib/ksl/record-writer.d.ts +46 -0
  73. package/dist/lib/ksl/record-writer.js +45 -0
  74. package/dist/lib/llm-cost-table.d.ts +36 -0
  75. package/dist/lib/llm-cost-table.js +90 -0
  76. package/dist/lib/local-model-router.d.ts +27 -0
  77. package/dist/lib/local-model-router.js +61 -0
  78. package/dist/lib/mc-client.d.ts +51 -0
  79. package/dist/lib/mc-client.js +249 -0
  80. package/dist/lib/model-router-contract.d.ts +91 -0
  81. package/dist/lib/model-router-contract.js +19 -0
  82. package/dist/lib/model-router-registry.d.ts +24 -0
  83. package/dist/lib/model-router-registry.js +52 -0
  84. package/dist/lib/model-router.d.ts +20 -0
  85. package/dist/lib/model-router.js +79 -0
  86. package/dist/lib/monotask-state-machine.d.ts +19 -0
  87. package/dist/lib/monotask-state-machine.js +131 -0
  88. package/dist/lib/neutral-prompt-checker.d.ts +22 -0
  89. package/dist/lib/neutral-prompt-checker.js +130 -0
  90. package/dist/lib/notion-direct.d.ts +92 -0
  91. package/dist/lib/notion-direct.js +381 -0
  92. package/dist/lib/ollama-client.d.ts +37 -0
  93. package/dist/lib/ollama-client.js +158 -0
  94. package/dist/lib/omel/credential-vault.d.ts +57 -0
  95. package/dist/lib/omel/credential-vault.js +324 -0
  96. package/dist/lib/omel/human-brake.d.ts +32 -0
  97. package/dist/lib/omel/human-brake.js +289 -0
  98. package/dist/lib/omel/index.d.ts +10 -0
  99. package/dist/lib/omel/index.js +26 -0
  100. package/dist/lib/omel/phantom-workspace.d.ts +31 -0
  101. package/dist/lib/omel/phantom-workspace.js +256 -0
  102. package/dist/lib/omel/wipe-witness.d.ts +75 -0
  103. package/dist/lib/omel/wipe-witness.js +398 -0
  104. package/dist/lib/orchestrate-engine.d.ts +25 -0
  105. package/dist/lib/orchestrate-engine.js +4436 -0
  106. package/dist/lib/perm-judge.d.ts +46 -0
  107. package/dist/lib/perm-judge.js +173 -0
  108. package/dist/lib/plumber/conformance.d.ts +54 -0
  109. package/dist/lib/plumber/conformance.js +121 -0
  110. package/dist/lib/plumber/index.d.ts +9 -0
  111. package/dist/lib/plumber/index.js +25 -0
  112. package/dist/lib/plumber/observer.d.ts +52 -0
  113. package/dist/lib/plumber/observer.js +180 -0
  114. package/dist/lib/plumber/types.d.ts +78 -0
  115. package/dist/lib/plumber/types.js +29 -0
  116. package/dist/lib/research-impl-gate.d.ts +16 -0
  117. package/dist/lib/research-impl-gate.js +105 -0
  118. package/dist/lib/sherlock-memory.d.ts +29 -0
  119. package/dist/lib/sherlock-memory.js +105 -0
  120. package/dist/lib/skill-crystalliser.d.ts +44 -0
  121. package/dist/lib/skill-crystalliser.js +60 -0
  122. package/dist/lib/sprint-runner-engine.d.ts +27 -0
  123. package/dist/lib/sprint-runner-engine.js +1042 -0
  124. package/dist/lib/sprint-state.d.ts +71 -0
  125. package/dist/lib/sprint-state.js +202 -0
  126. package/dist/lib/stuck-handler.d.ts +17 -0
  127. package/dist/lib/stuck-handler.js +249 -0
  128. package/dist/lib/task-contract-checker.d.ts +17 -0
  129. package/dist/lib/task-contract-checker.js +29 -0
  130. package/dist/lib/task-router/index.d.ts +17 -0
  131. package/dist/lib/task-router/index.js +52 -0
  132. package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
  133. package/dist/lib/task-router/router/generate-execution-id.js +24 -0
  134. package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
  135. package/dist/lib/task-router/router/resolve-route.js +49 -0
  136. package/dist/lib/task-router/types.d.ts +79 -0
  137. package/dist/lib/task-router/types.js +39 -0
  138. package/dist/lib/token-budget-validator.d.ts +44 -0
  139. package/dist/lib/token-budget-validator.js +84 -0
  140. package/dist/lib/trust-score-updater.d.ts +30 -0
  141. package/dist/lib/trust-score-updater.js +107 -0
  142. package/dist/lib/wallet-state.d.ts +26 -0
  143. package/dist/lib/wallet-state.js +85 -0
  144. package/package.json +27 -0
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * model-router-registry.ts — TICKET-215 Wave C: the injection point for the
4
+ * model-routing seam (see model-router-contract.ts).
5
+ *
6
+ * Products wire their router at startup:
7
+ * - Kognai: setModelRouter(<viem-backed ClawRouter v2 adapter>)
8
+ * - others: inject a local/product-specific ModelRouter, or leave the
9
+ * fail-loud default in place until one is provided.
10
+ *
11
+ * The default is intentionally NOT a silent no-op: a routeCall with no router
12
+ * configured throws a clear, actionable error rather than returning empty
13
+ * completions. Replace with a real local-only default when one exists.
14
+ *
15
+ * Per-agent gating note: an injected router MAY consult the ACP capability
16
+ * registry (see ./acp) before performing a paid/on-chain call, so that only
17
+ * capability-bearing agents can spend — everyone else falls back to local.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.setModelRouter = setModelRouter;
21
+ exports.getModelRouter = getModelRouter;
22
+ exports.isModelRouterConfigured = isModelRouterConfigured;
23
+ const NOT_CONFIGURED = 'No ModelRouter registered. Call setModelRouter(...) at startup ' +
24
+ '(Kognai injects its viem-backed ClawRouter; other products inject their own ' +
25
+ 'or a local router). See @kognai/orchestrator-core model-router-contract.ts.';
26
+ class UnconfiguredModelRouter {
27
+ routeCall(_req) {
28
+ throw new Error(NOT_CONFIGURED);
29
+ }
30
+ callLLM(_prompt, _opts) {
31
+ throw new Error(NOT_CONFIGURED);
32
+ }
33
+ healthCheck() {
34
+ throw new Error(NOT_CONFIGURED);
35
+ }
36
+ getDailyCostDigest() {
37
+ throw new Error(NOT_CONFIGURED);
38
+ }
39
+ }
40
+ let _router = new UnconfiguredModelRouter();
41
+ /** Inject the concrete router. Call once at process startup, before routing. */
42
+ function setModelRouter(router) {
43
+ _router = router;
44
+ }
45
+ /** Resolve the active router. The orchestrator routes every LLM call through this. */
46
+ function getModelRouter() {
47
+ return _router;
48
+ }
49
+ /** True once a real router has been injected (i.e. not the fail-loud default). */
50
+ function isModelRouterConfigured() {
51
+ return !(_router instanceof UnconfiguredModelRouter);
52
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * model-router.ts — Expertise routing matrix for ClawRouter model selection
3
+ *
4
+ * Maps task types to specialist models. Includes 3 Kognai-specific types
5
+ * beyond the Invoica base (refactor-complex, agent-framework, codebase-scan).
6
+ */
7
+ export type TaskType = 'code' | 'reason' | 'lang' | 'util' | 'audit' | 'content' | 'data' | 'refactor-complex' | 'agent-framework' | 'codebase-scan';
8
+ interface ModelRoute {
9
+ primary: string;
10
+ fallback: string;
11
+ }
12
+ declare const EXPERTISE_MODELS: Record<TaskType, ModelRoute>;
13
+ export declare function classifyTask(prompt: string): TaskType;
14
+ export declare function selectModel(prompt: string, requestedModel?: string): {
15
+ model: string;
16
+ taskType: TaskType;
17
+ autoClassified: boolean;
18
+ };
19
+ export declare function getFallbackModel(taskType: TaskType): string;
20
+ export { EXPERTISE_MODELS };
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * model-router.ts — Expertise routing matrix for ClawRouter model selection
4
+ *
5
+ * Maps task types to specialist models. Includes 3 Kognai-specific types
6
+ * beyond the Invoica base (refactor-complex, agent-framework, codebase-scan).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.EXPERTISE_MODELS = void 0;
10
+ exports.classifyTask = classifyTask;
11
+ exports.selectModel = selectModel;
12
+ exports.getFallbackModel = getFallbackModel;
13
+ const EXPERTISE_MODELS = {
14
+ // Base types (same as Invoica)
15
+ code: { primary: 'deepseek/deepseek-chat', fallback: 'anthropic/claude-haiku-4.5' },
16
+ reason: { primary: 'deepseek/deepseek-reasoner', fallback: 'anthropic/claude-sonnet-4.6' },
17
+ lang: { primary: 'anthropic/claude-haiku-4.5', fallback: 'google/gemini-2.5-flash' },
18
+ util: { primary: 'google/gemini-2.5-flash-lite', fallback: 'anthropic/claude-haiku-4.5' },
19
+ audit: { primary: 'anthropic/claude-sonnet-4.6', fallback: 'deepseek/deepseek-chat' },
20
+ content: { primary: 'anthropic/claude-haiku-4.5', fallback: 'google/gemini-2.5-flash' },
21
+ data: { primary: 'deepseek/deepseek-chat', fallback: 'anthropic/claude-haiku-4.5' },
22
+ // Kognai-specific extensions
23
+ 'refactor-complex': { primary: 'anthropic/claude-sonnet-4.6', fallback: 'deepseek/deepseek-reasoner' },
24
+ 'agent-framework': { primary: 'deepseek/deepseek-chat', fallback: 'anthropic/claude-haiku-4.5' },
25
+ 'codebase-scan': { primary: 'google/gemini-2.5-flash', fallback: 'deepseek/deepseek-chat' },
26
+ };
27
+ exports.EXPERTISE_MODELS = EXPERTISE_MODELS;
28
+ // ── Legacy Aliases ─────────────────────────────────────────────────────────────
29
+ const MODEL_ALIASES = {
30
+ 'MiniMax-M2.5': 'minimax/minimax-m2.5',
31
+ 'minimax-m2.5': 'minimax/minimax-m2.5',
32
+ 'minimax-m2.5-lightning': 'minimax/minimax-m2.5',
33
+ 'minimax': 'minimax/minimax-m2.5',
34
+ 'coding': 'deepseek/deepseek-chat',
35
+ 'claude-haiku-4-5': 'anthropic/claude-haiku-4.5',
36
+ 'claude-haiku-4.5': 'anthropic/claude-haiku-4.5',
37
+ 'claude-sonnet-4': 'anthropic/claude-sonnet-4.6',
38
+ 'claude-sonnet-4.6': 'anthropic/claude-sonnet-4.6',
39
+ 'claude-3-haiku-20240307': 'anthropic/claude-haiku-4.5',
40
+ 'claude-3-5-sonnet-20241022': 'anthropic/claude-sonnet-4.6',
41
+ 'claude-sonnet-4-20250514': 'anthropic/claude-sonnet-4.6',
42
+ };
43
+ // ── Task Classification ────────────────────────────────────────────────────────
44
+ const TASK_PATTERNS = [
45
+ { type: 'refactor-complex', keywords: /\b(refactor|restructure|migration|redesign|rewrite|overhaul)\b/i },
46
+ { type: 'agent-framework', keywords: /\b(agent|swarm|orchestrat|pipeline|workflow|framework\s+\w+)\b/i },
47
+ { type: 'codebase-scan', keywords: /\b(scan|codebase|search|index|inventory|dependency\s+audit|grep)\b/i },
48
+ { type: 'code', keywords: /\b(code|function|debug|implement|typescript|javascript|python|fix\s+bug|compile|syntax|class\s+\w+|import\s+|async\s+function|interface\s+\w+)\b/i },
49
+ { type: 'audit', keywords: /\b(security|audit|review|compliance|vulnerability|penetration|cve|owasp|exploit|threat|risk\s+assess)\b/i },
50
+ { type: 'data', keywords: /\b(query|sql|database|aggregate|join|select\s+|group\s+by|csv|dataframe|pandas|analytics|metrics)\b/i },
51
+ { type: 'lang', keywords: /\b(translate|translation|french|arabic|spanish|german|chinese|japanese|locali[sz]e|multilingual|i18n)\b/i },
52
+ { type: 'reason', keywords: /\b(why|explain|analy[sz]e|tradeoff|compare|evaluate|pros\s+and\s+cons|reasoning|think\s+through)\b/i },
53
+ { type: 'content', keywords: /\b(write|draft|compose|blog|caption|essay|article|tweet|post|newsletter|marketing|creative\s+writ)\b/i },
54
+ { type: 'util', keywords: /\b(classify|tag|format|parse|extract|convert|summarize|json|xml|regex|validate|clean|normalize)\b/i },
55
+ ];
56
+ function classifyTask(prompt) {
57
+ for (const { type, keywords } of TASK_PATTERNS) {
58
+ if (keywords.test(prompt))
59
+ return type;
60
+ }
61
+ return 'util';
62
+ }
63
+ function selectModel(prompt, requestedModel) {
64
+ if (requestedModel && requestedModel.includes('/')) {
65
+ return { model: requestedModel, taskType: classifyTask(prompt), autoClassified: false };
66
+ }
67
+ if (requestedModel && MODEL_ALIASES[requestedModel]) {
68
+ return { model: MODEL_ALIASES[requestedModel], taskType: classifyTask(prompt), autoClassified: false };
69
+ }
70
+ if (requestedModel && requestedModel in EXPERTISE_MODELS) {
71
+ const taskType = requestedModel;
72
+ return { model: EXPERTISE_MODELS[taskType].primary, taskType, autoClassified: false };
73
+ }
74
+ const taskType = classifyTask(prompt);
75
+ return { model: EXPERTISE_MODELS[taskType].primary, taskType, autoClassified: true };
76
+ }
77
+ function getFallbackModel(taskType) {
78
+ return EXPERTISE_MODELS[taskType].fallback;
79
+ }
@@ -0,0 +1,19 @@
1
+ export type MonotaskState = 'IDLE' | 'RESERVED' | 'ACTIVE';
2
+ export declare class MonotaskSM {
3
+ private static slots;
4
+ private static slot;
5
+ private static audit;
6
+ private static wipe;
7
+ /**
8
+ * Attempt to claim an agent for a task.
9
+ * Returns false if agent is already RESERVED or ACTIVE, or depth limit exceeded.
10
+ * @param depth Sub-agent chain depth (0 = top-level, 1 = first sub-agent, …)
11
+ */
12
+ static claim(agentId: string, taskId: string, depth?: number): boolean;
13
+ static start(agentId: string, taskId: string): boolean;
14
+ static complete(agentId: string, taskId: string): void;
15
+ static release(agentId: string, taskId: string, reason?: string): void;
16
+ static getState(agentId: string): MonotaskState;
17
+ static getDepth(agentId: string): number;
18
+ static isIdle(agentId: string): boolean;
19
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ // AMD-08 Monotask State Machine
3
+ // IDLE → RESERVED → ACTIVE → IDLE (atomic, audit-logged)
4
+ // Enforces: one task per agent at a time, sub-agent chain depth ≤ 3
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MonotaskSM = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ // ── Constants ────────────────────────────────────────────────────────────────
10
+ const MAX_DEPTH = 3;
11
+ const AUDIT_DIR = (0, path_1.join)(process.cwd(), 'logs', 'monotask');
12
+ const AUDIT_FILE = (0, path_1.join)(AUDIT_DIR, 'audit.jsonl');
13
+ // ── State Machine (Singleton) ────────────────────────────────────────────────
14
+ class MonotaskSM {
15
+ static slots = new Map();
16
+ // ── Internal helpers ──────────────────────────────────────────────────────
17
+ static slot(agentId) {
18
+ if (!this.slots.has(agentId)) {
19
+ this.slots.set(agentId, { state: 'IDLE', taskId: null, claimedAt: null, depth: 0 });
20
+ }
21
+ return this.slots.get(agentId);
22
+ }
23
+ static audit(entry) {
24
+ try {
25
+ (0, fs_1.mkdirSync)(AUDIT_DIR, { recursive: true });
26
+ (0, fs_1.appendFileSync)(AUDIT_FILE, JSON.stringify(entry) + '\n', 'utf-8');
27
+ }
28
+ catch {
29
+ process.stderr.write('[monotask] audit write failed\n');
30
+ }
31
+ }
32
+ static wipe(slot) {
33
+ // Context Wipe Protocol (AMD-08 §3.3): clear all slot state
34
+ slot.state = 'IDLE';
35
+ slot.taskId = null;
36
+ slot.claimedAt = null;
37
+ slot.depth = 0;
38
+ }
39
+ // ── Transition: IDLE → RESERVED ──────────────────────────────────────────
40
+ /**
41
+ * Attempt to claim an agent for a task.
42
+ * Returns false if agent is already RESERVED or ACTIVE, or depth limit exceeded.
43
+ * @param depth Sub-agent chain depth (0 = top-level, 1 = first sub-agent, …)
44
+ */
45
+ static claim(agentId, taskId, depth = 0) {
46
+ const s = this.slot(agentId);
47
+ const from = s.state;
48
+ if (from !== 'IDLE') {
49
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: 'claim_rejected',
50
+ from, to: from, depth, reason: `Agent already ${from} on task ${s.taskId ?? '?'}` });
51
+ process.stderr.write(`[monotask] CLAIM REJECTED — ${agentId} is ${from} (task: ${s.taskId})\n`);
52
+ return false;
53
+ }
54
+ if (depth > MAX_DEPTH) {
55
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: 'depth_exceeded',
56
+ from, to: from, depth, reason: `Depth ${depth} > MAX_DEPTH ${MAX_DEPTH}` });
57
+ process.stderr.write(`[monotask] DEPTH EXCEEDED — ${agentId} depth ${depth} > ${MAX_DEPTH}\n`);
58
+ return false;
59
+ }
60
+ s.state = 'RESERVED';
61
+ s.taskId = taskId;
62
+ s.claimedAt = new Date();
63
+ s.depth = depth;
64
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: 'IDLE→RESERVED',
65
+ from: 'IDLE', to: 'RESERVED', depth });
66
+ return true;
67
+ }
68
+ // ── Transition: RESERVED → ACTIVE ────────────────────────────────────────
69
+ static start(agentId, taskId) {
70
+ const s = this.slot(agentId);
71
+ const from = s.state;
72
+ if (from !== 'RESERVED') {
73
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: 'start_rejected',
74
+ from, to: from, reason: `Expected RESERVED, got ${from}` });
75
+ return false;
76
+ }
77
+ s.state = 'ACTIVE';
78
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: 'RESERVED→ACTIVE',
79
+ from: 'RESERVED', to: 'ACTIVE', depth: s.depth });
80
+ return true;
81
+ }
82
+ // ── Transition: ACTIVE → IDLE (successful completion) ────────────────────
83
+ static complete(agentId, taskId) {
84
+ const s = this.slot(agentId);
85
+ const from = s.state;
86
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: `${from}→IDLE`,
87
+ from, to: 'IDLE', depth: s.depth });
88
+ this.wipe(s);
89
+ }
90
+ // ── Transition: any → IDLE (error / rejection between retries) ───────────
91
+ static release(agentId, taskId, reason) {
92
+ const s = this.slot(agentId);
93
+ const from = s.state;
94
+ this.audit({ ts: new Date().toISOString(), agentId, taskId, transition: `${from}→IDLE(release)`,
95
+ from, to: 'IDLE', depth: s.depth, reason });
96
+ this.wipe(s);
97
+ }
98
+ // ── Queries ───────────────────────────────────────────────────────────────
99
+ static getState(agentId) {
100
+ return this.slot(agentId).state;
101
+ }
102
+ static getDepth(agentId) {
103
+ return this.slot(agentId).depth;
104
+ }
105
+ static isIdle(agentId) {
106
+ return this.slot(agentId).state === 'IDLE';
107
+ }
108
+ }
109
+ exports.MonotaskSM = MonotaskSM;
110
+ // ── Smoke test ────────────────────────────────────────────────────────────────
111
+ if (require.main === module) {
112
+ console.log('\n🤖 AMD-08 Monotask State Machine — Smoke Test\n');
113
+ const agent = 'test-agent';
114
+ // Happy path
115
+ console.assert(MonotaskSM.isIdle(agent), 'should start IDLE');
116
+ console.assert(MonotaskSM.claim(agent, 't1'), 'claim should succeed');
117
+ console.assert(MonotaskSM.getState(agent) === 'RESERVED', 'should be RESERVED');
118
+ console.assert(!MonotaskSM.claim(agent, 't2'), 'double-claim should fail');
119
+ console.assert(MonotaskSM.start(agent, 't1'), 'start should succeed');
120
+ console.assert(MonotaskSM.getState(agent) === 'ACTIVE', 'should be ACTIVE');
121
+ MonotaskSM.complete(agent, 't1');
122
+ console.assert(MonotaskSM.isIdle(agent), 'should be IDLE after complete');
123
+ // Depth guard
124
+ console.assert(MonotaskSM.claim('deep-agent', 'td', MAX_DEPTH + 1) === false, 'depth > MAX_DEPTH should fail');
125
+ // Release path
126
+ MonotaskSM.claim('r-agent', 'tr');
127
+ MonotaskSM.start('r-agent', 'tr');
128
+ MonotaskSM.release('r-agent', 'tr', 'rejection');
129
+ console.assert(MonotaskSM.isIdle('r-agent'), 'should be IDLE after release');
130
+ console.log('✅ PASS — audit log written to logs/monotask/audit.jsonl\n');
131
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * neutral-prompt-checker.ts — Sprint TICKET-005-RULE1
3
+ * Rule 1 (Neutral Prompts): detects answer-seeding in sprint descriptions.
4
+ *
5
+ * Answer-seeding = embedding specific implementation choices in a brief that
6
+ * should remain open-ended: code fences, import statements, copy-X patterns.
7
+ *
8
+ * Usage:
9
+ * npx ts-node scripts/governance/neutral-prompt-checker.ts --file=workspace/sprint-brief.md
10
+ * echo "description text" | npx ts-node scripts/governance/neutral-prompt-checker.ts
11
+ *
12
+ * Exit 0 = clean, Exit 1 = violations found
13
+ */
14
+ export interface NeutralCheckResult {
15
+ clean: boolean;
16
+ violations: Array<{
17
+ line: number;
18
+ pattern: string;
19
+ text: string;
20
+ }>;
21
+ }
22
+ export declare function checkText(text: string): NeutralCheckResult;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * neutral-prompt-checker.ts — Sprint TICKET-005-RULE1
4
+ * Rule 1 (Neutral Prompts): detects answer-seeding in sprint descriptions.
5
+ *
6
+ * Answer-seeding = embedding specific implementation choices in a brief that
7
+ * should remain open-ended: code fences, import statements, copy-X patterns.
8
+ *
9
+ * Usage:
10
+ * npx ts-node scripts/governance/neutral-prompt-checker.ts --file=workspace/sprint-brief.md
11
+ * echo "description text" | npx ts-node scripts/governance/neutral-prompt-checker.ts
12
+ *
13
+ * Exit 0 = clean, Exit 1 = violations found
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.checkText = checkText;
50
+ const fs = __importStar(require("fs"));
51
+ // Patterns that indicate answer-seeding
52
+ const SEEDING_RULES = [
53
+ {
54
+ pattern: 'code-fence',
55
+ regex: /^```/,
56
+ description: 'Code fence in brief — implementation details should not be pre-seeded',
57
+ },
58
+ {
59
+ pattern: 'import-statement',
60
+ regex: /^\s*(import |from ['"]|require\()/,
61
+ description: 'Import statement — specific module choices should not be pre-seeded',
62
+ },
63
+ {
64
+ pattern: 'code-statement',
65
+ regex: /^\s*(const |let |var |function |class |export |async function )\w+/,
66
+ description: 'Code statement — implementation should not be pre-seeded',
67
+ },
68
+ {
69
+ pattern: 'copy-pattern',
70
+ regex: /\b(copy from|same as Sprint|exactly like|same pattern|use the same code|port from)\b/i,
71
+ description: 'Copy-from instruction — should describe intent, not implementation',
72
+ },
73
+ {
74
+ pattern: 'hardcoded-identifier',
75
+ regex: /\b([a-z][a-zA-Z]{14,}|[a-z]+(_[a-z]+){4,})\b/,
76
+ description: 'Very long identifier — may indicate pre-seeded variable/function name',
77
+ },
78
+ ];
79
+ function checkText(text) {
80
+ const lines = text.split('\n');
81
+ const violations = [];
82
+ let inCodeFence = false;
83
+ for (let i = 0; i < lines.length; i++) {
84
+ const line = lines[i];
85
+ // Track code fence state
86
+ if (line.trim().startsWith('```')) {
87
+ inCodeFence = !inCodeFence;
88
+ // Flag the opening fence itself (but not closing)
89
+ if (inCodeFence) {
90
+ violations.push({ line: i + 1, pattern: 'code-fence', text: line.trim().slice(0, 60) });
91
+ }
92
+ continue;
93
+ }
94
+ // Skip lines inside code fences (already flagged by opening)
95
+ if (inCodeFence)
96
+ continue;
97
+ for (const rule of SEEDING_RULES) {
98
+ if (rule.pattern === 'code-fence')
99
+ continue; // handled above
100
+ if (rule.regex.test(line)) {
101
+ violations.push({ line: i + 1, pattern: rule.pattern, text: line.trim().slice(0, 80) });
102
+ break; // one violation per line
103
+ }
104
+ }
105
+ }
106
+ return { clean: violations.length === 0, violations };
107
+ }
108
+ // CLI entrypoint
109
+ if (require.main === module) {
110
+ let text = '';
111
+ const fileArg = process.argv.find(a => a.startsWith('--file='));
112
+ if (fileArg) {
113
+ text = fs.readFileSync(fileArg.split('=')[1], 'utf-8');
114
+ }
115
+ else {
116
+ text = fs.readFileSync('/dev/stdin', 'utf-8');
117
+ }
118
+ const result = checkText(text);
119
+ if (result.clean) {
120
+ console.log('✓ Rule 1 check PASS — no answer-seeding detected');
121
+ process.exit(0);
122
+ }
123
+ else {
124
+ console.error(`✗ Rule 1 check FAIL — ${result.violations.length} violation(s):`);
125
+ result.violations.forEach(v => {
126
+ console.error(` Line ${v.line} [${v.pattern}]: ${v.text}`);
127
+ });
128
+ process.exit(1);
129
+ }
130
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * notion-direct.ts — Zero-dep Notion API client for senior-coder.
3
+ *
4
+ * Bypasses both OpenClaw and the @notionhq/client npm package (which would be a
5
+ * new dep). Uses raw HTTPS to api.notion.com.
6
+ *
7
+ * Public surface — only what the bot actually needs:
8
+ * - upsertSprint(sprint) → push a sprint to Notion Sprints DB (create or update by Sprint ID)
9
+ * - updateSprintStatus(id, ...) → patch a sprint's status/outcome after completion
10
+ * - createBlocker(blocker) → push a blocker to Notion Blockers DB
11
+ * - archiveDailyBrief(date,...) → create a sub-page under the Daily Briefs parent
12
+ * - queryNewSprintsInNotion() → pull sprints with Status=pending that don't exist locally yet
13
+ *
14
+ * Fail-open: every function returns {ok: false, reason} on error rather than
15
+ * throwing, so the bot pipeline doesn't break when Notion is down or the
16
+ * integration loses access. Callers log the failure and continue.
17
+ *
18
+ * Created: 2026-04-29.
19
+ */
20
+ export interface NotionResult<T = unknown> {
21
+ ok: boolean;
22
+ reason?: string;
23
+ data?: T;
24
+ }
25
+ export interface SprintRow {
26
+ sprint_id: string;
27
+ title: string;
28
+ status: 'pending' | 'running' | 'done' | 'done-manual' | 'rejected' | 'loop-stuck' | 'skipped';
29
+ priority?: 'P0' | 'P1' | 'P2' | 'P3';
30
+ tasks_summary?: string;
31
+ authored_by?: 'human' | 'senior-coder' | 'swarm' | 'founder' | 'miner';
32
+ sprint_file_url?: string;
33
+ outcome?: string;
34
+ }
35
+ export interface BlockerRow {
36
+ title: string;
37
+ sprint_page_id?: string;
38
+ detected_at_iso: string;
39
+ pick_count_24h: number;
40
+ status: 'open' | 'awaiting-dev' | 'awaiting-founder' | 'resolved' | 'wont-fix';
41
+ diagnosis?: string;
42
+ resolution?: string;
43
+ }
44
+ /**
45
+ * Upsert a sprint row in Notion. Creates if not exists (by Sprint ID), updates if it does.
46
+ * Returns the page_id of the row in Notion (useful for setting Blocker.Sprint relation).
47
+ */
48
+ export declare function upsertSprint(s: SprintRow): Promise<NotionResult<{
49
+ page_id: string;
50
+ }>>;
51
+ /**
52
+ * Lighter-weight update: only patches status (+ optional outcome). Use after sprint completion.
53
+ */
54
+ export declare function updateSprintStatus(sprintId: string, status: SprintRow['status'], outcome?: string, tasksSummary?: string): Promise<NotionResult>;
55
+ export declare function createBlocker(b: BlockerRow): Promise<NotionResult<{
56
+ page_id: string;
57
+ }>>;
58
+ /**
59
+ * Create a sub-page under the Daily Briefs parent, titled by date, with the
60
+ * brief text as content. Notion pages support markdown-ish content via blocks;
61
+ * we use a single paragraph with the brief text (wrapped in code-block-style).
62
+ */
63
+ export declare function archiveDailyBrief(dateIso: string, briefText: string): Promise<NotionResult<{
64
+ page_id: string;
65
+ }>>;
66
+ export interface NotionAuthoredSprint {
67
+ page_id: string;
68
+ sprint_id: string;
69
+ title: string;
70
+ status: string;
71
+ priority?: string;
72
+ tasks_summary?: string;
73
+ authored_by?: string;
74
+ }
75
+ /**
76
+ * Fetch all sprint rows in Notion with Status=pending. Caller compares against
77
+ * local `workspace/sprints/` to find which ones to materialize as JSON files.
78
+ */
79
+ export declare function queryPendingSprintsFromNotion(): Promise<NotionResult<NotionAuthoredSprint[]>>;
80
+ /**
81
+ * Fetch ALL sprints in the Notion Sprints DB, return a Map of sprint_id → status.
82
+ * Used by sprint-runner.ts so Notion overrides local file state — protects against
83
+ * other Claude sessions reverting sprint files locally.
84
+ * Fail-safe: returns {ok: false} on Notion error so caller falls back to local-only.
85
+ */
86
+ export declare function getAllNotionSprintStatuses(): Promise<NotionResult<Map<string, string>>>;
87
+ export declare function notionHealthCheck(): Promise<NotionResult<{
88
+ bot_name: string;
89
+ sprints_db: boolean;
90
+ blockers_db: boolean;
91
+ briefs_page: boolean;
92
+ }>>;