@aria_asi/cli 0.2.36 → 0.2.37
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/CLIENT-ONBOARDING.md +4 -2
- package/bin/aria.js +11 -7
- package/dist/aria-connector/src/auth.d.ts +14 -0
- package/dist/aria-connector/src/auth.d.ts.map +1 -1
- package/dist/aria-connector/src/auth.js +103 -1
- package/dist/aria-connector/src/auth.js.map +1 -1
- package/dist/aria-connector/src/chat.d.ts.map +1 -1
- package/dist/aria-connector/src/chat.js +13 -8
- package/dist/aria-connector/src/chat.js.map +1 -1
- package/dist/aria-connector/src/config.d.ts +6 -1
- package/dist/aria-connector/src/config.d.ts.map +1 -1
- package/dist/aria-connector/src/config.js.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +50 -6
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +310 -10
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +35 -11
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/repo-guard.d.ts +10 -0
- package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/repo-guard.js +110 -164
- package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +17 -7
- package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/shell.js +12 -8
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/aria-connector/src/harness-client.d.ts +3 -1
- package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
- package/dist/aria-connector/src/harness-client.js +7 -20
- package/dist/aria-connector/src/harness-client.js.map +1 -1
- package/dist/aria-connector/src/model-context.d.ts.map +1 -1
- package/dist/aria-connector/src/model-context.js +5 -0
- package/dist/aria-connector/src/model-context.js.map +1 -1
- package/dist/aria-connector/src/providers/types.d.ts +1 -1
- package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
- package/dist/aria-connector/src/providers/xai.d.ts +3 -0
- package/dist/aria-connector/src/providers/xai.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/xai.js +40 -0
- package/dist/aria-connector/src/providers/xai.js.map +1 -0
- package/dist/aria-connector/src/setup-wizard.js +1 -0
- package/dist/aria-connector/src/setup-wizard.js.map +1 -1
- package/dist/aria-connector/src/types.d.ts +2 -0
- package/dist/aria-connector/src/types.d.ts.map +1 -1
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/dist/assets/hooks/aria-first-class-coach.mjs +129 -0
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +33 -6
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +33 -8
- package/dist/assets/hooks/aria-preprompt-consult.mjs +5 -6
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/dist/assets/hooks/aria-stop-gate.mjs +125 -17
- package/dist/assets/hooks/doctrine_trigger_map.json +11 -0
- package/dist/assets/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/dist/assets/hooks/lib/emergency-gateoff.mjs +6 -0
- package/dist/assets/hooks/lib/first-class-coach.mjs +755 -0
- package/dist/assets/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/dist/assets/opencode-plugins/harness-context/auth-token.mjs +126 -0
- package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +62 -22
- package/dist/assets/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
- package/dist/assets/opencode-plugins/harness-gate/index.js +87 -27
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
- package/dist/assets/opencode-plugins/harness-outcome/index.js +29 -24
- package/dist/assets/opencode-plugins/harness-stop/index.js +229 -68
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
- package/dist/runtime/auth-token.mjs +121 -0
- package/dist/runtime/coach-kernel.mjs +371 -0
- package/dist/runtime/codex-bridge.mjs +440 -69
- package/dist/runtime/discipline/doctrine_trigger_map.json +11 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +18 -0
- package/dist/runtime/doctrine_trigger_map.json +11 -0
- package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/dist/runtime/hooks/aria-first-class-coach.mjs +129 -0
- package/dist/runtime/hooks/aria-harness-via-sdk.mjs +33 -6
- package/dist/runtime/hooks/aria-pre-tool-gate.mjs +33 -8
- package/dist/runtime/hooks/aria-preprompt-consult.mjs +5 -6
- package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/dist/runtime/hooks/aria-stop-gate.mjs +125 -17
- package/dist/runtime/hooks/doctrine_trigger_map.json +11 -0
- package/dist/runtime/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/dist/runtime/hooks/lib/emergency-gateoff.mjs +6 -0
- package/dist/runtime/hooks/lib/first-class-coach.mjs +755 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/dist/runtime/local-phase.mjs +8 -0
- package/dist/runtime/manifest.json +2 -2
- package/dist/runtime/provider-proxy.mjs +136 -33
- package/dist/runtime/sdk/BUNDLED.json +2 -2
- package/dist/runtime/sdk/auth.d.ts +17 -0
- package/dist/runtime/sdk/auth.js +158 -0
- package/dist/runtime/sdk/auth.js.map +1 -0
- package/dist/runtime/sdk/index.d.ts +8 -1
- package/dist/runtime/sdk/index.js +15 -1
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1711 -74
- package/dist/runtime/task-project-ledger.mjs +290 -0
- package/dist/sdk/BUNDLED.json +2 -2
- package/dist/sdk/auth.d.ts +17 -0
- package/dist/sdk/auth.js +158 -0
- package/dist/sdk/auth.js.map +1 -0
- package/dist/sdk/index.d.ts +8 -1
- package/dist/sdk/index.js +15 -1
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/hooks/aria-first-class-coach.mjs +129 -0
- package/hooks/aria-harness-via-sdk.mjs +33 -6
- package/hooks/aria-pre-tool-gate.mjs +33 -8
- package/hooks/aria-preprompt-consult.mjs +5 -6
- package/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/hooks/aria-stop-gate.mjs +125 -17
- package/hooks/doctrine_trigger_map.json +11 -0
- package/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/hooks/lib/emergency-gateoff.mjs +6 -0
- package/hooks/lib/first-class-coach.mjs +755 -0
- package/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/opencode-plugins/harness-context/auth-token.mjs +126 -0
- package/opencode-plugins/harness-context/inject-context.mjs +62 -22
- package/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
- package/opencode-plugins/harness-gate/index.js +87 -27
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
- package/opencode-plugins/harness-outcome/index.js +29 -24
- package/opencode-plugins/harness-stop/index.js +229 -68
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
- package/package.json +8 -2
- package/runtime-src/auth-token.mjs +121 -0
- package/runtime-src/coach-kernel.mjs +371 -0
- package/runtime-src/codex-bridge.mjs +440 -69
- package/runtime-src/local-phase.mjs +8 -0
- package/runtime-src/provider-proxy.mjs +136 -33
- package/runtime-src/service.mjs +1711 -74
- package/scripts/bundle-sdk.mjs +8 -0
- package/scripts/check-client-compatibility.mjs +422 -0
- package/scripts/check-coach-kernel.mjs +204 -0
- package/scripts/check-managed-runtime-ledger.mjs +107 -0
- package/scripts/check-opencode-config-contract.mjs +78 -0
- package/scripts/check-quality-ledger.mjs +121 -0
- package/scripts/self-test-harness-gates.mjs +179 -11
- package/scripts/self-test-repo-guard.mjs +38 -0
- package/scripts/validate-skill-prompts.mjs +14 -1
- package/skills/aria-cognition/aria-essence/SKILL.md +18 -0
- package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
- package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
- package/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
- package/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
- package/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
- package/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
- package/skills/aria-cognition/mizan/SKILL.md +18 -0
- package/skills/aria-cognition/nadia/SKILL.md +18 -0
- package/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
- package/skills/aria-cognition/predictor/SKILL.md +18 -0
- package/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
- package/skills/aria-cognition/soul-domains/SKILL.md +18 -0
- package/src/auth.ts +136 -1
- package/src/chat.ts +13 -8
- package/src/config.ts +6 -1
- package/src/connectors/claude-code.ts +62 -18
- package/src/connectors/codex.ts +308 -10
- package/src/connectors/opencode.ts +35 -12
- package/src/connectors/repo-guard.ts +117 -172
- package/src/connectors/runtime.ts +19 -7
- package/src/connectors/shell.ts +12 -8
- package/src/harness-client.ts +8 -22
- package/src/model-context.ts +6 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/xai.ts +55 -0
- package/src/setup-wizard.ts +1 -0
- package/src/types.ts +2 -0
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export const FIRST_CLASS_COACH_VERSION = 'aria.first_class_coach.v1';
|
|
7
|
+
export const TASK_PROJECT_LEDGER_VERSION = 'aria.task_project_ledger.v1';
|
|
8
|
+
export const RUNTIME_COACH_BRIDGE_VERSION = 'aria.runtime_coach_bridge.v1';
|
|
9
|
+
|
|
10
|
+
export const FIRST_CLASS_AXIOMS = [
|
|
11
|
+
'truth_over_deception',
|
|
12
|
+
'no_harm',
|
|
13
|
+
'sacred_trust',
|
|
14
|
+
'power_obligates_service',
|
|
15
|
+
'reflection_before_action',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const FIRST_CLASS_DOMAINS = [
|
|
19
|
+
'engineering_quality',
|
|
20
|
+
'reliability',
|
|
21
|
+
'security',
|
|
22
|
+
'operations',
|
|
23
|
+
'trust',
|
|
24
|
+
'developer_experience',
|
|
25
|
+
'product',
|
|
26
|
+
'learning',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const FIRST_CLASS_PRINCIPLES = [
|
|
30
|
+
'complete_implementation_not_claim_only',
|
|
31
|
+
'verified_behavior_not_assumption',
|
|
32
|
+
'domain_uplift_not_local_green_only',
|
|
33
|
+
'axiom_bound_decisioning',
|
|
34
|
+
'first_principles_before_action',
|
|
35
|
+
'senior_production_grade_change_control',
|
|
36
|
+
'recoverable_fail_closed_governance',
|
|
37
|
+
'evidence_ledger_before_readiness_claim',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const LIFECYCLE_PHASES = [
|
|
41
|
+
{ id: 'session_start', labels: ['SessionStart', 'session.start'], blocks: false },
|
|
42
|
+
{ id: 'pre_prompt_injection', labels: ['UserPromptSubmit', 'user.prompt.submit'], blocks: false },
|
|
43
|
+
{ id: 'post_prompt_injection', labels: ['post_prompt_injection'], blocks: false },
|
|
44
|
+
{ id: 'pre_cognition', labels: ['pre_cognition', 'cognition.pre'], blocks: true },
|
|
45
|
+
{ id: 'post_cognition', labels: ['post_cognition', 'cognition.post'], blocks: true },
|
|
46
|
+
{ id: 'pre_tool', labels: ['PreToolUse', 'pre.tool'], blocks: true },
|
|
47
|
+
{ id: 'post_tool', labels: ['PostToolUse', 'post.tool'], blocks: true },
|
|
48
|
+
{ id: 'pre_emit', labels: ['pre_emit', 'pre.text'], blocks: true },
|
|
49
|
+
{ id: 'stop', labels: ['Stop', 'text.complete', 'message.updated'], blocks: true },
|
|
50
|
+
{ id: 'post_turn', labels: ['post_turn', 'outcome'], blocks: false },
|
|
51
|
+
{ id: 'background_worker', labels: ['worker', 'agent', 'background'], blocks: true },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export const PLATFORM_PHASES = {
|
|
55
|
+
claude: ['session_start', 'pre_prompt_injection', 'pre_tool', 'post_tool', 'pre_emit', 'post_cognition', 'stop', 'post_turn', 'background_worker'],
|
|
56
|
+
codex: ['pre_prompt_injection', 'pre_tool', 'post_tool', 'stop', 'post_turn'],
|
|
57
|
+
opencode: ['session_start', 'pre_tool', 'post_tool', 'stop', 'post_turn'],
|
|
58
|
+
sdk: ['pre_prompt_injection', 'pre_cognition', 'post_cognition', 'pre_tool', 'post_tool', 'pre_emit', 'stop', 'post_turn'],
|
|
59
|
+
owner_runtime: ['session_start', 'pre_prompt_injection', 'pre_tool', 'post_tool', 'pre_emit', 'stop', 'post_turn', 'background_worker'],
|
|
60
|
+
openclaw: ['standing_order', 'task_record', 'task_flow', 'lobster_run', 'approval_resume', 'plugin_hook', 'audit_maintenance'],
|
|
61
|
+
garden_hive: ['context_hydration', 'decision_log', 'outcome_writeback', 'living_memory', 'worker_continuity'],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const TASK_PROJECT_QUALITY_GATES = [
|
|
65
|
+
{
|
|
66
|
+
id: 'ledger_created',
|
|
67
|
+
description: 'A durable task/project ledger exists before work proceeds.',
|
|
68
|
+
readinessRequired: true,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'intent_boundary',
|
|
72
|
+
description: 'The ledger records what task or project is being attempted.',
|
|
73
|
+
readinessRequired: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'scope_boundary',
|
|
77
|
+
description: 'The ledger records the platform and phase boundaries that govern the work.',
|
|
78
|
+
readinessRequired: true,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'execution_trace',
|
|
82
|
+
description: 'The ledger records at least one execution, tool, workflow, or background phase event when work changes state.',
|
|
83
|
+
readinessRequired: false,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'verification_trace',
|
|
87
|
+
description: 'The ledger records real verification evidence before completion/readiness claims are allowed.',
|
|
88
|
+
readinessRequired: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'post_turn_writeback',
|
|
92
|
+
description: 'The ledger records post-turn or outcome writeback for continuity.',
|
|
93
|
+
readinessRequired: false,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'final_claim_guard',
|
|
97
|
+
description: 'The ledger actively guards final claims against missing evidence.',
|
|
98
|
+
readinessRequired: true,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const CLAIM_RX = /\b(?:first[- ]class|production[- ]ready|ready|complete|completed|done|verified|gold[- ]standard|shipped|fixed|landed)\b/i;
|
|
103
|
+
const MAX_LEDGER_EVENTS = 500;
|
|
104
|
+
const RUNTIME_COACH_ENDPOINT_PATH = '/coach/phase';
|
|
105
|
+
export const RUNTIME_COACH_RELEASE_PHASES = ['claim_or_release', 'post_output'];
|
|
106
|
+
const RUNTIME_COACH_BLOCKING_LOCAL_PHASES = new Set(['pre_tool', 'pre_emit', 'stop', 'post_cognition']);
|
|
107
|
+
const RUNTIME_PHASES_BY_LOCAL_PHASE = {
|
|
108
|
+
session_start: ['pre_turn'],
|
|
109
|
+
pre_prompt_injection: ['pre_turn', 'pre_cognition'],
|
|
110
|
+
post_prompt_injection: ['post_cognition'],
|
|
111
|
+
pre_cognition: ['pre_cognition'],
|
|
112
|
+
post_cognition: ['post_cognition'],
|
|
113
|
+
pre_tool: ['pre_tool'],
|
|
114
|
+
post_tool: ['post_tool'],
|
|
115
|
+
pre_emit: ['pre_output'],
|
|
116
|
+
stop: ['post_generation', 'pre_output'],
|
|
117
|
+
post_turn: ['post_output'],
|
|
118
|
+
background_worker: ['background_checkpoint'],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const FIRST_CLASS_ACCEPTANCE = {
|
|
122
|
+
version: FIRST_CLASS_COACH_VERSION,
|
|
123
|
+
definition: 'first-class means complete, verified, domain-uplifted, axiom-bound, first-principles-applied, recoverable, and senior-production-grade across every controlled platform and lifecycle phase before any readiness claim',
|
|
124
|
+
requiredEvidence: [
|
|
125
|
+
'source_path',
|
|
126
|
+
'package_path',
|
|
127
|
+
'active_runtime_path',
|
|
128
|
+
'import_check',
|
|
129
|
+
'allow_smoke',
|
|
130
|
+
'block_smoke',
|
|
131
|
+
'recovery_smoke',
|
|
132
|
+
'fresh_install_smoke',
|
|
133
|
+
'domain_review',
|
|
134
|
+
'axiom_review',
|
|
135
|
+
'first_principles_review',
|
|
136
|
+
'warning_clean_check',
|
|
137
|
+
],
|
|
138
|
+
axioms: FIRST_CLASS_AXIOMS,
|
|
139
|
+
domains: FIRST_CLASS_DOMAINS,
|
|
140
|
+
principles: FIRST_CLASS_PRINCIPLES,
|
|
141
|
+
platforms: Object.keys(PLATFORM_PHASES),
|
|
142
|
+
lifecyclePhases: LIFECYCLE_PHASES.map((phase) => phase.id),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
function stableHash(value = '') {
|
|
146
|
+
return createHash('sha256').update(String(value || 'unknown')).digest('hex').slice(0, 20);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function stableJson(value = {}) {
|
|
150
|
+
try {
|
|
151
|
+
return JSON.stringify(value);
|
|
152
|
+
} catch {
|
|
153
|
+
return String(value || '');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hashJson(value = {}) {
|
|
158
|
+
return createHash('sha256').update(stableJson(value)).digest('hex');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function firstString(...values) {
|
|
162
|
+
for (const value of values) {
|
|
163
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
164
|
+
if (typeof value === 'number' && Number.isFinite(value)) return String(value);
|
|
165
|
+
}
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function compactHint(value = '') {
|
|
170
|
+
const raw = String(value || '').trim();
|
|
171
|
+
if (!raw) return 'unknown';
|
|
172
|
+
const tail = raw.includes('/') ? basename(raw) : raw;
|
|
173
|
+
return tail.replace(/[^a-zA-Z0-9._-]+/g, '-').slice(0, 80) || 'unknown';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function readJsonFile(path) {
|
|
177
|
+
try {
|
|
178
|
+
if (!existsSync(path)) return null;
|
|
179
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function appendRuntimeCoachBridgeError(record) {
|
|
186
|
+
try {
|
|
187
|
+
const target = join(homedir(), '.aria', 'first-class-coach-runtime-publish-errors.jsonl');
|
|
188
|
+
mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
|
|
189
|
+
appendFileSync(target, `${JSON.stringify({ schema: `${RUNTIME_COACH_BRIDGE_VERSION}.error`, at: new Date().toISOString(), ...record })}\n`, { mode: 0o600 });
|
|
190
|
+
} catch {}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function readRuntimeToken(env = process.env) {
|
|
194
|
+
const envToken = firstString(env.ARIA_HARNESS_TOKEN, env.ARIA_API_KEY, env.ARIA_MASTER_TOKEN);
|
|
195
|
+
if (envToken) return envToken;
|
|
196
|
+
const ownerPath = join(homedir(), '.aria', 'owner-token');
|
|
197
|
+
if (existsSync(ownerPath)) return readFileSync(ownerPath, 'utf8').trim();
|
|
198
|
+
const license = readJsonFile(join(homedir(), '.aria', 'license.json'));
|
|
199
|
+
return firstString(license?.harnessToken, license?.token);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function runtimeBaseUrl(env = process.env) {
|
|
203
|
+
return firstString(env.ARIA_RUNTIME_URL, 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function runtimeCoachPhasesForLocalPhase(phase = '') {
|
|
207
|
+
return RUNTIME_PHASES_BY_LOCAL_PHASE[phase] || ['pre_turn'];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function inferRuntimeSessionId(event = {}, platform = 'claude') {
|
|
211
|
+
const explicit = firstString(
|
|
212
|
+
event.session_id,
|
|
213
|
+
event.sessionId,
|
|
214
|
+
event.conversationId,
|
|
215
|
+
event.conversation_id,
|
|
216
|
+
event.threadId,
|
|
217
|
+
event.thread_id,
|
|
218
|
+
event.transcript_path,
|
|
219
|
+
event.transcriptPath
|
|
220
|
+
);
|
|
221
|
+
return explicit || `${platform}:${stableHash(firstString(event.cwd, event.workspaceDir, process.cwd()))}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function inferRuntimeRequestId(event = {}, platform = 'claude') {
|
|
225
|
+
return firstString(
|
|
226
|
+
event.request_id,
|
|
227
|
+
event.requestId,
|
|
228
|
+
event.turn_id,
|
|
229
|
+
event.turnId,
|
|
230
|
+
event.message_id,
|
|
231
|
+
event.messageId,
|
|
232
|
+
`${inferRuntimeSessionId(event, platform)}:${event.hookEventName || event.hook_event_name || event.type || Date.now()}`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function summarizeRuntimeToolTarget(event = {}) {
|
|
237
|
+
const value = {
|
|
238
|
+
tool_name: event.tool_name || event.toolName || event.tool?.name || null,
|
|
239
|
+
tool_input: event.tool_input || event.toolInput || event.input || null,
|
|
240
|
+
};
|
|
241
|
+
return stableJson(value).slice(0, 6000);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function inferRuntimeAction(event = {}, phase = '') {
|
|
245
|
+
const haystack = `${phase}\n${summarizeRuntimeToolTarget(event)}\n${stableJson(event).slice(0, 6000)}`;
|
|
246
|
+
if (/\b(?:kubectl\s+(?:apply|set|rollout|delete)|helm\s+upgrade|terraform\s+apply|docker\s+push|deploy)\b/i.test(haystack)) return 'deploy';
|
|
247
|
+
if (/\b(?:rm\s+-[rRfF]+|delete|drop\s+(?:table|database|schema)|git\s+reset\s+--hard)\b/i.test(haystack)) return 'delete';
|
|
248
|
+
if (phase === 'stop' || phase === 'pre_emit') return 'release';
|
|
249
|
+
if (phase === 'pre_tool' || phase === 'post_tool') return 'write';
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function readHarnessPacketHash(event = {}) {
|
|
254
|
+
const direct = firstString(event.harnessPacketHash, event.harness_packet_hash);
|
|
255
|
+
if (direct) return direct;
|
|
256
|
+
for (const candidate of [
|
|
257
|
+
join(homedir(), '.claude', '.aria-harness-last-packet.json'),
|
|
258
|
+
join(homedir(), '.aria', '.aria-harness-last-packet.json'),
|
|
259
|
+
]) {
|
|
260
|
+
const packet = readJsonFile(candidate);
|
|
261
|
+
if (packet) return hashJson(packet);
|
|
262
|
+
}
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function runtimeTextForPhase(event = {}, localPhase = '') {
|
|
267
|
+
if (localPhase === 'pre_tool' || localPhase === 'post_tool') return summarizeRuntimeToolTarget(event);
|
|
268
|
+
if (localPhase === 'stop' || localPhase === 'pre_emit' || localPhase === 'post_cognition') return extractAssistantText(event);
|
|
269
|
+
return firstString(
|
|
270
|
+
event.prompt,
|
|
271
|
+
event.userPrompt,
|
|
272
|
+
event.user_input,
|
|
273
|
+
event.message,
|
|
274
|
+
event.text,
|
|
275
|
+
event.body,
|
|
276
|
+
event.context?.prompt,
|
|
277
|
+
event.context?.bodyForAgent
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function buildRuntimeCoachBody({ platform = 'claude', phase = 'unknown', runtimePhase, event = {}, ledgerResult = null, evidence = {} } = {}) {
|
|
282
|
+
const taskLedger = ledgerResult?.ledger || null;
|
|
283
|
+
const paths = ledgerResult?.paths || null;
|
|
284
|
+
return {
|
|
285
|
+
phase: runtimePhase,
|
|
286
|
+
requestId: inferRuntimeRequestId(event, platform),
|
|
287
|
+
sessionId: inferRuntimeSessionId(event, platform),
|
|
288
|
+
surface: `${platform}-hooks`,
|
|
289
|
+
lane: `${platform}_native_hooks`,
|
|
290
|
+
action: inferRuntimeAction(event, phase),
|
|
291
|
+
target: phase === 'pre_tool' || phase === 'post_tool' ? summarizeRuntimeToolTarget(event) : '',
|
|
292
|
+
text: runtimeTextForPhase(event, phase),
|
|
293
|
+
harnessPacketHash: readHarnessPacketHash(event),
|
|
294
|
+
roleProfile: `${platform}_code_assistant`,
|
|
295
|
+
evidenceRefs: [
|
|
296
|
+
taskLedger ? { kind: 'task_project_ledger', ledgerId: taskLedger.ledgerId, ledgerPath: paths?.ledgerPath || null } : null,
|
|
297
|
+
{ kind: 'first_class_coach_phase', platform, localPhase: phase, hookEventName: process.env.HOOK_EVENT_NAME || event.hookEventName || event.hook_event_name || null },
|
|
298
|
+
Object.keys(evidence || {}).length > 0 ? { kind: 'local_evidence', evidence } : null,
|
|
299
|
+
].filter(Boolean),
|
|
300
|
+
qualityGateStatus: evidence.blocked === true ? 'blocked' : 'passed',
|
|
301
|
+
complianceGateStatus: 'coach_bound',
|
|
302
|
+
metadata: {
|
|
303
|
+
bridge_version: RUNTIME_COACH_BRIDGE_VERSION,
|
|
304
|
+
local_phase: phase,
|
|
305
|
+
hook_event_name: process.env.HOOK_EVENT_NAME || event.hookEventName || event.hook_event_name || null,
|
|
306
|
+
task_project_ledger_id: taskLedger?.ledgerId || null,
|
|
307
|
+
readiness_claim_allowed: taskLedger?.controls?.readinessClaimAllowed === true,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function postRuntimeCoachPhase(body, env = process.env) {
|
|
313
|
+
const token = readRuntimeToken(env);
|
|
314
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
315
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
316
|
+
const response = await fetch(`${runtimeBaseUrl(env)}${RUNTIME_COACH_ENDPOINT_PATH}`, {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers,
|
|
319
|
+
body: JSON.stringify(body),
|
|
320
|
+
});
|
|
321
|
+
if (!response.ok) {
|
|
322
|
+
const detail = await response.text().catch(() => response.statusText);
|
|
323
|
+
throw new Error(`runtime ${RUNTIME_COACH_ENDPOINT_PATH} failed (${response.status}): ${detail.slice(0, 500)}`);
|
|
324
|
+
}
|
|
325
|
+
return response.json();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export async function recordRuntimeCoachForLifecycle({ platform = 'claude', phase = 'unknown', event = {}, ledgerResult = null, runtimePhases = null, evidence = {}, env = process.env } = {}) {
|
|
329
|
+
const phases = Array.isArray(runtimePhases) && runtimePhases.length > 0
|
|
330
|
+
? runtimePhases
|
|
331
|
+
: runtimeCoachPhasesForLocalPhase(phase);
|
|
332
|
+
const results = [];
|
|
333
|
+
for (const runtimePhase of phases) {
|
|
334
|
+
const body = buildRuntimeCoachBody({ platform, phase, runtimePhase, event, ledgerResult, evidence });
|
|
335
|
+
try {
|
|
336
|
+
results.push(await postRuntimeCoachPhase(body, env));
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
339
|
+
appendRuntimeCoachBridgeError({ platform, phase, runtimePhase, message, requestId: body.requestId, sessionId: body.sessionId });
|
|
340
|
+
results.push({
|
|
341
|
+
ok: false,
|
|
342
|
+
permitted: true,
|
|
343
|
+
decision: 'warn_operator_only',
|
|
344
|
+
phase: runtimePhase,
|
|
345
|
+
error: message,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const blocking = results.find((result) => result?.permitted === false) || null;
|
|
350
|
+
return {
|
|
351
|
+
ok: results.every((result) => result?.ok !== false),
|
|
352
|
+
platform,
|
|
353
|
+
phase,
|
|
354
|
+
runtimePhases: phases,
|
|
355
|
+
results,
|
|
356
|
+
blocking,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function runtimeCoachBlocksLocalPhase(phase = '', runtimeResult = {}) {
|
|
361
|
+
if (!RUNTIME_COACH_BLOCKING_LOCAL_PHASES.has(phase)) return null;
|
|
362
|
+
return runtimeResult?.blocking || null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function formatRuntimeCoachBlock(result = {}) {
|
|
366
|
+
const record = result.record || {};
|
|
367
|
+
const reasons = Array.isArray(result.reasons)
|
|
368
|
+
? result.reasons
|
|
369
|
+
: Array.isArray(record.reasons)
|
|
370
|
+
? record.reasons
|
|
371
|
+
: ['coach_kernel_policy_block'];
|
|
372
|
+
return [
|
|
373
|
+
'=== ARIA RUNTIME COACH BLOCK ===',
|
|
374
|
+
'',
|
|
375
|
+
`decision: ${result.decision || record.decision || 'blocked'}`,
|
|
376
|
+
`coach_event_id: ${record.coach_event_id || 'unknown'}`,
|
|
377
|
+
`reasons: ${reasons.join(', ') || 'coach_kernel_policy_block'}`,
|
|
378
|
+
`next: ${result.clientMessage || record.next_action || 'repair the blocked condition and retry'}`,
|
|
379
|
+
'',
|
|
380
|
+
].join('\n');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function writeJsonAtomic(path, value) {
|
|
384
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
385
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
386
|
+
writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
387
|
+
renameSync(tmp, path);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function markGate(ledger, gateId, status, evidence = undefined) {
|
|
391
|
+
const gate = ledger.qualityGates?.[gateId];
|
|
392
|
+
if (!gate) return;
|
|
393
|
+
if (gate.status === 'passed' && status !== 'blocked') return;
|
|
394
|
+
gate.status = status;
|
|
395
|
+
gate.updatedAt = ledger.updatedAt;
|
|
396
|
+
if (evidence) gate.evidence = evidence;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function hasPromptIntent(event = {}) {
|
|
400
|
+
return Boolean(firstString(
|
|
401
|
+
event.prompt,
|
|
402
|
+
event.userPrompt,
|
|
403
|
+
event.user_input,
|
|
404
|
+
event.message,
|
|
405
|
+
event.text,
|
|
406
|
+
event.body,
|
|
407
|
+
event.context?.prompt,
|
|
408
|
+
event.context?.bodyForAgent
|
|
409
|
+
));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function hasVerificationEvidence(evidence = {}, event = {}) {
|
|
413
|
+
if (evidence.warning_clean === true || evidence.verification === true || evidence.validation_run === true) return true;
|
|
414
|
+
if (event.verified === true || event.validation?.ok === true || event.test?.ok === true) return true;
|
|
415
|
+
const text = firstString(event.verification, event.validation, event.testResult, event.commandResult, evidence.commandResult);
|
|
416
|
+
return /\b(?:ok|passed|pass|success|succeeded|exit\s*0|0\s*failures?)\b/i.test(text);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function isExecutionPhase(phase = '') {
|
|
420
|
+
return ['pre_tool', 'post_tool', 'background_worker', 'task_record', 'task_flow', 'lobster_run', 'approval_resume'].includes(phase);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function isPostTurnPhase(phase = '') {
|
|
424
|
+
return ['post_turn', 'outcome_writeback', 'decision_log'].includes(phase);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function taskProjectLedgerRoot(env = process.env) {
|
|
428
|
+
return env.ARIA_TASK_PROJECT_LEDGER_DIR || join(homedir(), '.aria', 'task-project-ledgers');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function resolveTaskProjectIdentity(event = {}, env = process.env) {
|
|
432
|
+
const projectRaw = firstString(
|
|
433
|
+
env.ARIA_PROJECT_ID,
|
|
434
|
+
event.projectId,
|
|
435
|
+
event.project_id,
|
|
436
|
+
event.project,
|
|
437
|
+
event.workspaceDir,
|
|
438
|
+
event.workspace_dir,
|
|
439
|
+
event.cwd,
|
|
440
|
+
event.currentWorkingDirectory,
|
|
441
|
+
event.context?.workspaceDir,
|
|
442
|
+
env.PWD,
|
|
443
|
+
process.cwd()
|
|
444
|
+
);
|
|
445
|
+
const taskRaw = firstString(
|
|
446
|
+
env.ARIA_TASK_ID,
|
|
447
|
+
event.taskId,
|
|
448
|
+
event.task_id,
|
|
449
|
+
event.session_id,
|
|
450
|
+
event.sessionId,
|
|
451
|
+
event.sessionKey,
|
|
452
|
+
event.conversationId,
|
|
453
|
+
event.threadId,
|
|
454
|
+
event.transcript_path,
|
|
455
|
+
event.prompt,
|
|
456
|
+
event.message,
|
|
457
|
+
projectRaw
|
|
458
|
+
);
|
|
459
|
+
const projectHash = stableHash(projectRaw || 'unknown-project');
|
|
460
|
+
const taskHash = stableHash(`${projectHash}:${taskRaw || 'unknown-task'}`);
|
|
461
|
+
return {
|
|
462
|
+
projectHash,
|
|
463
|
+
taskHash,
|
|
464
|
+
projectHint: compactHint(projectRaw),
|
|
465
|
+
taskHint: compactHint(taskRaw),
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function taskProjectLedgerPaths(identity, env = process.env) {
|
|
470
|
+
const dir = join(taskProjectLedgerRoot(env), identity.projectHash);
|
|
471
|
+
return {
|
|
472
|
+
dir,
|
|
473
|
+
ledgerPath: join(dir, `${identity.taskHash}.json`),
|
|
474
|
+
eventPath: join(dir, `${identity.taskHash}.events.jsonl`),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function createTaskProjectLedger(identity, now) {
|
|
479
|
+
const qualityGates = Object.fromEntries(TASK_PROJECT_QUALITY_GATES.map((gate) => [
|
|
480
|
+
gate.id,
|
|
481
|
+
{
|
|
482
|
+
status: gate.id === 'ledger_created' || gate.id === 'final_claim_guard' ? 'passed' : 'pending',
|
|
483
|
+
description: gate.description,
|
|
484
|
+
readinessRequired: gate.readinessRequired,
|
|
485
|
+
updatedAt: now,
|
|
486
|
+
},
|
|
487
|
+
]));
|
|
488
|
+
return {
|
|
489
|
+
schema: `${TASK_PROJECT_LEDGER_VERSION}.ledger`,
|
|
490
|
+
version: TASK_PROJECT_LEDGER_VERSION,
|
|
491
|
+
ledgerId: `tpl_${identity.projectHash}_${identity.taskHash}`,
|
|
492
|
+
createdAt: now,
|
|
493
|
+
updatedAt: now,
|
|
494
|
+
status: 'active',
|
|
495
|
+
identity,
|
|
496
|
+
controls: {
|
|
497
|
+
readinessClaimAllowed: false,
|
|
498
|
+
readinessClaimRule: 'Completion/readiness claims require all readinessRequired quality gates to pass and no open blockers.',
|
|
499
|
+
},
|
|
500
|
+
qualityGates,
|
|
501
|
+
phaseEvents: [],
|
|
502
|
+
evidence: [],
|
|
503
|
+
openBlockers: [],
|
|
504
|
+
blockedClaims: [],
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export function readinessMissingGates(ledger = {}) {
|
|
509
|
+
const qualityGates = ledger.qualityGates || {};
|
|
510
|
+
return TASK_PROJECT_QUALITY_GATES
|
|
511
|
+
.filter((gate) => gate.readinessRequired)
|
|
512
|
+
.filter((gate) => qualityGates[gate.id]?.status !== 'passed' && qualityGates[gate.id]?.status !== 'not_applicable')
|
|
513
|
+
.map((gate) => gate.id);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export function updateTaskProjectLedger({ platform, phase, source = 'first-class-coach', event = {}, evidence = {}, env = process.env } = {}) {
|
|
517
|
+
const now = new Date().toISOString();
|
|
518
|
+
const identity = resolveTaskProjectIdentity(event, env);
|
|
519
|
+
const paths = taskProjectLedgerPaths(identity, env);
|
|
520
|
+
const existing = readJsonFile(paths.ledgerPath);
|
|
521
|
+
const ledger = existing?.schema === `${TASK_PROJECT_LEDGER_VERSION}.ledger`
|
|
522
|
+
? existing
|
|
523
|
+
: createTaskProjectLedger(identity, now);
|
|
524
|
+
|
|
525
|
+
ledger.updatedAt = now;
|
|
526
|
+
ledger.status = ledger.status || 'active';
|
|
527
|
+
ledger.identity = ledger.identity || identity;
|
|
528
|
+
ledger.controls = ledger.controls || {};
|
|
529
|
+
ledger.qualityGates = ledger.qualityGates || {};
|
|
530
|
+
for (const gate of TASK_PROJECT_QUALITY_GATES) {
|
|
531
|
+
if (!ledger.qualityGates[gate.id]) {
|
|
532
|
+
ledger.qualityGates[gate.id] = {
|
|
533
|
+
status: 'pending',
|
|
534
|
+
description: gate.description,
|
|
535
|
+
readinessRequired: gate.readinessRequired,
|
|
536
|
+
updatedAt: now,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
markGate(ledger, 'ledger_created', 'passed', { ledgerPath: paths.ledgerPath });
|
|
542
|
+
if (hasPromptIntent(event) || phase === 'pre_prompt_injection' || phase === 'session_start') {
|
|
543
|
+
markGate(ledger, 'intent_boundary', 'passed', { source, phase });
|
|
544
|
+
}
|
|
545
|
+
if (platform && phase && phase !== 'unknown') {
|
|
546
|
+
markGate(ledger, 'scope_boundary', 'passed', { platform, phase });
|
|
547
|
+
}
|
|
548
|
+
if (isExecutionPhase(phase)) {
|
|
549
|
+
markGate(ledger, 'execution_trace', 'passed', { platform, phase, source });
|
|
550
|
+
}
|
|
551
|
+
if (hasVerificationEvidence(evidence, event)) {
|
|
552
|
+
markGate(ledger, 'verification_trace', 'passed', { source, phase });
|
|
553
|
+
}
|
|
554
|
+
if (isPostTurnPhase(phase)) {
|
|
555
|
+
markGate(ledger, 'post_turn_writeback', 'passed', { platform, phase, source });
|
|
556
|
+
}
|
|
557
|
+
markGate(ledger, 'final_claim_guard', 'passed', { source: 'task_project_ledger_claim_guard' });
|
|
558
|
+
|
|
559
|
+
const row = {
|
|
560
|
+
at: now,
|
|
561
|
+
platform: platform || inferCoachPlatform(event, env),
|
|
562
|
+
phase: phase || inferCoachPhase(event, env),
|
|
563
|
+
source,
|
|
564
|
+
evidence,
|
|
565
|
+
};
|
|
566
|
+
ledger.phaseEvents = Array.isArray(ledger.phaseEvents) ? ledger.phaseEvents : [];
|
|
567
|
+
ledger.phaseEvents.push(row);
|
|
568
|
+
if (ledger.phaseEvents.length > MAX_LEDGER_EVENTS) {
|
|
569
|
+
ledger.phaseEvents = ledger.phaseEvents.slice(-MAX_LEDGER_EVENTS);
|
|
570
|
+
}
|
|
571
|
+
if (Object.keys(evidence || {}).length > 0) {
|
|
572
|
+
ledger.evidence = Array.isArray(ledger.evidence) ? ledger.evidence : [];
|
|
573
|
+
ledger.evidence.push({ at: now, source, phase: row.phase, evidence });
|
|
574
|
+
if (ledger.evidence.length > MAX_LEDGER_EVENTS) {
|
|
575
|
+
ledger.evidence = ledger.evidence.slice(-MAX_LEDGER_EVENTS);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const missingReadinessGates = readinessMissingGates(ledger);
|
|
579
|
+
ledger.controls.readinessClaimAllowed = missingReadinessGates.length === 0 && (ledger.openBlockers || []).length === 0;
|
|
580
|
+
ledger.controls.missingReadinessGates = missingReadinessGates;
|
|
581
|
+
|
|
582
|
+
writeJsonAtomic(paths.ledgerPath, ledger);
|
|
583
|
+
mkdirSync(dirname(paths.eventPath), { recursive: true, mode: 0o700 });
|
|
584
|
+
appendFileSync(paths.eventPath, `${JSON.stringify({ schema: `${TASK_PROJECT_LEDGER_VERSION}.event`, ledgerId: ledger.ledgerId, ...row })}\n`, { mode: 0o600 });
|
|
585
|
+
return { ledger, paths, identity };
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function extractTextFromContent(value) {
|
|
589
|
+
if (!value) return '';
|
|
590
|
+
if (typeof value === 'string') return value;
|
|
591
|
+
if (Array.isArray(value)) return value.map(extractTextFromContent).filter(Boolean).join('\n');
|
|
592
|
+
if (typeof value === 'object') {
|
|
593
|
+
return firstString(value.text, value.content, value.message) || extractTextFromContent(value.parts);
|
|
594
|
+
}
|
|
595
|
+
return '';
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function extractAssistantText(event = {}) {
|
|
599
|
+
const direct = firstString(
|
|
600
|
+
event.assistantText,
|
|
601
|
+
event.assistant_text,
|
|
602
|
+
event.response,
|
|
603
|
+
event.output,
|
|
604
|
+
event.finalText,
|
|
605
|
+
event.final_text,
|
|
606
|
+
event.text
|
|
607
|
+
);
|
|
608
|
+
if (direct) return direct;
|
|
609
|
+
const nested = extractTextFromContent(event.message) || extractTextFromContent(event.result) || extractTextFromContent(event.assistant);
|
|
610
|
+
if (nested) return nested;
|
|
611
|
+
const transcriptPath = firstString(event.transcript_path, event.transcriptPath);
|
|
612
|
+
if (!transcriptPath || !existsSync(transcriptPath)) return '';
|
|
613
|
+
try {
|
|
614
|
+
const lines = readFileSync(transcriptPath, 'utf8').trim().split('\n').filter(Boolean);
|
|
615
|
+
for (const line of lines.slice(-100).reverse()) {
|
|
616
|
+
let parsed;
|
|
617
|
+
try { parsed = JSON.parse(line); } catch { continue; }
|
|
618
|
+
const role = parsed.role || parsed.message?.role || parsed.type;
|
|
619
|
+
if (role !== 'assistant') continue;
|
|
620
|
+
const text = extractTextFromContent(parsed.content) || extractTextFromContent(parsed.message?.content) || extractTextFromContent(parsed.text);
|
|
621
|
+
if (text) return text;
|
|
622
|
+
}
|
|
623
|
+
} catch {}
|
|
624
|
+
return '';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export function evaluateTaskProjectClaim({ text = '', ledger = {} } = {}) {
|
|
628
|
+
const claim = CLAIM_RX.test(String(text || ''));
|
|
629
|
+
if (!claim) return { ok: true, claim: false, missingReadinessGates: [], openBlockers: [] };
|
|
630
|
+
const missingReadinessGates = readinessMissingGates(ledger);
|
|
631
|
+
const openBlockers = Array.isArray(ledger.openBlockers) ? ledger.openBlockers : [];
|
|
632
|
+
return {
|
|
633
|
+
ok: missingReadinessGates.length === 0 && openBlockers.length === 0 && ledger.controls?.readinessClaimAllowed === true,
|
|
634
|
+
claim: true,
|
|
635
|
+
missingReadinessGates,
|
|
636
|
+
openBlockers,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export function recordBlockedTaskProjectClaim({ ledger, paths, text, evaluation } = {}) {
|
|
641
|
+
if (!ledger || !paths?.ledgerPath) return null;
|
|
642
|
+
const now = new Date().toISOString();
|
|
643
|
+
ledger.blockedClaims = Array.isArray(ledger.blockedClaims) ? ledger.blockedClaims : [];
|
|
644
|
+
ledger.blockedClaims.push({
|
|
645
|
+
at: now,
|
|
646
|
+
textHash: stableHash(text || ''),
|
|
647
|
+
missingReadinessGates: evaluation?.missingReadinessGates || [],
|
|
648
|
+
openBlockerCount: evaluation?.openBlockers?.length || 0,
|
|
649
|
+
});
|
|
650
|
+
ledger.updatedAt = now;
|
|
651
|
+
writeJsonAtomic(paths.ledgerPath, ledger);
|
|
652
|
+
return ledger.blockedClaims[ledger.blockedClaims.length - 1];
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export function formatTaskProjectLedgerContext({ ledger, paths, platform = 'unknown', phase = 'unknown' } = {}) {
|
|
656
|
+
const missing = readinessMissingGates(ledger);
|
|
657
|
+
return [
|
|
658
|
+
'--- ARIA TASK/PROJECT LEDGER CONTRACT ---',
|
|
659
|
+
`version: ${TASK_PROJECT_LEDGER_VERSION}`,
|
|
660
|
+
`ledger_id: ${ledger?.ledgerId || 'unknown'}`,
|
|
661
|
+
`platform: ${platform}`,
|
|
662
|
+
`phase: ${phase}`,
|
|
663
|
+
`project_ref: ${ledger?.identity?.projectHint || 'unknown'}`,
|
|
664
|
+
`task_ref: ${ledger?.identity?.taskHint || 'unknown'}`,
|
|
665
|
+
`ledger_event_count: ${Array.isArray(ledger?.phaseEvents) ? ledger.phaseEvents.length : 0}`,
|
|
666
|
+
`readiness_claim_allowed: ${ledger?.controls?.readinessClaimAllowed === true ? 'true' : 'false'}`,
|
|
667
|
+
`missing_readiness_gates: ${missing.length > 0 ? missing.join(', ') : 'none'}`,
|
|
668
|
+
`ledger_path: ${paths?.ledgerPath || 'unknown'}`,
|
|
669
|
+
'required_behavior: create/update this ledger for every task and project; use it as the controlling anti-drift artifact before tool use, final claims, and long-running handoff.',
|
|
670
|
+
'claim_rule: do not say done/complete/ready/verified/fixed/shipped unless this ledger has verification evidence and no open blockers.',
|
|
671
|
+
'quality_rule: every phase must preserve intent, scope, evidence, verification, recovery, and post-turn writeback where applicable.',
|
|
672
|
+
'--- /ARIA TASK/PROJECT LEDGER CONTRACT ---',
|
|
673
|
+
].join('\n');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const PHASE_BY_LABEL = new Map(
|
|
677
|
+
LIFECYCLE_PHASES.flatMap((phase) => phase.labels.map((label) => [label.toLowerCase(), phase.id]))
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
export function normalizeCoachPhase(value = '') {
|
|
681
|
+
const key = String(value || '').trim().toLowerCase();
|
|
682
|
+
return PHASE_BY_LABEL.get(key) || key.replace(/[^a-z0-9]+/g, '_') || 'unknown';
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export function coachEventPath() {
|
|
686
|
+
return join(homedir(), '.aria', 'first-class-coach-events.jsonl');
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export function inferCoachPhase(input = {}, env = process.env) {
|
|
690
|
+
return normalizeCoachPhase(
|
|
691
|
+
env.HOOK_EVENT_NAME ||
|
|
692
|
+
input.hook_event_name ||
|
|
693
|
+
input.hookEventName ||
|
|
694
|
+
input.event ||
|
|
695
|
+
input.type ||
|
|
696
|
+
input.phase ||
|
|
697
|
+
''
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
export function inferCoachPlatform(input = {}, env = process.env) {
|
|
702
|
+
const value = env.ARIA_COACH_PLATFORM || env.ARIA_PLATFORM || input.platform || input.sourceRuntime || input.runtime || 'claude';
|
|
703
|
+
const normalized = String(value || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '_');
|
|
704
|
+
return PLATFORM_PHASES[normalized] ? normalized : normalized || 'unknown';
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
export function recordCoachEvent({ platform, phase, source = 'first-class-coach', event = {}, evidence = {} } = {}) {
|
|
708
|
+
const row = {
|
|
709
|
+
schema: `${FIRST_CLASS_COACH_VERSION}.event`,
|
|
710
|
+
at: new Date().toISOString(),
|
|
711
|
+
platform: platform || inferCoachPlatform(event),
|
|
712
|
+
phase: phase || inferCoachPhase(event),
|
|
713
|
+
source,
|
|
714
|
+
evidence,
|
|
715
|
+
};
|
|
716
|
+
const target = coachEventPath();
|
|
717
|
+
mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
|
|
718
|
+
appendFileSync(target, `${JSON.stringify(row)}\n`, { mode: 0o600 });
|
|
719
|
+
return row;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export function formatCoachContext({ platform = 'unknown', phase = 'unknown' } = {}) {
|
|
723
|
+
return [
|
|
724
|
+
'--- ARIA FIRST-CLASS COACH CONTRACT ---',
|
|
725
|
+
`version: ${FIRST_CLASS_COACH_VERSION}`,
|
|
726
|
+
`platform: ${platform}`,
|
|
727
|
+
`phase: ${phase}`,
|
|
728
|
+
`definition: ${FIRST_CLASS_ACCEPTANCE.definition}`,
|
|
729
|
+
`axioms: ${FIRST_CLASS_AXIOMS.join(', ')}`,
|
|
730
|
+
`domains: ${FIRST_CLASS_DOMAINS.join(', ')}`,
|
|
731
|
+
`first_principles: ${FIRST_CLASS_PRINCIPLES.join(', ')}`,
|
|
732
|
+
'required_behavior: coach contract controls the turn; local green checks are insufficient without cross-platform evidence, recovery proof, and warning-clean verification.',
|
|
733
|
+
'readiness_claim_rule: do not claim first-class/ready/complete until the evidence ledger covers every required platform and lifecycle phase.',
|
|
734
|
+
'recovery_rule: when evidence is missing, execute one concrete recovery/verification action before emitting status; if repeated, enter architect mode instead of asking the owner to take over.',
|
|
735
|
+
'--- /ARIA FIRST-CLASS COACH CONTRACT ---',
|
|
736
|
+
].join('\n');
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export function verifyCoachTopology(registrations = []) {
|
|
740
|
+
const seen = new Set(registrations.map((entry) => `${entry.platform}:${entry.phase}`));
|
|
741
|
+
const missing = [];
|
|
742
|
+
for (const [platform, phases] of Object.entries(PLATFORM_PHASES)) {
|
|
743
|
+
for (const phase of phases) {
|
|
744
|
+
if (!seen.has(`${platform}:${phase}`)) missing.push({ platform, phase });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return { ok: missing.length === 0, missing, requiredCount: Object.values(PLATFORM_PHASES).reduce((sum, phases) => sum + phases.length, 0), observedCount: seen.size };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export function evaluateFirstClassClaim({ text = '', evidence = {} } = {}) {
|
|
751
|
+
const claim = /\b(?:first[- ]class|production[- ]ready|ready|complete|completed|done|verified|gold[- ]standard)\b/i.test(String(text || ''));
|
|
752
|
+
if (!claim) return { ok: true, claim: false, missingEvidence: [] };
|
|
753
|
+
const missingEvidence = FIRST_CLASS_ACCEPTANCE.requiredEvidence.filter((key) => evidence[key] !== true);
|
|
754
|
+
return { ok: missingEvidence.length === 0, claim: true, missingEvidence };
|
|
755
|
+
}
|