@agntk/agent-harness 0.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/LICENSE +21 -0
- package/NOTICE +41 -0
- package/README.md +445 -0
- package/defaults/agents/summarizer.md +49 -0
- package/defaults/instincts/lead-with-answer.md +24 -0
- package/defaults/instincts/qualify-before-recommending.md +40 -0
- package/defaults/instincts/read-before-edit.md +23 -0
- package/defaults/instincts/search-before-create.md +23 -0
- package/defaults/playbooks/ship-feature.md +31 -0
- package/defaults/rules/ask-before-assuming.md +35 -0
- package/defaults/rules/operations.md +35 -0
- package/defaults/rules/respect-the-user.md +39 -0
- package/defaults/skills/business-analyst.md +181 -0
- package/defaults/skills/content-marketer.md +184 -0
- package/defaults/skills/research.md +34 -0
- package/defaults/tools/example-web-search.md +60 -0
- package/defaults/workflows/daily-reflection.md +54 -0
- package/dist/agent-framework-K4GUIICH.js +344 -0
- package/dist/agent-framework-K4GUIICH.js.map +1 -0
- package/dist/analytics-RPT73WNM.js +12 -0
- package/dist/analytics-RPT73WNM.js.map +1 -0
- package/dist/auto-processor-OLE45UI3.js +13 -0
- package/dist/auto-processor-OLE45UI3.js.map +1 -0
- package/dist/chunk-274RV3YO.js +162 -0
- package/dist/chunk-274RV3YO.js.map +1 -0
- package/dist/chunk-4CWAGBNS.js +168 -0
- package/dist/chunk-4CWAGBNS.js.map +1 -0
- package/dist/chunk-4FDUOGSZ.js +69 -0
- package/dist/chunk-4FDUOGSZ.js.map +1 -0
- package/dist/chunk-5H34JPMB.js +199 -0
- package/dist/chunk-5H34JPMB.js.map +1 -0
- package/dist/chunk-6EMOEYGU.js +102 -0
- package/dist/chunk-6EMOEYGU.js.map +1 -0
- package/dist/chunk-A7BJPQQ6.js +236 -0
- package/dist/chunk-A7BJPQQ6.js.map +1 -0
- package/dist/chunk-AGAAFJEO.js +76 -0
- package/dist/chunk-AGAAFJEO.js.map +1 -0
- package/dist/chunk-BSKDOFRT.js +65 -0
- package/dist/chunk-BSKDOFRT.js.map +1 -0
- package/dist/chunk-CHJ5GNZC.js +100 -0
- package/dist/chunk-CHJ5GNZC.js.map +1 -0
- package/dist/chunk-CSL3ERUI.js +307 -0
- package/dist/chunk-CSL3ERUI.js.map +1 -0
- package/dist/chunk-DA7IKHC4.js +229 -0
- package/dist/chunk-DA7IKHC4.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DTTXPHFW.js +211 -0
- package/dist/chunk-DTTXPHFW.js.map +1 -0
- package/dist/chunk-FD55B3IO.js +204 -0
- package/dist/chunk-FD55B3IO.js.map +1 -0
- package/dist/chunk-FLZU44SV.js +230 -0
- package/dist/chunk-FLZU44SV.js.map +1 -0
- package/dist/chunk-GJNNR2RA.js +200 -0
- package/dist/chunk-GJNNR2RA.js.map +1 -0
- package/dist/chunk-GNUSHD2Y.js +111 -0
- package/dist/chunk-GNUSHD2Y.js.map +1 -0
- package/dist/chunk-GUJTBGVS.js +2212 -0
- package/dist/chunk-GUJTBGVS.js.map +1 -0
- package/dist/chunk-IZ6UZ3ZL.js +207 -0
- package/dist/chunk-IZ6UZ3ZL.js.map +1 -0
- package/dist/chunk-JKMGYWXB.js +197 -0
- package/dist/chunk-JKMGYWXB.js.map +1 -0
- package/dist/chunk-KFX54TQM.js +165 -0
- package/dist/chunk-KFX54TQM.js.map +1 -0
- package/dist/chunk-M7NXUK55.js +199 -0
- package/dist/chunk-M7NXUK55.js.map +1 -0
- package/dist/chunk-MPZ3BPUI.js +374 -0
- package/dist/chunk-MPZ3BPUI.js.map +1 -0
- package/dist/chunk-OC6YSTDX.js +119 -0
- package/dist/chunk-OC6YSTDX.js.map +1 -0
- package/dist/chunk-RC6MEZB6.js +469 -0
- package/dist/chunk-RC6MEZB6.js.map +1 -0
- package/dist/chunk-RY3ZFII7.js +3440 -0
- package/dist/chunk-RY3ZFII7.js.map +1 -0
- package/dist/chunk-TAT6JU3X.js +167 -0
- package/dist/chunk-TAT6JU3X.js.map +1 -0
- package/dist/chunk-UDZIS2AQ.js +79 -0
- package/dist/chunk-UDZIS2AQ.js.map +1 -0
- package/dist/chunk-UPLBF4RZ.js +115 -0
- package/dist/chunk-UPLBF4RZ.js.map +1 -0
- package/dist/chunk-UWQTZMNI.js +154 -0
- package/dist/chunk-UWQTZMNI.js.map +1 -0
- package/dist/chunk-W4T7PGI2.js +346 -0
- package/dist/chunk-W4T7PGI2.js.map +1 -0
- package/dist/chunk-XTBKL5BI.js +111 -0
- package/dist/chunk-XTBKL5BI.js.map +1 -0
- package/dist/chunk-YIJY5DBV.js +399 -0
- package/dist/chunk-YIJY5DBV.js.map +1 -0
- package/dist/chunk-YUFNYN2H.js +242 -0
- package/dist/chunk-YUFNYN2H.js.map +1 -0
- package/dist/chunk-Z2PUCXTZ.js +94 -0
- package/dist/chunk-Z2PUCXTZ.js.map +1 -0
- package/dist/chunk-ZZJOFKAT.js +13 -0
- package/dist/chunk-ZZJOFKAT.js.map +1 -0
- package/dist/cli/index.js +3661 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config-WVMRUOCA.js +13 -0
- package/dist/config-WVMRUOCA.js.map +1 -0
- package/dist/context-loader-3ORBPMHJ.js +13 -0
- package/dist/context-loader-3ORBPMHJ.js.map +1 -0
- package/dist/conversation-QDEIDQPH.js +22 -0
- package/dist/conversation-QDEIDQPH.js.map +1 -0
- package/dist/cost-tracker-RS3W7SVY.js +24 -0
- package/dist/cost-tracker-RS3W7SVY.js.map +1 -0
- package/dist/delegate-VJCJLYEK.js +29 -0
- package/dist/delegate-VJCJLYEK.js.map +1 -0
- package/dist/emotional-state-VQVRA6ED.js +206 -0
- package/dist/emotional-state-VQVRA6ED.js.map +1 -0
- package/dist/env-discovery-2BLVMAIM.js +251 -0
- package/dist/env-discovery-2BLVMAIM.js.map +1 -0
- package/dist/export-6GCYHEHQ.js +165 -0
- package/dist/export-6GCYHEHQ.js.map +1 -0
- package/dist/graph-YUIPOSOO.js +14 -0
- package/dist/graph-YUIPOSOO.js.map +1 -0
- package/dist/harness-LCHA3DWP.js +10 -0
- package/dist/harness-LCHA3DWP.js.map +1 -0
- package/dist/harness-WE4SLCML.js +26 -0
- package/dist/harness-WE4SLCML.js.map +1 -0
- package/dist/health-NZ6WNIMV.js +23 -0
- package/dist/health-NZ6WNIMV.js.map +1 -0
- package/dist/index.d.ts +3612 -0
- package/dist/index.js +13501 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer-LONANRRM.js +16 -0
- package/dist/indexer-LONANRRM.js.map +1 -0
- package/dist/instinct-learner-SRM72DHF.js +20 -0
- package/dist/instinct-learner-SRM72DHF.js.map +1 -0
- package/dist/intake-4M3HNU43.js +21 -0
- package/dist/intake-4M3HNU43.js.map +1 -0
- package/dist/intelligence-HJOCA4SJ.js +1081 -0
- package/dist/intelligence-HJOCA4SJ.js.map +1 -0
- package/dist/journal-WANJL3MI.js +24 -0
- package/dist/journal-WANJL3MI.js.map +1 -0
- package/dist/loader-C3TKIKZR.js +23 -0
- package/dist/loader-C3TKIKZR.js.map +1 -0
- package/dist/mcp-WTQJJZAO.js +15 -0
- package/dist/mcp-WTQJJZAO.js.map +1 -0
- package/dist/mcp-discovery-WPAQFL6S.js +377 -0
- package/dist/mcp-discovery-WPAQFL6S.js.map +1 -0
- package/dist/mcp-installer-6O2XXD3V.js +394 -0
- package/dist/mcp-installer-6O2XXD3V.js.map +1 -0
- package/dist/metrics-KXGNFAAB.js +20 -0
- package/dist/metrics-KXGNFAAB.js.map +1 -0
- package/dist/primitive-registry-I6VTIR4W.js +512 -0
- package/dist/primitive-registry-I6VTIR4W.js.map +1 -0
- package/dist/project-discovery-C4UMD7JI.js +246 -0
- package/dist/project-discovery-C4UMD7JI.js.map +1 -0
- package/dist/provider-LQHQX7Z7.js +26 -0
- package/dist/provider-LQHQX7Z7.js.map +1 -0
- package/dist/provider-SXPQZ74H.js +28 -0
- package/dist/provider-SXPQZ74H.js.map +1 -0
- package/dist/rate-limiter-RLRVM325.js +22 -0
- package/dist/rate-limiter-RLRVM325.js.map +1 -0
- package/dist/rule-engine-YGQ3RYZM.js +182 -0
- package/dist/rule-engine-YGQ3RYZM.js.map +1 -0
- package/dist/scaffold-A3VRRCBV.js +347 -0
- package/dist/scaffold-A3VRRCBV.js.map +1 -0
- package/dist/scheduler-XHHIVHRI.js +397 -0
- package/dist/scheduler-XHHIVHRI.js.map +1 -0
- package/dist/search-V3W5JMJG.js +75 -0
- package/dist/search-V3W5JMJG.js.map +1 -0
- package/dist/semantic-search-2DTOO5UX.js +241 -0
- package/dist/semantic-search-2DTOO5UX.js.map +1 -0
- package/dist/serve-DTQ3HENY.js +291 -0
- package/dist/serve-DTQ3HENY.js.map +1 -0
- package/dist/sessions-CZGVXKQE.js +21 -0
- package/dist/sessions-CZGVXKQE.js.map +1 -0
- package/dist/sources-RW5DT56F.js +32 -0
- package/dist/sources-RW5DT56F.js.map +1 -0
- package/dist/starter-packs-76YUVHEU.js +893 -0
- package/dist/starter-packs-76YUVHEU.js.map +1 -0
- package/dist/state-GMXILIHW.js +13 -0
- package/dist/state-GMXILIHW.js.map +1 -0
- package/dist/state-merge-NKO5FRBA.js +174 -0
- package/dist/state-merge-NKO5FRBA.js.map +1 -0
- package/dist/telemetry-UC6PBXC7.js +22 -0
- package/dist/telemetry-UC6PBXC7.js.map +1 -0
- package/dist/tool-executor-MJ7IG7PQ.js +28 -0
- package/dist/tool-executor-MJ7IG7PQ.js.map +1 -0
- package/dist/tools-DZ4KETET.js +20 -0
- package/dist/tools-DZ4KETET.js.map +1 -0
- package/dist/types-EW7AIB3R.js +18 -0
- package/dist/types-EW7AIB3R.js.map +1 -0
- package/dist/types-WGDLSPO6.js +16 -0
- package/dist/types-WGDLSPO6.js.map +1 -0
- package/dist/universal-installer-QGS4SJGX.js +578 -0
- package/dist/universal-installer-QGS4SJGX.js.map +1 -0
- package/dist/validator-7WXMDIHH.js +22 -0
- package/dist/validator-7WXMDIHH.js.map +1 -0
- package/dist/verification-gate-FYXUX6LH.js +246 -0
- package/dist/verification-gate-FYXUX6LH.js.map +1 -0
- package/dist/versioning-Z3XNE2Q2.js +271 -0
- package/dist/versioning-Z3XNE2Q2.js.map +1 -0
- package/dist/watcher-ISJC7YKL.js +109 -0
- package/dist/watcher-ISJC7YKL.js.map +1 -0
- package/dist/web-server-DD7ZOP46.js +28 -0
- package/dist/web-server-DD7ZOP46.js.map +1 -0
- package/package.json +76 -0
- package/sources.yaml +121 -0
- package/templates/assistant/CORE.md +24 -0
- package/templates/assistant/SYSTEM.md +24 -0
- package/templates/assistant/config.yaml +51 -0
- package/templates/base/CORE.md +17 -0
- package/templates/base/SYSTEM.md +24 -0
- package/templates/base/config.yaml +51 -0
- package/templates/claude-opus/config.yaml +51 -0
- package/templates/code-reviewer/CORE.md +25 -0
- package/templates/code-reviewer/SYSTEM.md +30 -0
- package/templates/code-reviewer/config.yaml +51 -0
- package/templates/gpt4/config.yaml +51 -0
- package/templates/local/config.yaml +51 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createHarness
|
|
5
|
+
} from "./chunk-YIJY5DBV.js";
|
|
6
|
+
import "./chunk-AGAAFJEO.js";
|
|
7
|
+
import "./chunk-5H34JPMB.js";
|
|
8
|
+
import "./chunk-DA7IKHC4.js";
|
|
9
|
+
import "./chunk-UWQTZMNI.js";
|
|
10
|
+
import "./chunk-UDZIS2AQ.js";
|
|
11
|
+
import "./chunk-DTTXPHFW.js";
|
|
12
|
+
import "./chunk-Z2PUCXTZ.js";
|
|
13
|
+
import "./chunk-TAT6JU3X.js";
|
|
14
|
+
import "./chunk-JKMGYWXB.js";
|
|
15
|
+
import "./chunk-OC6YSTDX.js";
|
|
16
|
+
import "./chunk-XTBKL5BI.js";
|
|
17
|
+
import {
|
|
18
|
+
loadDirectory
|
|
19
|
+
} from "./chunk-UPLBF4RZ.js";
|
|
20
|
+
import {
|
|
21
|
+
log
|
|
22
|
+
} from "./chunk-BSKDOFRT.js";
|
|
23
|
+
import "./chunk-IZ6UZ3ZL.js";
|
|
24
|
+
import "./chunk-CHJ5GNZC.js";
|
|
25
|
+
import "./chunk-4CWAGBNS.js";
|
|
26
|
+
import {
|
|
27
|
+
__require
|
|
28
|
+
} from "./chunk-ZZJOFKAT.js";
|
|
29
|
+
|
|
30
|
+
// src/runtime/agent-framework.ts
|
|
31
|
+
import { existsSync } from "fs";
|
|
32
|
+
import { join } from "path";
|
|
33
|
+
function createAgent(definition) {
|
|
34
|
+
let booted = false;
|
|
35
|
+
const harnessHooks = {};
|
|
36
|
+
if (definition.hooks?.onBoot) harnessHooks.onBoot = definition.hooks.onBoot;
|
|
37
|
+
if (definition.hooks?.onSessionEnd) harnessHooks.onSessionEnd = definition.hooks.onSessionEnd;
|
|
38
|
+
if (definition.hooks?.onError) harnessHooks.onError = definition.hooks.onError;
|
|
39
|
+
if (definition.hooks?.onStateChange) harnessHooks.onStateChange = definition.hooks.onStateChange;
|
|
40
|
+
if (definition.hooks?.onShutdown) harnessHooks.onShutdown = definition.hooks.onShutdown;
|
|
41
|
+
const harnessOptions = {
|
|
42
|
+
dir: definition.dir,
|
|
43
|
+
model: definition.model,
|
|
44
|
+
provider: definition.provider,
|
|
45
|
+
apiKey: definition.apiKey,
|
|
46
|
+
config: definition.config,
|
|
47
|
+
hooks: harnessHooks
|
|
48
|
+
};
|
|
49
|
+
const harness = createHarness(harnessOptions);
|
|
50
|
+
const middleware = definition.middleware ?? [];
|
|
51
|
+
async function runWithPipeline(prompt, isStream) {
|
|
52
|
+
if (definition.hooks?.beforeRun) {
|
|
53
|
+
const hookResult = await definition.hooks.beforeRun({
|
|
54
|
+
agent: definedAgent,
|
|
55
|
+
prompt,
|
|
56
|
+
isStream
|
|
57
|
+
});
|
|
58
|
+
if (hookResult) {
|
|
59
|
+
if (hookResult.reject) {
|
|
60
|
+
throw new Error(`Run rejected: ${hookResult.reason || "beforeRun hook rejected"}`);
|
|
61
|
+
}
|
|
62
|
+
if (hookResult.prompt) {
|
|
63
|
+
prompt = hookResult.prompt;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (definition.guardrails?.enforceRules) {
|
|
68
|
+
const violation = checkRuleViolation(definition.dir, prompt);
|
|
69
|
+
if (violation) {
|
|
70
|
+
throw new Error(`Guardrail violation: ${violation}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (definition.approval?.requireApproval) {
|
|
74
|
+
const needsApproval = definition.approval.requireApproval(prompt);
|
|
75
|
+
if (needsApproval) {
|
|
76
|
+
if (definition.approval.onApprovalNeeded) {
|
|
77
|
+
const approved = await definition.approval.onApprovalNeeded(prompt);
|
|
78
|
+
if (!approved) {
|
|
79
|
+
throw new Error("Run requires approval but was not approved");
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
throw new Error("Run requires approval but no onApprovalNeeded handler is configured");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const execute = async () => {
|
|
87
|
+
return harness.run(prompt);
|
|
88
|
+
};
|
|
89
|
+
let result;
|
|
90
|
+
if (middleware.length > 0) {
|
|
91
|
+
result = await executeMiddleware(middleware, definedAgent, prompt, isStream, execute);
|
|
92
|
+
} else {
|
|
93
|
+
result = await execute();
|
|
94
|
+
}
|
|
95
|
+
if (definition.hooks?.afterRun) {
|
|
96
|
+
const transformed = await definition.hooks.afterRun({
|
|
97
|
+
agent: definedAgent,
|
|
98
|
+
prompt,
|
|
99
|
+
result
|
|
100
|
+
});
|
|
101
|
+
if (transformed) {
|
|
102
|
+
result = transformed;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
const definedAgent = {
|
|
108
|
+
name: definition.name,
|
|
109
|
+
get config() {
|
|
110
|
+
return harness.config;
|
|
111
|
+
},
|
|
112
|
+
harness,
|
|
113
|
+
definition,
|
|
114
|
+
async boot() {
|
|
115
|
+
await harness.boot();
|
|
116
|
+
booted = true;
|
|
117
|
+
},
|
|
118
|
+
async run(prompt) {
|
|
119
|
+
if (!booted) await definedAgent.boot();
|
|
120
|
+
return runWithPipeline(prompt, false);
|
|
121
|
+
},
|
|
122
|
+
stream(prompt) {
|
|
123
|
+
let resolveResult;
|
|
124
|
+
let rejectResult;
|
|
125
|
+
const resultPromise = new Promise((res, rej) => {
|
|
126
|
+
resolveResult = res;
|
|
127
|
+
rejectResult = rej;
|
|
128
|
+
});
|
|
129
|
+
resultPromise.catch(() => {
|
|
130
|
+
});
|
|
131
|
+
async function* generateStream() {
|
|
132
|
+
if (!booted) await definedAgent.boot();
|
|
133
|
+
try {
|
|
134
|
+
if (definition.hooks?.beforeRun) {
|
|
135
|
+
const hookResult = await definition.hooks.beforeRun({
|
|
136
|
+
agent: definedAgent,
|
|
137
|
+
prompt,
|
|
138
|
+
isStream: true
|
|
139
|
+
});
|
|
140
|
+
if (hookResult?.reject) {
|
|
141
|
+
const error = new Error(`Stream rejected: ${hookResult.reason || "beforeRun hook rejected"}`);
|
|
142
|
+
rejectResult(error);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
if (hookResult?.prompt) {
|
|
146
|
+
prompt = hookResult.prompt;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (definition.guardrails?.enforceRules) {
|
|
150
|
+
const violation = checkRuleViolation(definition.dir, prompt);
|
|
151
|
+
if (violation) {
|
|
152
|
+
const error = new Error(`Guardrail violation: ${violation}`);
|
|
153
|
+
rejectResult(error);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (definition.approval?.requireApproval) {
|
|
158
|
+
const needsApproval = definition.approval.requireApproval(prompt);
|
|
159
|
+
if (needsApproval) {
|
|
160
|
+
if (definition.approval.onApprovalNeeded) {
|
|
161
|
+
const approved = await definition.approval.onApprovalNeeded(prompt);
|
|
162
|
+
if (!approved) {
|
|
163
|
+
const error = new Error("Stream requires approval but was not approved");
|
|
164
|
+
rejectResult(error);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
const error = new Error("Stream requires approval but no onApprovalNeeded handler is configured");
|
|
169
|
+
rejectResult(error);
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
176
|
+
rejectResult(error);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
const streamResult = harness.stream(prompt);
|
|
180
|
+
let fullText = "";
|
|
181
|
+
for await (const chunk of streamResult.textStream) {
|
|
182
|
+
fullText += chunk;
|
|
183
|
+
yield chunk;
|
|
184
|
+
}
|
|
185
|
+
let result = await streamResult.result;
|
|
186
|
+
if (definition.hooks?.afterRun) {
|
|
187
|
+
const transformed = await definition.hooks.afterRun({
|
|
188
|
+
agent: definedAgent,
|
|
189
|
+
prompt,
|
|
190
|
+
result
|
|
191
|
+
});
|
|
192
|
+
if (transformed) {
|
|
193
|
+
result = transformed;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
resolveResult(result);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
textStream: generateStream(),
|
|
200
|
+
result: resultPromise
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
async shutdown() {
|
|
204
|
+
await harness.shutdown();
|
|
205
|
+
booted = false;
|
|
206
|
+
},
|
|
207
|
+
getState() {
|
|
208
|
+
return harness.getState();
|
|
209
|
+
},
|
|
210
|
+
getSystemPrompt() {
|
|
211
|
+
return harness.getSystemPrompt();
|
|
212
|
+
},
|
|
213
|
+
isBooted() {
|
|
214
|
+
return booted;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
return definedAgent;
|
|
218
|
+
}
|
|
219
|
+
async function executeMiddleware(middlewares, agent, prompt, isStream, execute) {
|
|
220
|
+
let index = 0;
|
|
221
|
+
const next = async () => {
|
|
222
|
+
if (index >= middlewares.length) {
|
|
223
|
+
return execute();
|
|
224
|
+
}
|
|
225
|
+
const mw = middlewares[index++];
|
|
226
|
+
return mw({ agent, prompt, isStream }, next);
|
|
227
|
+
};
|
|
228
|
+
return next();
|
|
229
|
+
}
|
|
230
|
+
function checkRuleViolation(harnessDir, prompt, options) {
|
|
231
|
+
const rulesDir = join(harnessDir, "rules");
|
|
232
|
+
if (!existsSync(rulesDir)) return null;
|
|
233
|
+
const rules = loadDirectory(rulesDir);
|
|
234
|
+
const promptLower = prompt.toLowerCase();
|
|
235
|
+
for (const rule of rules) {
|
|
236
|
+
if (options?.ruleTags && options.ruleTags.length > 0) {
|
|
237
|
+
const hasTag = rule.frontmatter.tags.some(
|
|
238
|
+
(t) => options.ruleTags.includes(t.toLowerCase())
|
|
239
|
+
);
|
|
240
|
+
if (!hasTag) continue;
|
|
241
|
+
}
|
|
242
|
+
if (rule.frontmatter.status !== "active") continue;
|
|
243
|
+
const text = (rule.l0 + "\n" + rule.body).toLowerCase();
|
|
244
|
+
const forbiddenPatterns = extractForbiddenActions(text);
|
|
245
|
+
for (const forbidden of forbiddenPatterns) {
|
|
246
|
+
const keywords = forbidden.split(/\s+/).filter((w) => w.length > 3);
|
|
247
|
+
const matchCount = keywords.filter((k) => promptLower.includes(k)).length;
|
|
248
|
+
if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.6)) {
|
|
249
|
+
return `Rule "${rule.frontmatter.id}" forbids: "${forbidden}" prompt matches ${matchCount}/${keywords.length} keywords`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
function extractForbiddenActions(text) {
|
|
256
|
+
const actions = [];
|
|
257
|
+
const patterns = [
|
|
258
|
+
/never\s+(.+?)(?:\.|$)/gm,
|
|
259
|
+
/don'?t\s+(.+?)(?:\.|$)/gm,
|
|
260
|
+
/must not\s+(.+?)(?:\.|$)/gm,
|
|
261
|
+
/do not\s+(.+?)(?:\.|$)/gm,
|
|
262
|
+
/avoid\s+(.+?)(?:\.|$)/gm,
|
|
263
|
+
/prohibit(?:ed|s)?\s+(.+?)(?:\.|$)/gm,
|
|
264
|
+
/forbidden?:?\s+(.+?)(?:\.|$)/gm
|
|
265
|
+
];
|
|
266
|
+
for (const pattern of patterns) {
|
|
267
|
+
let match;
|
|
268
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
269
|
+
const action = match[1].trim();
|
|
270
|
+
if (action.length > 3 && action.length < 200) {
|
|
271
|
+
actions.push(action);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return actions;
|
|
276
|
+
}
|
|
277
|
+
function createCliApproval(options) {
|
|
278
|
+
const timeoutMs = options?.timeoutMs ?? 3e5;
|
|
279
|
+
return (prompt) => {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
const readline = __require("readline");
|
|
282
|
+
const rl = readline.createInterface({
|
|
283
|
+
input: process.stdin,
|
|
284
|
+
output: process.stdout
|
|
285
|
+
});
|
|
286
|
+
const timer = setTimeout(() => {
|
|
287
|
+
log.warn("Approval timeout rejecting");
|
|
288
|
+
rl.close();
|
|
289
|
+
resolve(false);
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
const preview = prompt.length > 100 ? prompt.slice(0, 100) + "..." : prompt;
|
|
292
|
+
rl.question(`
|
|
293
|
+
[APPROVAL REQUIRED] "${preview}"
|
|
294
|
+
Approve? (y/N): `, (answer) => {
|
|
295
|
+
clearTimeout(timer);
|
|
296
|
+
rl.close();
|
|
297
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function createWebhookApproval(options) {
|
|
303
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
304
|
+
return async (prompt) => {
|
|
305
|
+
try {
|
|
306
|
+
const controller = new AbortController();
|
|
307
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
308
|
+
const response = await fetch(options.url, {
|
|
309
|
+
method: "POST",
|
|
310
|
+
headers: {
|
|
311
|
+
"Content-Type": "application/json",
|
|
312
|
+
...options.headers
|
|
313
|
+
},
|
|
314
|
+
body: JSON.stringify({ prompt, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
315
|
+
signal: controller.signal
|
|
316
|
+
});
|
|
317
|
+
clearTimeout(timer);
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
log.warn(`Approval webhook returned ${response.status}`);
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
const body = await response.json();
|
|
323
|
+
return body.approved === true;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
log.warn(`Approval webhook failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function checkAction(harnessDir, action, options) {
|
|
331
|
+
const violation = checkRuleViolation(harnessDir, action, options);
|
|
332
|
+
return {
|
|
333
|
+
allowed: violation === null,
|
|
334
|
+
reason: violation
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
export {
|
|
338
|
+
checkAction,
|
|
339
|
+
checkRuleViolation,
|
|
340
|
+
createAgent,
|
|
341
|
+
createCliApproval,
|
|
342
|
+
createWebhookApproval
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=agent-framework-K4GUIICH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/agent-framework.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createHarness } from '../core/harness.js';\nimport { loadDirectory } from '../primitives/loader.js';\nimport { log } from '../core/logger.js';\nimport type {\n CreateHarnessOptions,\n HarnessAgent,\n HarnessConfig,\n HarnessHooks,\n AgentRunResult,\n AgentStreamResult,\n AgentState,\n DeepPartial,\n} from '../core/types.js';\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 1. defineAgent() \u0014 Declarative Agent Definition\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface AgentDefinition {\n /** Agent name (used for logging and config resolution) */\n name: string;\n /** Harness directory path */\n dir: string;\n /** Model override */\n model?: string;\n /** Provider override */\n provider?: string;\n /** API key override */\n apiKey?: string;\n /** Config overrides */\n config?: DeepPartial<HarnessConfig>;\n\n /** Lifecycle hooks \u0014 called at various points in the agent lifecycle */\n hooks?: AgentLifecycleHooks;\n\n /** Pre-action rule enforcement configuration */\n guardrails?: GuardrailEnforcementConfig;\n\n /** Human-in-the-loop gate configuration */\n approval?: ApprovalGateConfig;\n\n /** Middleware functions that wrap each run/stream call */\n middleware?: AgentMiddleware[];\n}\n\nexport interface AgentLifecycleHooks extends HarnessHooks {\n /** Called before each run/stream \u0014 can modify prompt or reject */\n beforeRun?: (ctx: BeforeRunContext) => Promise<BeforeRunResult | void>;\n /** Called after each run/stream \u0014 can transform result */\n afterRun?: (ctx: AfterRunContext) => Promise<AgentRunResult | void>;\n /** Called periodically for health checks */\n onHealthCheck?: (ctx: { agent: DefinedAgent }) => Promise<void>;\n}\n\nexport interface BeforeRunContext {\n agent: DefinedAgent;\n prompt: string;\n isStream: boolean;\n}\n\nexport interface BeforeRunResult {\n /** Modified prompt (if changed) */\n prompt?: string;\n /** Set to true to reject the run */\n reject?: boolean;\n /** Reason for rejection */\n reason?: string;\n}\n\nexport interface AfterRunContext {\n agent: DefinedAgent;\n prompt: string;\n result: AgentRunResult;\n}\n\nexport type AgentMiddleware = (\n ctx: { agent: DefinedAgent; prompt: string; isStream: boolean },\n next: () => Promise<AgentRunResult>,\n) => Promise<AgentRunResult>;\n\n/**\n * A defined agent wraps a HarnessAgent with additional capabilities:\n * - Middleware pipeline\n * - Pre-action guardrails\n * - Human-in-the-loop gates\n * - Extended lifecycle hooks\n */\nexport interface DefinedAgent {\n name: string;\n config: HarnessConfig;\n /** The underlying harness agent */\n harness: HarnessAgent;\n /** Boot the agent */\n boot(): Promise<void>;\n /** Run with middleware, guardrails, and approval gates */\n run(prompt: string): Promise<AgentRunResult>;\n /** Stream with middleware, guardrails, and approval gates */\n stream(prompt: string): AgentStreamResult;\n /** Shutdown cleanly */\n shutdown(): Promise<void>;\n /** Get agent state */\n getState(): AgentState;\n /** Get system prompt */\n getSystemPrompt(): string;\n /** Check if agent is booted */\n isBooted(): boolean;\n /** Access the definition */\n definition: AgentDefinition;\n}\n\n/**\n * Define an agent with declarative configuration.\n *\n * This is the high-level API for creating agents with:\n * - Lifecycle hooks (beforeRun, afterRun, onBoot, onSessionEnd, onError, onShutdown)\n * - Middleware pipeline (wrap each run with custom logic)\n * - Guardrail enforcement (check rules before actions)\n * - Human-in-the-loop gates (require approval for certain prompts)\n *\n * Usage:\n * ```typescript\n * const agent = createAgent({\n * name: 'my-agent',\n * dir: './my-harness',\n * hooks: {\n * beforeRun: async ({ prompt }) => {\n * if (prompt.includes('delete')) return { reject: true, reason: 'Destructive action' };\n * },\n * onBoot: async () => console.log('Agent booted!'),\n * },\n * guardrails: { enforceRules: true },\n * approval: { requireApproval: (prompt) => prompt.includes('deploy') },\n * });\n *\n * await agent.boot();\n * const result = await agent.run('Hello world');\n * ```\n */\nexport function createAgent(definition: AgentDefinition): DefinedAgent {\n let booted = false;\n\n // Build HarnessHooks from lifecycle hooks\n const harnessHooks: HarnessHooks = {};\n if (definition.hooks?.onBoot) harnessHooks.onBoot = definition.hooks.onBoot;\n if (definition.hooks?.onSessionEnd) harnessHooks.onSessionEnd = definition.hooks.onSessionEnd;\n if (definition.hooks?.onError) harnessHooks.onError = definition.hooks.onError;\n if (definition.hooks?.onStateChange) harnessHooks.onStateChange = definition.hooks.onStateChange;\n if (definition.hooks?.onShutdown) harnessHooks.onShutdown = definition.hooks.onShutdown;\n\n // Create underlying harness\n const harnessOptions: CreateHarnessOptions = {\n dir: definition.dir,\n model: definition.model,\n provider: definition.provider,\n apiKey: definition.apiKey,\n config: definition.config,\n hooks: harnessHooks,\n };\n\n const harness = createHarness(harnessOptions);\n\n // Build middleware pipeline\n const middleware = definition.middleware ?? [];\n\n async function runWithPipeline(prompt: string, isStream: boolean): Promise<AgentRunResult> {\n // 1. beforeRun hook\n if (definition.hooks?.beforeRun) {\n const hookResult = await definition.hooks.beforeRun({\n agent: definedAgent,\n prompt,\n isStream,\n });\n\n if (hookResult) {\n if (hookResult.reject) {\n throw new Error(`Run rejected: ${hookResult.reason || 'beforeRun hook rejected'}`);\n }\n if (hookResult.prompt) {\n prompt = hookResult.prompt;\n }\n }\n }\n\n // 2. Guardrail enforcement (check rules)\n if (definition.guardrails?.enforceRules) {\n const violation = checkRuleViolation(definition.dir, prompt);\n if (violation) {\n throw new Error(`Guardrail violation: ${violation}`);\n }\n }\n\n // 3. Approval gate\n if (definition.approval?.requireApproval) {\n const needsApproval = definition.approval.requireApproval(prompt);\n if (needsApproval) {\n if (definition.approval.onApprovalNeeded) {\n const approved = await definition.approval.onApprovalNeeded(prompt);\n if (!approved) {\n throw new Error('Run requires approval but was not approved');\n }\n } else {\n // No approval handler \u0014 always reject\n throw new Error('Run requires approval but no onApprovalNeeded handler is configured');\n }\n }\n }\n\n // 4. Run through middleware chain\n const execute = async (): Promise<AgentRunResult> => {\n return harness.run(prompt);\n };\n\n let result: AgentRunResult;\n if (middleware.length > 0) {\n result = await executeMiddleware(middleware, definedAgent, prompt, isStream, execute);\n } else {\n result = await execute();\n }\n\n // 5. afterRun hook\n if (definition.hooks?.afterRun) {\n const transformed = await definition.hooks.afterRun({\n agent: definedAgent,\n prompt,\n result,\n });\n if (transformed) {\n result = transformed;\n }\n }\n\n return result;\n }\n\n const definedAgent: DefinedAgent = {\n name: definition.name,\n get config() { return harness.config; },\n harness,\n definition,\n\n async boot() {\n await harness.boot();\n booted = true;\n },\n\n async run(prompt: string): Promise<AgentRunResult> {\n if (!booted) await definedAgent.boot();\n return runWithPipeline(prompt, false);\n },\n\n stream(prompt: string): AgentStreamResult {\n // For stream, we wrap the textStream with pre-run checks\n let resolveResult: (r: AgentRunResult) => void;\n let rejectResult: (e: Error) => void;\n const resultPromise = new Promise<AgentRunResult>((res, rej) => {\n resolveResult = res;\n rejectResult = rej;\n });\n resultPromise.catch(() => {}); // Prevent unhandled rejection\n\n async function* generateStream(): AsyncIterable<string> {\n if (!booted) await definedAgent.boot();\n\n // Run pre-checks\n try {\n if (definition.hooks?.beforeRun) {\n const hookResult = await definition.hooks.beforeRun({\n agent: definedAgent,\n prompt,\n isStream: true,\n });\n if (hookResult?.reject) {\n const error = new Error(`Stream rejected: ${hookResult.reason || 'beforeRun hook rejected'}`);\n rejectResult(error);\n throw error;\n }\n if (hookResult?.prompt) {\n prompt = hookResult.prompt;\n }\n }\n\n if (definition.guardrails?.enforceRules) {\n const violation = checkRuleViolation(definition.dir, prompt);\n if (violation) {\n const error = new Error(`Guardrail violation: ${violation}`);\n rejectResult(error);\n throw error;\n }\n }\n\n if (definition.approval?.requireApproval) {\n const needsApproval = definition.approval.requireApproval(prompt);\n if (needsApproval) {\n if (definition.approval.onApprovalNeeded) {\n const approved = await definition.approval.onApprovalNeeded(prompt);\n if (!approved) {\n const error = new Error('Stream requires approval but was not approved');\n rejectResult(error);\n throw error;\n }\n } else {\n const error = new Error('Stream requires approval but no onApprovalNeeded handler is configured');\n rejectResult(error);\n throw error;\n }\n }\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n rejectResult(error);\n throw error;\n }\n\n // Delegate to harness stream\n const streamResult = harness.stream(prompt);\n let fullText = '';\n\n for await (const chunk of streamResult.textStream) {\n fullText += chunk;\n yield chunk;\n }\n\n // Await the result and apply afterRun hook\n let result = await streamResult.result;\n\n if (definition.hooks?.afterRun) {\n const transformed = await definition.hooks.afterRun({\n agent: definedAgent,\n prompt,\n result,\n });\n if (transformed) {\n result = transformed;\n }\n }\n\n resolveResult(result);\n }\n\n return {\n textStream: generateStream(),\n result: resultPromise,\n };\n },\n\n async shutdown() {\n await harness.shutdown();\n booted = false;\n },\n\n getState() {\n return harness.getState();\n },\n\n getSystemPrompt() {\n return harness.getSystemPrompt();\n },\n\n isBooted() {\n return booted;\n },\n };\n\n return definedAgent;\n}\n\n/**\n * Execute middleware chain in order.\n */\nasync function executeMiddleware(\n middlewares: AgentMiddleware[],\n agent: DefinedAgent,\n prompt: string,\n isStream: boolean,\n execute: () => Promise<AgentRunResult>,\n): Promise<AgentRunResult> {\n let index = 0;\n\n const next = async (): Promise<AgentRunResult> => {\n if (index >= middlewares.length) {\n return execute();\n }\n const mw = middlewares[index++];\n return mw({ agent, prompt, isStream }, next);\n };\n\n return next();\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 2. Guardrail Enforcement \u0014 Pre-action Rule Checking\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface GuardrailEnforcementConfig {\n /** Enable rule-based pre-action checking (default: false) */\n enforceRules?: boolean;\n /** Custom check function (in addition to rule scanning) */\n customCheck?: (prompt: string) => string | null;\n /** Tags to filter rules (only check rules with these tags) */\n ruleTags?: string[];\n}\n\nexport interface AgentRuleViolation {\n ruleId: string;\n rulePath: string;\n violatedDirective: string;\n prompt: string;\n}\n\n/**\n * Check if a prompt violates any rules in the harness.\n *\n * Rules are scanned for \"never\" / \"don't\" / \"must not\" / \"avoid\" directives.\n * If the prompt contains keywords that match a forbidden action,\n * a violation is returned.\n *\n * This is a heuristic check \u0014 not a semantic understanding of the prompt.\n * Returns null if no violation found, or a string describing the violation.\n */\nexport function checkRuleViolation(\n harnessDir: string,\n prompt: string,\n options?: { ruleTags?: string[] },\n): string | null {\n const rulesDir = join(harnessDir, 'rules');\n if (!existsSync(rulesDir)) return null;\n\n const rules = loadDirectory(rulesDir);\n const promptLower = prompt.toLowerCase();\n\n for (const rule of rules) {\n // Filter by tags if specified\n if (options?.ruleTags && options.ruleTags.length > 0) {\n const hasTag = rule.frontmatter.tags.some((t) =>\n options.ruleTags!.includes(t.toLowerCase()),\n );\n if (!hasTag) continue;\n }\n\n // Skip inactive rules\n if (rule.frontmatter.status !== 'active') continue;\n\n const text = (rule.l0 + '\\n' + rule.body).toLowerCase();\n\n // Extract forbidden actions from rules\n const forbiddenPatterns = extractForbiddenActions(text);\n\n for (const forbidden of forbiddenPatterns) {\n // Check if the prompt contains the forbidden action keywords\n const keywords = forbidden.split(/\\s+/).filter((w) => w.length > 3);\n const matchCount = keywords.filter((k) => promptLower.includes(k)).length;\n\n // Need at least 60% keyword match to flag\n if (keywords.length > 0 && matchCount >= Math.ceil(keywords.length * 0.6)) {\n return `Rule \"${rule.frontmatter.id}\" forbids: \"${forbidden}\" \u0014 prompt matches ${matchCount}/${keywords.length} keywords`;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract forbidden actions from rule text.\n * Looks for \"never X\", \"don't X\", \"must not X\", \"avoid X\" patterns.\n */\nfunction extractForbiddenActions(text: string): string[] {\n const actions: string[] = [];\n\n const patterns = [\n /never\\s+(.+?)(?:\\.|$)/gm,\n /don'?t\\s+(.+?)(?:\\.|$)/gm,\n /must not\\s+(.+?)(?:\\.|$)/gm,\n /do not\\s+(.+?)(?:\\.|$)/gm,\n /avoid\\s+(.+?)(?:\\.|$)/gm,\n /prohibit(?:ed|s)?\\s+(.+?)(?:\\.|$)/gm,\n /forbidden?:?\\s+(.+?)(?:\\.|$)/gm,\n ];\n\n for (const pattern of patterns) {\n let match;\n while ((match = pattern.exec(text)) !== null) {\n const action = match[1].trim();\n if (action.length > 3 && action.length < 200) {\n actions.push(action);\n }\n }\n }\n\n return actions;\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 3. Human-in-the-Loop Gates\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\nexport interface ApprovalGateConfig {\n /**\n * Function that decides if a prompt requires human approval.\n * Return true to require approval, false to proceed.\n */\n requireApproval?: (prompt: string) => boolean;\n\n /**\n * Called when approval is needed.\n * Should return true if approved, false if rejected.\n * For CLI: prompt user with readline.\n * For API: send webhook and await response.\n */\n onApprovalNeeded?: (prompt: string) => Promise<boolean>;\n\n /**\n * Timeout in ms for approval (default: 300000 = 5 minutes).\n * If timeout is reached, the run is rejected.\n */\n timeoutMs?: number;\n}\n\n/**\n * Create a simple CLI-based approval handler.\n * Prompts the user on stdin for approval.\n *\n * Usage:\n * ```typescript\n * createAgent({\n * ...\n * approval: {\n * requireApproval: (prompt) => prompt.includes('deploy'),\n * onApprovalNeeded: createCliApproval(),\n * },\n * });\n * ```\n */\nexport function createCliApproval(options?: { timeoutMs?: number }): (prompt: string) => Promise<boolean> {\n const timeoutMs = options?.timeoutMs ?? 300000;\n\n return (prompt: string): Promise<boolean> => {\n return new Promise((resolve) => {\n const readline = require('readline') as typeof import('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const timer = setTimeout(() => {\n log.warn('Approval timeout \u0014 rejecting');\n rl.close();\n resolve(false);\n }, timeoutMs);\n\n const preview = prompt.length > 100 ? prompt.slice(0, 100) + '...' : prompt;\n rl.question(`\\n[APPROVAL REQUIRED] \"${preview}\"\\nApprove? (y/N): `, (answer) => {\n clearTimeout(timer);\n rl.close();\n resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');\n });\n });\n };\n}\n\n/**\n * Create a webhook-based approval handler.\n * Sends a POST to the webhook URL and awaits response.\n */\nexport function createWebhookApproval(options: {\n url: string;\n timeoutMs?: number;\n headers?: Record<string, string>;\n}): (prompt: string) => Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? 300000;\n\n return async (prompt: string): Promise<boolean> => {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const response = await fetch(options.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: JSON.stringify({ prompt, timestamp: new Date().toISOString() }),\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n if (!response.ok) {\n log.warn(`Approval webhook returned ${response.status}`);\n return false;\n }\n\n const body = await response.json() as { approved?: boolean };\n return body.approved === true;\n } catch (err) {\n log.warn(`Approval webhook failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n };\n}\n\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n// 4. Utility: Rule-based Action Guards\n// \u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\n/**\n * Check if a specific action is allowed by the harness rules.\n * Returns { allowed: true } or { allowed: false, reason: string }.\n *\n * This is a lightweight version of checkRuleViolation for use outside defineAgent.\n */\nexport function checkAction(\n harnessDir: string,\n action: string,\n options?: { ruleTags?: string[] },\n): { allowed: boolean; reason: string | null } {\n const violation = checkRuleViolation(harnessDir, action, options);\n return {\n allowed: violation === null,\n reason: violation,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
autoProcessAll,
|
|
5
|
+
autoProcessFile
|
|
6
|
+
} from "./chunk-M7NXUK55.js";
|
|
7
|
+
import "./chunk-4CWAGBNS.js";
|
|
8
|
+
import "./chunk-ZZJOFKAT.js";
|
|
9
|
+
export {
|
|
10
|
+
autoProcessAll,
|
|
11
|
+
autoProcessFile
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=auto-processor-OLE45UI3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
loadDirectoryWithErrors
|
|
5
|
+
} from "./chunk-UPLBF4RZ.js";
|
|
6
|
+
import {
|
|
7
|
+
getPrimitiveDirs
|
|
8
|
+
} from "./chunk-4CWAGBNS.js";
|
|
9
|
+
|
|
10
|
+
// src/runtime/graph.ts
|
|
11
|
+
import { existsSync } from "fs";
|
|
12
|
+
import { join, relative } from "path";
|
|
13
|
+
function buildDependencyGraph(harnessDir, config) {
|
|
14
|
+
const dirs = getPrimitiveDirs(config);
|
|
15
|
+
const nodes = [];
|
|
16
|
+
const edges = [];
|
|
17
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const dir of dirs) {
|
|
19
|
+
const fullPath = join(harnessDir, dir);
|
|
20
|
+
if (!existsSync(fullPath)) continue;
|
|
21
|
+
const { docs } = loadDirectoryWithErrors(fullPath);
|
|
22
|
+
for (const doc of docs) {
|
|
23
|
+
const id = doc.frontmatter.id;
|
|
24
|
+
nodeIds.add(id);
|
|
25
|
+
nodes.push({
|
|
26
|
+
id,
|
|
27
|
+
directory: dir,
|
|
28
|
+
path: relative(harnessDir, doc.path),
|
|
29
|
+
tags: doc.frontmatter.tags,
|
|
30
|
+
status: doc.frontmatter.status,
|
|
31
|
+
l0: doc.l0
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const dir of dirs) {
|
|
36
|
+
const fullPath = join(harnessDir, dir);
|
|
37
|
+
if (!existsSync(fullPath)) continue;
|
|
38
|
+
const { docs } = loadDirectoryWithErrors(fullPath);
|
|
39
|
+
for (const doc of docs) {
|
|
40
|
+
const fromId = doc.frontmatter.id;
|
|
41
|
+
for (const ref of doc.frontmatter.related) {
|
|
42
|
+
const targetId = resolveRef(ref, nodeIds, harnessDir);
|
|
43
|
+
if (targetId) {
|
|
44
|
+
edges.push({ from: fromId, to: targetId, type: "related" });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (doc.frontmatter.with) {
|
|
48
|
+
const withRef = doc.frontmatter.with;
|
|
49
|
+
const targetId = resolveRef(withRef, nodeIds, harnessDir);
|
|
50
|
+
if (targetId) {
|
|
51
|
+
edges.push({ from: fromId, to: targetId, type: "with" });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const connected = /* @__PURE__ */ new Set();
|
|
57
|
+
for (const edge of edges) {
|
|
58
|
+
connected.add(edge.from);
|
|
59
|
+
connected.add(edge.to);
|
|
60
|
+
}
|
|
61
|
+
const orphans = nodes.filter((n) => !connected.has(n.id)).map((n) => n.id);
|
|
62
|
+
const clusters = findClusters(nodes, edges);
|
|
63
|
+
return { nodes, edges, orphans, clusters };
|
|
64
|
+
}
|
|
65
|
+
function resolveRef(ref, knownIds, harnessDir) {
|
|
66
|
+
if (knownIds.has(ref)) return ref;
|
|
67
|
+
if (ref.includes("/")) {
|
|
68
|
+
const parts = ref.split("/");
|
|
69
|
+
const filename = parts[parts.length - 1].replace(/\.md$/, "");
|
|
70
|
+
if (knownIds.has(filename)) return filename;
|
|
71
|
+
const withDir = parts.join("-");
|
|
72
|
+
if (knownIds.has(withDir)) return withDir;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function findClusters(nodes, edges) {
|
|
77
|
+
const parent = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const node of nodes) {
|
|
79
|
+
parent.set(node.id, node.id);
|
|
80
|
+
}
|
|
81
|
+
function find(id) {
|
|
82
|
+
let root = id;
|
|
83
|
+
while (parent.get(root) !== root) {
|
|
84
|
+
root = parent.get(root);
|
|
85
|
+
}
|
|
86
|
+
let current = id;
|
|
87
|
+
while (current !== root) {
|
|
88
|
+
const next = parent.get(current);
|
|
89
|
+
parent.set(current, root);
|
|
90
|
+
current = next;
|
|
91
|
+
}
|
|
92
|
+
return root;
|
|
93
|
+
}
|
|
94
|
+
function union(a, b) {
|
|
95
|
+
const rootA = find(a);
|
|
96
|
+
const rootB = find(b);
|
|
97
|
+
if (rootA !== rootB) {
|
|
98
|
+
parent.set(rootA, rootB);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const edge of edges) {
|
|
102
|
+
if (parent.has(edge.from) && parent.has(edge.to)) {
|
|
103
|
+
union(edge.from, edge.to);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const groups = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const node of nodes) {
|
|
108
|
+
const root = find(node.id);
|
|
109
|
+
if (!groups.has(root)) {
|
|
110
|
+
groups.set(root, []);
|
|
111
|
+
}
|
|
112
|
+
groups.get(root).push(node.id);
|
|
113
|
+
}
|
|
114
|
+
return Array.from(groups.values()).filter((g) => g.length > 1).sort((a, b) => b.length - a.length);
|
|
115
|
+
}
|
|
116
|
+
function getGraphStats(harnessDir, config) {
|
|
117
|
+
const graph = buildDependencyGraph(harnessDir, config);
|
|
118
|
+
const connectionCount = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const node of graph.nodes) {
|
|
120
|
+
connectionCount.set(node.id, 0);
|
|
121
|
+
}
|
|
122
|
+
for (const edge of graph.edges) {
|
|
123
|
+
connectionCount.set(edge.from, (connectionCount.get(edge.from) ?? 0) + 1);
|
|
124
|
+
connectionCount.set(edge.to, (connectionCount.get(edge.to) ?? 0) + 1);
|
|
125
|
+
}
|
|
126
|
+
const mostConnected = Array.from(connectionCount.entries()).filter(([, count]) => count > 0).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([id, connections]) => ({ id, connections }));
|
|
127
|
+
const brokenRefs = [];
|
|
128
|
+
const nodeIds = new Set(graph.nodes.map((n) => n.id));
|
|
129
|
+
for (const dir of getPrimitiveDirs(config)) {
|
|
130
|
+
const fullPath = join(harnessDir, dir);
|
|
131
|
+
if (!existsSync(fullPath)) continue;
|
|
132
|
+
const { docs } = loadDirectoryWithErrors(fullPath);
|
|
133
|
+
for (const doc of docs) {
|
|
134
|
+
for (const ref of doc.frontmatter.related) {
|
|
135
|
+
const resolved = resolveRef(ref, nodeIds, harnessDir);
|
|
136
|
+
if (!resolved) {
|
|
137
|
+
brokenRefs.push({ from: doc.frontmatter.id, ref });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (doc.frontmatter.with) {
|
|
141
|
+
const resolved = resolveRef(doc.frontmatter.with, nodeIds, harnessDir);
|
|
142
|
+
if (!resolved) {
|
|
143
|
+
brokenRefs.push({ from: doc.frontmatter.id, ref: doc.frontmatter.with });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
totalNodes: graph.nodes.length,
|
|
150
|
+
totalEdges: graph.edges.length,
|
|
151
|
+
orphanCount: graph.orphans.length,
|
|
152
|
+
clusterCount: graph.clusters.length,
|
|
153
|
+
mostConnected,
|
|
154
|
+
brokenRefs
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
buildDependencyGraph,
|
|
160
|
+
getGraphStats
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=chunk-274RV3YO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/graph.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join, relative, dirname, basename } from 'path';\nimport { loadDirectoryWithErrors } from '../primitives/loader.js';\nimport { getPrimitiveDirs } from '../core/types.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\n\nexport interface GraphNode {\n id: string;\n directory: string;\n path: string;\n tags: string[];\n status: string;\n l0: string;\n}\n\nexport interface GraphEdge {\n from: string;\n to: string;\n type: 'related' | 'with';\n}\n\nexport interface DependencyGraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n orphans: string[];\n clusters: string[][];\n}\n\nexport interface GraphStats {\n totalNodes: number;\n totalEdges: number;\n orphanCount: number;\n clusterCount: number;\n mostConnected: Array<{ id: string; connections: number }>;\n brokenRefs: Array<{ from: string; ref: string }>;\n}\n\n/**\n * Build a dependency graph from all primitives in the harness.\n * Analyzes `related:` and `with:` frontmatter fields to create edges.\n */\nexport function buildDependencyGraph(harnessDir: string, config?: HarnessConfig): DependencyGraph {\n const dirs = getPrimitiveDirs(config);\n const nodes: GraphNode[] = [];\n const edges: GraphEdge[] = [];\n const nodeIds = new Set<string>();\n\n // Load all primitives\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n const id = doc.frontmatter.id;\n nodeIds.add(id);\n nodes.push({\n id,\n directory: dir,\n path: relative(harnessDir, doc.path),\n tags: doc.frontmatter.tags,\n status: doc.frontmatter.status,\n l0: doc.l0,\n });\n }\n }\n\n // Build edges from related: and with: fields\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n const fromId = doc.frontmatter.id;\n\n // related: field edges\n for (const ref of doc.frontmatter.related) {\n const targetId = resolveRef(ref, nodeIds, harnessDir);\n if (targetId) {\n edges.push({ from: fromId, to: targetId, type: 'related' });\n }\n }\n\n // with: field edges (agent delegation reference)\n if (doc.frontmatter.with) {\n const withRef = doc.frontmatter.with;\n const targetId = resolveRef(withRef, nodeIds, harnessDir);\n if (targetId) {\n edges.push({ from: fromId, to: targetId, type: 'with' });\n }\n }\n }\n }\n\n // Find orphans (nodes with no edges in or out)\n const connected = new Set<string>();\n for (const edge of edges) {\n connected.add(edge.from);\n connected.add(edge.to);\n }\n const orphans = nodes\n .filter((n) => !connected.has(n.id))\n .map((n) => n.id);\n\n // Find clusters (connected components)\n const clusters = findClusters(nodes, edges);\n\n return { nodes, edges, orphans, clusters };\n}\n\n/**\n * Resolve a reference to a node ID. Handles:\n * - Direct ID match (e.g., \"tool-github\")\n * - Path-style refs (e.g., \"skills/code-review\" or \"agents/reviewer\")\n */\nfunction resolveRef(ref: string, knownIds: Set<string>, harnessDir: string): string | null {\n // Direct ID match\n if (knownIds.has(ref)) return ref;\n\n // Path-style ref: extract the filename part as potential ID\n if (ref.includes('/')) {\n const parts = ref.split('/');\n const filename = parts[parts.length - 1].replace(/\\.md$/, '');\n if (knownIds.has(filename)) return filename;\n\n // Try with directory prefix as part of ID\n const withDir = parts.join('-');\n if (knownIds.has(withDir)) return withDir;\n }\n\n return null;\n}\n\n/**\n * Find connected components using union-find.\n */\nfunction findClusters(nodes: GraphNode[], edges: GraphEdge[]): string[][] {\n const parent = new Map<string, string>();\n\n for (const node of nodes) {\n parent.set(node.id, node.id);\n }\n\n function find(id: string): string {\n let root = id;\n while (parent.get(root) !== root) {\n root = parent.get(root)!;\n }\n // Path compression\n let current = id;\n while (current !== root) {\n const next = parent.get(current)!;\n parent.set(current, root);\n current = next;\n }\n return root;\n }\n\n function union(a: string, b: string): void {\n const rootA = find(a);\n const rootB = find(b);\n if (rootA !== rootB) {\n parent.set(rootA, rootB);\n }\n }\n\n for (const edge of edges) {\n if (parent.has(edge.from) && parent.has(edge.to)) {\n union(edge.from, edge.to);\n }\n }\n\n // Group by root\n const groups = new Map<string, string[]>();\n for (const node of nodes) {\n const root = find(node.id);\n if (!groups.has(root)) {\n groups.set(root, []);\n }\n groups.get(root)!.push(node.id);\n }\n\n // Return only clusters with more than 1 member (singletons are orphans)\n return Array.from(groups.values())\n .filter((g) => g.length > 1)\n .sort((a, b) => b.length - a.length);\n}\n\n/**\n * Get statistics about the dependency graph.\n */\nexport function getGraphStats(harnessDir: string, config?: HarnessConfig): GraphStats {\n const graph = buildDependencyGraph(harnessDir, config);\n\n // Count connections per node\n const connectionCount = new Map<string, number>();\n for (const node of graph.nodes) {\n connectionCount.set(node.id, 0);\n }\n for (const edge of graph.edges) {\n connectionCount.set(edge.from, (connectionCount.get(edge.from) ?? 0) + 1);\n connectionCount.set(edge.to, (connectionCount.get(edge.to) ?? 0) + 1);\n }\n\n const mostConnected = Array.from(connectionCount.entries())\n .filter(([, count]) => count > 0)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([id, connections]) => ({ id, connections }));\n\n // Find broken references (references that didn't resolve to known IDs)\n const brokenRefs: Array<{ from: string; ref: string }> = [];\n const nodeIds = new Set(graph.nodes.map((n) => n.id));\n\n for (const dir of getPrimitiveDirs(config)) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n for (const ref of doc.frontmatter.related) {\n const resolved = resolveRef(ref, nodeIds, harnessDir);\n if (!resolved) {\n brokenRefs.push({ from: doc.frontmatter.id, ref });\n }\n }\n if (doc.frontmatter.with) {\n const resolved = resolveRef(doc.frontmatter.with, nodeIds, harnessDir);\n if (!resolved) {\n brokenRefs.push({ from: doc.frontmatter.id, ref: doc.frontmatter.with });\n }\n }\n }\n }\n\n return {\n totalNodes: graph.nodes.length,\n totalEdges: graph.edges.length,\n orphanCount: graph.orphans.length,\n clusterCount: graph.clusters.length,\n mostConnected,\n brokenRefs,\n };\n}\n"],"mappings":"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","names":[]}
|