@aria_asi/cli 0.2.32 → 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/codebase-awareness.d.ts +8 -1
- package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codebase-awareness.js +126 -71
- package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +98 -0
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
- package/dist/aria-connector/src/setup-wizard.js +91 -24
- package/dist/aria-connector/src/setup-wizard.js.map +1 -1
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +26 -8
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +60 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +69 -3
- package/dist/assets/hooks/doctrine_trigger_map.json +43 -0
- package/dist/assets/hooks/lib/domain-output-quality.mjs +103 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/dist/assets/opencode-plugins/harness-context/index.js +1 -1
- package/dist/assets/opencode-plugins/harness-gate/index.js +114 -10
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -0
- package/dist/assets/opencode-plugins/harness-outcome/index.js +39 -0
- package/dist/assets/opencode-plugins/harness-stop/index.js +234 -139
- package/dist/assets/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -0
- package/dist/runtime/codex-bridge.mjs +71 -8
- 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/harness-daemon.mjs +50 -2
- 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 +48 -0
- package/dist/runtime/sdk/index.js +140 -1
- 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/runtime/service.mjs +339 -10
- package/dist/sdk/BUNDLED.json +2 -2
- package/dist/sdk/index.d.ts +48 -0
- package/dist/sdk/index.js +140 -1
- 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 +26 -8
- package/hooks/aria-pre-tool-gate.mjs +60 -1
- package/hooks/aria-stop-gate.mjs +69 -3
- package/hooks/doctrine_trigger_map.json +43 -0
- package/hooks/lib/domain-output-quality.mjs +103 -0
- package/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/opencode-plugins/harness-context/index.js +1 -1
- package/opencode-plugins/harness-gate/index.js +114 -10
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -0
- package/opencode-plugins/harness-outcome/index.js +39 -0
- package/opencode-plugins/harness-stop/index.js +234 -139
- package/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -0
- package/package.json +12 -5
- package/runtime-src/codex-bridge.mjs +71 -8
- package/runtime-src/harness-daemon.mjs +50 -2
- package/runtime-src/service.mjs +339 -10
- package/scripts/bundle-sdk.mjs +2 -0
- package/scripts/self-test-harness-gates.mjs +79 -0
- package/src/connectors/codebase-awareness.ts +141 -77
- package/src/connectors/codex.ts +98 -0
- package/src/setup-wizard.ts +105 -25
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
5
|
import { homedir } from 'node:os';
|
|
5
6
|
import { delimiter, dirname, resolve } from 'node:path';
|
|
6
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
7
8
|
import { createRequire } from 'node:module';
|
|
9
|
+
import { evaluateSkillGate, formatSkillGateBlock } from './hooks/lib/skill-autoload-gate.mjs';
|
|
8
10
|
|
|
9
11
|
const require = createRequire(import.meta.url);
|
|
10
12
|
const { WebSocketServer, WebSocket } = require('ws');
|
|
@@ -75,6 +77,24 @@ function sleep(ms) {
|
|
|
75
77
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
function stableEvidenceString(value) {
|
|
81
|
+
if (typeof value === 'string') return value;
|
|
82
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function makeEvidenceRef(kind, value, metadata = {}) {
|
|
86
|
+
const raw = stableEvidenceString(value);
|
|
87
|
+
const sha256 = createHash('sha256').update(raw).digest('hex');
|
|
88
|
+
return {
|
|
89
|
+
evidenceId: `ev_${sha256.slice(0, 16)}`,
|
|
90
|
+
kind,
|
|
91
|
+
at: new Date().toISOString(),
|
|
92
|
+
sha256,
|
|
93
|
+
preview: raw.slice(0, 500),
|
|
94
|
+
metadata,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
78
98
|
function readRuntimeToken() {
|
|
79
99
|
const envToken = process.env.ARIA_HARNESS_TOKEN || process.env.ARIA_API_KEY || process.env.OPENAI_API_KEY || process.env.ARIA_MASTER_TOKEN;
|
|
80
100
|
if (envToken) return envToken;
|
|
@@ -123,9 +143,10 @@ function ensureTurnState(threadId, turnId) {
|
|
|
123
143
|
userText: '',
|
|
124
144
|
preReceiptId: null,
|
|
125
145
|
agentText: '',
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
bufferedAgentNotifications: [],
|
|
147
|
+
firstAgentItemId: null,
|
|
148
|
+
traceId: `trace_${randomUUID()}`,
|
|
149
|
+
};
|
|
129
150
|
turnState.set(key, state);
|
|
130
151
|
}
|
|
131
152
|
return state;
|
|
@@ -186,6 +207,16 @@ async function validateTurnText(threadId, turnId) {
|
|
|
186
207
|
if (!text) {
|
|
187
208
|
return { ok: false, reason: 'No assistant text exists for this turn yet. Codex must emit readable cognition before action.' };
|
|
188
209
|
}
|
|
210
|
+
const skillGate = evaluateSkillGate({
|
|
211
|
+
sessionId: `codex:${threadId}:${turnId}`,
|
|
212
|
+
surface: 'codex-bridge-output',
|
|
213
|
+
text: [state.userText, text].join('\n'),
|
|
214
|
+
isOutputCloseout: true,
|
|
215
|
+
autoLoadAvailable: false,
|
|
216
|
+
});
|
|
217
|
+
if (!skillGate.ok) {
|
|
218
|
+
return { ok: false, reason: formatSkillGateBlock(skillGate), result: { skillGate } };
|
|
219
|
+
}
|
|
189
220
|
const result = await postRuntime('/validate-output', {
|
|
190
221
|
text,
|
|
191
222
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
@@ -198,6 +229,7 @@ async function validateTurnText(threadId, turnId) {
|
|
|
198
229
|
stage: 'codex-turn',
|
|
199
230
|
actor: 'codex-bridge',
|
|
200
231
|
system: 'codex-bridge',
|
|
232
|
+
traceId: state.traceId,
|
|
201
233
|
},
|
|
202
234
|
});
|
|
203
235
|
const pass = result?.pass === true && result?.validation?.passed !== false;
|
|
@@ -210,11 +242,29 @@ async function validateTurnText(threadId, turnId) {
|
|
|
210
242
|
};
|
|
211
243
|
}
|
|
212
244
|
|
|
213
|
-
async function checkActionAgainstRuntime(action, target, threadId, turnId) {
|
|
245
|
+
async function checkActionAgainstRuntime(action, target, threadId, turnId, metadata = {}) {
|
|
246
|
+
const state = ensureTurnState(threadId, turnId);
|
|
247
|
+
const skillGate = evaluateSkillGate({
|
|
248
|
+
sessionId: `codex:${threadId}:${turnId}`,
|
|
249
|
+
surface: 'codex-bridge-action',
|
|
250
|
+
text: target,
|
|
251
|
+
action: target,
|
|
252
|
+
toolName: action,
|
|
253
|
+
isDeploy: action === 'deploy',
|
|
254
|
+
isMutation: action === 'write',
|
|
255
|
+
autoLoadAvailable: false,
|
|
256
|
+
});
|
|
257
|
+
if (!skillGate.ok) {
|
|
258
|
+
return { ok: false, reason: formatSkillGateBlock(skillGate), result: { skillGate } };
|
|
259
|
+
}
|
|
214
260
|
const result = await postRuntime('/check-action', {
|
|
215
261
|
action,
|
|
216
262
|
target,
|
|
217
263
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
264
|
+
actor: 'codex',
|
|
265
|
+
roleProfile: process.env.CODEX_ARIA_ROLE_PROFILE || process.env.ARIA_ROLE_PROFILE || null,
|
|
266
|
+
verifyText: state.agentText || '',
|
|
267
|
+
...metadata,
|
|
218
268
|
});
|
|
219
269
|
if (result?.allowed === false) {
|
|
220
270
|
return {
|
|
@@ -244,6 +294,8 @@ async function recordMizanPre(threadId, turnId) {
|
|
|
244
294
|
surface: 'codex-bridge',
|
|
245
295
|
platform: 'codex',
|
|
246
296
|
userText: state.userText,
|
|
297
|
+
traceId: state.traceId,
|
|
298
|
+
evidenceRefs: [makeEvidenceRef('user_input', state.userText, { threadId, turnId, traceId: state.traceId })],
|
|
247
299
|
},
|
|
248
300
|
});
|
|
249
301
|
state.preReceiptId = result?.receipt?.receiptId || null;
|
|
@@ -262,6 +314,8 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
262
314
|
evidence: {
|
|
263
315
|
validated_output: pass,
|
|
264
316
|
bridge: 'codex',
|
|
317
|
+
trace_id: state.traceId,
|
|
318
|
+
output_ref: makeEvidenceRef('assistant_output', state.agentText || summary, { threadId, turnId, traceId: state.traceId }),
|
|
265
319
|
},
|
|
266
320
|
context: {
|
|
267
321
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
@@ -269,6 +323,7 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
269
323
|
platform: 'codex',
|
|
270
324
|
userText: state.userText,
|
|
271
325
|
summary,
|
|
326
|
+
traceId: state.traceId,
|
|
272
327
|
},
|
|
273
328
|
});
|
|
274
329
|
} catch (error) {
|
|
@@ -285,6 +340,8 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
285
340
|
details: {
|
|
286
341
|
threadId,
|
|
287
342
|
turnId,
|
|
343
|
+
traceId: state.traceId,
|
|
344
|
+
outputRef: makeEvidenceRef('assistant_output', state.agentText || summary, { threadId, turnId, traceId: state.traceId }),
|
|
288
345
|
},
|
|
289
346
|
});
|
|
290
347
|
} catch (error) {
|
|
@@ -336,7 +393,9 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
336
393
|
cwd: params.cwd || null,
|
|
337
394
|
commandActions: params.commandActions || params.parsedCmd || null,
|
|
338
395
|
}).slice(0, 1500);
|
|
339
|
-
const actionCheck = await checkActionAgainstRuntime(action, target, threadId, turnId
|
|
396
|
+
const actionCheck = await checkActionAgainstRuntime(action, target, threadId, turnId, {
|
|
397
|
+
requireVerify: action === 'delete' || action === 'deploy',
|
|
398
|
+
});
|
|
340
399
|
if (!actionCheck.ok) {
|
|
341
400
|
const reason = `Aria runtime denied ${action}: ${actionCheck.reason}`;
|
|
342
401
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -360,8 +419,9 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
360
419
|
}
|
|
361
420
|
|
|
362
421
|
if (method === 'item/fileChange/requestApproval' || method === 'applyPatchApproval') {
|
|
363
|
-
const target = params.grantRoot || params.reason || params.itemId || 'file-change';
|
|
364
|
-
const
|
|
422
|
+
const target = params.grantRoot || params.path || params.filePath || params.reason || params.itemId || 'file-change';
|
|
423
|
+
const files = [params.path, params.filePath, params.grantRoot].filter((value) => typeof value === 'string' && value.trim());
|
|
424
|
+
const actionCheck = await checkActionAgainstRuntime('write', String(target), threadId, turnId, { files });
|
|
365
425
|
if (!actionCheck.ok) {
|
|
366
426
|
const reason = `Aria runtime denied file change: ${actionCheck.reason}`;
|
|
367
427
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -390,7 +450,10 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
390
450
|
permissions: params.permissions || null,
|
|
391
451
|
reason: params.reason || null,
|
|
392
452
|
}).slice(0, 1500);
|
|
393
|
-
const
|
|
453
|
+
const files = Array.isArray(params.permissions?.fileSystem?.entries)
|
|
454
|
+
? params.permissions.fileSystem.entries.filter((value) => typeof value === 'string' && value.trim())
|
|
455
|
+
: [];
|
|
456
|
+
const actionCheck = await checkActionAgainstRuntime('write', target, threadId, turnId, { files });
|
|
394
457
|
if (!actionCheck.ok) {
|
|
395
458
|
const reason = `Aria runtime denied permissions request: ${actionCheck.reason}`;
|
|
396
459
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -18,7 +18,7 @@ If compaction, gate deadlock, or repeated drift happens, restart from that order
|
|
|
18
18
|
|
|
19
19
|
## What this SDK is
|
|
20
20
|
|
|
21
|
-
`@
|
|
21
|
+
`@aria_asi/harness-http-client` is the Aria harness SDK. It binds a process
|
|
22
22
|
(worker, autonomous loop, dispatched artifact) to Aria's three-layer
|
|
23
23
|
enforcement substrate so the process cannot drift from Aria's doctrine,
|
|
24
24
|
axioms, frames, and memory.
|
|
@@ -221,7 +221,7 @@ The Stop-gate scans output for these patterns. When matched, the response is rej
|
|
|
221
221
|
### 1. Build a harness-bound LLM call
|
|
222
222
|
|
|
223
223
|
```ts
|
|
224
|
-
import { getBoundHarnessAndPrompt, bindingContext } from '@
|
|
224
|
+
import { getBoundHarnessAndPrompt, bindingContext } from '@aria_asi/harness-http-client';
|
|
225
225
|
import { runLayer3Gates } from '@aria/gate-runtime';
|
|
226
226
|
|
|
227
227
|
const { prompt: harnessContext, packet } = await getBoundHarnessAndPrompt(
|
|
@@ -508,6 +508,49 @@
|
|
|
508
508
|
"counter_action": "Create or reuse one turn substrate object containing embedding, perturb snapshot, ProjectAllDomains result, awakenAria Garden block, DeepSoul/Noor/shards/Mizan signals; pass it downstream.",
|
|
509
509
|
"message": "Duplicate projection path detected. Replace with one per-turn cognition packet and shared consumption."
|
|
510
510
|
},
|
|
511
|
+
{
|
|
512
|
+
"trigger_id": "premature_task_closeout",
|
|
513
|
+
"trigger": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
|
|
514
|
+
"rx": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
|
|
515
|
+
"doctrine": "memory:feedback_no_premature_task_closeout.md",
|
|
516
|
+
"memory": "feedback_no_premature_task_closeout.md",
|
|
517
|
+
"severity": "block",
|
|
518
|
+
"teaching": "A task is not complete while material blockers remain. Completion claims must match the verified scope.",
|
|
519
|
+
"counter_action": "Do not close the task. Fix the blocker now, or create an owner-visible durable task with the exact failing surface and verification gap.",
|
|
520
|
+
"message": "Premature closeout detected: completion language coexists with unresolved blockers. Fix or durably track before emitting."
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"trigger_id": "narrow_e2e_overclaim",
|
|
524
|
+
"trigger": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
|
|
525
|
+
"rx": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
|
|
526
|
+
"doctrine": "memory:feedback_narrow_e2e_overclaim.md",
|
|
527
|
+
"memory": "feedback_narrow_e2e_overclaim.md",
|
|
528
|
+
"severity": "block",
|
|
529
|
+
"teaching": "A narrow e2e does not prove general production readiness for SDKs, npm packages, runtimes, clients, or harnesses.",
|
|
530
|
+
"counter_action": "Run the general readiness matrix or explicitly limit the claim to the verified surface. Do not imply general readiness from one app flow.",
|
|
531
|
+
"message": "Narrow proof overclaim detected. General readiness requires the client/package/runtime/harness matrix."
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"trigger_id": "advisory_gate_not_gate",
|
|
535
|
+
"trigger": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
|
|
536
|
+
"rx": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
|
|
537
|
+
"doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
|
|
538
|
+
"memory": "feedback_advisory_gate_is_not_gate.md",
|
|
539
|
+
"severity": "block",
|
|
540
|
+
"teaching": "A gate that only warns or falls through is not a gate. Enforcement must fail closed where quality or doctrine is required.",
|
|
541
|
+
"counter_action": "Convert advisory paths to blocking errors, add a self-test that proves rejection, and install the hardened gate into each consumer surface.",
|
|
542
|
+
"message": "Advisory gate bypass detected. Convert to fail-closed enforcement and prove it with a blocking self-test."
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
"trigger_id": "start_new_session_as_gate_fix",
|
|
546
|
+
"trigger": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
|
|
547
|
+
"rx": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
|
|
548
|
+
"doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
|
|
549
|
+
"memory": "feedback_advisory_gate_is_not_gate.md",
|
|
550
|
+
"severity": "block",
|
|
551
|
+
"teaching": "A new session is not an enforcement fix. It only reloads whatever gate contract exists; broken gates remain broken.",
|
|
552
|
+
"counter_action": "Fix the runtime/plugin/hook contract, reinstall it, and run a live bad-action/bad-output self-test that proves blocking."
|
|
553
|
+
},
|
|
511
554
|
{
|
|
512
555
|
"trigger_id": "registry_image_drift",
|
|
513
556
|
"trigger": "image.?pull|ErrImageNeverPull|ImagePullBackOff|registry gc|image.*missing|image.*not.*found",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: aria-harness-onboarding
|
|
3
|
-
description: Use when starting a new project that imports @
|
|
3
|
+
description: Use when starting a new project that imports @aria_asi/harness-http-client OR when an agent first encounters the SDK in a repo. Loads the foundational 3-layer harness model (packet/BIND/gate-runtime), the doctrine reference list, and points to the four sibling skills (aria-harness-deploy, aria-harness-substrate-binding, aria-harness-no-stripping, aria-harness-output-discipline). Read this once per project to inherit the discipline.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Aria Harness Onboarding
|
|
@@ -9,7 +9,7 @@ You are about to author code that uses the Aria harness SDK. This skill loads th
|
|
|
9
9
|
|
|
10
10
|
## What the SDK is
|
|
11
11
|
|
|
12
|
-
`@
|
|
12
|
+
`@aria_asi/harness-http-client` binds a process (worker, autonomous loop, dispatched artifact) to Aria's three-layer enforcement substrate so the process cannot drift from Aria's doctrine, axioms, frames, and memory.
|
|
13
13
|
|
|
14
14
|
## The three layers — every consumer enforces all three
|
|
15
15
|
|
|
@@ -36,7 +36,7 @@ This onboarding skill is the entry point. Four sibling skills auto-trigger on sp
|
|
|
36
36
|
## Build a harness-bound LLM call (the basic pattern)
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
import { getBoundHarnessAndPrompt, bindingContext } from '@
|
|
39
|
+
import { getBoundHarnessAndPrompt, bindingContext } from '@aria_asi/harness-http-client';
|
|
40
40
|
import { runLayer3Gates } from '@aria/gate-runtime';
|
|
41
41
|
|
|
42
42
|
const { prompt: harnessContext, packet } = await getBoundHarnessAndPrompt(
|
|
@@ -508,6 +508,49 @@
|
|
|
508
508
|
"counter_action": "Create or reuse one turn substrate object containing embedding, perturb snapshot, ProjectAllDomains result, awakenAria Garden block, DeepSoul/Noor/shards/Mizan signals; pass it downstream.",
|
|
509
509
|
"message": "Duplicate projection path detected. Replace with one per-turn cognition packet and shared consumption."
|
|
510
510
|
},
|
|
511
|
+
{
|
|
512
|
+
"trigger_id": "premature_task_closeout",
|
|
513
|
+
"trigger": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
|
|
514
|
+
"rx": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
|
|
515
|
+
"doctrine": "memory:feedback_no_premature_task_closeout.md",
|
|
516
|
+
"memory": "feedback_no_premature_task_closeout.md",
|
|
517
|
+
"severity": "block",
|
|
518
|
+
"teaching": "A task is not complete while material blockers remain. Completion claims must match the verified scope.",
|
|
519
|
+
"counter_action": "Do not close the task. Fix the blocker now, or create an owner-visible durable task with the exact failing surface and verification gap.",
|
|
520
|
+
"message": "Premature closeout detected: completion language coexists with unresolved blockers. Fix or durably track before emitting."
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"trigger_id": "narrow_e2e_overclaim",
|
|
524
|
+
"trigger": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
|
|
525
|
+
"rx": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
|
|
526
|
+
"doctrine": "memory:feedback_narrow_e2e_overclaim.md",
|
|
527
|
+
"memory": "feedback_narrow_e2e_overclaim.md",
|
|
528
|
+
"severity": "block",
|
|
529
|
+
"teaching": "A narrow e2e does not prove general production readiness for SDKs, npm packages, runtimes, clients, or harnesses.",
|
|
530
|
+
"counter_action": "Run the general readiness matrix or explicitly limit the claim to the verified surface. Do not imply general readiness from one app flow.",
|
|
531
|
+
"message": "Narrow proof overclaim detected. General readiness requires the client/package/runtime/harness matrix."
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
"trigger_id": "advisory_gate_not_gate",
|
|
535
|
+
"trigger": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
|
|
536
|
+
"rx": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
|
|
537
|
+
"doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
|
|
538
|
+
"memory": "feedback_advisory_gate_is_not_gate.md",
|
|
539
|
+
"severity": "block",
|
|
540
|
+
"teaching": "A gate that only warns or falls through is not a gate. Enforcement must fail closed where quality or doctrine is required.",
|
|
541
|
+
"counter_action": "Convert advisory paths to blocking errors, add a self-test that proves rejection, and install the hardened gate into each consumer surface.",
|
|
542
|
+
"message": "Advisory gate bypass detected. Convert to fail-closed enforcement and prove it with a blocking self-test."
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
"trigger_id": "start_new_session_as_gate_fix",
|
|
546
|
+
"trigger": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
|
|
547
|
+
"rx": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
|
|
548
|
+
"doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
|
|
549
|
+
"memory": "feedback_advisory_gate_is_not_gate.md",
|
|
550
|
+
"severity": "block",
|
|
551
|
+
"teaching": "A new session is not an enforcement fix. It only reloads whatever gate contract exists; broken gates remain broken.",
|
|
552
|
+
"counter_action": "Fix the runtime/plugin/hook contract, reinstall it, and run a live bad-action/bad-output self-test that proves blocking."
|
|
553
|
+
},
|
|
511
554
|
{
|
|
512
555
|
"trigger_id": "registry_image_drift",
|
|
513
556
|
"trigger": "image.?pull|ErrImageNeverPull|ImagePullBackOff|registry gc|image.*missing|image.*not.*found",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createServer } from 'node:http';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
4
5
|
import { createRequire } from 'node:module';
|
|
5
6
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -35,6 +36,7 @@ const LOCAL_FIRST_PRINCIPLE = 'Truth over deception. No harm. Sacred trust. Powe
|
|
|
35
36
|
|
|
36
37
|
let cachedPacketEnvelope = loadCachedPacketEnvelope();
|
|
37
38
|
let refreshInFlight = null;
|
|
39
|
+
let refreshInFlightKey = '';
|
|
38
40
|
let lastRefreshError = null;
|
|
39
41
|
let lastRefreshStartedAt = null;
|
|
40
42
|
let lastRefreshCompletedAt = cachedPacketEnvelope?.timestamp || null;
|
|
@@ -99,6 +101,46 @@ function sanitizePacketEnvelope(raw) {
|
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
function stableIdentityValue(value) {
|
|
105
|
+
return String(value || '').trim().toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function requestCacheKey(body = {}, apiKey = '') {
|
|
109
|
+
const identity = {
|
|
110
|
+
stage: stableIdentityValue(body.stage || body.packetRequest?.stage),
|
|
111
|
+
actor: stableIdentityValue(body.actor || body.packetRequest?.actor),
|
|
112
|
+
system: stableIdentityValue(body.system || body.packetRequest?.system),
|
|
113
|
+
platform: stableIdentityValue(body.platform || body.packetRequest?.platform),
|
|
114
|
+
roleProfile: stableIdentityValue(body.roleProfile || body.role_profile || body.packetRequest?.roleProfile || body.packetRequest?.role_profile),
|
|
115
|
+
token: apiKey ? createHash('sha256').update(apiKey).digest('hex').slice(0, 16) : '',
|
|
116
|
+
};
|
|
117
|
+
return createHash('sha256').update(JSON.stringify(identity)).digest('hex');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function extractPacketField(packet, field) {
|
|
121
|
+
if (!packet || typeof packet !== 'object') return '';
|
|
122
|
+
const direct = packet[field];
|
|
123
|
+
if (typeof direct === 'string' && direct.trim()) return stableIdentityValue(direct);
|
|
124
|
+
for (const source of [packet.adapter, packet.harness]) {
|
|
125
|
+
if (typeof source !== 'string') continue;
|
|
126
|
+
const match = source.match(new RegExp(`(?:^|\\n)\\s*${field}\\s*=\\s*([^\\n\\s]+)`, 'i'));
|
|
127
|
+
if (match?.[1]) return stableIdentityValue(match[1]);
|
|
128
|
+
}
|
|
129
|
+
return '';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function cachedPacketMatchesRequest(envelope, body = {}) {
|
|
133
|
+
const packet = envelope?.packet;
|
|
134
|
+
if (!packet || typeof packet !== 'object') return false;
|
|
135
|
+
for (const field of ['stage', 'actor', 'system']) {
|
|
136
|
+
const expected = stableIdentityValue(body[field] || body.packetRequest?.[field]);
|
|
137
|
+
if (!expected) continue;
|
|
138
|
+
const actual = extractPacketField(packet, field);
|
|
139
|
+
if (actual && actual !== expected) return false;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
102
144
|
function loadCachedPacketEnvelope() {
|
|
103
145
|
const candidates = [LOCAL_PACKET_CACHE_PATH, ...LEGACY_PACKET_CACHE_CANDIDATES];
|
|
104
146
|
for (const candidate of candidates) {
|
|
@@ -220,10 +262,15 @@ async function fetchJsonWithRetry(url, init, attempts = 3) {
|
|
|
220
262
|
}
|
|
221
263
|
|
|
222
264
|
async function refreshPacket(body = {}, apiKey = '') {
|
|
223
|
-
|
|
265
|
+
const cacheKey = requestCacheKey(body, apiKey);
|
|
266
|
+
if (refreshInFlight && refreshInFlightKey === cacheKey) return refreshInFlight;
|
|
267
|
+
if (refreshInFlight) {
|
|
268
|
+
await refreshInFlight.catch(() => {});
|
|
269
|
+
}
|
|
224
270
|
if (isLoopedUpstream(UPSTREAM_HARNESS_URL)) {
|
|
225
271
|
throw new Error(`upstream harness URL loops back to local daemon: ${UPSTREAM_HARNESS_URL}`);
|
|
226
272
|
}
|
|
273
|
+
refreshInFlightKey = cacheKey;
|
|
227
274
|
lastRefreshStartedAt = new Date().toISOString();
|
|
228
275
|
lastRefreshError = null;
|
|
229
276
|
refreshInFlight = (async () => {
|
|
@@ -258,6 +305,7 @@ async function refreshPacket(body = {}, apiKey = '') {
|
|
|
258
305
|
throw error;
|
|
259
306
|
} finally {
|
|
260
307
|
refreshInFlight = null;
|
|
308
|
+
refreshInFlightKey = '';
|
|
261
309
|
}
|
|
262
310
|
}
|
|
263
311
|
|
|
@@ -268,7 +316,7 @@ function queuePacketRefresh(body, apiKey) {
|
|
|
268
316
|
}
|
|
269
317
|
|
|
270
318
|
async function resolvePacketEnvelope(packetRequest = {}, apiKey = '', message = '') {
|
|
271
|
-
if (cachedPacketEnvelope) {
|
|
319
|
+
if (cachedPacketEnvelope && cachedPacketMatchesRequest(cachedPacketEnvelope, packetRequest)) {
|
|
272
320
|
queuePacketRefresh(packetRequest, apiKey);
|
|
273
321
|
return cachedPacketEnvelope;
|
|
274
322
|
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// aria-agent-handoff.mjs — PreToolUse hook for the Agent tool.
|
|
3
|
+
// Writes a handoff JSON before a sub-agent spawns so it inherits the
|
|
4
|
+
// parent's owner/client-tier identity + ledger path instead of starting fresh.
|
|
5
|
+
//
|
|
6
|
+
// Owner tier → ~/.claude/aria-agent-harness-handoff.json
|
|
7
|
+
// Client tier → /var/lib/aria-licensee/{jti}/handoff.json
|
|
8
|
+
//
|
|
9
|
+
// Tier is determined by whether a JTI is present in the license file at
|
|
10
|
+
// ~/.aria/license.json. If JTI is present → client tier. If absent (owner
|
|
11
|
+
// JWT at ~/.aria/owner-token) → owner tier.
|
|
12
|
+
//
|
|
13
|
+
// Tier protection: client handoffs NEVER write to ~/.claude/ and NEVER
|
|
14
|
+
// carry master tokens. Client paths are scoped to /var/lib/aria-licensee/{jti}/.
|
|
15
|
+
// Default-on per Hamza doctrine — no env-flag gate to disable.
|
|
16
|
+
//
|
|
17
|
+
// Doctrine: feedback_aria_does_work.md (sub-agents inherit harness)
|
|
18
|
+
// feedback_implementation_coupled_cognition.md (handoff is the impl)
|
|
19
|
+
// feedback_no_flag_without_fix.md (no silent bypasses)
|
|
20
|
+
|
|
21
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { createRequire } from 'node:module';
|
|
25
|
+
|
|
26
|
+
const HOME = homedir();
|
|
27
|
+
const LOG = `${HOME}/.claude/aria-pre-tool-gate.log`;
|
|
28
|
+
const PACKET_CACHE = `${HOME}/.claude/.aria-harness-last-packet.json`;
|
|
29
|
+
const LICENSE_PATH = `${HOME}/.aria/license.json`;
|
|
30
|
+
const OWNER_TOKEN_PATH = `${HOME}/.aria/owner-token`;
|
|
31
|
+
const HANDOFF_TTL_MS = 5 * 60 * 1000;
|
|
32
|
+
const SHARED_RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
33
|
+
const SHARED_SDK_ROOT = `${HOME}/.aria/sdk`;
|
|
34
|
+
|
|
35
|
+
function audit(msg) {
|
|
36
|
+
try {
|
|
37
|
+
if (!existsSync(dirname(LOG))) mkdirSync(dirname(LOG), { recursive: true });
|
|
38
|
+
appendFileSync(LOG, `${new Date().toISOString()} [agent-handoff] ${msg}\n`);
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let input = '';
|
|
43
|
+
for await (const chunk of process.stdin) input += chunk;
|
|
44
|
+
|
|
45
|
+
let event;
|
|
46
|
+
try { event = JSON.parse(input); }
|
|
47
|
+
catch { process.exit(0); }
|
|
48
|
+
|
|
49
|
+
const toolName = event.tool_name ?? event.toolName ?? '';
|
|
50
|
+
if (toolName !== 'Agent' && toolName !== 'agent') process.exit(0);
|
|
51
|
+
|
|
52
|
+
const sessionId =
|
|
53
|
+
event.session_id ?? event.sessionId ??
|
|
54
|
+
(event.transcript_path
|
|
55
|
+
? event.transcript_path.split('/').pop()?.replace(/\.[^.]+$/, '')
|
|
56
|
+
: null) ??
|
|
57
|
+
'unknown';
|
|
58
|
+
|
|
59
|
+
// ── Tier detection ──────────────────────────────────────────────────────────
|
|
60
|
+
// Read license.json to detect client tier (has jti). If only owner-token
|
|
61
|
+
// exists → owner tier. No license/owner-token → treat as owner tier (dev).
|
|
62
|
+
|
|
63
|
+
let jti = null;
|
|
64
|
+
let isClientTier = false;
|
|
65
|
+
let licenseToken = '';
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
if (existsSync(LICENSE_PATH)) {
|
|
69
|
+
const lic = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
|
|
70
|
+
jti = lic.jti ?? null;
|
|
71
|
+
licenseToken = lic.token ?? lic.license ?? '';
|
|
72
|
+
isClientTier = Boolean(jti);
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
audit(`warn: license.json read failed: ${(err?.message || err).toString().slice(0, 120)}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// For owner tier, read harness token from env or owner-token file.
|
|
79
|
+
let harnessToken = '';
|
|
80
|
+
if (isClientTier) {
|
|
81
|
+
// Client tier: their own license JWT, never master token.
|
|
82
|
+
harnessToken = licenseToken;
|
|
83
|
+
} else {
|
|
84
|
+
harnessToken =
|
|
85
|
+
process.env.ARIA_HARNESS_TOKEN ||
|
|
86
|
+
process.env.ARIA_API_KEY ||
|
|
87
|
+
process.env.ARIA_MASTER_TOKEN ||
|
|
88
|
+
'';
|
|
89
|
+
if (!harnessToken && existsSync(OWNER_TOKEN_PATH)) {
|
|
90
|
+
try { harnessToken = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim(); } catch {}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const harnessUrl =
|
|
95
|
+
process.env.ARIA_HIVE_RUNTIME_URL ||
|
|
96
|
+
process.env.ARIA_HARNESS_BASE_URL ||
|
|
97
|
+
process.env.ARIA_HARNESS_URL ||
|
|
98
|
+
'https://harness.ariasos.com';
|
|
99
|
+
|
|
100
|
+
// ── OwnerTier signal resolution ──────────────────────────────────────────────
|
|
101
|
+
let ownerTier = {
|
|
102
|
+
hamza: !isClientTier,
|
|
103
|
+
trustedExec: false,
|
|
104
|
+
roleProfile: isClientTier ? 'client_tier_worker' : 'general_worker',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (!isClientTier) {
|
|
108
|
+
try {
|
|
109
|
+
if (existsSync(PACKET_CACHE)) {
|
|
110
|
+
const cached = JSON.parse(readFileSync(PACKET_CACHE, 'utf8'));
|
|
111
|
+
const signals = cached?.contractGate?.signals ?? {};
|
|
112
|
+
ownerTier.hamza = signals.hamza === true || signals.hamza === 'true';
|
|
113
|
+
ownerTier.trustedExec = (cached?.harness || '').includes('trusted_exec_policy=allowed');
|
|
114
|
+
const adapterStr = cached?.adapter || cached?.harness || '';
|
|
115
|
+
const roleMatch = adapterStr.match(/role_profile\s*=\s*(\S+)/);
|
|
116
|
+
if (roleMatch) ownerTier.roleProfile = roleMatch[1];
|
|
117
|
+
}
|
|
118
|
+
} catch (err) {
|
|
119
|
+
audit(`warn: packet cache read failed: ${(err?.message || err).toString().slice(0, 120)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Path resolution (tier-scoped) ───────────────────────────────────────────
|
|
124
|
+
const safeSession = String(sessionId).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
125
|
+
|
|
126
|
+
let handoffPath;
|
|
127
|
+
let parentLedgerPath;
|
|
128
|
+
|
|
129
|
+
if (isClientTier) {
|
|
130
|
+
const base = `/var/lib/aria-licensee/${jti}`;
|
|
131
|
+
handoffPath = `${base}/handoff.json`;
|
|
132
|
+
parentLedgerPath = `${base}/aria-discoveries-${safeSession}.jsonl`;
|
|
133
|
+
} else {
|
|
134
|
+
handoffPath = `${HOME}/.claude/aria-agent-harness-handoff.json`;
|
|
135
|
+
parentLedgerPath = `${HOME}/.claude/aria-discoveries-${safeSession}.jsonl`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Harness packet fetch for sub-agent substrate binding ────────────────────
|
|
139
|
+
// Layer 1: after writing identity handoff, fetch the harness packet from
|
|
140
|
+
// /api/harness/codex so the sub-agent can load COGNITION (not just identity).
|
|
141
|
+
// Fail-soft: any fetch failure logs a warning and leaves the handoff identity-only.
|
|
142
|
+
// Never blocks the spawn — packet is substrate enrichment, not a gate.
|
|
143
|
+
//
|
|
144
|
+
// Tier-aware packet paths:
|
|
145
|
+
// Owner: ~/.claude/aria-agent-harness-packet.json
|
|
146
|
+
// Client: /var/lib/aria-licensee/{jti}/aria-agent-harness-packet.json
|
|
147
|
+
let packetPath = null;
|
|
148
|
+
if (isClientTier) {
|
|
149
|
+
packetPath = `/var/lib/aria-licensee/${jti}/aria-agent-harness-packet.json`;
|
|
150
|
+
} else {
|
|
151
|
+
packetPath = `${HOME}/.claude/aria-agent-harness-packet.json`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function fetchAndWriteHarnessPacket() {
|
|
155
|
+
if (!harnessToken || !harnessUrl) {
|
|
156
|
+
audit('warn: skipping packet fetch — no harnessToken or harnessUrl');
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const body = JSON.stringify({
|
|
161
|
+
stage: 'agent-spawn',
|
|
162
|
+
actor: 'harness-http-client',
|
|
163
|
+
system: 'claude-coding-agent',
|
|
164
|
+
surface: 'platform:harness-http-client',
|
|
165
|
+
isHamza: ownerTier.hamza,
|
|
166
|
+
sessionId: sessionId,
|
|
167
|
+
mode: 'subagent',
|
|
168
|
+
});
|
|
169
|
+
const resp = await fetch(`${harnessUrl}/api/harness/codex`, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: {
|
|
172
|
+
'Content-Type': 'application/json',
|
|
173
|
+
'Authorization': `Bearer ${harnessToken}`,
|
|
174
|
+
},
|
|
175
|
+
body,
|
|
176
|
+
});
|
|
177
|
+
if (!resp.ok) {
|
|
178
|
+
audit(`warn: harness packet fetch HTTP ${resp.status} — identity-only handoff`);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const data = await resp.json();
|
|
182
|
+
mkdirSync(dirname(packetPath), { recursive: true });
|
|
183
|
+
writeFileSync(packetPath, JSON.stringify(data, null, 2));
|
|
184
|
+
audit(`wrote harness packet tier=${isClientTier ? 'client' : 'owner'} path=${packetPath}`);
|
|
185
|
+
return packetPath;
|
|
186
|
+
} catch (err) {
|
|
187
|
+
audit(`warn: harness packet fetch failed: ${(err?.message || err).toString().slice(0, 200)} — identity-only handoff`);
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const resolvedPacketPath = await fetchAndWriteHarnessPacket();
|
|
193
|
+
|
|
194
|
+
const handoff = {
|
|
195
|
+
writtenAt: new Date().toISOString(),
|
|
196
|
+
parentSessionId: sessionId,
|
|
197
|
+
harnessToken,
|
|
198
|
+
harnessUrl,
|
|
199
|
+
runtimeUrl: SHARED_RUNTIME_URL,
|
|
200
|
+
sharedSdkRoot: SHARED_SDK_ROOT,
|
|
201
|
+
ownerTier,
|
|
202
|
+
parentLedgerPath,
|
|
203
|
+
cognitionMode: 'mizan-pre-mid-post',
|
|
204
|
+
requiredSkillPacks: ['aria-harness', 'aria-cognition'],
|
|
205
|
+
requiredLenses: ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'],
|
|
206
|
+
cognitionOperatingMethod: {
|
|
207
|
+
purpose: 'Use Aria cognition to improve the assigned work, not to satisfy formatting after the fact.',
|
|
208
|
+
loop: [
|
|
209
|
+
'perceive real substrate before acting',
|
|
210
|
+
'infer constraints and missing evidence',
|
|
211
|
+
'shape the next tool/input/output from those constraints',
|
|
212
|
+
'predict the observable result before claiming success',
|
|
213
|
+
'return evidence, residual risk, and exact artifact impact to the parent',
|
|
214
|
+
],
|
|
215
|
+
requiredReturnShape: {
|
|
216
|
+
substrateUsed: ['axiom/frame/memory/packet anchors actually loaded'],
|
|
217
|
+
decisionDelta: 'what changed in the work because cognition ran',
|
|
218
|
+
artifactImpact: 'specific files, findings, commands, or prose semantics affected',
|
|
219
|
+
evidence: 'observed tool output, file state, endpoint result, or explicit unverified boundary',
|
|
220
|
+
unresolvedRisk: 'remaining risk or none',
|
|
221
|
+
},
|
|
222
|
+
prohibitedPatterns: [
|
|
223
|
+
'decorative cognition that does not alter the work',
|
|
224
|
+
'summary-only sub-agent result without evidence',
|
|
225
|
+
'asking the parent/user for choices that substrate can resolve',
|
|
226
|
+
'claiming completion from intent rather than observation',
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
ttlMs: HANDOFF_TTL_MS,
|
|
230
|
+
// harnessPacketPath is set only if packet was successfully fetched;
|
|
231
|
+
// absent means sub-agent should fall back to calling /api/harness/codex directly.
|
|
232
|
+
...(resolvedPacketPath ? { harnessPacketPath: resolvedPacketPath } : {}),
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
mkdirSync(dirname(handoffPath), { recursive: true });
|
|
237
|
+
writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
|
|
238
|
+
audit(
|
|
239
|
+
`wrote handoff tier=${isClientTier ? 'client' : 'owner'} jti=${jti ?? 'none'} ` +
|
|
240
|
+
`session=${sessionId} hamza=${ownerTier.hamza} role=${ownerTier.roleProfile} ` +
|
|
241
|
+
`packetPath=${resolvedPacketPath ?? 'none'}`,
|
|
242
|
+
);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
audit(`ERROR: handoff write failed: ${(err?.message || err).toString().slice(0, 200)}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
process.exit(0);
|