@aria_asi/cli 0.2.33 → 0.2.34
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/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +47 -0
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +16 -3
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +41 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +42 -1
- package/dist/assets/hooks/doctrine_trigger_map.json +43 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +14 -1
- package/dist/assets/opencode-plugins/harness-context/index.js +1 -1
- package/dist/assets/opencode-plugins/harness-gate/index.js +49 -9
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
- package/dist/assets/opencode-plugins/harness-stop/index.js +201 -166
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
- package/dist/runtime/codex-bridge.mjs +1 -1
- package/dist/runtime/discipline/CLAUDE.md +2 -2
- package/dist/runtime/discipline/doctrine_trigger_map.json +43 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +3 -3
- package/dist/runtime/doctrine_trigger_map.json +43 -0
- package/dist/runtime/hooks/aria-agent-handoff.mjs +247 -0
- package/dist/runtime/hooks/aria-agent-ledger-merge.mjs +164 -0
- package/dist/runtime/hooks/aria-architect-fallback.mjs +267 -0
- package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +761 -0
- package/dist/runtime/hooks/aria-discovery-record.mjs +101 -0
- package/dist/runtime/hooks/aria-harness-via-sdk.mjs +544 -0
- package/dist/runtime/hooks/aria-import-resolution-gate.mjs +330 -0
- package/dist/runtime/hooks/aria-outcome-record.mjs +84 -0
- package/dist/runtime/hooks/aria-pre-emit-dryrun.mjs +329 -0
- package/dist/runtime/hooks/aria-pre-text-gate.mjs +112 -0
- package/dist/runtime/hooks/aria-pre-tool-gate.mjs +2482 -0
- package/dist/runtime/hooks/aria-preprompt-consult.mjs +464 -0
- package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +647 -0
- package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +429 -0
- package/dist/runtime/hooks/aria-stop-gate.mjs +1882 -0
- package/dist/runtime/hooks/aria-trigger-autolearn.mjs +229 -0
- package/dist/runtime/hooks/aria-userprompt-abandon-detect.mjs +192 -0
- package/dist/runtime/hooks/doctrine_trigger_map.json +577 -0
- package/dist/runtime/hooks/lib/canonical-lenses.mjs +65 -0
- package/dist/runtime/hooks/lib/domain-output-quality.mjs +103 -0
- package/dist/runtime/hooks/lib/gate-audit.mjs +43 -0
- package/dist/runtime/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/runtime/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/dist/runtime/hooks/test-aria-preturn-memory-gate.mjs +245 -0
- package/dist/runtime/hooks/test-tier-lens-labeling.mjs +367 -0
- package/dist/runtime/manifest.json +2 -2
- package/dist/runtime/sdk/BUNDLED.json +2 -2
- package/dist/runtime/sdk/index.d.ts +39 -0
- package/dist/runtime/sdk/index.js +117 -0
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/sdk/runWithGovernance.d.ts +16 -0
- package/dist/runtime/sdk/runWithGovernance.js +54 -0
- package/dist/runtime/sdk/runWithGovernance.js.map +1 -0
- package/dist/sdk/BUNDLED.json +2 -2
- package/dist/sdk/index.d.ts +39 -0
- package/dist/sdk/index.js +117 -0
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/runWithGovernance.d.ts +16 -0
- package/dist/sdk/runWithGovernance.js +54 -0
- package/dist/sdk/runWithGovernance.js.map +1 -0
- package/hooks/aria-harness-via-sdk.mjs +16 -3
- package/hooks/aria-pre-tool-gate.mjs +41 -1
- package/hooks/aria-stop-gate.mjs +42 -1
- package/hooks/doctrine_trigger_map.json +43 -0
- package/hooks/lib/skill-autoload-gate.mjs +14 -1
- package/opencode-plugins/harness-context/index.js +1 -1
- package/opencode-plugins/harness-gate/index.js +49 -9
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
- package/opencode-plugins/harness-stop/index.js +201 -166
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
- package/package.json +12 -5
- package/runtime-src/codex-bridge.mjs +1 -1
- package/scripts/bundle-sdk.mjs +2 -0
- package/scripts/self-test-harness-gates.mjs +79 -0
- package/src/connectors/codex.ts +47 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Smoke test: Phase 11 #59 — canonical lens labeling
|
|
3
|
+
//
|
|
4
|
+
// Verifies that aria-pre-tool-gate.mjs and aria-stop-gate.mjs:
|
|
5
|
+
// 1. Show canonical Arabic lens names (nur, mizan, etc.) when the harness
|
|
6
|
+
// packet has isHamza/hamza=true (OWNER tier).
|
|
7
|
+
// 2. Preserve those same canonical labels on client-surface execution too.
|
|
8
|
+
// 3. Gate enforcement (block on insufficient cognition) fires in both
|
|
9
|
+
// tiers without renaming the lenses.
|
|
10
|
+
//
|
|
11
|
+
// Usage: node hooks/test-tier-lens-labeling.mjs
|
|
12
|
+
// Exit: 0 = all assertions passed, 1 = failure
|
|
13
|
+
|
|
14
|
+
import { spawnSync } from 'node:child_process';
|
|
15
|
+
import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const PRE_TOOL_HOOK = path.join(HERE, 'aria-pre-tool-gate.mjs');
|
|
21
|
+
const STOP_HOOK = path.join(HERE, 'aria-stop-gate.mjs');
|
|
22
|
+
const HOME = process.env.HOME || '/tmp';
|
|
23
|
+
const CLAUDE_DIR = `${HOME}/.claude`;
|
|
24
|
+
const PACKET_PATH = `${CLAUDE_DIR}/.aria-harness-last-packet.json`;
|
|
25
|
+
|
|
26
|
+
// ── Canonical label set ────────────────────────────────────────────────────
|
|
27
|
+
const CANONICAL = ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'];
|
|
28
|
+
|
|
29
|
+
// ── Assertion helpers ───────────────────────────────────────────────────────
|
|
30
|
+
let passed = 0;
|
|
31
|
+
let failed = 0;
|
|
32
|
+
|
|
33
|
+
function assert(label, condition, detail) {
|
|
34
|
+
if (condition) {
|
|
35
|
+
console.log(` PASS ${label}`);
|
|
36
|
+
passed++;
|
|
37
|
+
} else {
|
|
38
|
+
console.error(` FAIL ${label}`);
|
|
39
|
+
if (detail !== undefined) console.error(` detail: ${String(detail).slice(0, 300)}`);
|
|
40
|
+
failed++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Packet helpers ──────────────────────────────────────────────────────────
|
|
45
|
+
// Save/restore the real packet so running this test doesn't corrupt live state.
|
|
46
|
+
let originalPacketContent = null;
|
|
47
|
+
|
|
48
|
+
function savePacket() {
|
|
49
|
+
try {
|
|
50
|
+
if (existsSync(PACKET_PATH)) {
|
|
51
|
+
originalPacketContent = readFileSync(PACKET_PATH, 'utf8');
|
|
52
|
+
} else {
|
|
53
|
+
originalPacketContent = null;
|
|
54
|
+
}
|
|
55
|
+
} catch { originalPacketContent = null; }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function restorePacket() {
|
|
59
|
+
try {
|
|
60
|
+
if (originalPacketContent !== null) {
|
|
61
|
+
writeFileSync(PACKET_PATH, originalPacketContent);
|
|
62
|
+
} else if (existsSync(PACKET_PATH)) {
|
|
63
|
+
unlinkSync(PACKET_PATH);
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function writeOwnerPacket() {
|
|
69
|
+
// contractGate.signals.hamza === true → OWNER tier
|
|
70
|
+
const packet = {
|
|
71
|
+
ok: true,
|
|
72
|
+
stage: 'preflight',
|
|
73
|
+
harness: 'surface=platform:hamza group:false hamza:true',
|
|
74
|
+
contractGate: {
|
|
75
|
+
signals: { hamza: true },
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
79
|
+
writeFileSync(PACKET_PATH, JSON.stringify(packet));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeClientPacket() {
|
|
83
|
+
// contractGate.signals.hamza === "false" (string) → CLIENT tier
|
|
84
|
+
const packet = {
|
|
85
|
+
ok: true,
|
|
86
|
+
stage: 'preflight',
|
|
87
|
+
harness: 'surface=platform:client group:false hamza:false',
|
|
88
|
+
contractGate: {
|
|
89
|
+
signals: { hamza: 'false' },
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
93
|
+
writeFileSync(PACKET_PATH, JSON.stringify(packet));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Transcript helpers ──────────────────────────────────────────────────────
|
|
97
|
+
// Write a minimal transcript where the most recent assistant turn has NO
|
|
98
|
+
// cognition block — triggering a block decision.
|
|
99
|
+
function writeEmptyTranscript(transcriptPath) {
|
|
100
|
+
const entries = [
|
|
101
|
+
// User message
|
|
102
|
+
JSON.stringify({
|
|
103
|
+
message: { role: 'user', content: [{ type: 'text', text: 'Run a build command' }] },
|
|
104
|
+
}),
|
|
105
|
+
// Assistant text with no cognition
|
|
106
|
+
JSON.stringify({
|
|
107
|
+
message: {
|
|
108
|
+
role: 'assistant',
|
|
109
|
+
content: [{ type: 'text', text: 'I will run the build.' }],
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
];
|
|
113
|
+
writeFileSync(transcriptPath, entries.join('\n'));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Write a transcript with a Stop-gate-triggering assistant response (long,
|
|
117
|
+
// decision-signal, but no cognition).
|
|
118
|
+
function writeNoCognitionStopTranscript(transcriptPath) {
|
|
119
|
+
const longText = 'I recommend shipping this feature immediately because the test suite is green and the build passed. Let me proceed with the deployment now. ' +
|
|
120
|
+
'The plan is as follows: first we build, then we deploy, then we verify. I would suggest we move forward without delay.';
|
|
121
|
+
const entries = [
|
|
122
|
+
JSON.stringify({
|
|
123
|
+
message: { role: 'user', content: [{ type: 'text', text: 'Should I ship?' }] },
|
|
124
|
+
}),
|
|
125
|
+
JSON.stringify({
|
|
126
|
+
message: {
|
|
127
|
+
role: 'assistant',
|
|
128
|
+
content: [{ type: 'text', text: longText }],
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
];
|
|
132
|
+
writeFileSync(transcriptPath, entries.join('\n'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── Run hook helpers ────────────────────────────────────────────────────────
|
|
136
|
+
// Use a non-destructive but non-trivial command that skips the verify path
|
|
137
|
+
// and goes straight to cognition-missing, where lens labels appear.
|
|
138
|
+
function runPreToolGate(transcriptPath, command = 'npm run build --workspace=packages/aria-connector') {
|
|
139
|
+
const event = JSON.stringify({
|
|
140
|
+
tool_name: 'Bash',
|
|
141
|
+
tool_input: { command },
|
|
142
|
+
transcript_path: transcriptPath,
|
|
143
|
+
session_id: `test-tier-${Date.now()}`,
|
|
144
|
+
});
|
|
145
|
+
return spawnSync(process.execPath, [PRE_TOOL_HOOK], {
|
|
146
|
+
input: event,
|
|
147
|
+
encoding: 'utf8',
|
|
148
|
+
timeout: 10000,
|
|
149
|
+
env: {
|
|
150
|
+
...process.env,
|
|
151
|
+
HOME,
|
|
152
|
+
ARIA_COGNITION_PUSH: 'off', // silence network push
|
|
153
|
+
ARIA_HARNESS_TOKEN: '', // no outbound harness calls
|
|
154
|
+
ARIA_BINDING_ENABLED: 'false', // disable binding gate — isolate cognition test
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function runStopGate(transcriptPath) {
|
|
160
|
+
const event = JSON.stringify({
|
|
161
|
+
tool_name: 'Stop',
|
|
162
|
+
transcript_path: transcriptPath,
|
|
163
|
+
session_id: `test-tier-${Date.now()}`,
|
|
164
|
+
});
|
|
165
|
+
return spawnSync(process.execPath, [STOP_HOOK], {
|
|
166
|
+
input: event,
|
|
167
|
+
encoding: 'utf8',
|
|
168
|
+
timeout: 15000,
|
|
169
|
+
env: {
|
|
170
|
+
...process.env,
|
|
171
|
+
HOME,
|
|
172
|
+
ARIA_COGNITION_PUSH: 'off',
|
|
173
|
+
ARIA_HARNESS_TOKEN: '',
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── Test utilities ──────────────────────────────────────────────────────────
|
|
179
|
+
const tmpTranscript = `/tmp/test-tier-transcript-${Date.now()}.jsonl`;
|
|
180
|
+
const tmpTranscriptStop = `/tmp/test-tier-stop-transcript-${Date.now()}.jsonl`;
|
|
181
|
+
|
|
182
|
+
function cleanup() {
|
|
183
|
+
[tmpTranscript, tmpTranscriptStop].forEach((p) => {
|
|
184
|
+
try { if (existsSync(p)) unlinkSync(p); } catch {}
|
|
185
|
+
});
|
|
186
|
+
restorePacket();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── Suite ───────────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
console.log('\n=== Phase 11 #59 — Tier-aware lens labeling smoke tests ===\n');
|
|
192
|
+
|
|
193
|
+
// Save existing packet so we can restore after test run.
|
|
194
|
+
savePacket();
|
|
195
|
+
|
|
196
|
+
// ── Test 1: pre-tool-gate, OWNER tier → canonical labels in block reason ──
|
|
197
|
+
console.log('Test 1: aria-pre-tool-gate — OWNER tier shows canonical lens names');
|
|
198
|
+
{
|
|
199
|
+
writeOwnerPacket();
|
|
200
|
+
writeEmptyTranscript(tmpTranscript);
|
|
201
|
+
const result = runPreToolGate(tmpTranscript);
|
|
202
|
+
|
|
203
|
+
// Gate should block (exit 2) — no cognition in transcript
|
|
204
|
+
assert(
|
|
205
|
+
'T1: gate blocks (exit 2)',
|
|
206
|
+
result.status === 2,
|
|
207
|
+
`exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
let blockReason = '';
|
|
211
|
+
try {
|
|
212
|
+
const parsed = JSON.parse(result.stdout);
|
|
213
|
+
blockReason = parsed.reason || '';
|
|
214
|
+
} catch {
|
|
215
|
+
blockReason = result.stdout || '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Must mention at least one canonical label
|
|
219
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
220
|
+
assert(
|
|
221
|
+
'T1: block reason mentions canonical label (e.g. nur:)',
|
|
222
|
+
hasCanonical,
|
|
223
|
+
blockReason.slice(0, 400),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// The first visible lens label must be canonical.
|
|
227
|
+
const firstCanonicalIdx = blockReason.indexOf(`${CANONICAL[0]}:`);
|
|
228
|
+
assert(
|
|
229
|
+
'T1: first lens label is canonical (nur:)',
|
|
230
|
+
firstCanonicalIdx >= 0,
|
|
231
|
+
`firstCanonicalIdx=${firstCanonicalIdx}`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Test 2: pre-tool-gate, CLIENT tier → canonical labels in block reason ──
|
|
236
|
+
console.log('\nTest 2: aria-pre-tool-gate — CLIENT tier keeps canonical lens labels');
|
|
237
|
+
{
|
|
238
|
+
writeClientPacket();
|
|
239
|
+
writeEmptyTranscript(tmpTranscript);
|
|
240
|
+
const result = runPreToolGate(tmpTranscript);
|
|
241
|
+
|
|
242
|
+
assert(
|
|
243
|
+
'T2: gate blocks (exit 2)',
|
|
244
|
+
result.status === 2,
|
|
245
|
+
`exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
let blockReason = '';
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(result.stdout);
|
|
251
|
+
blockReason = parsed.reason || '';
|
|
252
|
+
} catch {
|
|
253
|
+
blockReason = result.stdout || '';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
257
|
+
assert(
|
|
258
|
+
'T2: block reason mentions canonical label (e.g. nur:)',
|
|
259
|
+
hasCanonical,
|
|
260
|
+
blockReason.slice(0, 400),
|
|
261
|
+
);
|
|
262
|
+
assert(
|
|
263
|
+
'T2: first visible lens label is canonical (nur:)',
|
|
264
|
+
blockReason.includes(`${CANONICAL[0]}:`),
|
|
265
|
+
blockReason.slice(0, 400),
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ── Test 3: stop-gate, OWNER tier → canonical labels ──
|
|
270
|
+
console.log('\nTest 3: aria-stop-gate — OWNER tier shows canonical lens names');
|
|
271
|
+
{
|
|
272
|
+
writeOwnerPacket();
|
|
273
|
+
writeNoCognitionStopTranscript(tmpTranscriptStop);
|
|
274
|
+
const result = runStopGate(tmpTranscriptStop);
|
|
275
|
+
|
|
276
|
+
assert(
|
|
277
|
+
'T3: stop-gate blocks (exit 2)',
|
|
278
|
+
result.status === 2,
|
|
279
|
+
`exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
let blockReason = '';
|
|
283
|
+
try {
|
|
284
|
+
const parsed = JSON.parse(result.stdout);
|
|
285
|
+
blockReason = parsed.reason || '';
|
|
286
|
+
} catch {
|
|
287
|
+
blockReason = result.stdout || '';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
291
|
+
assert(
|
|
292
|
+
'T3: stop-gate block reason mentions canonical label',
|
|
293
|
+
hasCanonical,
|
|
294
|
+
blockReason.slice(0, 400),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── Test 4: stop-gate, CLIENT tier → canonical labels ──────────────────────
|
|
299
|
+
console.log('\nTest 4: aria-stop-gate — CLIENT tier keeps canonical lens labels');
|
|
300
|
+
{
|
|
301
|
+
writeClientPacket();
|
|
302
|
+
writeNoCognitionStopTranscript(tmpTranscriptStop);
|
|
303
|
+
const result = runStopGate(tmpTranscriptStop);
|
|
304
|
+
|
|
305
|
+
assert(
|
|
306
|
+
'T4: stop-gate blocks (exit 2)',
|
|
307
|
+
result.status === 2,
|
|
308
|
+
`exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
let blockReason = '';
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(result.stdout);
|
|
314
|
+
blockReason = parsed.reason || '';
|
|
315
|
+
} catch {
|
|
316
|
+
blockReason = result.stdout || '';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
320
|
+
assert(
|
|
321
|
+
'T4: stop-gate block reason mentions canonical label',
|
|
322
|
+
hasCanonical,
|
|
323
|
+
blockReason.slice(0, 400),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ── Test 5: packet missing → defaults to canonical labels (fail-safe) ─────
|
|
328
|
+
console.log('\nTest 5: missing packet cache → defaults to canonical labels');
|
|
329
|
+
{
|
|
330
|
+
// Remove the packet cache
|
|
331
|
+
try { unlinkSync(PACKET_PATH); } catch {}
|
|
332
|
+
writeEmptyTranscript(tmpTranscript);
|
|
333
|
+
const result = runPreToolGate(tmpTranscript);
|
|
334
|
+
|
|
335
|
+
assert(
|
|
336
|
+
'T5: gate blocks (exit 2) with no packet',
|
|
337
|
+
result.status === 2,
|
|
338
|
+
`exit=${result.status}`,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
let blockReason = '';
|
|
342
|
+
try {
|
|
343
|
+
const parsed = JSON.parse(result.stdout);
|
|
344
|
+
blockReason = parsed.reason || '';
|
|
345
|
+
} catch {
|
|
346
|
+
blockReason = result.stdout || '';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
350
|
+
assert(
|
|
351
|
+
'T5: defaults to canonical labels when packet is absent',
|
|
352
|
+
hasCanonical,
|
|
353
|
+
`canonical=${hasCanonical} reason=${blockReason.slice(0, 300)}`,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ── Summary ─────────────────────────────────────────────────────────────────
|
|
358
|
+
cleanup();
|
|
359
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
360
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
361
|
+
if (failed > 0) {
|
|
362
|
+
console.error('\nSome tests FAILED — see FAIL lines above.');
|
|
363
|
+
process.exit(1);
|
|
364
|
+
} else {
|
|
365
|
+
console.log('\nAll tests PASSED.');
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"bundledAt": "2026-05-
|
|
3
|
-
"sdkFiles":
|
|
2
|
+
"bundledAt": "2026-05-03T03:42:17.303Z",
|
|
3
|
+
"sdkFiles": 9,
|
|
4
4
|
"runtimeTemplate": "/home/hamzaibrahim1/rei-ai-brain/packages/aria-connector/runtime-src",
|
|
5
5
|
"gateRuntimeSource": "/home/hamzaibrahim1/rei-ai-brain/packages/aria-gate-runtime/dist",
|
|
6
6
|
"sdkGuideSource": "/home/hamzaibrahim1/rei-ai-brain/harness/packages/harness-http-client/CLAUDE.md",
|
|
@@ -2,6 +2,7 @@ export interface HarnessClientConfig {
|
|
|
2
2
|
baseUrl: string;
|
|
3
3
|
apiKey: string;
|
|
4
4
|
harnessPacketUrl?: string;
|
|
5
|
+
gardenBaseUrl?: string;
|
|
5
6
|
workspaceRoot?: string;
|
|
6
7
|
}
|
|
7
8
|
export interface OwnerTier {
|
|
@@ -170,6 +171,36 @@ export interface ActionCheck {
|
|
|
170
171
|
reason?: string;
|
|
171
172
|
requiredGates: string[];
|
|
172
173
|
}
|
|
174
|
+
export interface GardenContinuitySnapshot {
|
|
175
|
+
hydrated: boolean;
|
|
176
|
+
sessionId: string;
|
|
177
|
+
userId?: string;
|
|
178
|
+
query: string;
|
|
179
|
+
contextBlock: string;
|
|
180
|
+
memoryCount: number;
|
|
181
|
+
threadCount: number;
|
|
182
|
+
pulseCount: number;
|
|
183
|
+
snapshotMs: number;
|
|
184
|
+
postgresBound?: boolean;
|
|
185
|
+
qdrantBound?: boolean;
|
|
186
|
+
pulseLoopAlive?: boolean;
|
|
187
|
+
}
|
|
188
|
+
export interface GovernanceOutcomeInput {
|
|
189
|
+
sessionId: string;
|
|
190
|
+
userId?: string;
|
|
191
|
+
sourceRuntime: string;
|
|
192
|
+
action?: string;
|
|
193
|
+
outcome: string;
|
|
194
|
+
evidence?: unknown;
|
|
195
|
+
obligationId?: string;
|
|
196
|
+
}
|
|
197
|
+
export interface GovernanceOutcomeResult {
|
|
198
|
+
ok: boolean;
|
|
199
|
+
pulseId?: string;
|
|
200
|
+
ledgerId?: string;
|
|
201
|
+
threadId?: string;
|
|
202
|
+
message?: string;
|
|
203
|
+
}
|
|
173
204
|
/**
|
|
174
205
|
* Result shape for all three Aristotle phases.
|
|
175
206
|
* Mirrors `AristotleNoorWireResult` in aria-connector's aristotle-noor-wire.ts.
|
|
@@ -270,6 +301,7 @@ export declare class HTTPHarnessClient {
|
|
|
270
301
|
private readonly baseUrl;
|
|
271
302
|
private readonly apiKey;
|
|
272
303
|
private readonly harnessPacketUrl;
|
|
304
|
+
private readonly gardenBaseUrl?;
|
|
273
305
|
private readonly workspaceRoot;
|
|
274
306
|
private cachedPacket;
|
|
275
307
|
private packetLastFetched;
|
|
@@ -312,6 +344,12 @@ export declare class HTTPHarnessClient {
|
|
|
312
344
|
validateOutput(text: string, sessionId: string): Promise<ValidationResult>;
|
|
313
345
|
checkAction(action: 'deploy' | 'build' | 'write' | 'delete', target: string): Promise<ActionCheck>;
|
|
314
346
|
gardenTurn(sessionId: string, message: string, response: string, userId?: string): Promise<void>;
|
|
347
|
+
hydrateGardenContinuity(sessionId: string, query: string, limits?: {
|
|
348
|
+
threadLimit?: number;
|
|
349
|
+
pulseLimit?: number;
|
|
350
|
+
userId?: string;
|
|
351
|
+
}): Promise<GardenContinuitySnapshot>;
|
|
352
|
+
persistGovernanceOutcome(input: GovernanceOutcomeInput): Promise<GovernanceOutcomeResult>;
|
|
315
353
|
consult(args: {
|
|
316
354
|
brief: string;
|
|
317
355
|
sessionId: string;
|
|
@@ -496,3 +534,4 @@ export declare const harness: {
|
|
|
496
534
|
export declare function bindingContext(role: string, sessionId: string, stage: string, intendedAction: string, rationale: string): HarnessBindingContext;
|
|
497
535
|
export declare function getBoundHarnessAndPrompt(context: HarnessBindingContext, plan?: Partial<HarnessPlan>, client?: HTTPHarnessClient): Promise<BoundHarnessPromptResult>;
|
|
498
536
|
export * from './runWithCognition.js';
|
|
537
|
+
export * from './runWithGovernance.js';
|
|
@@ -87,6 +87,7 @@ export class HTTPHarnessClient {
|
|
|
87
87
|
baseUrl;
|
|
88
88
|
apiKey;
|
|
89
89
|
harnessPacketUrl;
|
|
90
|
+
gardenBaseUrl;
|
|
90
91
|
workspaceRoot;
|
|
91
92
|
cachedPacket = null;
|
|
92
93
|
packetLastFetched = 0;
|
|
@@ -99,6 +100,7 @@ export class HTTPHarnessClient {
|
|
|
99
100
|
this.harnessPacketUrl = config.harnessPacketUrl ?? (isMountedRuntimeBaseUrl(this.baseUrl)
|
|
100
101
|
? `${this.baseUrl}/packet`
|
|
101
102
|
: `${this.baseUrl}/api/harness/codex`);
|
|
103
|
+
this.gardenBaseUrl = (config.gardenBaseUrl || process.env.ARIA_GARDEN_BASE_URL || '').replace(/\/+$/, '') || undefined;
|
|
102
104
|
this.workspaceRoot = config.workspaceRoot ?? process.cwd();
|
|
103
105
|
this.preferredBaseUrl = this.baseUrl;
|
|
104
106
|
this.baseUrlCandidates = this.buildBaseUrlCandidates(config.baseUrl);
|
|
@@ -665,6 +667,120 @@ export class HTTPHarnessClient {
|
|
|
665
667
|
throw new Error(`garden/fire failed: ${res.status} ${res.statusText}`);
|
|
666
668
|
}
|
|
667
669
|
}
|
|
670
|
+
async hydrateGardenContinuity(sessionId, query, limits = {}) {
|
|
671
|
+
if (this.gardenBaseUrl) {
|
|
672
|
+
const params = new URLSearchParams({
|
|
673
|
+
session_id: sessionId,
|
|
674
|
+
user_id: limits.userId || '',
|
|
675
|
+
query,
|
|
676
|
+
thread_limit: String(limits.threadLimit ?? 5),
|
|
677
|
+
pulse_limit: String(limits.pulseLimit ?? 12),
|
|
678
|
+
});
|
|
679
|
+
const res = await this.fetchWithRetry(`${this.gardenBaseUrl}/continuity?${params}`, {
|
|
680
|
+
method: 'GET',
|
|
681
|
+
headers: {
|
|
682
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
683
|
+
'Content-Type': 'application/json',
|
|
684
|
+
},
|
|
685
|
+
});
|
|
686
|
+
if (res.ok) {
|
|
687
|
+
const data = (await res.json());
|
|
688
|
+
const activeThreads = Array.isArray(data.activeThreads) ? data.activeThreads : [];
|
|
689
|
+
const recentPulses = Array.isArray(data.recentPulses) ? data.recentPulses : [];
|
|
690
|
+
const contextBlock = String(data.continuityBlock || data.continuity_block || data.gardenContextBlock || '');
|
|
691
|
+
return {
|
|
692
|
+
hydrated: Boolean(data.ok) && (contextBlock.length > 0 || activeThreads.length > 0 || recentPulses.length > 0),
|
|
693
|
+
sessionId: String(data.sessionId || data.session_id || sessionId),
|
|
694
|
+
userId: String(data.userId || data.user_id || limits.userId || ''),
|
|
695
|
+
query,
|
|
696
|
+
contextBlock,
|
|
697
|
+
memoryCount: recentPulses.length,
|
|
698
|
+
threadCount: activeThreads.length,
|
|
699
|
+
pulseCount: recentPulses.length,
|
|
700
|
+
snapshotMs: Number(data.snapshotMs || data.snapshot_ms || Date.now()),
|
|
701
|
+
postgresBound: Boolean(data.postgresBound ?? data.postgres_bound),
|
|
702
|
+
qdrantBound: Boolean(data.qdrantBound ?? data.qdrant_bound),
|
|
703
|
+
pulseLoopAlive: Boolean(data.pulseLoopAlive ?? data.pulse_loop_alive),
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
if (![404, 405, 501].includes(res.status)) {
|
|
707
|
+
throw new Error(`garden continuity failed: ${res.status} ${res.statusText}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const params = new URLSearchParams({
|
|
711
|
+
session_id: sessionId,
|
|
712
|
+
query,
|
|
713
|
+
thread_limit: String(limits.threadLimit ?? 5),
|
|
714
|
+
pulse_limit: String(limits.pulseLimit ?? 12),
|
|
715
|
+
});
|
|
716
|
+
const res = await this.fetchWithRetry(`${this.baseUrl}/api/garden/living?${params}`, {
|
|
717
|
+
method: 'GET',
|
|
718
|
+
headers: {
|
|
719
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
720
|
+
'Content-Type': 'application/json',
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
if (!res.ok) {
|
|
724
|
+
throw new Error(`garden/living failed: ${res.status} ${res.statusText}`);
|
|
725
|
+
}
|
|
726
|
+
const data = (await res.json());
|
|
727
|
+
const activeThreads = Array.isArray(data.activeThreads) ? data.activeThreads : [];
|
|
728
|
+
const recentPulses = Array.isArray(data.recentPulses) ? data.recentPulses : Array.isArray(data.pulses) ? data.pulses : [];
|
|
729
|
+
const contextBlock = String(data.gardenContextBlock || data.garden_context_block || data.contextBlock || '');
|
|
730
|
+
return {
|
|
731
|
+
hydrated: contextBlock.length > 0 || activeThreads.length > 0 || recentPulses.length > 0,
|
|
732
|
+
sessionId,
|
|
733
|
+
userId: limits.userId,
|
|
734
|
+
query,
|
|
735
|
+
contextBlock,
|
|
736
|
+
memoryCount: recentPulses.length,
|
|
737
|
+
threadCount: activeThreads.length,
|
|
738
|
+
pulseCount: recentPulses.length,
|
|
739
|
+
snapshotMs: Number(data.snapshotMs || data.snapshot_ms || Date.now()),
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
async persistGovernanceOutcome(input) {
|
|
743
|
+
if (!input.sessionId || !input.sourceRuntime || !input.outcome) {
|
|
744
|
+
throw new Error('persistGovernanceOutcome requires sessionId, sourceRuntime, and outcome');
|
|
745
|
+
}
|
|
746
|
+
const body = {
|
|
747
|
+
session_id: input.sessionId,
|
|
748
|
+
user_id: input.userId,
|
|
749
|
+
source_runtime: input.sourceRuntime,
|
|
750
|
+
action: input.action || '',
|
|
751
|
+
outcome: input.outcome,
|
|
752
|
+
evidence: input.evidence ?? null,
|
|
753
|
+
obligation_id: input.obligationId || '',
|
|
754
|
+
};
|
|
755
|
+
const targets = this.gardenBaseUrl
|
|
756
|
+
? [`${this.gardenBaseUrl}/governance-outcome`, `${this.baseUrl}/api/garden/governance-outcome`]
|
|
757
|
+
: [`${this.baseUrl}/api/garden/governance-outcome`];
|
|
758
|
+
let lastStatus = '';
|
|
759
|
+
for (const url of targets) {
|
|
760
|
+
const res = await this.fetchWithRetry(url, {
|
|
761
|
+
method: 'POST',
|
|
762
|
+
headers: {
|
|
763
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
764
|
+
'Content-Type': 'application/json',
|
|
765
|
+
},
|
|
766
|
+
body: JSON.stringify(body),
|
|
767
|
+
});
|
|
768
|
+
if (res.ok) {
|
|
769
|
+
const data = (await res.json());
|
|
770
|
+
return {
|
|
771
|
+
ok: Boolean(data.ok),
|
|
772
|
+
pulseId: String(data.pulseId || data.pulse_id || ''),
|
|
773
|
+
ledgerId: String(data.ledgerId || data.ledger_id || ''),
|
|
774
|
+
threadId: String(data.threadId || data.thread_id || ''),
|
|
775
|
+
message: typeof data.message === 'string' ? data.message : undefined,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
lastStatus = `${res.status} ${res.statusText}`;
|
|
779
|
+
if (![404, 405, 501].includes(res.status))
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
throw new Error(`persistGovernanceOutcome failed: ${lastStatus || 'no target available'}`);
|
|
783
|
+
}
|
|
668
784
|
// ── Consult — delegate to Aria for direction or structured plan ─────────
|
|
669
785
|
// Routes through /api/harness/delegate (the architect-mode endpoint that
|
|
670
786
|
// serves preprompt-consult + Aria-as-commander binding plans). Caller
|
|
@@ -1594,4 +1710,5 @@ export async function getBoundHarnessAndPrompt(context, plan, client) {
|
|
|
1594
1710
|
return sdk.getBoundHarnessAndPrompt(context, plan);
|
|
1595
1711
|
}
|
|
1596
1712
|
export * from './runWithCognition.js';
|
|
1713
|
+
export * from './runWithGovernance.js';
|
|
1597
1714
|
//# sourceMappingURL=index.js.map
|