@aria_asi/cli 0.2.31 → 0.2.33

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.
Files changed (79) hide show
  1. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  2. package/dist/aria-connector/src/connectors/claude-code.js +30 -3
  3. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  4. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts +8 -1
  5. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -1
  6. package/dist/aria-connector/src/connectors/codebase-awareness.js +126 -71
  7. package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -1
  8. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
  9. package/dist/aria-connector/src/connectors/codex.js +76 -9
  10. package/dist/aria-connector/src/connectors/codex.js.map +1 -1
  11. package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -1
  12. package/dist/aria-connector/src/connectors/must-read.js +4 -0
  13. package/dist/aria-connector/src/connectors/must-read.js.map +1 -1
  14. package/dist/aria-connector/src/connectors/opencode.js +25 -9
  15. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  16. package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
  17. package/dist/aria-connector/src/setup-wizard.js +91 -24
  18. package/dist/aria-connector/src/setup-wizard.js.map +1 -1
  19. package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
  20. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +69 -3
  21. package/dist/assets/hooks/aria-harness-via-sdk.mjs +10 -5
  22. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
  23. package/dist/assets/hooks/aria-pre-tool-gate.mjs +217 -17
  24. package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
  25. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +30 -2
  26. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +31 -1
  27. package/dist/assets/hooks/aria-stop-gate.mjs +154 -37
  28. package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
  29. package/dist/assets/hooks/lib/domain-output-quality.mjs +103 -0
  30. package/dist/assets/hooks/lib/skill-autoload-gate.mjs +1 -0
  31. package/dist/assets/opencode-plugins/harness-gate/index.js +84 -7
  32. package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -0
  33. package/dist/assets/opencode-plugins/harness-outcome/index.js +39 -0
  34. package/dist/assets/opencode-plugins/harness-stop/index.js +101 -7
  35. package/dist/assets/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
  36. package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -0
  37. package/dist/runtime/codex-bridge.mjs +71 -8
  38. package/dist/runtime/discipline/CLAUDE.md +16 -0
  39. package/dist/runtime/discipline/doctrine_trigger_map.json +55 -0
  40. package/dist/runtime/doctrine_trigger_map.json +55 -0
  41. package/dist/runtime/harness-daemon.mjs +80 -5
  42. package/dist/runtime/manifest.json +1 -1
  43. package/dist/runtime/sdk/BUNDLED.json +1 -1
  44. package/dist/runtime/sdk/index.d.ts +14 -0
  45. package/dist/runtime/sdk/index.js +23 -1
  46. package/dist/runtime/sdk/index.js.map +1 -1
  47. package/dist/runtime/service.mjs +385 -11
  48. package/dist/sdk/BUNDLED.json +1 -1
  49. package/dist/sdk/index.d.ts +14 -0
  50. package/dist/sdk/index.js +23 -1
  51. package/dist/sdk/index.js.map +1 -1
  52. package/hooks/aria-agent-handoff.mjs +23 -0
  53. package/hooks/aria-cognition-substrate-binding.mjs +69 -3
  54. package/hooks/aria-harness-via-sdk.mjs +10 -5
  55. package/hooks/aria-pre-emit-dryrun.mjs +35 -0
  56. package/hooks/aria-pre-tool-gate.mjs +217 -17
  57. package/hooks/aria-preprompt-consult.mjs +28 -2
  58. package/hooks/aria-preturn-memory-gate.mjs +30 -2
  59. package/hooks/aria-repo-doctrine-gate.mjs +31 -1
  60. package/hooks/aria-stop-gate.mjs +154 -37
  61. package/hooks/doctrine_trigger_map.json +55 -0
  62. package/hooks/lib/domain-output-quality.mjs +103 -0
  63. package/hooks/lib/skill-autoload-gate.mjs +1 -0
  64. package/opencode-plugins/harness-gate/index.js +84 -7
  65. package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -0
  66. package/opencode-plugins/harness-outcome/index.js +39 -0
  67. package/opencode-plugins/harness-stop/index.js +101 -7
  68. package/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
  69. package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -0
  70. package/package.json +1 -1
  71. package/runtime-src/codex-bridge.mjs +71 -8
  72. package/runtime-src/harness-daemon.mjs +80 -5
  73. package/runtime-src/service.mjs +385 -11
  74. package/src/connectors/claude-code.ts +31 -3
  75. package/src/connectors/codebase-awareness.ts +141 -77
  76. package/src/connectors/codex.ts +76 -9
  77. package/src/connectors/must-read.ts +4 -0
  78. package/src/connectors/opencode.ts +25 -9
  79. package/src/setup-wizard.ts +105 -25
