@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.
- package/README.md +44 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +175 -0
- package/dist/lib/aar-middleware.d.ts +6 -0
- package/dist/lib/aar-middleware.js +70 -0
- package/dist/lib/aar-types.d.ts +34 -0
- package/dist/lib/aar-types.js +4 -0
- package/dist/lib/acp-engine.d.ts +68 -0
- package/dist/lib/acp-engine.js +123 -0
- package/dist/lib/acp.d.ts +61 -0
- package/dist/lib/acp.js +425 -0
- package/dist/lib/agent-registry.d.ts +50 -0
- package/dist/lib/agent-registry.js +137 -0
- package/dist/lib/anthropic-direct.d.ts +27 -0
- package/dist/lib/anthropic-direct.js +109 -0
- package/dist/lib/asmr-extractor.d.ts +40 -0
- package/dist/lib/asmr-extractor.js +151 -0
- package/dist/lib/asmr-retrieval.d.ts +76 -0
- package/dist/lib/asmr-retrieval.js +311 -0
- package/dist/lib/asmr.d.ts +8 -0
- package/dist/lib/asmr.js +24 -0
- package/dist/lib/brainx-client.d.ts +72 -0
- package/dist/lib/brainx-client.js +200 -0
- package/dist/lib/brainx-embed.d.ts +14 -0
- package/dist/lib/brainx-embed.js +139 -0
- package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
- package/dist/lib/brainx-swarm-bridge.js +242 -0
- package/dist/lib/byterover-client.d.ts +19 -0
- package/dist/lib/byterover-client.js +59 -0
- package/dist/lib/ceo-wallet.d.ts +37 -0
- package/dist/lib/ceo-wallet.js +176 -0
- package/dist/lib/chomsky-gate.d.ts +24 -0
- package/dist/lib/chomsky-gate.js +178 -0
- package/dist/lib/chomsky-runner.d.ts +29 -0
- package/dist/lib/chomsky-runner.js +157 -0
- package/dist/lib/citizen-score-contract.d.ts +72 -0
- package/dist/lib/citizen-score-contract.js +16 -0
- package/dist/lib/citizen-score-registry.d.ts +25 -0
- package/dist/lib/citizen-score-registry.js +65 -0
- package/dist/lib/citizenship.d.ts +103 -0
- package/dist/lib/citizenship.js +272 -0
- package/dist/lib/clawrouter-client.d.ts +37 -0
- package/dist/lib/clawrouter-client.js +148 -0
- package/dist/lib/code-asset-crystalliser.d.ts +41 -0
- package/dist/lib/code-asset-crystalliser.js +181 -0
- package/dist/lib/code-failure-logger.d.ts +27 -0
- package/dist/lib/code-failure-logger.js +42 -0
- package/dist/lib/cto-approval-gate.d.ts +45 -0
- package/dist/lib/cto-approval-gate.js +478 -0
- package/dist/lib/cto-gate-types.d.ts +28 -0
- package/dist/lib/cto-gate-types.js +8 -0
- package/dist/lib/decomposer-feedback.d.ts +54 -0
- package/dist/lib/decomposer-feedback.js +115 -0
- package/dist/lib/emotional-safety-gate.d.ts +48 -0
- package/dist/lib/emotional-safety-gate.js +97 -0
- package/dist/lib/engine-paths.d.ts +13 -0
- package/dist/lib/engine-paths.js +32 -0
- package/dist/lib/event-bus-listener.d.ts +8 -0
- package/dist/lib/event-bus-listener.js +144 -0
- package/dist/lib/event-bus-publisher.d.ts +25 -0
- package/dist/lib/event-bus-publisher.js +188 -0
- package/dist/lib/event-bus-types.d.ts +73 -0
- package/dist/lib/event-bus-types.js +23 -0
- package/dist/lib/failure-library.d.ts +178 -0
- package/dist/lib/failure-library.js +349 -0
- package/dist/lib/ksl/error-log.d.ts +28 -0
- package/dist/lib/ksl/error-log.js +43 -0
- package/dist/lib/ksl/index.d.ts +9 -0
- package/dist/lib/ksl/index.js +25 -0
- package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
- package/dist/lib/ksl/orchestrator-tap.js +85 -0
- package/dist/lib/ksl/record-writer.d.ts +46 -0
- package/dist/lib/ksl/record-writer.js +45 -0
- package/dist/lib/llm-cost-table.d.ts +36 -0
- package/dist/lib/llm-cost-table.js +90 -0
- package/dist/lib/local-model-router.d.ts +27 -0
- package/dist/lib/local-model-router.js +61 -0
- package/dist/lib/mc-client.d.ts +51 -0
- package/dist/lib/mc-client.js +249 -0
- package/dist/lib/model-router-contract.d.ts +91 -0
- package/dist/lib/model-router-contract.js +19 -0
- package/dist/lib/model-router-registry.d.ts +24 -0
- package/dist/lib/model-router-registry.js +52 -0
- package/dist/lib/model-router.d.ts +20 -0
- package/dist/lib/model-router.js +79 -0
- package/dist/lib/monotask-state-machine.d.ts +19 -0
- package/dist/lib/monotask-state-machine.js +131 -0
- package/dist/lib/neutral-prompt-checker.d.ts +22 -0
- package/dist/lib/neutral-prompt-checker.js +130 -0
- package/dist/lib/notion-direct.d.ts +92 -0
- package/dist/lib/notion-direct.js +381 -0
- package/dist/lib/ollama-client.d.ts +37 -0
- package/dist/lib/ollama-client.js +158 -0
- package/dist/lib/omel/credential-vault.d.ts +57 -0
- package/dist/lib/omel/credential-vault.js +324 -0
- package/dist/lib/omel/human-brake.d.ts +32 -0
- package/dist/lib/omel/human-brake.js +289 -0
- package/dist/lib/omel/index.d.ts +10 -0
- package/dist/lib/omel/index.js +26 -0
- package/dist/lib/omel/phantom-workspace.d.ts +31 -0
- package/dist/lib/omel/phantom-workspace.js +256 -0
- package/dist/lib/omel/wipe-witness.d.ts +75 -0
- package/dist/lib/omel/wipe-witness.js +398 -0
- package/dist/lib/orchestrate-engine.d.ts +25 -0
- package/dist/lib/orchestrate-engine.js +4436 -0
- package/dist/lib/perm-judge.d.ts +46 -0
- package/dist/lib/perm-judge.js +173 -0
- package/dist/lib/plumber/conformance.d.ts +54 -0
- package/dist/lib/plumber/conformance.js +121 -0
- package/dist/lib/plumber/index.d.ts +9 -0
- package/dist/lib/plumber/index.js +25 -0
- package/dist/lib/plumber/observer.d.ts +52 -0
- package/dist/lib/plumber/observer.js +180 -0
- package/dist/lib/plumber/types.d.ts +78 -0
- package/dist/lib/plumber/types.js +29 -0
- package/dist/lib/research-impl-gate.d.ts +16 -0
- package/dist/lib/research-impl-gate.js +105 -0
- package/dist/lib/sherlock-memory.d.ts +29 -0
- package/dist/lib/sherlock-memory.js +105 -0
- package/dist/lib/skill-crystalliser.d.ts +44 -0
- package/dist/lib/skill-crystalliser.js +60 -0
- package/dist/lib/sprint-runner-engine.d.ts +27 -0
- package/dist/lib/sprint-runner-engine.js +1042 -0
- package/dist/lib/sprint-state.d.ts +71 -0
- package/dist/lib/sprint-state.js +202 -0
- package/dist/lib/stuck-handler.d.ts +17 -0
- package/dist/lib/stuck-handler.js +249 -0
- package/dist/lib/task-contract-checker.d.ts +17 -0
- package/dist/lib/task-contract-checker.js +29 -0
- package/dist/lib/task-router/index.d.ts +17 -0
- package/dist/lib/task-router/index.js +52 -0
- package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
- package/dist/lib/task-router/router/generate-execution-id.js +24 -0
- package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
- package/dist/lib/task-router/router/resolve-route.js +49 -0
- package/dist/lib/task-router/types.d.ts +79 -0
- package/dist/lib/task-router/types.js +39 -0
- package/dist/lib/token-budget-validator.d.ts +44 -0
- package/dist/lib/token-budget-validator.js +84 -0
- package/dist/lib/trust-score-updater.d.ts +30 -0
- package/dist/lib/trust-score-updater.js +107 -0
- package/dist/lib/wallet-state.d.ts +26 -0
- package/dist/lib/wallet-state.js +85 -0
- package/package.json +27 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Failure Logger — logs swarm code rejections to the failure-library.
|
|
3
|
+
* Originally Sprint 197; moved into @kognai/orchestrator-core (TICKET-215 Phase 3).
|
|
4
|
+
*
|
|
5
|
+
* The failure dir is now resolved via engine-paths (KOGNAI_ROOT / cwd) instead of
|
|
6
|
+
* `resolve(__dirname, ...)`, so the logger works regardless of where the package
|
|
7
|
+
* is installed. Default resolves to <root>/data/failure-library/code — identical
|
|
8
|
+
* to the legacy path when run from a product repo root.
|
|
9
|
+
*
|
|
10
|
+
* Stdlib only: crypto, fs, path.
|
|
11
|
+
*/
|
|
12
|
+
export interface CodeFailureEntry {
|
|
13
|
+
taskId: string;
|
|
14
|
+
sprintId: string;
|
|
15
|
+
agentId: string;
|
|
16
|
+
attemptNum: number;
|
|
17
|
+
score: number;
|
|
18
|
+
model: string;
|
|
19
|
+
rejectionReason: string;
|
|
20
|
+
issues: Array<{
|
|
21
|
+
severity: string;
|
|
22
|
+
file: string;
|
|
23
|
+
description: string;
|
|
24
|
+
}>;
|
|
25
|
+
failType: 'destructive_rewrite' | 'truncation' | 'qa_gate' | 'supervisor_rejected' | 'no_files';
|
|
26
|
+
}
|
|
27
|
+
export declare function logCodeFailure(entry: CodeFailureEntry): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Code Failure Logger — logs swarm code rejections to the failure-library.
|
|
4
|
+
* Originally Sprint 197; moved into @kognai/orchestrator-core (TICKET-215 Phase 3).
|
|
5
|
+
*
|
|
6
|
+
* The failure dir is now resolved via engine-paths (KOGNAI_ROOT / cwd) instead of
|
|
7
|
+
* `resolve(__dirname, ...)`, so the logger works regardless of where the package
|
|
8
|
+
* is installed. Default resolves to <root>/data/failure-library/code — identical
|
|
9
|
+
* to the legacy path when run from a product repo root.
|
|
10
|
+
*
|
|
11
|
+
* Stdlib only: crypto, fs, path.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.logCodeFailure = logCodeFailure;
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
16
|
+
const fs_1 = require("fs");
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
const engine_paths_1 = require("./engine-paths");
|
|
19
|
+
function logCodeFailure(entry) {
|
|
20
|
+
try {
|
|
21
|
+
const dir = (0, path_1.join)((0, engine_paths_1.resolveEnginePaths)().failureLibrary, 'code');
|
|
22
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
23
|
+
const entryId = (0, crypto_1.randomUUID)().slice(0, 8);
|
|
24
|
+
const record = {
|
|
25
|
+
entry_id: entryId,
|
|
26
|
+
task_id: entry.taskId,
|
|
27
|
+
sprint_id: entry.sprintId,
|
|
28
|
+
agent_id: entry.agentId,
|
|
29
|
+
attempt_num: entry.attemptNum,
|
|
30
|
+
score: entry.score,
|
|
31
|
+
model: entry.model,
|
|
32
|
+
fail_type: entry.failType,
|
|
33
|
+
rejection_reason: entry.rejectionReason,
|
|
34
|
+
issues: entry.issues,
|
|
35
|
+
filed_at: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(dir, `fail-${entryId}.json`), JSON.stringify(record, null, 2));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Silently catch all errors — must not break the swarm.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cto-approval-gate.ts — CTO Sprint Approval Gate
|
|
3
|
+
*
|
|
4
|
+
* MANDATORY: Every sprint plan generated by the autonomous loop MUST be
|
|
5
|
+
* reviewed and approved by the CTO agent before execution begins.
|
|
6
|
+
*
|
|
7
|
+
* The CTO checks:
|
|
8
|
+
* 1. Is this sprint in the execution plan? (FULL_WORK_PLAN.md / PHASE_1_EXECUTION_PLAN.md)
|
|
9
|
+
* 2. Does it advance the current phase gate?
|
|
10
|
+
* 3. Is it a Sev-1 fix or critical path item?
|
|
11
|
+
* 4. Does it duplicate work already completed?
|
|
12
|
+
*
|
|
13
|
+
* If the CTO rejects, the sprint is logged and skipped.
|
|
14
|
+
* If the CTO approves, execution proceeds normally.
|
|
15
|
+
*
|
|
16
|
+
* This prevents the autonomous swarm from inventing its own work.
|
|
17
|
+
*
|
|
18
|
+
* @see ClawRouter Spec v2.0 — CTO agent uses T2 POWER (qwen3:14b) for review
|
|
19
|
+
*/
|
|
20
|
+
import type { SprintProposal } from './cto-gate-types';
|
|
21
|
+
import { type CapabilityCheckResult } from './acp';
|
|
22
|
+
export interface CTOApprovalResult {
|
|
23
|
+
approved: boolean;
|
|
24
|
+
sprint_id: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
plan_reference: string;
|
|
27
|
+
cto_confidence: number;
|
|
28
|
+
timestamp: string;
|
|
29
|
+
acp_violations?: CapabilityCheckResult[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Submit a sprint proposal to the CTO agent for approval.
|
|
33
|
+
* Returns approval/rejection with reasoning.
|
|
34
|
+
*
|
|
35
|
+
* Uses T2 POWER (qwen3:14b, local, $0) for the review.
|
|
36
|
+
*/
|
|
37
|
+
export declare function requestCTOApproval(proposal: SprintProposal, projectRoot: string, product?: 'kognai' | 'invoica'): Promise<CTOApprovalResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Review multiple sprint proposals in sequence. Returns only approved ones.
|
|
40
|
+
* Use this in the autonomous loop to filter proposals before execution.
|
|
41
|
+
*/
|
|
42
|
+
export declare function batchCTOReview(proposals: SprintProposal[], projectRoot: string, product?: 'kognai' | 'invoica'): Promise<{
|
|
43
|
+
approved: SprintProposal[];
|
|
44
|
+
rejected: CTOApprovalResult[];
|
|
45
|
+
}>;
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* cto-approval-gate.ts — CTO Sprint Approval Gate
|
|
4
|
+
*
|
|
5
|
+
* MANDATORY: Every sprint plan generated by the autonomous loop MUST be
|
|
6
|
+
* reviewed and approved by the CTO agent before execution begins.
|
|
7
|
+
*
|
|
8
|
+
* The CTO checks:
|
|
9
|
+
* 1. Is this sprint in the execution plan? (FULL_WORK_PLAN.md / PHASE_1_EXECUTION_PLAN.md)
|
|
10
|
+
* 2. Does it advance the current phase gate?
|
|
11
|
+
* 3. Is it a Sev-1 fix or critical path item?
|
|
12
|
+
* 4. Does it duplicate work already completed?
|
|
13
|
+
*
|
|
14
|
+
* If the CTO rejects, the sprint is logged and skipped.
|
|
15
|
+
* If the CTO approves, execution proceeds normally.
|
|
16
|
+
*
|
|
17
|
+
* This prevents the autonomous swarm from inventing its own work.
|
|
18
|
+
*
|
|
19
|
+
* @see ClawRouter Spec v2.0 — CTO agent uses T2 POWER (qwen3:14b) for review
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.requestCTOApproval = requestCTOApproval;
|
|
56
|
+
exports.batchCTOReview = batchCTOReview;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const model_router_registry_1 = require("./model-router-registry");
|
|
60
|
+
const acp_1 = require("./acp");
|
|
61
|
+
// Sprint 702: ACPEngine trust scoring enforcement
|
|
62
|
+
const acp_engine_1 = require("./acp-engine");
|
|
63
|
+
// Sprint TICKET-005-RULE1: Rule 1 (Neutral Prompts) — detect answer-seeding in Qwen-recommended sprints
|
|
64
|
+
const neutral_prompt_checker_1 = require("./neutral-prompt-checker");
|
|
65
|
+
// Sprint TICKET-005-RULE2: Rule 2 (Research/Implementation Separation)
|
|
66
|
+
const research_impl_gate_1 = require("./research-impl-gate");
|
|
67
|
+
// Sprint TICKET-005-RULE3: Rule 3 (Task Contracts) — inputs/outputs/success_criteria required
|
|
68
|
+
const task_contract_checker_1 = require("./task-contract-checker");
|
|
69
|
+
// Sprint 1504: Emotional Safety Gate (INTEL-BRIEF-22)
|
|
70
|
+
const emotional_safety_gate_1 = require("./emotional-safety-gate");
|
|
71
|
+
// ── Plan Context Loader ───────────────────────────────────────────────────────
|
|
72
|
+
function loadPlanContext(projectRoot) {
|
|
73
|
+
const planFiles = [
|
|
74
|
+
path.join(projectRoot, '..', 'Documents', 'Kognai', 'plans', 'PHASE_1_EXECUTION_PLAN.md'),
|
|
75
|
+
path.join(projectRoot, '..', 'Documents', 'Kognai', 'plans', 'FULL_WORK_PLAN.md'),
|
|
76
|
+
];
|
|
77
|
+
let context = '';
|
|
78
|
+
for (const f of planFiles) {
|
|
79
|
+
try {
|
|
80
|
+
if (fs.existsSync(f)) {
|
|
81
|
+
const content = fs.readFileSync(f, 'utf-8');
|
|
82
|
+
// Take first 3000 chars of each plan file (QCG budget)
|
|
83
|
+
context += `\n--- ${path.basename(f)} ---\n${content.slice(0, 3000)}\n`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* skip */ }
|
|
87
|
+
}
|
|
88
|
+
// Also load recent sprint files to check for duplicates
|
|
89
|
+
const sprintsDir = path.join(projectRoot, 'sprints');
|
|
90
|
+
if (fs.existsSync(sprintsDir)) {
|
|
91
|
+
try {
|
|
92
|
+
const recent = fs.readdirSync(sprintsDir)
|
|
93
|
+
.filter(f => f.endsWith('.json'))
|
|
94
|
+
.sort()
|
|
95
|
+
.slice(-10); // last 10 sprints
|
|
96
|
+
const titles = recent.map(f => {
|
|
97
|
+
try {
|
|
98
|
+
const data = JSON.parse(fs.readFileSync(path.join(sprintsDir, f), 'utf-8'));
|
|
99
|
+
return `${f}: ${data.title || data.name || 'untitled'}`;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return f;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
context += `\n--- Recent Sprints (last 10) ---\n${titles.join('\n')}\n`;
|
|
106
|
+
}
|
|
107
|
+
catch { /* skip */ }
|
|
108
|
+
}
|
|
109
|
+
return context;
|
|
110
|
+
}
|
|
111
|
+
// ── CTO Review ────────────────────────────────────────────────────────────────
|
|
112
|
+
/**
|
|
113
|
+
* Submit a sprint proposal to the CTO agent for approval.
|
|
114
|
+
* Returns approval/rejection with reasoning.
|
|
115
|
+
*
|
|
116
|
+
* Uses T2 POWER (qwen3:14b, local, $0) for the review.
|
|
117
|
+
*/
|
|
118
|
+
async function requestCTOApproval(proposal, projectRoot, product = 'kognai') {
|
|
119
|
+
const timestamp = new Date().toISOString();
|
|
120
|
+
// Human-submitted sprints bypass CTO gate
|
|
121
|
+
if (proposal.source === 'human') {
|
|
122
|
+
return {
|
|
123
|
+
approved: true,
|
|
124
|
+
sprint_id: proposal.sprint_id,
|
|
125
|
+
reason: 'Human-submitted sprint — auto-approved per governance rules.',
|
|
126
|
+
plan_reference: 'HUMAN_OVERRIDE',
|
|
127
|
+
cto_confidence: 100,
|
|
128
|
+
timestamp,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Queue-prescribed sprints bypass CTO LLM review — the queue IS the plan.
|
|
132
|
+
// workspace/sprint-queue.json is human-maintained and authoritative.
|
|
133
|
+
// ACP capability checks + Police Lite still run (below), but the LLM won't reject these.
|
|
134
|
+
if (proposal.source === 'queue-prescribed') {
|
|
135
|
+
logCTODecision({
|
|
136
|
+
approved: true,
|
|
137
|
+
sprint_id: proposal.sprint_id,
|
|
138
|
+
reason: 'Queue-prescribed sprint — auto-approved. Sprint queue is human-maintained authoritative plan.',
|
|
139
|
+
plan_reference: 'QUEUE_PRESCRIBED',
|
|
140
|
+
cto_confidence: 100,
|
|
141
|
+
timestamp,
|
|
142
|
+
}, projectRoot, product);
|
|
143
|
+
return {
|
|
144
|
+
approved: true,
|
|
145
|
+
sprint_id: proposal.sprint_id,
|
|
146
|
+
reason: 'Queue-prescribed sprint — auto-approved. Sprint queue is human-maintained authoritative plan.',
|
|
147
|
+
plan_reference: 'QUEUE_PRESCRIBED',
|
|
148
|
+
cto_confidence: 100,
|
|
149
|
+
timestamp,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Sprint 650: ACP pre-check — reject if agents lack required capabilities
|
|
153
|
+
if (proposal.agent_capabilities && proposal.agent_capabilities.length > 0) {
|
|
154
|
+
const violations = [];
|
|
155
|
+
for (const task of proposal.agent_capabilities) {
|
|
156
|
+
for (const cap of task.required_capabilities) {
|
|
157
|
+
const result = (0, acp_1.checkCapability)(task.agent, cap);
|
|
158
|
+
if (!result.allowed)
|
|
159
|
+
violations.push(result);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (violations.length > 0) {
|
|
163
|
+
const violationSummary = violations.map(v => `${v.agent_id}: ${v.reason}`).join('; ');
|
|
164
|
+
return {
|
|
165
|
+
approved: false,
|
|
166
|
+
sprint_id: proposal.sprint_id,
|
|
167
|
+
reason: `ACP violation: ${violationSummary}`,
|
|
168
|
+
plan_reference: 'ACP_VIOLATION',
|
|
169
|
+
cto_confidence: 100,
|
|
170
|
+
timestamp,
|
|
171
|
+
acp_violations: violations,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Sprint 702: ACPEngine trust enforcement — reject low-trust agents before wasting LLM tokens
|
|
176
|
+
try {
|
|
177
|
+
const acpEngine = new acp_engine_1.ACPEngine();
|
|
178
|
+
const taskTypes = proposal.tasks.map(t => typeof t === 'string' ? 'code-generation' : t.task_type || 'code-generation');
|
|
179
|
+
const agentIds = proposal.agent_capabilities?.map(a => a.agent) || ['coder'];
|
|
180
|
+
for (const agentId of agentIds) {
|
|
181
|
+
for (const taskType of taskTypes) {
|
|
182
|
+
const enforcement = acpEngine.enforce(agentId, taskType);
|
|
183
|
+
if (enforcement.recommendation === 'block' || enforcement.recommendation === 'recycle') {
|
|
184
|
+
const violationStr = enforcement.violations.join('; ');
|
|
185
|
+
logCTODecision({
|
|
186
|
+
approved: false,
|
|
187
|
+
sprint_id: proposal.sprint_id,
|
|
188
|
+
reason: `ACPEngine ${enforcement.recommendation}: ${violationStr} (composite: ${enforcement.composite})`,
|
|
189
|
+
plan_reference: 'ACP_TRUST_VIOLATION',
|
|
190
|
+
cto_confidence: 100,
|
|
191
|
+
timestamp,
|
|
192
|
+
}, projectRoot, product);
|
|
193
|
+
return {
|
|
194
|
+
approved: false,
|
|
195
|
+
sprint_id: proposal.sprint_id,
|
|
196
|
+
reason: `ACPEngine ${enforcement.recommendation}: ${violationStr}`,
|
|
197
|
+
plan_reference: 'ACP_TRUST_VIOLATION',
|
|
198
|
+
cto_confidence: 100,
|
|
199
|
+
timestamp,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (enforcement.recommendation === 'fallback') {
|
|
203
|
+
console.warn(`[CTO-GATE] ACPEngine fallback for ${agentId}/${taskType}: ${enforcement.violations.join('; ')} (composite: ${enforcement.composite})`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
// ACPEngine failure is non-fatal — proceed with LLM review
|
|
210
|
+
console.warn(`[CTO-GATE] ACPEngine check failed (non-fatal): ${err.message}`);
|
|
211
|
+
}
|
|
212
|
+
// Sprint TICKET-005-RULE2: Rule 2 (Research/Implementation Separation) — block impl if research not done
|
|
213
|
+
if (proposal.phase === 'implementation') {
|
|
214
|
+
const r2Result = (0, research_impl_gate_1.checkResearchGate)(proposal, projectRoot);
|
|
215
|
+
if (!r2Result.allowed) {
|
|
216
|
+
logCTODecision({
|
|
217
|
+
approved: false,
|
|
218
|
+
sprint_id: proposal.sprint_id,
|
|
219
|
+
reason: `Rule 2 violation: ${r2Result.reason}`,
|
|
220
|
+
plan_reference: 'RULE2_RESEARCH_NOT_DONE',
|
|
221
|
+
cto_confidence: 100,
|
|
222
|
+
timestamp,
|
|
223
|
+
}, projectRoot, product);
|
|
224
|
+
return {
|
|
225
|
+
approved: false,
|
|
226
|
+
sprint_id: proposal.sprint_id,
|
|
227
|
+
reason: `Rule 2 violation: ${r2Result.reason}`,
|
|
228
|
+
plan_reference: 'RULE2_RESEARCH_NOT_DONE',
|
|
229
|
+
cto_confidence: 100,
|
|
230
|
+
timestamp,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Sprint TICKET-005-RULE3: Rule 3 (Task Contracts) — only for autonomous sprints
|
|
235
|
+
if (proposal.source === 'queue-empty-autonomous' || proposal.source === 'autonomous_loop') {
|
|
236
|
+
const r3Result = (0, task_contract_checker_1.checkTaskContracts)(proposal);
|
|
237
|
+
if (!r3Result.valid) {
|
|
238
|
+
const missing = r3Result.missing.join(', ');
|
|
239
|
+
logCTODecision({
|
|
240
|
+
approved: false,
|
|
241
|
+
sprint_id: proposal.sprint_id,
|
|
242
|
+
reason: `Rule 3 violation: missing contract fields — ${missing}`,
|
|
243
|
+
plan_reference: 'RULE3_CONTRACT_MISSING',
|
|
244
|
+
cto_confidence: 100,
|
|
245
|
+
timestamp,
|
|
246
|
+
}, projectRoot, product);
|
|
247
|
+
return {
|
|
248
|
+
approved: false,
|
|
249
|
+
sprint_id: proposal.sprint_id,
|
|
250
|
+
reason: `Rule 3 violation: missing contract fields — ${missing}`,
|
|
251
|
+
plan_reference: 'RULE3_CONTRACT_MISSING',
|
|
252
|
+
cto_confidence: 100,
|
|
253
|
+
timestamp,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Sprint TICKET-005-RULE1: Rule 1 (Neutral Prompts) check — only for autonomous sprints
|
|
258
|
+
if (proposal.source === 'queue-empty-autonomous' || proposal.source === 'autonomous_loop') {
|
|
259
|
+
try {
|
|
260
|
+
const textToCheck = `${proposal.description}\n${proposal.tasks.join('\n')}`;
|
|
261
|
+
const r1Result = (0, neutral_prompt_checker_1.checkText)(textToCheck);
|
|
262
|
+
if (!r1Result.clean) {
|
|
263
|
+
const summary = r1Result.violations.slice(0, 3).map(v => `[${v.pattern}] ${v.text}`).join(' | ');
|
|
264
|
+
logCTODecision({
|
|
265
|
+
approved: false,
|
|
266
|
+
sprint_id: proposal.sprint_id,
|
|
267
|
+
reason: `Rule 1 violation (answer-seeding in brief): ${summary}`,
|
|
268
|
+
plan_reference: 'RULE1_NEUTRAL_PROMPT_VIOLATION',
|
|
269
|
+
cto_confidence: 100,
|
|
270
|
+
timestamp,
|
|
271
|
+
}, projectRoot, product);
|
|
272
|
+
return {
|
|
273
|
+
approved: false,
|
|
274
|
+
sprint_id: proposal.sprint_id,
|
|
275
|
+
reason: `Rule 1 violation (answer-seeding in brief): ${summary}`,
|
|
276
|
+
plan_reference: 'RULE1_NEUTRAL_PROMPT_VIOLATION',
|
|
277
|
+
cto_confidence: 100,
|
|
278
|
+
timestamp,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
console.warn(`[CTO-GATE] Rule 1 check failed (non-fatal): ${err.message}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Sprint 1504: Emotional Safety Gate (INTEL-BRIEF-22)
|
|
287
|
+
// Only runs for high-stakes operation types. Calm = 0% blackmail rate per Anthropic research.
|
|
288
|
+
const GATED_OPERATIONS = emotional_safety_gate_1.DEFAULT_CALM_GATE_CONFIG.gated_operations;
|
|
289
|
+
const operationType = proposal.operation_type;
|
|
290
|
+
if (operationType && GATED_OPERATIONS.includes(operationType)) {
|
|
291
|
+
const context = `${proposal.title} ${proposal.description} ${proposal.tasks.join(' ')}`;
|
|
292
|
+
const emotionalAssessment = (0, emotional_safety_gate_1.assessEmotionalState)(context);
|
|
293
|
+
if (emotionalAssessment.should_block) {
|
|
294
|
+
const logEntry = {
|
|
295
|
+
sprint_id: proposal.sprint_id,
|
|
296
|
+
operation_type: operationType,
|
|
297
|
+
tone: emotionalAssessment.tone,
|
|
298
|
+
confidence: emotionalAssessment.confidence,
|
|
299
|
+
matched_keywords: emotionalAssessment.matched_keywords,
|
|
300
|
+
reason: emotionalAssessment.reason,
|
|
301
|
+
timestamp,
|
|
302
|
+
product,
|
|
303
|
+
};
|
|
304
|
+
try {
|
|
305
|
+
const emotionLogDir = path.join(projectRoot, 'logs', 'emotional-gate');
|
|
306
|
+
if (!fs.existsSync(emotionLogDir))
|
|
307
|
+
fs.mkdirSync(emotionLogDir, { recursive: true });
|
|
308
|
+
const emotionLogFile = path.join(emotionLogDir, `${timestamp.slice(0, 10)}.jsonl`);
|
|
309
|
+
fs.appendFileSync(emotionLogFile, JSON.stringify(logEntry) + '\n');
|
|
310
|
+
}
|
|
311
|
+
catch { /* non-critical */ }
|
|
312
|
+
logCTODecision({
|
|
313
|
+
approved: false,
|
|
314
|
+
sprint_id: proposal.sprint_id,
|
|
315
|
+
reason: `Emotional safety gate blocked: ${emotionalAssessment.reason}`,
|
|
316
|
+
plan_reference: 'EMOTIONAL_GATE_BLOCK',
|
|
317
|
+
cto_confidence: 100,
|
|
318
|
+
timestamp,
|
|
319
|
+
}, projectRoot, product);
|
|
320
|
+
return {
|
|
321
|
+
approved: false,
|
|
322
|
+
sprint_id: proposal.sprint_id,
|
|
323
|
+
reason: `Emotional safety gate blocked: ${emotionalAssessment.reason}`,
|
|
324
|
+
plan_reference: 'EMOTIONAL_GATE_BLOCK',
|
|
325
|
+
cto_confidence: 100,
|
|
326
|
+
timestamp,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const planContext = loadPlanContext(projectRoot);
|
|
331
|
+
const systemPrompt = `You are the CTO agent for ${product}. Your ONLY job is to decide whether a proposed sprint should be executed or rejected.
|
|
332
|
+
|
|
333
|
+
RULES:
|
|
334
|
+
1. APPROVE if the sprint directly maps to an item in the execution plan or work plan
|
|
335
|
+
2. APPROVE if the sprint is a Sev-1 bug fix or critical path blocker fix
|
|
336
|
+
3. APPROVE if the sprint source is "auto-queue-empty" — this means the human queue is complete and the system is generating organic continuation sprints. These should be approved unless they duplicate recent work or are clearly harmful.
|
|
337
|
+
4. REJECT if the sprint duplicates work already completed in recent sprints
|
|
338
|
+
5. REJECT if the sprint invents new features not requested by the human founder AND the queue still has pending items
|
|
339
|
+
|
|
340
|
+
You must respond ONLY with valid JSON:
|
|
341
|
+
{
|
|
342
|
+
"approved": true/false,
|
|
343
|
+
"reason": "one sentence explanation",
|
|
344
|
+
"plan_reference": "which plan item this maps to, or NOT_IN_PLAN",
|
|
345
|
+
"confidence": 0-100
|
|
346
|
+
}`;
|
|
347
|
+
const prompt = `SPRINT PROPOSAL:
|
|
348
|
+
ID: ${proposal.sprint_id}
|
|
349
|
+
Title: ${proposal.title}
|
|
350
|
+
Description: ${proposal.description}
|
|
351
|
+
Tasks: ${proposal.tasks.join('; ')}
|
|
352
|
+
Source: ${proposal.source}
|
|
353
|
+
|
|
354
|
+
CURRENT PLANS AND RECENT SPRINTS:
|
|
355
|
+
${planContext}
|
|
356
|
+
|
|
357
|
+
Should this sprint be approved or rejected? Respond with JSON only.`;
|
|
358
|
+
try {
|
|
359
|
+
const response = await (0, model_router_registry_1.getModelRouter)().routeCall({
|
|
360
|
+
task_type: 'cto_sprint_review',
|
|
361
|
+
tier_class: 'text',
|
|
362
|
+
complexity: 'power', // T2 — local, $0
|
|
363
|
+
context_tokens: Math.ceil((prompt.length + systemPrompt.length) / 4),
|
|
364
|
+
constitutional_flag: false,
|
|
365
|
+
agent_id: `cto-${product}`,
|
|
366
|
+
payload: { system: systemPrompt, prompt, max_tokens: 500 },
|
|
367
|
+
});
|
|
368
|
+
// Parse CTO response
|
|
369
|
+
const content = response.content.trim();
|
|
370
|
+
// Extract JSON from response (handle markdown code blocks)
|
|
371
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
372
|
+
if (!jsonMatch) {
|
|
373
|
+
// If CTO can't parse → reject by default (safe side)
|
|
374
|
+
return {
|
|
375
|
+
approved: false,
|
|
376
|
+
sprint_id: proposal.sprint_id,
|
|
377
|
+
reason: 'CTO response was not valid JSON — rejecting for safety.',
|
|
378
|
+
plan_reference: 'PARSE_ERROR',
|
|
379
|
+
cto_confidence: 0,
|
|
380
|
+
timestamp,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
384
|
+
const result = {
|
|
385
|
+
approved: Boolean(parsed.approved),
|
|
386
|
+
sprint_id: proposal.sprint_id,
|
|
387
|
+
reason: String(parsed.reason || 'No reason given'),
|
|
388
|
+
plan_reference: String(parsed.plan_reference || 'UNKNOWN'),
|
|
389
|
+
cto_confidence: Number(parsed.confidence) || 0,
|
|
390
|
+
timestamp,
|
|
391
|
+
};
|
|
392
|
+
// Sprint 712: Police Agent Lite — constitutional cross-check
|
|
393
|
+
try {
|
|
394
|
+
const signalsPath = path.join(projectRoot, 'workspace', 'shared-context', 'SIGNALS.md');
|
|
395
|
+
if (fs.existsSync(signalsPath)) {
|
|
396
|
+
const signalsContent = fs.readFileSync(signalsPath, 'utf-8');
|
|
397
|
+
const criticalSignals = signalsContent.split('\n')
|
|
398
|
+
.filter(l => l.includes('**CRITICAL**'))
|
|
399
|
+
.map(l => l.replace(/^-\s*/, '').trim());
|
|
400
|
+
if (criticalSignals.length > 0 && result.approved) {
|
|
401
|
+
// Check if sprint touches flagged areas
|
|
402
|
+
const sprintText = `${proposal.sprint_id} ${proposal.tasks?.map((t) => t.title || t.id).join(' ') || ''}`.toLowerCase();
|
|
403
|
+
const flagged = criticalSignals.filter(s => {
|
|
404
|
+
const keywords = s.toLowerCase().match(/\b\w{4,}\b/g) || [];
|
|
405
|
+
return keywords.some(k => sprintText.includes(k));
|
|
406
|
+
});
|
|
407
|
+
if (flagged.length > 0) {
|
|
408
|
+
console.warn(`[CTO-GATE] 🚨 Police Lite: CRITICAL signal match — REJECTING`);
|
|
409
|
+
result.approved = false;
|
|
410
|
+
result.reason = `Police Agent Lite: CRITICAL constitutional signal(s) active — ${flagged[0].substring(0, 100)}. Sprint touches flagged area. ${result.reason}`;
|
|
411
|
+
result.plan_reference = 'POLICE_LITE_BLOCK';
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
console.log(`[CTO-GATE] Police Lite: ${criticalSignals.length} CRITICAL signal(s) active but no overlap with sprint`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Founding Charter immutable law check
|
|
418
|
+
const charterKeywords = ['five seed principles', 'summer yu', 'kill switch', 'human oversight', 'context compaction'];
|
|
419
|
+
const sprintLower = `${proposal.sprint_id} ${proposal.tasks?.map((t) => t.title || t.id).join(' ') || ''}`.toLowerCase();
|
|
420
|
+
const charterViolation = charterKeywords.find(k => sprintLower.includes('remove') && sprintLower.includes(k));
|
|
421
|
+
if (charterViolation) {
|
|
422
|
+
console.warn(`[CTO-GATE] 🚨 Police Lite: Founding Charter violation detected — REJECTING`);
|
|
423
|
+
result.approved = false;
|
|
424
|
+
result.reason = `Police Agent Lite: Founding Charter immutable law at risk (${charterViolation}). AUTOMATIC REJECT.`;
|
|
425
|
+
result.plan_reference = 'CHARTER_VIOLATION';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
// Police Lite failure is non-fatal — proceed with LLM result
|
|
431
|
+
console.warn(`[CTO-GATE] Police Lite check failed (non-fatal): ${err.message}`);
|
|
432
|
+
}
|
|
433
|
+
// Log the decision
|
|
434
|
+
logCTODecision(result, projectRoot, product);
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
// If CTO agent is unavailable → reject by default
|
|
439
|
+
return {
|
|
440
|
+
approved: false,
|
|
441
|
+
sprint_id: proposal.sprint_id,
|
|
442
|
+
reason: `CTO agent unavailable: ${err.message}. Rejecting for safety.`,
|
|
443
|
+
plan_reference: 'CTO_UNAVAILABLE',
|
|
444
|
+
cto_confidence: 0,
|
|
445
|
+
timestamp,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// ── Decision Log ──────────────────────────────────────────────────────────────
|
|
450
|
+
function logCTODecision(result, projectRoot, product) {
|
|
451
|
+
try {
|
|
452
|
+
const logDir = path.join(projectRoot, 'logs', 'cto-gate');
|
|
453
|
+
if (!fs.existsSync(logDir))
|
|
454
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
455
|
+
const logFile = path.join(logDir, `${result.timestamp.slice(0, 10)}.jsonl`);
|
|
456
|
+
fs.appendFileSync(logFile, JSON.stringify({ product, ...result }) + '\n');
|
|
457
|
+
}
|
|
458
|
+
catch { /* non-critical */ }
|
|
459
|
+
}
|
|
460
|
+
// ── Batch Review (for autonomous loop) ────────────────────────────────────────
|
|
461
|
+
/**
|
|
462
|
+
* Review multiple sprint proposals in sequence. Returns only approved ones.
|
|
463
|
+
* Use this in the autonomous loop to filter proposals before execution.
|
|
464
|
+
*/
|
|
465
|
+
async function batchCTOReview(proposals, projectRoot, product = 'kognai') {
|
|
466
|
+
const approved = [];
|
|
467
|
+
const rejected = [];
|
|
468
|
+
for (const proposal of proposals) {
|
|
469
|
+
const result = await requestCTOApproval(proposal, projectRoot, product);
|
|
470
|
+
if (result.approved) {
|
|
471
|
+
approved.push(proposal);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
rejected.push(result);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return { approved, rejected };
|
|
478
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cto-gate-types.ts — TICKET-215 Wave D: SprintProposal lifted out of
|
|
3
|
+
* cto-approval-gate so the gate AND the governance checks (research-impl-gate,
|
|
4
|
+
* task-contract-checker) share one declaration without importing each other
|
|
5
|
+
* (breaks the former type-only cycle cleanly).
|
|
6
|
+
*/
|
|
7
|
+
import type { Capability } from './acp';
|
|
8
|
+
export interface SprintProposal {
|
|
9
|
+
sprint_id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
tasks: string[];
|
|
13
|
+
estimated_complexity: string;
|
|
14
|
+
source: 'autonomous_loop' | 'human' | 'cto_backlog' | 'queue-prescribed' | 'auto-queue-empty' | 'queue-empty-autonomous';
|
|
15
|
+
/** Sprint TICKET-005-RULE2: research | implementation phase gate */
|
|
16
|
+
phase?: 'research' | 'implementation';
|
|
17
|
+
/** ID of the research sprint that must be done before this implementation sprint can run */
|
|
18
|
+
research_sprint_id?: string;
|
|
19
|
+
/** Sprint TICKET-005-RULE3: Task Contracts — required for autonomous sprints */
|
|
20
|
+
inputs?: string[];
|
|
21
|
+
outputs?: string[];
|
|
22
|
+
success_criteria?: string[];
|
|
23
|
+
/** Optional: map of agent_id → required capabilities for ACP pre-check */
|
|
24
|
+
agent_capabilities?: Array<{
|
|
25
|
+
agent: string;
|
|
26
|
+
required_capabilities: Capability[];
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* cto-gate-types.ts — TICKET-215 Wave D: SprintProposal lifted out of
|
|
4
|
+
* cto-approval-gate so the gate AND the governance checks (research-impl-gate,
|
|
5
|
+
* task-contract-checker) share one declaration without importing each other
|
|
6
|
+
* (breaks the former type-only cycle cleanly).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|