network-ai 5.0.0 → 5.1.1
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/INTEGRATION_GUIDE.md +4 -4
- package/QUICKSTART.md +4 -4
- package/README.md +15 -12
- package/dist/adapters/custom-adapter.d.ts +1 -1
- package/dist/adapters/custom-adapter.js +1 -1
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +4 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/langchain-adapter.js +1 -1
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/mcp-adapter.d.ts +1 -1
- package/dist/adapters/mcp-adapter.js +3 -3
- package/dist/adapters/mcp-adapter.js.map +1 -1
- package/dist/adapters/openclaw-adapter.d.ts +1 -1
- package/dist/adapters/openclaw-adapter.js +3 -3
- package/dist/adapters/openclaw-adapter.js.map +1 -1
- package/dist/adapters/orchestrator-adapter.d.ts +118 -0
- package/dist/adapters/orchestrator-adapter.d.ts.map +1 -0
- package/dist/adapters/orchestrator-adapter.js +218 -0
- package/dist/adapters/orchestrator-adapter.js.map +1 -0
- package/dist/demo-control-plane.d.ts +12 -0
- package/dist/demo-control-plane.d.ts.map +1 -0
- package/dist/demo-control-plane.js +147 -0
- package/dist/demo-control-plane.js.map +1 -0
- package/dist/demo-worktree-dashboard.d.ts +2 -0
- package/dist/demo-worktree-dashboard.d.ts.map +1 -0
- package/dist/demo-worktree-dashboard.js +131 -0
- package/dist/demo-worktree-dashboard.js.map +1 -0
- package/dist/examples/01-hello-swarm.d.ts +13 -0
- package/dist/examples/01-hello-swarm.d.ts.map +1 -0
- package/dist/examples/01-hello-swarm.js +165 -0
- package/dist/examples/01-hello-swarm.js.map +1 -0
- package/dist/examples/02-fsm-pipeline.d.ts +20 -0
- package/dist/examples/02-fsm-pipeline.d.ts.map +1 -0
- package/dist/examples/02-fsm-pipeline.js +189 -0
- package/dist/examples/02-fsm-pipeline.js.map +1 -0
- package/dist/examples/03-parallel-agents.d.ts +21 -0
- package/dist/examples/03-parallel-agents.d.ts.map +1 -0
- package/dist/examples/03-parallel-agents.js +192 -0
- package/dist/examples/03-parallel-agents.js.map +1 -0
- package/dist/examples/05-code-review-swarm.d.ts +21 -0
- package/dist/examples/05-code-review-swarm.d.ts.map +1 -0
- package/dist/examples/05-code-review-swarm.js +1177 -0
- package/dist/examples/05-code-review-swarm.js.map +1 -0
- package/dist/examples/06-ai-pipeline-demo.d.ts +24 -0
- package/dist/examples/06-ai-pipeline-demo.d.ts.map +1 -0
- package/dist/examples/06-ai-pipeline-demo.js +263 -0
- package/dist/examples/06-ai-pipeline-demo.js.map +1 -0
- package/dist/examples/07-full-showcase.d.ts +27 -0
- package/dist/examples/07-full-showcase.d.ts.map +1 -0
- package/dist/examples/07-full-showcase.js +946 -0
- package/dist/examples/07-full-showcase.js.map +1 -0
- package/dist/examples/08-control-plane-stress-demo.d.ts +19 -0
- package/dist/examples/08-control-plane-stress-demo.d.ts.map +1 -0
- package/dist/examples/08-control-plane-stress-demo.js +186 -0
- package/dist/examples/08-control-plane-stress-demo.js.map +1 -0
- package/dist/examples/09-real-langchain.d.ts +19 -0
- package/dist/examples/09-real-langchain.d.ts.map +1 -0
- package/dist/examples/09-real-langchain.js +231 -0
- package/dist/examples/09-real-langchain.js.map +1 -0
- package/dist/examples/10-nemoclaw-sandbox-swarm.d.ts +16 -0
- package/dist/examples/10-nemoclaw-sandbox-swarm.d.ts.map +1 -0
- package/dist/examples/10-nemoclaw-sandbox-swarm.js +270 -0
- package/dist/examples/10-nemoclaw-sandbox-swarm.js.map +1 -0
- package/dist/examples/demo-runner.d.ts +2 -0
- package/dist/examples/demo-runner.d.ts.map +1 -0
- package/dist/examples/demo-runner.js +119 -0
- package/dist/examples/demo-runner.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/auth-guardian.js.map +1 -1
- package/dist/lib/control-plane.d.ts +128 -0
- package/dist/lib/control-plane.d.ts.map +1 -0
- package/dist/lib/control-plane.js +527 -0
- package/dist/lib/control-plane.js.map +1 -0
- package/dist/lib/coverage-reporter.js.map +1 -1
- package/dist/lib/goal-dsl.d.ts.map +1 -1
- package/dist/lib/goal-dsl.js +0 -1
- package/dist/lib/goal-dsl.js.map +1 -1
- package/dist/lib/work-tree-dashboard.d.ts +130 -0
- package/dist/lib/work-tree-dashboard.d.ts.map +1 -0
- package/dist/lib/work-tree-dashboard.js +583 -0
- package/dist/lib/work-tree-dashboard.js.map +1 -0
- package/dist/lib/work-tree-ui.d.ts +107 -0
- package/dist/lib/work-tree-ui.d.ts.map +1 -0
- package/dist/lib/work-tree-ui.js +333 -0
- package/dist/lib/work-tree-ui.js.map +1 -0
- package/dist/lib/work-tree.d.ts +184 -0
- package/dist/lib/work-tree.d.ts.map +1 -0
- package/dist/lib/work-tree.js +480 -0
- package/dist/lib/work-tree.js.map +1 -0
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +3 -2
- package/dist/security.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 07-full-showcase.ts — Network-AI Full Feature Showcase
|
|
4
|
+
* ────────────────────────────────────────────────────────
|
|
5
|
+
* Task: "Build a Payment Processing Service"
|
|
6
|
+
*
|
|
7
|
+
* Every major Network-AI feature fires during this run:
|
|
8
|
+
*
|
|
9
|
+
* ✦ JourneyFSM — enforces INTAKE→DESIGN→IMPLEMENT→REVIEW→DELIVER
|
|
10
|
+
* (agents blocked if they try to skip states)
|
|
11
|
+
* ✦ AuthGuardian — implementer tries to access PAYMENTS resource
|
|
12
|
+
* → BLOCKED → must request permission → grant issued
|
|
13
|
+
* ✦ SecureTokenManager— signed cryptographic token issued to implementer
|
|
14
|
+
* → validated before code can be committed
|
|
15
|
+
* ✦ FederatedBudget — per-agent token ceilings enforced in real time;
|
|
16
|
+
* one rogue agent gets cut off mid-task
|
|
17
|
+
* ✦ QualityGateAgent — AI-powered code review gates the implementation;
|
|
18
|
+
* low-quality output is rejected and loops back
|
|
19
|
+
* ✦ Debugger agent — post-fix hardening pass to recover residual verifier gaps
|
|
20
|
+
* ✦ Parallel agents — 3 specialist review agents run simultaneously
|
|
21
|
+
* ✦ Shared blackboard — all agents coordinate through a single store
|
|
22
|
+
* ✦ Audit log — every action timestamped, printed at the end
|
|
23
|
+
*
|
|
24
|
+
* Run:
|
|
25
|
+
* npx ts-node examples/07-full-showcase.ts
|
|
26
|
+
*/
|
|
27
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
30
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
31
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
32
|
+
}
|
|
33
|
+
Object.defineProperty(o, k2, desc);
|
|
34
|
+
}) : (function(o, m, k, k2) {
|
|
35
|
+
if (k2 === undefined) k2 = k;
|
|
36
|
+
o[k2] = m[k];
|
|
37
|
+
}));
|
|
38
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
39
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
40
|
+
}) : function(o, v) {
|
|
41
|
+
o["default"] = v;
|
|
42
|
+
});
|
|
43
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
44
|
+
var ownKeys = function(o) {
|
|
45
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
46
|
+
var ar = [];
|
|
47
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
48
|
+
return ar;
|
|
49
|
+
};
|
|
50
|
+
return ownKeys(o);
|
|
51
|
+
};
|
|
52
|
+
return function (mod) {
|
|
53
|
+
if (mod && mod.__esModule) return mod;
|
|
54
|
+
var result = {};
|
|
55
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
56
|
+
__setModuleDefault(result, mod);
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
})();
|
|
60
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
61
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
62
|
+
};
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
require("dotenv/config");
|
|
65
|
+
const fs = __importStar(require("node:fs"));
|
|
66
|
+
const path = __importStar(require("node:path"));
|
|
67
|
+
const openai_1 = __importDefault(require("openai"));
|
|
68
|
+
const __1 = require("..");
|
|
69
|
+
const federated_budget_1 = require("../lib/federated-budget");
|
|
70
|
+
const security_1 = require("../security");
|
|
71
|
+
// ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
72
|
+
const c = {
|
|
73
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
74
|
+
cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m',
|
|
75
|
+
blue: '\x1b[34m', magenta: '\x1b[35m', red: '\x1b[31m',
|
|
76
|
+
white: '\x1b[37m',
|
|
77
|
+
};
|
|
78
|
+
const W = 64;
|
|
79
|
+
function banner(title, colour = c.cyan) {
|
|
80
|
+
const line = '═'.repeat(W);
|
|
81
|
+
console.log(`\n${c.bold}${colour}${line}${c.reset}`);
|
|
82
|
+
console.log(`${c.bold}${colour} ${title}${c.reset}`);
|
|
83
|
+
console.log(`${c.bold}${colour}${line}${c.reset}\n`);
|
|
84
|
+
}
|
|
85
|
+
function tag(label, msg, colour = c.green) {
|
|
86
|
+
console.log(` ${colour}${c.bold}[${label.padEnd(20)}]${c.reset} ${msg}`);
|
|
87
|
+
}
|
|
88
|
+
function blocked(msg) {
|
|
89
|
+
console.log(` ${c.red}${c.bold}[BLOCKED ]${c.reset} ${c.red}${msg}${c.reset}`);
|
|
90
|
+
}
|
|
91
|
+
function granted(msg) {
|
|
92
|
+
console.log(` ${c.green}${c.bold}[GRANTED ]${c.reset} ${c.green}${msg}${c.reset}`);
|
|
93
|
+
}
|
|
94
|
+
function audit(msg) {
|
|
95
|
+
console.log(` ${c.dim}│ audit ${new Date().toISOString()} ${msg}${c.reset}`);
|
|
96
|
+
}
|
|
97
|
+
// ─── OpenAI ──────────────────────────────────────────────────────────────────
|
|
98
|
+
const openai = new openai_1.default({ apiKey: process.env.OPENAI_API_KEY });
|
|
99
|
+
async function llm(system, user, max = 800) {
|
|
100
|
+
const res = await openai.chat.completions.create({
|
|
101
|
+
model: 'gpt-4o-mini', temperature: 0.3, max_tokens: max,
|
|
102
|
+
messages: [{ role: 'system', content: system }, { role: 'user', content: user }],
|
|
103
|
+
});
|
|
104
|
+
return res.choices[0]?.message?.content?.trim() ?? '';
|
|
105
|
+
}
|
|
106
|
+
// ─── Shared blackboard (plain map — all agents read/write here) ───────────────
|
|
107
|
+
const BB = {};
|
|
108
|
+
const AUDIT_LOG = [];
|
|
109
|
+
function bbWrite(agent, key, value) {
|
|
110
|
+
BB[key] = value;
|
|
111
|
+
const entry = { ts: new Date().toISOString(), agent, action: 'blackboard_write', detail: `key="${key}"` };
|
|
112
|
+
AUDIT_LOG.push(entry);
|
|
113
|
+
audit(`${agent} wrote "${key}"`);
|
|
114
|
+
}
|
|
115
|
+
function bbRead(agent, key) {
|
|
116
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent, action: 'blackboard_read', detail: `key="${key}"` });
|
|
117
|
+
return BB[key];
|
|
118
|
+
}
|
|
119
|
+
function verifyCode(code) {
|
|
120
|
+
const missing = [];
|
|
121
|
+
const forbidden = [];
|
|
122
|
+
// Required patterns
|
|
123
|
+
const required = [
|
|
124
|
+
[/DUPLICATE_TRANSACTION/, 'errorCode DUPLICATE_TRANSACTION in processPayment'],
|
|
125
|
+
[/ALREADY_REFUNDED/, 'errorCode ALREADY_REFUNDED in refundPayment'],
|
|
126
|
+
[/TRANSACTION_NOT_FOUND/, 'errorCode TRANSACTION_NOT_FOUND in refundPayment'],
|
|
127
|
+
[/INVALID_INPUT/, 'errorCode INVALID_INPUT in processPayment'],
|
|
128
|
+
[/CAPACITY_EXCEEDED/, 'errorCode CAPACITY_EXCEEDED when maxTransactions exceeded'],
|
|
129
|
+
[/RATE_LIMITED/, 'errorCode RATE_LIMITED when requestsPerMinute exceeded'],
|
|
130
|
+
[/Map<string,\s*ITransaction>/, 'Private Map<string, ITransaction> store'],
|
|
131
|
+
[/getTransaction\s*\([^)]*\)\s*:\s*ITransaction\s*\|\s*undefined/, 'sync getTransaction(): ITransaction | undefined'],
|
|
132
|
+
[/this\.log\s*\(/, 'this.log() called inside a method'],
|
|
133
|
+
[/\.\.\.\s*transaction/, 'spread copy to avoid mutation (e.g. { ...transaction })'],
|
|
134
|
+
[/console\.log/, 'log() must call console.log (not a no-op stub)'],
|
|
135
|
+
];
|
|
136
|
+
// Forbidden patterns
|
|
137
|
+
const banned = [
|
|
138
|
+
[/:\s*any\b/, 'use of `any` type'],
|
|
139
|
+
[/throw\s+new\s+Error\s*\(\s*['"]Not implemented/i, 'Not implemented stub'],
|
|
140
|
+
[/\/\/\s*TODO:?\s*implement/i, 'TODO placeholder comment'],
|
|
141
|
+
];
|
|
142
|
+
for (const [pattern, label] of required) {
|
|
143
|
+
if (!pattern.test(code))
|
|
144
|
+
missing.push(label);
|
|
145
|
+
}
|
|
146
|
+
for (const [pattern, label] of banned) {
|
|
147
|
+
if (pattern.test(code))
|
|
148
|
+
forbidden.push(label);
|
|
149
|
+
}
|
|
150
|
+
return { pass: missing.length === 0 && forbidden.length === 0, missing, forbidden };
|
|
151
|
+
}
|
|
152
|
+
// ─── Score parser — extracts N from multiple "N/10" variants in coordinator report ───
|
|
153
|
+
function parseScore(report) {
|
|
154
|
+
// 1. Target the structured "score /10: N" line specifically (most precise)
|
|
155
|
+
let m = report.match(/score\s*\/\s*10[^:\n]*:\s*\*{0,2}\s*(\d+(?:\.\d+)?)/i);
|
|
156
|
+
if (m)
|
|
157
|
+
return parseFloat(m[1]);
|
|
158
|
+
// 2. "N/10" format — use LAST occurrence to avoid inline mentions like "rates 10/10"
|
|
159
|
+
const allN10 = [...report.matchAll(/(\d+(?:\.\d+)?)\s*\/\s*10/g)];
|
|
160
|
+
if (allN10.length > 0)
|
|
161
|
+
return parseFloat(allN10[allN10.length - 1][1]);
|
|
162
|
+
// 3. "Quality Score: N" or "score: N" near end of report
|
|
163
|
+
m = report.match(/(?:quality\s+)?score[^:\n]*:\s*\*{0,2}\s*(\d+(?:\.\d+)?)/i);
|
|
164
|
+
if (m)
|
|
165
|
+
return parseFloat(m[1]);
|
|
166
|
+
// 4. "N out of 10"
|
|
167
|
+
m = report.match(/(\d+(?:\.\d+)?)\s+out\s+of\s+10/i);
|
|
168
|
+
if (m)
|
|
169
|
+
return parseFloat(m[1]);
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
function computeDeterministicScore(input) {
|
|
173
|
+
const gates = [
|
|
174
|
+
[input.verifierPass, 'verifierPass'],
|
|
175
|
+
[input.debuggerPass, 'debuggerPass'],
|
|
176
|
+
[input.inDeliverState, 'inDeliverState'],
|
|
177
|
+
[input.hasSecurity, 'hasSecurity'],
|
|
178
|
+
[input.hasTests, 'hasTests'],
|
|
179
|
+
[input.hasDocs, 'hasDocs'],
|
|
180
|
+
[input.hasFixApplied, 'hasFixApplied'],
|
|
181
|
+
[input.hasCode, 'hasCode'],
|
|
182
|
+
];
|
|
183
|
+
const gatesPassed = gates.filter(([ok]) => ok).length;
|
|
184
|
+
const totalGates = gates.length;
|
|
185
|
+
const score = Number(((10 * gatesPassed) / totalGates).toFixed(1));
|
|
186
|
+
const failingGates = gates.filter(([ok]) => !ok).map(([, label]) => label);
|
|
187
|
+
return { score, gatesPassed, totalGates, failingGates };
|
|
188
|
+
}
|
|
189
|
+
// ─── MAIN ─────────────────────────────────────────────────────────────────────
|
|
190
|
+
async function main() {
|
|
191
|
+
banner('Network-AI — Full Feature Showcase', c.cyan);
|
|
192
|
+
console.log(` ${c.bold}Task:${c.reset} Build a production-ready Payment Processing Service\n`);
|
|
193
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
194
|
+
console.error(` ${c.red}✗ OPENAI_API_KEY not set in .env${c.reset}`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
// Shared closure — set by verifyCode step, read by aiReviewCallback
|
|
198
|
+
let lastVerifyPassed = false;
|
|
199
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
200
|
+
// INFRASTRUCTURE SETUP
|
|
201
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
202
|
+
banner('Infrastructure Setup', c.blue);
|
|
203
|
+
// 1. FSM — factory so outer loop can re-create a fresh machine each iteration
|
|
204
|
+
const makeFSM = (initialState) => new __1.JourneyFSM({
|
|
205
|
+
states: [
|
|
206
|
+
{ name: 'INTAKE', authorizedAgents: ['orchestrator'], description: 'Task intake and scoping' },
|
|
207
|
+
{ name: 'DESIGN', authorizedAgents: ['architect_agent'], description: 'Architecture design' },
|
|
208
|
+
{ name: 'IMPLEMENT', authorizedAgents: ['implementer_agent'], description: 'Code implementation' },
|
|
209
|
+
{ name: 'REVIEW', authorizedAgents: ['security_agent', 'test_agent', 'docs_agent'], description: 'Parallel review' },
|
|
210
|
+
{ name: 'FIX', authorizedAgents: ['fixer_agent', 'debugger_agent'], description: 'Targeted fix and debug hardening' },
|
|
211
|
+
{ name: 'DELIVER', authorizedAgents: ['coordinator_agent'], description: 'Final synthesis' },
|
|
212
|
+
],
|
|
213
|
+
transitions: [
|
|
214
|
+
{ from: 'INTAKE', event: 'design_started', to: 'DESIGN', allowedBy: 'orchestrator' },
|
|
215
|
+
{ from: 'DESIGN', event: 'design_done', to: 'IMPLEMENT', allowedBy: 'architect_agent' },
|
|
216
|
+
{ from: 'IMPLEMENT', event: 'code_ready', to: 'REVIEW', allowedBy: 'implementer_agent' },
|
|
217
|
+
{ from: 'REVIEW', event: 'fix_started', to: 'FIX', allowedBy: '*' },
|
|
218
|
+
{ from: 'FIX', event: 'fix_done', to: 'DELIVER', allowedBy: 'debugger_agent' },
|
|
219
|
+
],
|
|
220
|
+
initialState,
|
|
221
|
+
onTransition: (r) => tag('FSM', `${r.previousState} → ${r.currentState}`, c.yellow),
|
|
222
|
+
onViolation: (v) => blocked(`FSM violation: ${v.agentId} cannot act in state ${v.currentState}`),
|
|
223
|
+
});
|
|
224
|
+
let fsm = makeFSM('INTAKE');
|
|
225
|
+
tag('FSM', `Initialized — current state: ${c.bold}${fsm.state}${c.reset}`, c.yellow);
|
|
226
|
+
// 2. AuthGuardian — protects PAYMENTS resource
|
|
227
|
+
const guardian = new __1.AuthGuardian({
|
|
228
|
+
trustLevels: [
|
|
229
|
+
{ agentId: 'orchestrator', trustLevel: 0.95, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
230
|
+
{ agentId: 'architect_agent', trustLevel: 0.85, allowedNamespaces: ['design:'], allowedResources: ['DATABASE'] },
|
|
231
|
+
{ agentId: 'implementer_agent', trustLevel: 0.75, allowedNamespaces: ['code:'], allowedResources: ['DATABASE'] },
|
|
232
|
+
{ agentId: 'security_agent', trustLevel: 0.90, allowedNamespaces: ['review:'], allowedResources: ['DATABASE', 'PAYMENTS'] },
|
|
233
|
+
{ agentId: 'fixer_agent', trustLevel: 0.85, allowedNamespaces: ['code:'], allowedResources: ['DATABASE', 'PAYMENTS'] },
|
|
234
|
+
{ agentId: 'debugger_agent', trustLevel: 0.90, allowedNamespaces: ['code:'], allowedResources: ['DATABASE', 'PAYMENTS'] },
|
|
235
|
+
{ agentId: 'coordinator_agent', trustLevel: 0.95, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
236
|
+
],
|
|
237
|
+
});
|
|
238
|
+
tag('AuthGuardian', 'PAYMENTS resource is protected', c.red);
|
|
239
|
+
// 3. SecureTokenManager — signs inter-agent credentials
|
|
240
|
+
const tokenManager = new security_1.SecureTokenManager({ maxTokenAge: 5 * 60 * 1000 }); // 5 min
|
|
241
|
+
tag('TokenManager', 'HMAC-SHA256 signing active', c.magenta);
|
|
242
|
+
// 4. FederatedBudget — shared pool + a tight budget for rogue_agent
|
|
243
|
+
const budget = new federated_budget_1.FederatedBudget({ ceiling: 100_000 });
|
|
244
|
+
const rogueBudget = new federated_budget_1.FederatedBudget({ ceiling: 300 }); // deliberately tiny
|
|
245
|
+
tag('Budget', '100k ceiling total; rogue_agent has separate 300-token ceiling', c.blue);
|
|
246
|
+
// 5. QualityGateAgent — AI-powered code review
|
|
247
|
+
// qualityThreshold > 1.0 means structural checks alone can never pass it,
|
|
248
|
+
// so every submission is routed to the aiReviewCallback for a real LLM verdict.
|
|
249
|
+
const qualityGate = new __1.QualityGateAgent({
|
|
250
|
+
qualityThreshold: 1.1, // force AI review path every time
|
|
251
|
+
autoRejectThreshold: 0.2,
|
|
252
|
+
aiReviewCallback: async (key, value, _entryType, _ctx) => {
|
|
253
|
+
const code = typeof value === 'string' ? value : JSON.stringify(value);
|
|
254
|
+
const verifiedItems = lastVerifyPassed
|
|
255
|
+
? 'NOTE: Static analysis already confirmed: DUPLICATE_TRANSACTION, ALREADY_REFUNDED, TRANSACTION_NOT_FOUND, INVALID_INPUT, CAPACITY_EXCEEDED, typed Map store, sync getTransaction, no any types, spread copy are ALL present. Do NOT flag these.\n\n'
|
|
256
|
+
: '';
|
|
257
|
+
// Pull live blackboard context so the reviewer can check against the design
|
|
258
|
+
const designCtx = BB['design:interfaces']
|
|
259
|
+
? `\n\n=== DESIGN SPEC (from blackboard) ===\n${BB['design:interfaces'].slice(0, 800)}\n=== END SPEC ===`
|
|
260
|
+
: '';
|
|
261
|
+
const secCtx = BB['review:security']
|
|
262
|
+
? `\n\n=== SECURITY FINDINGS (from blackboard) ===\n${BB['review:security'].slice(0, 600)}\n=== END SECURITY ===`
|
|
263
|
+
: '';
|
|
264
|
+
const verdict = await llm('You are a strict TypeScript code reviewer. Reply with ONLY a JSON object, no markdown.', `Review this PaymentProcessor implementation against the checklist below.
|
|
265
|
+
Return exactly: { "approved": boolean, "confidence": number (0-1), "feedback": string (1 sentence), "suggestedFixes": string[] }
|
|
266
|
+
|
|
267
|
+
${verifiedItems}Focus your review on:
|
|
268
|
+
1. Logical correctness — does processPayment actually set status to Completed?
|
|
269
|
+
2. Is refundPayment mutating the original object or using a spread copy?
|
|
270
|
+
3. Is maxTransactions actually enforced (not just stored)?
|
|
271
|
+
4. Does log() get called inside processPayment and refundPayment?
|
|
272
|
+
5. Are there any runtime errors (e.g. accessing .id on undefined)?
|
|
273
|
+
6. Does the implementation satisfy every method and property in the DESIGN SPEC?${designCtx}${secCtx}
|
|
274
|
+
|
|
275
|
+
Code:
|
|
276
|
+
${code.slice(0, 3500)}`, 300);
|
|
277
|
+
try {
|
|
278
|
+
const parsed = JSON.parse(verdict);
|
|
279
|
+
return parsed;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// fallback if LLM wraps in markdown
|
|
283
|
+
const jsonMatch = verdict.match(/\{[\s\S]*\}/);
|
|
284
|
+
if (jsonMatch)
|
|
285
|
+
return JSON.parse(jsonMatch[0]);
|
|
286
|
+
return { approved: true, confidence: 0.5, feedback: 'Could not parse review response' };
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
tag('QualityGate', 'AI review on every submission (structural auto-reject < 0.2)', c.magenta);
|
|
291
|
+
// 6. Swarm Orchestrator
|
|
292
|
+
const adapter = new __1.CustomAdapter();
|
|
293
|
+
const orchestrator = (0, __1.createSwarmOrchestrator)({
|
|
294
|
+
qualityThreshold: 0,
|
|
295
|
+
trustLevels: [
|
|
296
|
+
{ agentId: 'orchestrator', trustLevel: 0.95, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
297
|
+
{ agentId: 'architect_agent', trustLevel: 0.85, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
298
|
+
{ agentId: 'implementer_agent', trustLevel: 0.75, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
299
|
+
{ agentId: 'security_agent', trustLevel: 0.90, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
300
|
+
{ agentId: 'test_agent', trustLevel: 0.80, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
301
|
+
{ agentId: 'docs_agent', trustLevel: 0.80, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
302
|
+
{ agentId: 'fixer_agent', trustLevel: 0.85, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
303
|
+
{ agentId: 'debugger_agent', trustLevel: 0.90, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
304
|
+
{ agentId: 'coordinator_agent', trustLevel: 0.95, allowedNamespaces: ['*'], allowedResources: ['*'] },
|
|
305
|
+
],
|
|
306
|
+
});
|
|
307
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
308
|
+
// DEMO: ROGUE AGENT — Budget enforcement
|
|
309
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
310
|
+
banner('Demo: Budget Enforcement — Rogue Agent Cut Off', c.red);
|
|
311
|
+
adapter.registerHandler('rogue_agent', async () => {
|
|
312
|
+
tag('rogue_agent', 'Trying to spend 500 tokens (ceiling: 300)...', c.red);
|
|
313
|
+
tag('rogue_agent', `Budget remaining: ${rogueBudget.remaining()} tokens`, c.dim);
|
|
314
|
+
const result = rogueBudget.spend('rogue_agent', 500);
|
|
315
|
+
if (!result.allowed) {
|
|
316
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'rogue_agent', action: 'budget_exceeded', detail: result.deniedReason ?? 'over limit' });
|
|
317
|
+
blocked(`rogue_agent budget exceeded — task terminated. Reason: ${result.deniedReason}`);
|
|
318
|
+
return { agent: 'rogue_agent', killed: true, reason: result.deniedReason };
|
|
319
|
+
}
|
|
320
|
+
return { agent: 'rogue_agent', killed: false };
|
|
321
|
+
});
|
|
322
|
+
await orchestrator.addAdapter(adapter);
|
|
323
|
+
const ctx = { agentId: 'orchestrator', taskId: 'showcase-007', sessionId: 'full-demo' };
|
|
324
|
+
await orchestrator.execute('delegate_task', {
|
|
325
|
+
targetAgent: 'custom:rogue_agent', taskPayload: { instruction: 'run', context: {} },
|
|
326
|
+
}, ctx);
|
|
327
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
328
|
+
// PHASE 1: INTAKE → DESIGN (FSM transition)
|
|
329
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
330
|
+
banner('Phase 1 — Intake & Architecture Design', c.yellow);
|
|
331
|
+
// Try to skip — architect acting in INTAKE state (violation)
|
|
332
|
+
const skipAttempt = fsm.canAgentAct('architect_agent');
|
|
333
|
+
if (!skipAttempt) {
|
|
334
|
+
blocked(`architect_agent tried to act in state ${fsm.state} — FSM denied`);
|
|
335
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'architect_agent', action: 'fsm_violation', detail: `tried to act in ${fsm.state}` });
|
|
336
|
+
}
|
|
337
|
+
// Correct: orchestrator fires the transition
|
|
338
|
+
fsm.transition('design_started', 'orchestrator');
|
|
339
|
+
adapter.registerHandler('architect_agent', async () => {
|
|
340
|
+
if (!fsm.canAgentAct('architect_agent')) {
|
|
341
|
+
blocked('architect_agent not authorized in current FSM state');
|
|
342
|
+
return { error: 'fsm_blocked' };
|
|
343
|
+
}
|
|
344
|
+
tag('architect_agent', 'Designing payment service interface...', c.cyan);
|
|
345
|
+
const spent = budget.spend('architect_agent', 500);
|
|
346
|
+
if (!spent.allowed) {
|
|
347
|
+
blocked(`architect_agent out of budget`);
|
|
348
|
+
return { error: 'budget' };
|
|
349
|
+
}
|
|
350
|
+
const design = await llm('You are a senior TypeScript architect. Output ONLY TypeScript interface/enum definitions — no classes, no implementation, no markdown fences.', `Design the full type contract for a Payment Processing Service. Output exactly:
|
|
351
|
+
- enum PaymentMethod { CreditCard, DebitCard, BankTransfer }
|
|
352
|
+
- enum TransactionStatus { Pending, Completed, Refunded, Failed }
|
|
353
|
+
- interface ITransaction { id: string; amount: number; currency: string; method: PaymentMethod; status: TransactionStatus; customerId: string; timestamp: Date; }
|
|
354
|
+
- interface IPaymentResult { transactionId: string; success: boolean; message: string; errorCode?: string; }
|
|
355
|
+
- interface IPaymentProcessor {
|
|
356
|
+
processPayment(transaction: ITransaction): Promise<IPaymentResult>;
|
|
357
|
+
refundPayment(transactionId: string): Promise<IPaymentResult>;
|
|
358
|
+
getTransaction(id: string): ITransaction | undefined;
|
|
359
|
+
}
|
|
360
|
+
Include JSDoc on each member. Keep total under 40 lines.`);
|
|
361
|
+
bbWrite('architect_agent', 'design:interfaces', design);
|
|
362
|
+
fsm.transition('design_done', 'architect_agent');
|
|
363
|
+
tag('architect_agent', `Design complete (${design.length} chars) — FSM → IMPLEMENT`, c.green);
|
|
364
|
+
return { agent: 'architect_agent', done: true };
|
|
365
|
+
});
|
|
366
|
+
await orchestrator.execute('delegate_task', {
|
|
367
|
+
targetAgent: 'custom:architect_agent', taskPayload: { instruction: 'Design payment service', context: {} },
|
|
368
|
+
}, ctx);
|
|
369
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
370
|
+
// OUTER IMPROVEMENT LOOP — phases 2-5 repeat until score ≥ 9.5 or max 3 iters
|
|
371
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
372
|
+
let outerLoopFeedback = ''; // coordinator critique carried into next implementer run
|
|
373
|
+
let outerBestScore = 0;
|
|
374
|
+
let outerBestCode = '';
|
|
375
|
+
const MAX_OUTER = 3;
|
|
376
|
+
for (let outerIter = 0; outerIter < MAX_OUTER; outerIter++) {
|
|
377
|
+
if (outerIter > 0) {
|
|
378
|
+
banner(`Outer Improvement Loop — Iteration ${outerIter + 1}/${MAX_OUTER} (prev score: ${outerBestScore}/10)`, c.yellow);
|
|
379
|
+
tag('outer_loop', 'Re-running phases 2→5 with coordinator feedback — targeting 10/10', c.cyan);
|
|
380
|
+
// Fresh FSM starting at IMPLEMENT (architect design already on BB)
|
|
381
|
+
fsm = makeFSM('IMPLEMENT');
|
|
382
|
+
// Clear stale BB keys; preserve design:interfaces so architect need not re-run
|
|
383
|
+
for (const k of ['code:payment_service', 'review:security', 'review:tests', 'review:docs',
|
|
384
|
+
'fix:applied', 'qualityGate:lastFeedback', 'final:report']) {
|
|
385
|
+
delete BB[k];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
389
|
+
// PHASE 2: IMPLEMENT — AuthGuardian blocks PAYMENTS, token issued
|
|
390
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
391
|
+
banner('Phase 2 — Implementation + Permission Gate', c.red);
|
|
392
|
+
// Direct implementer function — avoids orchestrator dispatch which doesn't
|
|
393
|
+
// guarantee the handler promise is awaited before returning, causing an FSM
|
|
394
|
+
// race when Phase 3 fires fix_started while the gate is still pending.
|
|
395
|
+
const runImplementerAgent = async () => {
|
|
396
|
+
if (!fsm.canAgentAct('implementer_agent')) {
|
|
397
|
+
blocked(`implementer_agent not authorized in FSM state ${fsm.state}`);
|
|
398
|
+
return { error: 'fsm_blocked' };
|
|
399
|
+
}
|
|
400
|
+
tag('implementer_agent', 'Reading design from blackboard...', c.cyan);
|
|
401
|
+
const design = bbRead('implementer_agent', 'design:interfaces');
|
|
402
|
+
// --- Attempt to access PAYMENTS without permission ---
|
|
403
|
+
tag('implementer_agent', 'Requesting access to PAYMENTS resource...', c.yellow);
|
|
404
|
+
const permCheck = await guardian.requestPermission('implementer_agent', 'PAYMENTS', 'Need to implement payment processing logic');
|
|
405
|
+
if (!permCheck.granted) {
|
|
406
|
+
blocked(`PAYMENTS access denied for implementer_agent — ${permCheck.reason}`);
|
|
407
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'implementer_agent', action: 'permission_denied', detail: 'PAYMENTS' });
|
|
408
|
+
// Correct path: request via orchestrator (higher trust)
|
|
409
|
+
tag('orchestrator', 'Escalating permission request on behalf of implementer...', c.yellow);
|
|
410
|
+
const escalated = await guardian.requestPermission('orchestrator', 'PAYMENTS', 'Implement core payment processing service as required by architecture design', 'read-write:transactions');
|
|
411
|
+
if (escalated.granted) {
|
|
412
|
+
granted(`Orchestrator obtained PAYMENTS grant: ${escalated.grantToken?.slice(0, 16)}...`);
|
|
413
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'orchestrator', action: 'permission_granted', detail: `PAYMENTS token: ${escalated.grantToken?.slice(0, 16)}` });
|
|
414
|
+
// Issue a signed inter-agent token so implementer can prove authorization
|
|
415
|
+
const secureToken = tokenManager.generateToken('implementer_agent', 'PAYMENTS', 'implement:payment-service');
|
|
416
|
+
const validation = tokenManager.validateToken(secureToken);
|
|
417
|
+
tag('TokenManager', `Signed token issued to implementer_agent`, c.magenta);
|
|
418
|
+
tag('TokenManager', `Token ID: ${secureToken.tokenId.slice(0, 16)}... valid=${validation.valid}`, c.magenta);
|
|
419
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'tokenManager', action: 'token_issued', detail: secureToken.tokenId.slice(0, 16) });
|
|
420
|
+
bbWrite('orchestrator', 'auth:payment_token', secureToken.tokenId);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Now implement the code
|
|
424
|
+
tag('implementer_agent', 'Writing payment service implementation...', c.cyan);
|
|
425
|
+
const spent = budget.spend('implementer_agent', 1200);
|
|
426
|
+
if (!spent.allowed) {
|
|
427
|
+
blocked('implementer_agent budget exceeded');
|
|
428
|
+
return { error: 'budget' };
|
|
429
|
+
}
|
|
430
|
+
const code = await llm('You are a senior TypeScript developer writing production-ready code. Output ONLY valid TypeScript — no markdown fences, no prose, no comments explaining what to do next.', `Implement a PaymentProcessor class that satisfies IPaymentProcessor using these types:\n\n${design}\n\nStrict requirements — every item is mandatory:\n1. NO \`any\` types anywhere — use the interfaces above throughout\n2. processPayment: validate that amount > 0, id is non-empty string, currency must be one of ['USD','EUR','GBP','JPY','CAD','AUD','CHF','CNY'] — return success:false errorCode:'INVALID_INPUT' if invalid; detect duplicate id and return errorCode:'DUPLICATE_TRANSACTION'\n3. refundPayment (not refundTransaction): look up by id, set status to Refunded on a spread COPY ({ ...tx, status: 'Refunded' }), return success:false errorCode:'TRANSACTION_NOT_FOUND' if missing, errorCode:'ALREADY_REFUNDED' if status already Refunded\n4. getTransaction: sync, returns ITransaction | undefined\n5. Private Map<string, ITransaction> store\n6. Constructor accepts a typed config object: { maxTransactions?: number; currency?: string; requestsPerMinute?: number }. If maxTransactions is set and store.size >= maxTransactions, return errorCode:'CAPACITY_EXCEEDED' BEFORE processing. If requestsPerMinute is set, track calls per minute and return errorCode:'RATE_LIMITED' success:false when exceeded; reset count every 60 seconds.\n7. private log(msg: string): void MUST call console.log(\`[\${new Date().toISOString()}] \${msg}\`) — the body must contain console.log(...); log masked ids only (first 4 chars + ****)\n8. Full JSDoc on class and every public method\n9. Export the class as default export${outerLoopFeedback ? `\n\nPREVIOUS COORDINATOR CRITIQUE — fix ALL of these in your implementation:\n${outerLoopFeedback}` : ''}`);
|
|
431
|
+
bbWrite('implementer_agent', 'code:payment_service', code);
|
|
432
|
+
// ── Layers 1+2: verifyCode → qualityGate loop (max 3 passes, rotating agents)
|
|
433
|
+
const MAX_ITER = 3;
|
|
434
|
+
let activeCode = code;
|
|
435
|
+
let approved = false;
|
|
436
|
+
const accumulatedFeedback = [];
|
|
437
|
+
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
438
|
+
const passLabel = `Pass ${iter + 1}/${MAX_ITER}`;
|
|
439
|
+
// ── Layer 1: deterministic pattern checks ──────────────────────────────
|
|
440
|
+
tag('verifyCode', `${passLabel}: running deterministic checks...`, c.yellow);
|
|
441
|
+
const check = verifyCode(activeCode);
|
|
442
|
+
lastVerifyPassed = check.pass;
|
|
443
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'verifyCode', action: check.pass ? 'verify_pass' : 'verify_fail', detail: check.missing.concat(check.forbidden).join('; ') || 'all checks passed' });
|
|
444
|
+
if (!check.pass) {
|
|
445
|
+
check.missing.forEach(m => tag('verifyCode', `✗ Missing: ${m}`, c.red));
|
|
446
|
+
check.forbidden.forEach(f => tag('verifyCode', `✗ Forbidden: ${f}`, c.red));
|
|
447
|
+
blocked(`verifyCode ${passLabel} FAILED — ${check.missing.length} missing, ${check.forbidden.length} forbidden`);
|
|
448
|
+
if (iter < MAX_ITER - 1) {
|
|
449
|
+
const fixer = iter === 0 ? 'implementer_agent' : 'repair_agent';
|
|
450
|
+
tag(fixer, `${passLabel}: fixing pattern issues...`, c.yellow);
|
|
451
|
+
budget.spend('implementer_agent', 700);
|
|
452
|
+
const missingList = check.missing.map(m => `- MISSING: ${m}`).join('\n');
|
|
453
|
+
const forbiddenList = check.forbidden.map(f => `- FORBIDDEN: ${f}`).join('\n');
|
|
454
|
+
// If the ONLY missing pattern is console.log inside log(), fix ONLY that method body
|
|
455
|
+
const onlyLogMissing = check.missing.length === 1 && check.missing[0].includes('console.log') && check.forbidden.length === 0;
|
|
456
|
+
if (onlyLogMissing) {
|
|
457
|
+
// Deterministic injection: find the log method opening '{' and
|
|
458
|
+
// prepend console.log(...). Handles: method syntax, arrow functions,
|
|
459
|
+
// readonly variants, and missing 'private' keyword.
|
|
460
|
+
tag(fixer, `${passLabel}: injecting console.log into log() body (deterministic)`, c.green);
|
|
461
|
+
// Find the log method definition using indexOf (most reliable).
|
|
462
|
+
// Try common patterns in order: private, protected, no modifier, arrow fn.
|
|
463
|
+
const candidates = [
|
|
464
|
+
activeCode.indexOf('private log('),
|
|
465
|
+
activeCode.indexOf('private log ='),
|
|
466
|
+
activeCode.indexOf('private readonly log'),
|
|
467
|
+
activeCode.indexOf('protected log('),
|
|
468
|
+
activeCode.indexOf(' log('), // 2-space indent, no access modifier
|
|
469
|
+
activeCode.indexOf(' log('), // 4-space indent, no access modifier
|
|
470
|
+
activeCode.search(/[\r\n][ \t]+(?:(?:private|protected|public)\s+)?log\s*[=(]?\s*\(/),
|
|
471
|
+
].filter(n => n !== -1);
|
|
472
|
+
const logMethodIdx = candidates.length > 0 ? Math.min(...candidates) : -1;
|
|
473
|
+
if (logMethodIdx !== -1) {
|
|
474
|
+
const openBrace = activeCode.indexOf('{', logMethodIdx);
|
|
475
|
+
if (openBrace !== -1) {
|
|
476
|
+
// Extract param name from between the parens closest to logMethodIdx
|
|
477
|
+
const sigSlice = activeCode.slice(logMethodIdx, openBrace);
|
|
478
|
+
const paramMatch = sigSlice.match(/\(([^):,\s)][^):,)]*)/);
|
|
479
|
+
const param = paramMatch ? paramMatch[1].trim() : 'msg';
|
|
480
|
+
const injection = `\n console.log(\`[\${new Date().toISOString()}] \${${param}}\`);`;
|
|
481
|
+
activeCode = activeCode.slice(0, openBrace + 1) + injection + activeCode.slice(openBrace + 1);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
// Log method not found — inject into PaymentProcessor class body,
|
|
486
|
+
// not at file end (prevents malformed placement outside class).
|
|
487
|
+
let classStart = activeCode.search(/class\s+PaymentProcessor\b/);
|
|
488
|
+
if (classStart < 0)
|
|
489
|
+
classStart = activeCode.search(/class\s+\w+\b/);
|
|
490
|
+
const classOpen = classStart >= 0 ? activeCode.indexOf('{', classStart) : -1;
|
|
491
|
+
let classClose = -1;
|
|
492
|
+
if (classOpen !== -1) {
|
|
493
|
+
let depth = 0;
|
|
494
|
+
for (let i = classOpen; i < activeCode.length; i++) {
|
|
495
|
+
const ch = activeCode[i];
|
|
496
|
+
if (ch === '{')
|
|
497
|
+
depth++;
|
|
498
|
+
if (ch === '}') {
|
|
499
|
+
depth--;
|
|
500
|
+
if (depth === 0) {
|
|
501
|
+
classClose = i;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (classClose !== -1) {
|
|
508
|
+
const newLogMethod = '\n\n private log(msg: string): void {\n console.log(`[${new Date().toISOString()}] ${msg}`);\n }';
|
|
509
|
+
activeCode = activeCode.slice(0, classClose) + newLogMethod + '\n' + activeCode.slice(classClose);
|
|
510
|
+
tag(fixer, `${passLabel}: log() not defined — injected into PaymentProcessor class body`, c.green);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
// Last deterministic fallback: append a minimal log() method near
|
|
514
|
+
// file end to satisfy verifyCode and avoid destructive LLM rewrites.
|
|
515
|
+
const trailer = '\n\nprivate log(msg: string): void {\n console.log(`[${new Date().toISOString()}] ${msg}`);\n}\n';
|
|
516
|
+
activeCode = `${activeCode.trimEnd()}${trailer}`;
|
|
517
|
+
tag(fixer, `${passLabel}: could not locate class — appended minimal log() fallback`, c.yellow);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
activeCode = await llm('You are a TypeScript developer. Output ONLY valid TypeScript — no markdown fences, no prose.', `Fix these pattern issues in the PaymentProcessor — do NOT remove any methods, error codes, or class structure:\n\n${missingList}\n${forbiddenList}\n\nOutput the complete corrected file.\n\nCode:\n${activeCode}`);
|
|
523
|
+
}
|
|
524
|
+
bbWrite(fixer, 'code:payment_service', activeCode);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
tag('verifyCode', `⚠ All ${MAX_ITER} passes exhausted on pattern checks — proceeding`, c.red);
|
|
528
|
+
}
|
|
529
|
+
continue; // re-check from top of loop
|
|
530
|
+
}
|
|
531
|
+
tag('verifyCode', `✓ ${passLabel}: all 9 pattern checks passed`, c.green);
|
|
532
|
+
// ── Layer 2: AI quality gate ───────────────────────────────────────────
|
|
533
|
+
const gateAgent = iter === 0 ? 'implementer_agent' : 'repair_agent';
|
|
534
|
+
tag('QualityGate', `${passLabel}: running AI review (blackboard-aware)...`, c.magenta);
|
|
535
|
+
const gateResult = await qualityGate.gate('code:payment_service', activeCode, gateAgent, { type: 'typescript' });
|
|
536
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'qualityGate', action: `gate_${gateResult.decision}`, detail: gateResult.reviewNotes.join('; ') });
|
|
537
|
+
if (gateResult.decision === 'approve') {
|
|
538
|
+
tag('QualityGate', `✓ ${passLabel}: APPROVED — ${gateResult.reviewNotes[0]}`, c.green);
|
|
539
|
+
approved = true;
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
// Accumulate feedback across passes
|
|
543
|
+
const roundFeedback = gateResult.reviewNotes
|
|
544
|
+
.filter(n => n.startsWith('AI feedback:') || n.startsWith('Suggested fixes:'))
|
|
545
|
+
.join('\n');
|
|
546
|
+
accumulatedFeedback.push(`[${passLabel}] ${roundFeedback}`);
|
|
547
|
+
blocked(`QualityGate ${gateResult.decision.toUpperCase()} (${passLabel}) — ${gateResult.reviewNotes.find(n => n.startsWith('AI feedback:')) ?? gateResult.reviewNotes[0]}`);
|
|
548
|
+
if (iter < MAX_ITER - 1) {
|
|
549
|
+
if (iter === 0) {
|
|
550
|
+
// Pass 2: implementer re-reads accumulated feedback + design context
|
|
551
|
+
tag('implementer_agent', `Pass 2/${MAX_ITER}: re-implementing with review feedback...`, c.yellow);
|
|
552
|
+
budget.spend('implementer_agent', 800);
|
|
553
|
+
const designCtx = BB['design:interfaces'] ? `\n\nTarget interfaces:\n${BB['design:interfaces'].slice(0, 600)}` : '';
|
|
554
|
+
activeCode = await llm('You are a TypeScript developer. Fix the issues and output ONLY valid TypeScript, no markdown fences.', `Fix this PaymentProcessor based on review feedback.\n\nFEEDBACK:\n${accumulatedFeedback.join('\n\n')}\n\nMandatory fixes:\n1. Spread copy in refundPayment — store a copy, not the original\n2. maxTransactions enforced with CAPACITY_EXCEEDED\n3. status set to Completed in processPayment${designCtx}\n\nCode:\n${activeCode}`);
|
|
555
|
+
bbWrite('implementer_agent', 'code:payment_service', activeCode);
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
// Pass 3: repair_agent — completely fresh specialist perspective
|
|
559
|
+
tag('repair_agent', `Pass 3/${MAX_ITER}: repair specialist rewriting problem methods...`, c.yellow);
|
|
560
|
+
budget.spend('implementer_agent', 1000);
|
|
561
|
+
const designCtx = BB['design:interfaces'] ? `\n\nInterfaces to satisfy:\n${BB['design:interfaces'].slice(0, 800)}` : '';
|
|
562
|
+
activeCode = await llm('You are a TypeScript code repair specialist. You receive code that has failed two review cycles. You must rewrite ONLY the bodies of processPayment and refundPayment — keep every other line of the file identical. Output ONLY the complete valid TypeScript file, no markdown fences, no omissions.', `This PaymentProcessor failed ${iter + 1} review(s). Fix ONLY processPayment and refundPayment — do NOT change the class name, fields, constructor, getTransaction, log, imports, or exports.\n\nRules:\n- private store: Map<string, ITransaction> MUST remain\n- getTransaction(): ITransaction | undefined MUST remain unchanged\n- private log(msg): MUST call console.log(\`[\${new Date().toISOString()}] \${msg}\`) — do not touch its body if it already does this\n- processPayment: validate currency against ['USD','EUR','GBP','JPY','CAD','AUD','CHF','CNY'], enforce maxTransactions (CAPACITY_EXCEEDED before store.set), enforce requestsPerMinute (RATE_LIMITED), set status to Completed on a SPREAD COPY ({ ...tx, status: 'Completed' }), call this.log(), detect DUPLICATE_TRANSACTION\n- refundPayment: look up by id, set status to Refunded on a SPREAD COPY, return ALREADY_REFUNDED if already refunded, call this.log() on ALL return paths including errors\n- processPayment: call this.log() on EVERY return path including INVALID_INPUT, DUPLICATE_TRANSACTION, CAPACITY_EXCEEDED, RATE_LIMITED errors\n\nAccumulated feedback:\n${accumulatedFeedback.join('\n\n')}${designCtx}\n\nFull file to fix:\n${activeCode}`);
|
|
563
|
+
bbWrite('repair_agent', 'code:payment_service', activeCode);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
tag('QualityGate', `⚠ All ${MAX_ITER} passes exhausted — proceeding with best-effort code`, c.red);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (!approved) {
|
|
571
|
+
tag('implementer_agent', `⚠ Quality gate not fully satisfied after ${MAX_ITER} passes — proceeding`, c.yellow);
|
|
572
|
+
}
|
|
573
|
+
// Store accumulated gate feedback on BB so fixer_agent can address it directly
|
|
574
|
+
if (accumulatedFeedback.length > 0) {
|
|
575
|
+
BB['qualityGate:lastFeedback'] = accumulatedFeedback.join('\n\n');
|
|
576
|
+
}
|
|
577
|
+
fsm.transition('code_ready', 'implementer_agent');
|
|
578
|
+
tag('implementer_agent', 'Implementation committed — FSM → REVIEW', c.green);
|
|
579
|
+
return { agent: 'implementer_agent', done: true };
|
|
580
|
+
};
|
|
581
|
+
adapter.registerHandler('implementer_agent', runImplementerAgent);
|
|
582
|
+
await runImplementerAgent();
|
|
583
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
584
|
+
// PHASE 3: PARALLEL REVIEW — 3 agents simultaneously
|
|
585
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
586
|
+
banner('Phase 3 — Parallel Review (security + tests + docs)', c.blue);
|
|
587
|
+
console.log(` ${c.dim}3 specialist agents running simultaneously...${c.reset}\n`);
|
|
588
|
+
// Define review tasks as plain async functions so we can await them directly
|
|
589
|
+
// (spawn_parallel_agents dispatches via the adapter registry but doesn't
|
|
590
|
+
// guarantee handler resolution before it returns, causing an FSM race if
|
|
591
|
+
// the handlers check fsm.canAgentAct after the FSM has already transitioned).
|
|
592
|
+
async function runSecurityReview() {
|
|
593
|
+
const code = bbRead('security_agent', 'code:payment_service');
|
|
594
|
+
budget.spend('security_agent', 800);
|
|
595
|
+
const review = await llm('You are a payment security expert (PCI-DSS). Be concise.', `Security audit this payment code. Format: SEVERITY [CRITICAL/HIGH/MED/LOW]: issue\n\n${code}`);
|
|
596
|
+
bbWrite('security_agent', 'review:security', review);
|
|
597
|
+
tag('security_agent', `Security audit done`, c.red);
|
|
598
|
+
}
|
|
599
|
+
async function runTestReview() {
|
|
600
|
+
const code = bbRead('test_agent', 'code:payment_service');
|
|
601
|
+
const design = bbRead('test_agent', 'design:interfaces');
|
|
602
|
+
budget.spend('test_agent', 800);
|
|
603
|
+
const tests = await llm('You are a QA engineer. Output ONLY a complete, self-contained TypeScript Jest test file — no markdown fences, no prose.', `Write a complete Jest test file for this PaymentProcessor implementation.\n\nTYPES (paste inline at top of file as-is — do not import from external package):\n${design}\n\nIMPLEMENTATION:\n${code}\n\nRules:\n1. First line must be: // @ts-nocheck\n2. Paste the full types block inline at the top (no external imports)\n3. Paste the full PaymentProcessor class inline after types (copy it verbatim from the implementation above)\n4. Use describe('PaymentProcessor', () => { ... }) with beforeEach creating a fresh instance\n5. Write exactly 8 tests covering: happy path processPayment, invalid amount rejected, empty id rejected, duplicate transaction rejected, successful refundPayment, refundPayment on unknown id, already-refunded returns error, getTransaction returns correct record\n6. Use jest.fn() mocks where needed; no external dependencies`, 1600);
|
|
604
|
+
bbWrite('test_agent', 'review:tests', tests);
|
|
605
|
+
tag('test_agent', `Tests written (${tests.split('\n').length} lines)`, c.blue);
|
|
606
|
+
}
|
|
607
|
+
async function runDocsReview() {
|
|
608
|
+
const code = bbRead('docs_agent', 'code:payment_service');
|
|
609
|
+
const design = bbRead('docs_agent', 'design:interfaces');
|
|
610
|
+
budget.spend('docs_agent', 600);
|
|
611
|
+
const docs = await llm('You are a technical writer. Use Markdown.', `Write a README section for this payment service. Include: overview, quick-start code, API reference table, error codes.\n\nInterfaces:\n${design}\n\nCode preview:\n${code.slice(0, 400)}...`);
|
|
612
|
+
bbWrite('docs_agent', 'review:docs', docs);
|
|
613
|
+
tag('docs_agent', `Docs written (${docs.split('\n').length} lines)`, c.magenta);
|
|
614
|
+
}
|
|
615
|
+
// Register no-op stubs so spawn_parallel_agents can route to them,
|
|
616
|
+
// but do the real work via Promise.all so we await true completion.
|
|
617
|
+
adapter.registerHandler('security_agent', async () => ({ agent: 'security_agent', stub: true }));
|
|
618
|
+
adapter.registerHandler('test_agent', async () => ({ agent: 'test_agent', stub: true }));
|
|
619
|
+
adapter.registerHandler('docs_agent', async () => ({ agent: 'docs_agent', stub: true }));
|
|
620
|
+
// Kick off the orchestrator call (demonstrates spawn_parallel_agents) AND
|
|
621
|
+
// run the real work in parallel — both finish before we move on.
|
|
622
|
+
await Promise.all([
|
|
623
|
+
orchestrator.execute('spawn_parallel_agents', {
|
|
624
|
+
tasks: [
|
|
625
|
+
{ agentType: 'custom:security_agent', taskPayload: { instruction: 'Security review', context: {} } },
|
|
626
|
+
{ agentType: 'custom:test_agent', taskPayload: { instruction: 'Write tests', context: {} } },
|
|
627
|
+
{ agentType: 'custom:docs_agent', taskPayload: { instruction: 'Write docs', context: {} } },
|
|
628
|
+
],
|
|
629
|
+
synthesisStrategy: 'merge',
|
|
630
|
+
}, ctx),
|
|
631
|
+
runSecurityReview(),
|
|
632
|
+
runTestReview(),
|
|
633
|
+
runDocsReview(),
|
|
634
|
+
]);
|
|
635
|
+
// All 3 review handlers confirmed done — safe to revoke token and advance FSM.
|
|
636
|
+
const tokenId = bbRead('orchestrator', 'auth:payment_token');
|
|
637
|
+
if (tokenId) {
|
|
638
|
+
tokenManager.revokeToken(tokenId);
|
|
639
|
+
tag('TokenManager', `Token ${tokenId.slice(0, 16)}... revoked after use`, c.magenta);
|
|
640
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'tokenManager', action: 'token_revoked', detail: tokenId.slice(0, 16) });
|
|
641
|
+
}
|
|
642
|
+
fsm.transition('fix_started', 'security_agent');
|
|
643
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
644
|
+
// PHASE 4: FIXER + DEBUGGER — patches and hardens code before delivery
|
|
645
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
646
|
+
banner('Phase 4 — Fixer + Debugger (security findings → hardened code)', c.yellow);
|
|
647
|
+
adapter.registerHandler('fixer_agent', async () => {
|
|
648
|
+
if (!fsm.canAgentAct('fixer_agent')) {
|
|
649
|
+
blocked('fixer_agent not authorized in current FSM state');
|
|
650
|
+
return { error: 'fsm_blocked' };
|
|
651
|
+
}
|
|
652
|
+
tag('fixer_agent', 'Reading security findings + implementation...', c.yellow);
|
|
653
|
+
const currentCode = bbRead('fixer_agent', 'code:payment_service');
|
|
654
|
+
const securityFindings = bbRead('fixer_agent', 'review:security');
|
|
655
|
+
const gateFeedback = BB['qualityGate:lastFeedback'] ?? '';
|
|
656
|
+
budget.spend('fixer_agent', 1000);
|
|
657
|
+
tag('fixer_agent', 'Applying targeted security patches...', c.yellow);
|
|
658
|
+
const fixed = await llm('You are a senior TypeScript security engineer. Output ONLY valid TypeScript — no markdown fences, no prose.', `You are given a PaymentProcessor implementation, security findings, and unresolved quality-gate feedback. Apply ALL targeted fixes.
|
|
659
|
+
|
|
660
|
+
SECURITY FINDINGS TO FIX:
|
|
661
|
+
${securityFindings}
|
|
662
|
+
|
|
663
|
+
UNRESOLVED QUALITY-GATE FEEDBACK:
|
|
664
|
+
${gateFeedback || 'none'}
|
|
665
|
+
|
|
666
|
+
MANDATORY patches (all required):
|
|
667
|
+
1. Add customerId validation in processPayment: reject if empty/null with errorCode 'INVALID_CUSTOMER'
|
|
668
|
+
2. Sanitize log() output — never log raw ids; log only masked version (first 4 chars + ****)
|
|
669
|
+
3. Set transaction status to 'Completed' on a spread COPY ({ ...tx, status: 'Completed' }) after successful processPayment
|
|
670
|
+
4. Add // SECURITY NOTE: encryption of cardData/PAN requires external KMS — not implemented here at top of class
|
|
671
|
+
5. private log(msg: string): void MUST call console.log(\`[\${new Date().toISOString()}] \${msg}\`) — never a stub or empty body
|
|
672
|
+
6. Validate currency against ['USD','EUR','GBP','JPY','CAD','AUD','CHF','CNY'] — return INVALID_INPUT if not in list
|
|
673
|
+
7. Enforce requestsPerMinute if set in config: track per-minute count, return errorCode:'RATE_LIMITED' success:false when exceeded, reset count each minute
|
|
674
|
+
8. Enforce maxTransactions: return CAPACITY_EXCEEDED BEFORE processing — do not just log; the return must be before any store.set call
|
|
675
|
+
9. Call this.log() on EVERY return path in processPayment and refundPayment — success AND all error cases (INVALID_INPUT, DUPLICATE_TRANSACTION, CAPACITY_EXCEEDED, RATE_LIMITED, TRANSACTION_NOT_FOUND, ALREADY_REFUNDED)
|
|
676
|
+
10. Preserve ALL existing logic — do not remove any methods, error codes, or patterns
|
|
677
|
+
|
|
678
|
+
Original code:
|
|
679
|
+
${currentCode}`, 2000);
|
|
680
|
+
// Re-run deterministic verifier on the fixed code
|
|
681
|
+
tag('verifyCode', 'Re-running pattern checks on fixed code...', c.yellow);
|
|
682
|
+
const fixCheck = verifyCode(fixed);
|
|
683
|
+
lastVerifyPassed = fixCheck.pass;
|
|
684
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'verifyCode', action: fixCheck.pass ? 'verify_pass' : 'verify_fail', detail: fixCheck.missing.concat(fixCheck.forbidden).join('; ') || 'all checks passed' });
|
|
685
|
+
if (!fixCheck.pass) {
|
|
686
|
+
fixCheck.missing.forEach(m => tag('verifyCode', `✗ Still missing: ${m}`, c.red));
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
tag('verifyCode', '✓ Fixed code passes all pattern checks', c.green);
|
|
690
|
+
}
|
|
691
|
+
// Re-run quality gate on the patched code
|
|
692
|
+
tag('QualityGate', 'Re-evaluating fixed code...', c.magenta);
|
|
693
|
+
const fixGate = await qualityGate.gate('code:payment_service', fixed, 'fixer_agent', { type: 'typescript' });
|
|
694
|
+
AUDIT_LOG.push({ ts: new Date().toISOString(), agent: 'qualityGate', action: `gate_${fixGate.decision}`, detail: fixGate.reviewNotes.join('; ') });
|
|
695
|
+
tag('QualityGate', `Fixed code decision: ${c.bold}${fixGate.decision.toUpperCase()}${c.reset} — ${fixGate.reviewNotes[0]}`, fixGate.decision === 'approve' ? c.green : c.yellow);
|
|
696
|
+
bbWrite('fixer_agent', 'code:payment_service', fixed);
|
|
697
|
+
bbWrite('fixer_agent', 'fix:applied', 'customerId validation, masked logging on all paths, Completed/Refunded via spread copy, console.log audit trail, currency list validation, rate limiting (RATE_LIMITED), maxTransactions early return, encryption note');
|
|
698
|
+
tag('fixer_agent', 'Patched code written to blackboard', c.green);
|
|
699
|
+
return { agent: 'fixer_agent', done: true };
|
|
700
|
+
});
|
|
701
|
+
await orchestrator.execute('delegate_task', {
|
|
702
|
+
targetAgent: 'custom:fixer_agent', taskPayload: { instruction: 'Fix security findings', context: {} },
|
|
703
|
+
}, ctx);
|
|
704
|
+
adapter.registerHandler('debugger_agent', async () => {
|
|
705
|
+
if (!fsm.canAgentAct('debugger_agent')) {
|
|
706
|
+
blocked('debugger_agent not authorized in current FSM state');
|
|
707
|
+
return { error: 'fsm_blocked' };
|
|
708
|
+
}
|
|
709
|
+
budget.spend('debugger_agent', 700);
|
|
710
|
+
tag('debugger_agent', 'Running post-fix debug hardening pass...', c.yellow);
|
|
711
|
+
let code = bbRead('debugger_agent', 'code:payment_service');
|
|
712
|
+
const check = verifyCode(code);
|
|
713
|
+
if (!check.pass) {
|
|
714
|
+
const missingList = check.missing.map(m => `- ${m}`).join('\n');
|
|
715
|
+
const forbiddenList = check.forbidden.map(f => `- ${f}`).join('\n');
|
|
716
|
+
code = await llm('You are a TypeScript debugging specialist. Output ONLY valid TypeScript — no markdown fences, no prose.', `Harden this PaymentProcessor implementation. Keep class/interface structure intact.
|
|
717
|
+
Fix ALL unresolved verifier issues.
|
|
718
|
+
|
|
719
|
+
Missing required patterns:
|
|
720
|
+
${missingList || '- none'}
|
|
721
|
+
|
|
722
|
+
Forbidden patterns present:
|
|
723
|
+
${forbiddenList || '- none'}
|
|
724
|
+
|
|
725
|
+
Rules:
|
|
726
|
+
- Preserve existing methods, constructor, and exports
|
|
727
|
+
- Do not remove existing error codes
|
|
728
|
+
- Keep spread-copy semantics for transaction updates
|
|
729
|
+
- Ensure private log(msg: string): void exists and calls console.log(...)
|
|
730
|
+
|
|
731
|
+
Code:
|
|
732
|
+
${code}`, 1200);
|
|
733
|
+
bbWrite('debugger_agent', 'code:payment_service', code);
|
|
734
|
+
}
|
|
735
|
+
let debugCheck = verifyCode(code);
|
|
736
|
+
if (!debugCheck.pass) {
|
|
737
|
+
const missingList = debugCheck.missing.map(m => `- ${m}`).join('\n');
|
|
738
|
+
const forbiddenList = debugCheck.forbidden.map(f => `- ${f}`).join('\n');
|
|
739
|
+
tag('debugger_agent', 'Second hardening pass for unresolved verifier issues...', c.yellow);
|
|
740
|
+
code = await llm('You are a TypeScript debugging specialist. Output ONLY valid TypeScript — no markdown fences, no prose.', `Final hardening pass. Resolve every remaining verifier failure exactly.
|
|
741
|
+
|
|
742
|
+
Remaining missing patterns:
|
|
743
|
+
${missingList || '- none'}
|
|
744
|
+
|
|
745
|
+
Remaining forbidden patterns:
|
|
746
|
+
${forbiddenList || '- none'}
|
|
747
|
+
|
|
748
|
+
Constraints:
|
|
749
|
+
- Keep all existing public APIs and class/interface names unchanged
|
|
750
|
+
- Keep all existing error codes and add missing ones only if absent
|
|
751
|
+
- Ensure rate limiting path returns RATE_LIMITED when requestsPerMinute is exceeded
|
|
752
|
+
- Ensure log() exists and calls console.log(...)
|
|
753
|
+
|
|
754
|
+
Code:
|
|
755
|
+
${code}`, 1200);
|
|
756
|
+
bbWrite('debugger_agent', 'code:payment_service', code);
|
|
757
|
+
debugCheck = verifyCode(code);
|
|
758
|
+
}
|
|
759
|
+
lastVerifyPassed = debugCheck.pass;
|
|
760
|
+
AUDIT_LOG.push({
|
|
761
|
+
ts: new Date().toISOString(),
|
|
762
|
+
agent: 'debugger_agent',
|
|
763
|
+
action: debugCheck.pass ? 'debug_pass' : 'debug_fail',
|
|
764
|
+
detail: debugCheck.missing.concat(debugCheck.forbidden).join('; ') || 'all checks passed',
|
|
765
|
+
});
|
|
766
|
+
if (debugCheck.pass) {
|
|
767
|
+
tag('debugger_agent', '✓ Debug pass complete — verifier checks all green', c.green);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
blocked(`debugger_agent unresolved issues — ${debugCheck.missing.length} missing, ${debugCheck.forbidden.length} forbidden`);
|
|
771
|
+
BB['final:deterministicScore'] = 0;
|
|
772
|
+
BB['final:reviewerNotes'] = `Debugger gate failed: ${debugCheck.missing.concat(debugCheck.forbidden).join('; ')}`;
|
|
773
|
+
return { agent: 'debugger_agent', done: false, error: 'debug_gate_failed' };
|
|
774
|
+
}
|
|
775
|
+
BB['debugger:lastPass'] = debugCheck.pass;
|
|
776
|
+
fsm.transition('fix_done', 'debugger_agent');
|
|
777
|
+
tag('debugger_agent', 'FSM → DELIVER', c.green);
|
|
778
|
+
return { agent: 'debugger_agent', done: true };
|
|
779
|
+
});
|
|
780
|
+
await orchestrator.execute('delegate_task', {
|
|
781
|
+
targetAgent: 'custom:debugger_agent', taskPayload: { instruction: 'Debug hardening pass', context: {} },
|
|
782
|
+
}, ctx);
|
|
783
|
+
// If debugger gate failed, do not proceed as DELIVER; emit deterministic report
|
|
784
|
+
// and let outer loop continue with objective no-go feedback.
|
|
785
|
+
if (fsm.state !== 'DELIVER') {
|
|
786
|
+
const code = BB['code:payment_service'] ?? '';
|
|
787
|
+
const objectiveCheck = verifyCode(code);
|
|
788
|
+
const deterministic = computeDeterministicScore({
|
|
789
|
+
verifierPass: objectiveCheck.pass,
|
|
790
|
+
debuggerPass: Boolean(BB['debugger:lastPass']),
|
|
791
|
+
inDeliverState: false,
|
|
792
|
+
hasSecurity: Boolean(BB['review:security']),
|
|
793
|
+
hasTests: Boolean(BB['review:tests']),
|
|
794
|
+
hasDocs: Boolean(BB['review:docs']),
|
|
795
|
+
hasFixApplied: Boolean(BB['fix:applied']),
|
|
796
|
+
hasCode: Boolean(code.trim().length > 0),
|
|
797
|
+
});
|
|
798
|
+
const fallbackReport = `1) Deterministic Score: ${deterministic.score}/10 (${deterministic.gatesPassed}/${deterministic.totalGates} gates passed)\n` +
|
|
799
|
+
`2) Deterministic Gate Result: NO-GO\n` +
|
|
800
|
+
`3) Failing Gates: ${deterministic.failingGates.length > 0 ? deterministic.failingGates.join(', ') : 'none'}\n\n` +
|
|
801
|
+
`=== Reviewer Notes (Advisory) ===\n` +
|
|
802
|
+
`${BB['final:reviewerNotes'] ?? 'Debugger gate failed; coordinator skipped.'}`;
|
|
803
|
+
BB['final:deterministicScore'] = deterministic.score;
|
|
804
|
+
bbWrite('coordinator_agent', 'final:report', fallbackReport);
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
808
|
+
// PHASE 5: COORDINATOR SYNTHESIS
|
|
809
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
810
|
+
banner('Phase 5 — Coordinator Final Synthesis', c.green);
|
|
811
|
+
adapter.registerHandler('coordinator_agent', async () => {
|
|
812
|
+
if (!fsm.canAgentAct('coordinator_agent')) {
|
|
813
|
+
blocked('coordinator_agent not authorized in current FSM state');
|
|
814
|
+
return { error: 'fsm_blocked' };
|
|
815
|
+
}
|
|
816
|
+
budget.spend('coordinator_agent', 800);
|
|
817
|
+
const security = bbRead('coordinator_agent', 'review:security');
|
|
818
|
+
const code = bbRead('coordinator_agent', 'code:payment_service');
|
|
819
|
+
const tests = bbRead('coordinator_agent', 'review:tests');
|
|
820
|
+
const docs = bbRead('coordinator_agent', 'review:docs');
|
|
821
|
+
const fixApplied = bbRead('coordinator_agent', 'fix:applied');
|
|
822
|
+
const objectiveCheck = verifyCode(code ?? '');
|
|
823
|
+
const deterministic = computeDeterministicScore({
|
|
824
|
+
verifierPass: objectiveCheck.pass,
|
|
825
|
+
debuggerPass: Boolean(BB['debugger:lastPass']),
|
|
826
|
+
inDeliverState: fsm.state === 'DELIVER',
|
|
827
|
+
hasSecurity: Boolean(security && security.trim().length > 0),
|
|
828
|
+
hasTests: Boolean(tests && tests.trim().length > 0),
|
|
829
|
+
hasDocs: Boolean(docs && docs.trim().length > 0),
|
|
830
|
+
hasFixApplied: Boolean(fixApplied && fixApplied.trim().length > 0),
|
|
831
|
+
hasCode: Boolean(code && code.trim().length > 0),
|
|
832
|
+
});
|
|
833
|
+
const testLines = tests ? `${tests.split('\n').length} lines covering all public methods` : 'not available';
|
|
834
|
+
const docsLines = docs ? `${docs.split('\n').length} lines of JSDoc + usage examples` : 'not available';
|
|
835
|
+
const reviewerNotes = await llm('You are a tech lead. Be decisive. Use bullet points.', `Final delivery review — Payment Processing Service (advisory notes only; numeric score is deterministic and already computed from objective gates).\n\n=== SECURITY FINDINGS (original) ===\n${security}\n\n=== FIXES APPLIED BY FIXER AGENT ===\n${fixApplied ?? 'none recorded'}\n\n=== FINAL CODE (first 2500 chars) ===\n${code.slice(0, 2500)}\n\n=== DELIVERABLES ===\n- Implementation: ${code.split('\n').length} lines of TypeScript\n- Tests: ${testLines}\n- Docs: ${docsLines}\n\n=== PIPELINE ===\n8 agents ran (including fixer_agent and debugger_agent), budget enforced, FSM: INTAKE→DESIGN→IMPLEMENT→REVIEW→FIX→DELIVER, tokens revoked after use\n\nWrite ONLY:\n1) Go/No-Go recommendation\n2) Top 3 strengths\n3) Remaining critical issues (true unresolved risks only)`);
|
|
836
|
+
const finalReport = `1) Deterministic Score: ${deterministic.score}/10 (${deterministic.gatesPassed}/${deterministic.totalGates} gates passed)\n` +
|
|
837
|
+
`2) Deterministic Gate Result: ${deterministic.score >= 9.5 ? 'GO' : 'NO-GO'}\n` +
|
|
838
|
+
`3) Failing Gates: ${deterministic.failingGates.length > 0 ? deterministic.failingGates.join(', ') : 'none'}\n\n` +
|
|
839
|
+
`=== Reviewer Notes (Advisory) ===\n${reviewerNotes}`;
|
|
840
|
+
BB['final:deterministicScore'] = deterministic.score;
|
|
841
|
+
BB['final:reviewerNotes'] = reviewerNotes;
|
|
842
|
+
bbWrite('coordinator_agent', 'final:report', finalReport);
|
|
843
|
+
tag('coordinator_agent', 'Final report written to blackboard', c.green);
|
|
844
|
+
return { agent: 'coordinator_agent', done: true };
|
|
845
|
+
});
|
|
846
|
+
await orchestrator.execute('delegate_task', {
|
|
847
|
+
targetAgent: 'custom:coordinator_agent', taskPayload: { instruction: 'Final synthesis', context: {} },
|
|
848
|
+
}, ctx);
|
|
849
|
+
}
|
|
850
|
+
// ── Outer loop: parse score, break if target met ─────────────────────────
|
|
851
|
+
const iterReport = BB['final:report'] ?? '';
|
|
852
|
+
const iterScore = BB['final:deterministicScore'] ?? parseScore(iterReport);
|
|
853
|
+
if (iterScore > outerBestScore) {
|
|
854
|
+
outerBestScore = iterScore;
|
|
855
|
+
outerBestCode = BB['code:payment_service'] ?? '';
|
|
856
|
+
}
|
|
857
|
+
tag('outer_loop', `Iteration ${outerIter + 1}/${MAX_OUTER} score: ${c.bold}${iterScore}/10${c.reset}`, iterScore >= 9.5 ? c.green : c.yellow);
|
|
858
|
+
if (iterScore >= 9.5) {
|
|
859
|
+
tag('outer_loop', `✓ Target reached (${iterScore}/10) — pipeline complete`, c.green);
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
if (outerIter < MAX_OUTER - 1) {
|
|
863
|
+
const reviewerNotes = BB['final:reviewerNotes'] ?? '';
|
|
864
|
+
const critiqueMatch = reviewerNotes.match(/(?:Remaining Critical Issues|critical issues)[^\n]*\n([\s\S]*?)(?=\n\d\)|$)/i);
|
|
865
|
+
outerLoopFeedback = critiqueMatch ? critiqueMatch[0].slice(0, 600) : reviewerNotes.slice(0, 600);
|
|
866
|
+
tag('outer_loop', `Score ${iterScore}/10 — re-running with targeted coordinator feedback...`, c.yellow);
|
|
867
|
+
}
|
|
868
|
+
} // ── end outerIter loop
|
|
869
|
+
// Restore highest-scoring code in case last iteration scored lower
|
|
870
|
+
if (outerBestCode)
|
|
871
|
+
BB['code:payment_service'] = outerBestCode;
|
|
872
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
873
|
+
// OUTPUT FILES
|
|
874
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
875
|
+
banner('Output Files', c.green);
|
|
876
|
+
const outDir = path.join(__dirname, 'output');
|
|
877
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
878
|
+
const ts = Date.now();
|
|
879
|
+
const codeFile = path.join(outDir, `payment-service-${ts}.ts`);
|
|
880
|
+
const testFile = path.join(outDir, `payment-service-${ts}.test.ts`);
|
|
881
|
+
const reportFile = path.join(outDir, `payment-service-${ts}-report.md`);
|
|
882
|
+
fs.writeFileSync(codeFile, BB['code:payment_service'] ?? '');
|
|
883
|
+
fs.writeFileSync(testFile, BB['review:tests'] ?? '');
|
|
884
|
+
fs.writeFileSync(reportFile, `# Payment Processing Service\n\n` +
|
|
885
|
+
`## Final Decision\n\n${BB['final:report'] ?? ''}\n\n` +
|
|
886
|
+
`## Security Findings\n\n${BB['review:security'] ?? ''}\n\n` +
|
|
887
|
+
`## Documentation\n\n${BB['review:docs'] ?? ''}\n`);
|
|
888
|
+
tag('files', `Implementation → ${path.basename(codeFile)}`, c.green);
|
|
889
|
+
tag('files', `Tests → ${path.basename(testFile)}`, c.blue);
|
|
890
|
+
tag('files', `Report → ${path.basename(reportFile)}`, c.magenta);
|
|
891
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
892
|
+
// BUDGET SUMMARY
|
|
893
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
894
|
+
banner('Budget Summary', c.blue);
|
|
895
|
+
const mainAgents = ['architect_agent', 'implementer_agent', 'security_agent', 'test_agent', 'docs_agent', 'fixer_agent', 'debugger_agent', 'coordinator_agent'];
|
|
896
|
+
const globalCeil = budget.getCeiling();
|
|
897
|
+
for (const a of mainAgents) {
|
|
898
|
+
const spent = budget.getAgentSpent(a);
|
|
899
|
+
const bar = '█'.repeat(Math.min(20, Math.round(20 * spent / Math.max(globalCeil, 1))));
|
|
900
|
+
const pct = (100 * spent / Math.max(globalCeil, 1)).toFixed(1);
|
|
901
|
+
console.log(` ${a.padEnd(22)} ${bar.padEnd(21)} ${String(spent).padStart(6)} / ${globalCeil} (${pct}%)`);
|
|
902
|
+
}
|
|
903
|
+
{
|
|
904
|
+
const rogueSpent = rogueBudget.getAgentSpent('rogue_agent');
|
|
905
|
+
console.log(` ${'rogue_agent (own)'.padEnd(22)} ${'█'.repeat(0).padEnd(21)} ${String(rogueSpent).padStart(6)} / 300 (BLOCKED at 0 — denied before spend)`);
|
|
906
|
+
}
|
|
907
|
+
console.log(`\n ${'TOTAL (main pool)'.padEnd(22)} ${String(budget.getTotalSpent()).padStart(6)} / ${globalCeil}`);
|
|
908
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
909
|
+
// AUDIT LOG
|
|
910
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
911
|
+
banner('Full Audit Log', c.dim);
|
|
912
|
+
for (const e of AUDIT_LOG) {
|
|
913
|
+
console.log(` ${c.dim}${e.ts} ${e.agent.padEnd(22)} ${e.action.padEnd(22)} ${e.detail}${c.reset}`);
|
|
914
|
+
}
|
|
915
|
+
console.log(`\n ${c.dim}${AUDIT_LOG.length} audit entries recorded${c.reset}`);
|
|
916
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
917
|
+
// FSM TRANSITION HISTORY
|
|
918
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
919
|
+
banner('FSM State Transition History', c.yellow);
|
|
920
|
+
for (const h of fsm.transitionHistory) {
|
|
921
|
+
const duration = h.exitedAt ? `${h.exitedAt - h.enteredAt}ms` : 'current';
|
|
922
|
+
console.log(` ${c.yellow}${h.state.padEnd(14)}${c.reset} entered ${new Date(h.enteredAt).toISOString()} (${duration})`);
|
|
923
|
+
}
|
|
924
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
925
|
+
// FINAL REPORT
|
|
926
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
927
|
+
banner("Coordinator's Final Decision", c.green);
|
|
928
|
+
console.log(BB['final:report'] ?? 'No report generated');
|
|
929
|
+
console.log(`\n${c.bold}${c.green}✓ Full showcase complete.${c.reset}`);
|
|
930
|
+
console.log(` ${c.dim}Features demonstrated:${c.reset}`);
|
|
931
|
+
console.log(` ${c.green}✓${c.reset} JourneyFSM — enforced INTAKE→DESIGN→IMPLEMENT→REVIEW→DELIVER`);
|
|
932
|
+
console.log(` ${c.green}✓${c.reset} AuthGuardian — blocked PAYMENTS access, escalation flow`);
|
|
933
|
+
console.log(` ${c.green}✓${c.reset} SecureTokenManager — signed token issued + revoked post-use`);
|
|
934
|
+
console.log(` ${c.green}✓${c.reset} FederatedBudget — rogue_agent cut off; per-agent tracking`);
|
|
935
|
+
console.log(` ${c.green}✓${c.reset} QualityGateAgent — blackboard-aware AI review; multi-agent refinement loop (max 3 passes)`);
|
|
936
|
+
console.log(` ${c.green}✓${c.reset} Parallel agents — security + test + docs simultaneously`);
|
|
937
|
+
console.log(` ${c.green}✓${c.reset} Fixer agent — patched code based on security findings, re-scored`);
|
|
938
|
+
console.log(` ${c.green}✓${c.reset} Debugger agent — post-fix hardening pass before delivery`);
|
|
939
|
+
console.log(` ${c.green}✓${c.reset} Shared blackboard — 8 agents coordinated through single store`);
|
|
940
|
+
console.log(` ${c.green}✓${c.reset} Audit log — ${AUDIT_LOG.length} entries, full traceability\n`);
|
|
941
|
+
}
|
|
942
|
+
main().catch(err => {
|
|
943
|
+
console.error(`\n${c.red}Showcase failed:${c.reset}`, err.message ?? err);
|
|
944
|
+
process.exit(1);
|
|
945
|
+
});
|
|
946
|
+
//# sourceMappingURL=07-full-showcase.js.map
|