@@ -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
- if (refreshInFlight) return refreshInFlight;
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
  }
@@ -312,18 +360,45 @@ function forgeSynthesizeUrl() {
312
360
  return `${trimUrl(UPSTREAM_HARNESS_URL)}/forge/synthesize`;
313
361
  }
314
362
 
363
+ const APPLIED_COGNITION_BLOCK_RX = /<applied_cognition>([\s\S]*?)<\/applied_cognition>/i;
364
+
365
+ function validateAppliedCognitionContract(text) {
366
+ const match = String(text || '').match(APPLIED_COGNITION_BLOCK_RX);
367
+ if (!match) return { ok: false, violations: ['missing <applied_cognition> contract'] };
368
+ const body = match[1] || '';
369
+ const required = [
370
+ ['decision_delta', /\bdecision[_ -]?delta\s*:/i],
371
+ ['dominant_domain', /\bdominant[_ -]?domain\s*:/i],
372
+ ['binds_to', /\bbinds[_ -]?to\s*:/i],
373
+ ['expected_predicate', /\bexpected[_ -]?predicate\s*:/i],
374
+ ['artifact_change', /\bartifact[_ -]?change\s*:/i],
375
+ ];
376
+ const violations = [];
377
+ for (const [name, rx] of required) {
378
+ if (!rx.test(body)) violations.push(`missing ${name}`);
379
+ }
380
+ if (/decision[_ -]?delta\s*:\s*(?:none|n\/a|no change|unchanged|same)/i.test(body)) {
381
+ violations.push('decision_delta says cognition changed nothing');
382
+ }
383
+ return { ok: violations.length === 0, violations, contract: body.trim() };
384
+ }
385
+
315
386
  function buildLocalValidation(text, packetEnvelope, requireCognitionBlock = false) {
316
387
  const layer3 = runFullChain(text, {
317
388
  substrate: packetToSubstrateSet(packetEnvelope),
318
389
  requireCognitionBlock,
319
390
  });
320
391
  const failures = Array.isArray(layer3?.failures) ? layer3.failures : [];
321
- const hardFailures = failures.filter((failure) => failure?.severity === 'block');
392
+ const applied = validateAppliedCognitionContract(text);
393
+ const appliedFailures = applied.ok ? [] : applied.violations.map((detail) => ({ kind: 'applied-cognition-contract', severity: 'block', detail }));
394
+ const allFailures = [...failures, ...appliedFailures];
395
+ const hardFailures = allFailures.filter((failure) => failure?.severity === 'block');
322
396
  return {
323
397
  passed: hardFailures.length === 0,
324
398
  severity: hardFailures.length === 0 ? 'warn' : 'block',
325
- violations: failures.map((failure) => failure?.detail || failure?.kind).filter(Boolean),
326
- gateTriggers: failures.map((failure) => failure?.kind).filter(Boolean),
399
+ violations: allFailures.map((failure) => failure?.detail || failure?.kind).filter(Boolean),
400
+ gateTriggers: allFailures.map((failure) => failure?.kind).filter(Boolean),
401
+ appliedCognition: applied,
327
402
  local: true,
328
403
  summary: layer3?.summary || '',
329
404
  layer3,
@@ -68,6 +68,7 @@ const OFFLINE_BUNDLE_PATH = join(STATE_DIR, 'offline-policy-bundle.enc');
68
68
  const RUNTIME_META_PATH = join(STATE_DIR, 'runtime-meta.json');
69
69
  const AUTONOMY_STATE_PATH = join(STATE_DIR, 'autonomy.json');
70
70
  const COGNITION_STATE_PATH = join(STATE_DIR, 'cognition-state.enc');
71
+ const HIVE_FILE_LEASES_PATH = join(STATE_DIR, 'hive-file-leases.json');
71
72
  const REVOCATION_LOCK_PATH = join(STATE_DIR, 'revoked.json');
72
73
  const CONFIG_PATH = join(process.env.HOME || '', '.aria', 'config.json');
73
74
  const CODEBASE_AWARENESS_STATE_PATH = join(process.env.HOME || '', '.aria', 'codebase-awareness-state.json');
@@ -906,6 +907,248 @@ function writeJsonFile(filePath, payload, mode = 0o600) {
906
907
  writeFileSync(filePath, JSON.stringify(payload, null, 2) + '\n', { mode });
907
908
  }
908
909
 
910
+ function readHiveFileLeaseState() {
911
+ const state = readJsonFile(HIVE_FILE_LEASES_PATH, { leases: [] });
912
+ return {
913
+ leases: Array.isArray(state.leases) ? state.leases : [],
914
+ };
915
+ }
916
+
917
+ function writeHiveFileLeaseState(state) {
918
+ writeJsonFile(HIVE_FILE_LEASES_PATH, {
919
+ updatedAt: new Date().toISOString(),
920
+ leases: Array.isArray(state.leases) ? state.leases : [],
921
+ });
922
+ }
923
+
924
+ function normalizeLeasePath(value) {
925
+ const raw = String(value || '').trim();
926
+ if (!raw) return '';
927
+ return raw.replace(/\\/g, '/').replace(/\/+/g, '/');
928
+ }
929
+
930
+ function pathsOverlap(left, right) {
931
+ const a = normalizeLeasePath(left);
932
+ const b = normalizeLeasePath(right);
933
+ if (!a || !b) return false;
934
+ return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`);
935
+ }
936
+
937
+ function coerceTargetFiles(body) {
938
+ const files = [];
939
+ const push = (value) => {
940
+ const normalized = normalizeLeasePath(value);
941
+ if (normalized && !files.includes(normalized)) files.push(normalized);
942
+ };
943
+
944
+ if (Array.isArray(body.files)) body.files.forEach(push);
945
+ if (Array.isArray(body.targetFiles)) body.targetFiles.forEach(push);
946
+ if (typeof body.filePath === 'string') push(body.filePath);
947
+ if (typeof body.target === 'string') {
948
+ try {
949
+ const parsed = JSON.parse(body.target);
950
+ if (typeof parsed?.filePath === 'string') push(parsed.filePath);
951
+ if (Array.isArray(parsed?.files)) parsed.files.forEach(push);
952
+ if (Array.isArray(parsed?.targetFiles)) parsed.targetFiles.forEach(push);
953
+ } catch {}
954
+ }
955
+ return files;
956
+ }
957
+
958
+ function extractVerifyText(body) {
959
+ const candidates = [body.verifyText, body.verifyBlock, body.text, body.target];
960
+ for (const candidate of candidates) {
961
+ if (typeof candidate === 'string' && candidate.trim()) return candidate;
962
+ }
963
+ return '';
964
+ }
965
+
966
+ function actionRequiresExplicitVerify(action, body) {
967
+ if (body.requireVerify === true) return true;
968
+ return /^(?:delete|deploy|db_mutation|infra_mutation)$/i.test(String(action || ''));
969
+ }
970
+
971
+ function buildObservedVerify(action, files, sessionId, roleProfile) {
972
+ const observations = files.slice(0, 12).map((filePath) => {
973
+ try {
974
+ const stats = statSync(filePath);
975
+ return {
976
+ path: filePath,
977
+ exists: true,
978
+ isFile: stats.isFile(),
979
+ isDirectory: stats.isDirectory(),
980
+ size: stats.size,
981
+ mtimeMs: stats.mtimeMs,
982
+ };
983
+ } catch {
984
+ return { path: filePath, exists: false };
985
+ }
986
+ });
987
+ return {
988
+ target: files,
989
+ role: roleProfile || 'unknown',
990
+ verified: [
991
+ 'runtime observed target file metadata before action',
992
+ 'Hive file lease check completed before mutation',
993
+ ],
994
+ observations,
995
+ rollback: 'Use VCS diff or prior file snapshot for rollback; no destructive rollback was inferred by runtime.',
996
+ axiom: 'truth_over_deception/no_harm/sacred_trust/reflection_before_action',
997
+ sessionId,
998
+ action,
999
+ };
1000
+ }
1001
+
1002
+ function acquireHiveFileLease({ sessionId, actor, roleProfile, action, files, ttlMs = 120000 }) {
1003
+ const now = Date.now();
1004
+ const expiresAt = new Date(now + ttlMs).toISOString();
1005
+ const normalizedFiles = files.map(normalizeLeasePath).filter(Boolean);
1006
+ const state = readHiveFileLeaseState();
1007
+ const activeLeases = state.leases.filter((lease) => Date.parse(lease.expiresAt || '') > now);
1008
+ const conflicts = activeLeases.filter((lease) => {
1009
+ if (lease.sessionId === sessionId) return false;
1010
+ const leaseFiles = Array.isArray(lease.files) ? lease.files : [];
1011
+ return normalizedFiles.some((file) => leaseFiles.some((leaseFile) => pathsOverlap(file, leaseFile)));
1012
+ });
1013
+
1014
+ if (conflicts.length > 0) {
1015
+ writeHiveFileLeaseState({ leases: activeLeases });
1016
+ return {
1017
+ status: 'blocked',
1018
+ leaseId: null,
1019
+ expiresAt: null,
1020
+ conflicts: conflicts.map((lease) => ({
1021
+ leaseId: lease.leaseId,
1022
+ sessionId: lease.sessionId,
1023
+ actor: lease.actor,
1024
+ roleProfile: lease.roleProfile,
1025
+ action: lease.action,
1026
+ files: lease.files,
1027
+ expiresAt: lease.expiresAt,
1028
+ })),
1029
+ };
1030
+ }
1031
+
1032
+ const existing = activeLeases.filter((lease) => lease.sessionId !== sessionId);
1033
+ const lease = {
1034
+ leaseId: `hive_lease_${randomUUID()}`,
1035
+ sessionId,
1036
+ actor: actor || 'unknown',
1037
+ roleProfile: roleProfile || 'unknown',
1038
+ action,
1039
+ files: normalizedFiles,
1040
+ grantedAt: new Date(now).toISOString(),
1041
+ expiresAt,
1042
+ };
1043
+ writeHiveFileLeaseState({ leases: [...existing, lease] });
1044
+ return { status: 'granted', leaseId: lease.leaseId, expiresAt, conflicts: [] };
1045
+ }
1046
+
1047
+ function releaseHiveFileLease(sessionId, files = []) {
1048
+ const normalizedFiles = files.map(normalizeLeasePath).filter(Boolean);
1049
+ const state = readHiveFileLeaseState();
1050
+ const now = Date.now();
1051
+ const leases = state.leases.filter((lease) => {
1052
+ if (Date.parse(lease.expiresAt || '') <= now) return false;
1053
+ if (lease.sessionId !== sessionId) return true;
1054
+ if (normalizedFiles.length === 0) return false;
1055
+ const leaseFiles = Array.isArray(lease.files) ? lease.files : [];
1056
+ return !normalizedFiles.some((file) => leaseFiles.some((leaseFile) => pathsOverlap(file, leaseFile)));
1057
+ });
1058
+ writeHiveFileLeaseState({ leases });
1059
+ return { released: state.leases.length - leases.length, active: leases.length };
1060
+ }
1061
+
1062
+ function validateVerifyAndHive({ req, body, action }) {
1063
+ const sessionId = deriveSessionId(req, body, 'runtime-action');
1064
+ const files = coerceTargetFiles(body);
1065
+ const roleProfile = body.roleProfile || body.role_profile || body?.metadata?.roleProfile || body?.metadata?.role_profile || null;
1066
+ const actor = body.actor || body?.metadata?.actor || req.headers['x-aria-actor'] || 'runtime-client';
1067
+ const explicitVerifyRequired = actionRequiresExplicitVerify(action, body);
1068
+ const verifyText = extractVerifyText(body);
1069
+ const hasVerifyBlock = VERIFY_BLOCK_RX.test(verifyText);
1070
+ const canUseObservedVerify = !explicitVerifyRequired && files.length > 0 && /^(?:write|edit|bash)$/i.test(String(action || ''));
1071
+
1072
+ if (explicitVerifyRequired && !hasVerifyBlock) {
1073
+ return {
1074
+ allowed: false,
1075
+ reason: 'verify block required before this high-risk action. Include <verify> with target, role, verified, rollback, and axiom.',
1076
+ requiredGates: ['verify-block'],
1077
+ verify: { status: 'missing', required: true },
1078
+ hive: { status: 'not_checked', conflicts: [] },
1079
+ };
1080
+ }
1081
+
1082
+ if (!hasVerifyBlock && !canUseObservedVerify && explicitVerifyRequired) {
1083
+ return {
1084
+ allowed: false,
1085
+ reason: 'verify block required before mutation, or target files must be provided so runtime can perform observed verification.',
1086
+ requiredGates: ['verify-block-or-observed-target-files'],
1087
+ verify: { status: 'missing', required: true },
1088
+ hive: { status: 'not_checked', conflicts: [] },
1089
+ };
1090
+ }
1091
+
1092
+ if (!hasVerifyBlock && !canUseObservedVerify && files.length === 0) {
1093
+ return {
1094
+ allowed: true,
1095
+ reason: 'no target files supplied for Hive lease; remote action gate will still evaluate the action',
1096
+ requiredGates: [],
1097
+ verify: { status: 'skipped', required: false },
1098
+ hive: { status: 'skipped', leaseId: null, expiresAt: null, conflicts: [] },
1099
+ };
1100
+ }
1101
+
1102
+ const hive = files.length > 0
1103
+ ? acquireHiveFileLease({ sessionId, actor, roleProfile, action, files })
1104
+ : { status: 'skipped', leaseId: null, expiresAt: null, conflicts: [] };
1105
+ if (hive.status === 'blocked') {
1106
+ const verify = hasVerifyBlock
1107
+ ? { status: 'provided', required: explicitVerifyRequired }
1108
+ : { status: 'observed', required: false, block: buildObservedVerify(action, files, sessionId, roleProfile) };
1109
+ const queued = enqueueAutonomyJob({
1110
+ kind: 'tool_lane:queued_action',
1111
+ surface: 'tool_lane',
1112
+ sessionId,
1113
+ priority: 15,
1114
+ message: 'Tool lane contention queued this action instead of failing it.',
1115
+ payload: { action, files, verify },
1116
+ metadata: {
1117
+ actor,
1118
+ roleProfile,
1119
+ conflicts: hive.conflicts,
1120
+ recovery: {
1121
+ mode: 'queued_not_failed',
1122
+ reason: 'overlapping_hive_file_lease',
1123
+ next: 'Retry when the overlapping Hive lease expires or a tool-lane worker claims this queued action.',
1124
+ },
1125
+ },
1126
+ });
1127
+ return {
1128
+ allowed: false,
1129
+ queued: true,
1130
+ queue: {
1131
+ jobId: queued.job.jobId,
1132
+ kind: queued.job.kind,
1133
+ status: queued.job.status,
1134
+ queueDepth: queued.stats.queued,
1135
+ },
1136
+ reason: `Tool lane queued this action because another active session is editing overlapping file(s): ${hive.conflicts.map((c) => `${c.sessionId}:${(c.files || []).join(',')}`).join(' | ')}`,
1137
+ requiredGates: ['hive-file-lease'],
1138
+ verify,
1139
+ hive,
1140
+ };
1141
+ }
1142
+
1143
+ return {
1144
+ allowed: true,
1145
+ reason: 'verify and Hive pre-action checks passed',
1146
+ requiredGates: [],
1147
+ verify: hasVerifyBlock ? { status: 'provided', required: explicitVerifyRequired } : { status: 'observed', required: false, block: buildObservedVerify(action, files, sessionId, roleProfile) },
1148
+ hive,
1149
+ };
1150
+ }
1151
+
909
1152
  function ensureRuntimeMeta() {
910
1153
  const existing = readJsonFile(RUNTIME_META_PATH, null);
911
1154
  if (existing?.runtimeId) return existing;
@@ -972,6 +1215,33 @@ function queueStats(state) {
972
1215
  };
973
1216
  }
974
1217
 
1218
+ function enqueueAutonomyJob(jobDef) {
1219
+ const state = sweepAutonomyState(loadAutonomyState());
1220
+ const now = new Date().toISOString();
1221
+ const job = {
1222
+ jobId: randomUUID(),
1223
+ kind: jobDef.kind,
1224
+ surface: jobDef.surface || 'tool_lane',
1225
+ sessionId: jobDef.sessionId || null,
1226
+ payload: jobDef.payload || {},
1227
+ metadata: jobDef.metadata || {},
1228
+ priority: Number.isFinite(Number(jobDef.priority)) ? Number(jobDef.priority) : 25,
1229
+ status: 'queued',
1230
+ attempts: 0,
1231
+ maxAttempts: Number.isFinite(Number(jobDef.maxAttempts)) ? Number(jobDef.maxAttempts) : 3,
1232
+ createdAt: now,
1233
+ updatedAt: now,
1234
+ workerId: null,
1235
+ claimedAt: null,
1236
+ claimExpiresAt: null,
1237
+ progress: [{ at: now, status: 'queued', message: jobDef.message || 'Queued for tool lane execution.' }],
1238
+ garden: null,
1239
+ };
1240
+ state.jobs.push(job);
1241
+ saveAutonomyState(state);
1242
+ return { job, stats: queueStats(state) };
1243
+ }
1244
+
975
1245
  function asJsonPayload(value) {
976
1246
  if (value == null) return null;
977
1247
  if (typeof value === 'string') {
@@ -1233,6 +1503,47 @@ function findVerifiedState(text) {
1233
1503
  return /(?:verified|confirmed|observed|tested|health[- ]check|response code|exit code|pod image|digest)/i.test(String(text || ''));
1234
1504
  }
1235
1505
 
1506
+ const APPLIED_COGNITION_BLOCK_RX = /<applied_cognition>([\s\S]*?)<\/applied_cognition>/i;
1507
+
1508
+ function validateAppliedCognitionContract(text) {
1509
+ const match = String(text || '').match(APPLIED_COGNITION_BLOCK_RX);
1510
+ if (!match) return { ok: false, violations: ['missing <applied_cognition> contract'] };
1511
+ const body = match[1] || '';
1512
+ const required = [
1513
+ ['decision_delta', /\bdecision[_ -]?delta\s*:/i],
1514
+ ['dominant_domain', /\bdominant[_ -]?domain\s*:/i],
1515
+ ['binds_to', /\bbinds[_ -]?to\s*:/i],
1516
+ ['expected_predicate', /\bexpected[_ -]?predicate\s*:/i],
1517
+ ['artifact_change', /\bartifact[_ -]?change\s*:/i],
1518
+ ];
1519
+ const violations = [];
1520
+ for (const [name, rx] of required) {
1521
+ if (!rx.test(body)) violations.push(`missing ${name}`);
1522
+ }
1523
+ if (/decision[_ -]?delta\s*:\s*(?:none|n\/a|no change|unchanged|same)/i.test(body)) {
1524
+ violations.push('decision_delta says cognition changed nothing');
1525
+ }
1526
+ return { ok: violations.length === 0, violations, contract: body.trim() };
1527
+ }
1528
+
1529
+ function mergeAppliedCognitionValidation(validation, applied) {
1530
+ if (applied.ok) {
1531
+ return {
1532
+ ...validation,
1533
+ appliedCognition: { ok: true, contract: applied.contract },
1534
+ gateTriggers: [...new Set([...(validation.gateTriggers || []), 'applied-cognition-contract'])],
1535
+ };
1536
+ }
1537
+ return {
1538
+ ...validation,
1539
+ passed: false,
1540
+ severity: 'block',
1541
+ violations: [...(validation.violations || []), ...applied.violations.map((v) => `applied_cognition: ${v}`)],
1542
+ gateTriggers: [...new Set([...(validation.gateTriggers || []), 'applied-cognition-contract-missing'])],
1543
+ appliedCognition: { ok: false, violations: applied.violations },
1544
+ };
1545
+ }
1546
+
1236
1547
  function toTelemetryEvent(payload, source = 'aria-mounted-runtime') {
1237
1548
  return {
1238
1549
  event_type: payload.event_type || 'runtime.cognition.turn',
@@ -1418,7 +1729,7 @@ function buildOwnerBypassPacket(message, reason = 'owner-local-bypass') {
1418
1729
  }
1419
1730
 
1420
1731
  async function loadRuntimePacket(req, body, client, packetRequest, message) {
1421
- if (body.packet) return body.packet;
1732
+ if (body.packet) return enrichPacketWithCodebaseSnapshot(body.packet);
1422
1733
  const apiKey = resolveApiKey(req, body);
1423
1734
  ensureOfflineBundleSeeded(apiKey, leaseCache.get(hashKey(apiKey)) || loadEncryptedLease(apiKey));
1424
1735
  try {
@@ -1433,7 +1744,7 @@ async function loadRuntimePacket(req, body, client, packetRequest, message) {
1433
1744
  doctrineBundleHash: lease?.claims?.doctrine_bundle_hash || null,
1434
1745
  lastUpstreamError: null,
1435
1746
  });
1436
- return packet;
1747
+ return enrichPacketWithCodebaseSnapshot(packet);
1437
1748
  } catch (error) {
1438
1749
  const bundle = ensureOfflineBundleSeeded(apiKey, leaseCache.get(hashKey(apiKey)) || loadEncryptedLease(apiKey)) || loadEncryptedOfflineBundle(apiKey);
1439
1750
  const bundleStatus = computeOfflineBundleStatus(bundle);
@@ -1445,19 +1756,52 @@ async function loadRuntimePacket(req, body, client, packetRequest, message) {
1445
1756
  lastUpstreamError: error instanceof Error ? error.message : String(error),
1446
1757
  lastUpstreamOkAt: bundle.lastUpstreamOkAt || bundle.cachedAt || new Date().toISOString(),
1447
1758
  });
1448
- return fallbackPacket;
1759
+ return enrichPacketWithCodebaseSnapshot(fallbackPacket);
1449
1760
  }
1450
1761
  }
1451
1762
  if (!isOwnerBypassRequest(req, body)) {
1452
1763
  throw error;
1453
1764
  }
1454
- return buildOwnerBypassPacket(
1765
+ return enrichPacketWithCodebaseSnapshot(buildOwnerBypassPacket(
1455
1766
  message || packetRequest?.message || '',
1456
1767
  error instanceof Error ? error.message : 'owner-local-bypass',
1457
- );
1768
+ ));
1458
1769
  }
1459
1770
  }
1460
1771
 
1772
+ function enrichPacketWithCodebaseSnapshot(packet) {
1773
+ if (!packet || typeof packet !== 'object') return packet;
1774
+ const codebase = compactCodebaseSnapshot();
1775
+ return {
1776
+ ...packet,
1777
+ runtime: {
1778
+ ...(packet.runtime && typeof packet.runtime === 'object' ? packet.runtime : {}),
1779
+ codebase_awareness: codebase,
1780
+ },
1781
+ };
1782
+ }
1783
+
1784
+ function buildMizanPacketRequest(body = {}) {
1785
+ const context = body.context && typeof body.context === 'object' ? body.context : body;
1786
+ const existing = body.packetRequest && typeof body.packetRequest === 'object' ? body.packetRequest : {};
1787
+ const message = String(
1788
+ context.message ||
1789
+ context.intendedAction ||
1790
+ body.message ||
1791
+ body.text ||
1792
+ ''
1793
+ );
1794
+ const platform = String(context.platform || context.surface || body.platform || body.client || 'mounted-runtime');
1795
+ return {
1796
+ stage: 'preflight',
1797
+ actor: String(context.actor || platform || 'mizan'),
1798
+ system: String(context.system || platform || 'aria-mounted-runtime'),
1799
+ platform,
1800
+ message,
1801
+ ...existing,
1802
+ };
1803
+ }
1804
+
1461
1805
  async function buildRuntimeTurnContext(req, body, client) {
1462
1806
  const sessionId = deriveSessionId(req, body, body.provider === 'anthropic' ? 'anthropic' : 'openai');
1463
1807
  const userId = deriveUserId(req, body);
@@ -2059,6 +2403,8 @@ function compactCodebaseSnapshot() {
2059
2403
  ? config.schemaImages
2060
2404
  : {};
2061
2405
  const repoSnapshots = Array.isArray(awareness.repoSnapshots) ? awareness.repoSnapshots : [];
2406
+ const heartbeatAt = awareness?.daemon?.heartbeatAt || null;
2407
+ const heartbeatAgeMs = heartbeatAt ? Date.now() - Date.parse(heartbeatAt) : null;
2062
2408
  return {
2063
2409
  repositories: repositories.slice(0, 6).map((repo) => ({
2064
2410
  name: repo?.name || null,
@@ -2071,6 +2417,8 @@ function compactCodebaseSnapshot() {
2071
2417
  awareness: {
2072
2418
  status: awareness.status || 'idle',
2073
2419
  updatedAt: awareness.updatedAt || null,
2420
+ daemon: awareness.daemon || null,
2421
+ stale: typeof heartbeatAgeMs === 'number' ? heartbeatAgeMs > 120000 : true,
2074
2422
  repoSnapshots: repoSnapshots.slice(0, 4).map((repo) => ({
2075
2423
  repoName: repo?.repoName || repo?.name || null,
2076
2424
  filesIndexed: repo?.filesIndexed || repo?.fileCount || null,
@@ -3288,7 +3636,10 @@ async function handleRoute(req, res) {
3288
3636
 
3289
3637
  let client;
3290
3638
  try {
3291
- client = createClient(req, body);
3639
+ // HQ routes use their own auth middleware — skip the global API key gate
3640
+ if (!url.pathname.startsWith('/hq/')) {
3641
+ client = createClient(req, body);
3642
+ }
3292
3643
  } catch (error) {
3293
3644
  return json(res, 401, { ok: false, error: error.message });
3294
3645
  }
@@ -3321,7 +3672,8 @@ async function handleRoute(req, res) {
3321
3672
 
3322
3673
  if (url.pathname === '/phase/pre' || url.pathname === '/mizan/pre') {
3323
3674
  const apiKey = resolveApiKey(req, body);
3324
- const packet = body.packet || await client.getHarnessPacket(body.packetRequest || {});
3675
+ const packetRequest = buildMizanPacketRequest(body);
3676
+ const packet = body.packet || await loadRuntimePacket(req, body, client, packetRequest, packetRequest.message || body.text || '');
3325
3677
  const bundle = evaluateMizanPre(packet, body.context || body, {
3326
3678
  sessionId: body.sessionId || body.context?.sessionId || deriveSessionId(req, body, 'mizan-pre'),
3327
3679
  runtimeId: ensureRuntimeMeta().runtimeId,
@@ -3341,7 +3693,8 @@ async function handleRoute(req, res) {
3341
3693
 
3342
3694
  if (url.pathname === '/phase/mid' || url.pathname === '/mizan/mid') {
3343
3695
  const apiKey = resolveApiKey(req, body);
3344
- const packet = body.packet || null;
3696
+ const packetRequest = buildMizanPacketRequest(body);
3697
+ const packet = body.packet || await loadRuntimePacket(req, body, client, packetRequest, packetRequest.message || body.message || '');
3345
3698
  const bundle = evaluateMizanMid(body.message || '', body.plannedApproach || '', packet, body.context || body, {
3346
3699
  sessionId: body.sessionId || body.context?.sessionId || deriveSessionId(req, body, 'mizan-mid'),
3347
3700
  runtimeId: ensureRuntimeMeta().runtimeId,
@@ -3361,7 +3714,8 @@ async function handleRoute(req, res) {
3361
3714
 
3362
3715
  if (url.pathname === '/phase/post' || url.pathname === '/mizan/post') {
3363
3716
  const apiKey = resolveApiKey(req, body);
3364
- const packet = body.packet || null;
3717
+ const packetRequest = buildMizanPacketRequest(body);
3718
+ const packet = body.packet || await loadRuntimePacket(req, body, client, packetRequest, packetRequest.message || body.text || '');
3365
3719
  const bundle = evaluateMizanPost(body.text || '', body.evidence || {}, packet, body.context || body, {
3366
3720
  sessionId: body.sessionId || body.context?.sessionId || deriveSessionId(req, body, 'mizan-post'),
3367
3721
  runtimeId: ensureRuntimeMeta().runtimeId,
@@ -3551,8 +3905,27 @@ async function handleRoute(req, res) {
3551
3905
  }
3552
3906
 
3553
3907
  if (url.pathname === '/check-action') {
3554
- const result = await client.checkAction(body.action, body.target || '');
3555
- return json(res, 200, { ok: true, ...result });
3908
+ const action = body.action || 'write';
3909
+ const localGate = validateVerifyAndHive({ req, body, action });
3910
+ if (!localGate.allowed) {
3911
+ return json(res, 200, { ok: true, allowed: false, ...localGate });
3912
+ }
3913
+ let result;
3914
+ try {
3915
+ result = await client.checkAction(action, body.target || '');
3916
+ } catch (error) {
3917
+ releaseHiveFileLease(deriveSessionId(req, body, 'runtime-action'), coerceTargetFiles(body));
3918
+ throw error;
3919
+ }
3920
+ if (result?.allowed === false) {
3921
+ releaseHiveFileLease(deriveSessionId(req, body, 'runtime-action'), coerceTargetFiles(body));
3922
+ }
3923
+ return json(res, 200, { ok: true, ...result, verify: localGate.verify, hive: localGate.hive });
3924
+ }
3925
+
3926
+ if (url.pathname === '/hive/release') {
3927
+ const sessionId = deriveSessionId(req, body, 'runtime-action');
3928
+ return json(res, 200, { ok: true, hive: releaseHiveFileLease(sessionId, coerceTargetFiles(body)) });
3556
3929
  }
3557
3930
 
3558
3931
  if (url.pathname === '/validate-output' || url.pathname === '/api/harness/validate') {
@@ -3574,6 +3947,7 @@ async function handleRoute(req, res) {
3574
3947
  gateTriggers: ['owner-local-bypass'],
3575
3948
  };
3576
3949
  }
3950
+ validation = mergeAppliedCognitionValidation(validation, validateAppliedCognitionContract(body.text));
3577
3951
  const response = { ok: true, validation };
3578
3952
 
3579
3953
  if (body.runLayer3 !== false) {