@aria_asi/cli 0.2.30 → 0.2.32
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/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +115 -20
- 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 +551 -11
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.js +115 -0
- package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +27 -9
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +231 -19
- 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 +76 -3
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
- package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
- package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
- package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
- package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
- package/dist/assets/opencode-plugins/harness-gate/index.js +40 -5
- package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +28 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
- package/dist/runtime/doctrine_trigger_map.json +534 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +460 -0
- package/dist/runtime/manifest.json +1 -1
- package/dist/runtime/metering.mjs +100 -0
- package/dist/runtime/onboarding-engine.mjs +89 -0
- package/dist/runtime/plugin-engine.mjs +196 -0
- package/dist/runtime/sdk/BUNDLED.json +1 -1
- package/dist/runtime/sdk/index.d.ts +12 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1140 -48
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +12 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-agent-handoff.mjs +23 -0
- package/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/hooks/aria-pre-tool-gate.mjs +383 -93
- package/hooks/aria-preprompt-consult.mjs +28 -2
- package/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/hooks/aria-stop-gate.mjs +346 -81
- package/hooks/doctrine_trigger_map.json +55 -0
- package/hooks/lib/canonical-lenses.mjs +6 -5
- package/hooks/lib/gate-loop-state.mjs +50 -0
- package/hooks/lib/hook-message-window.mjs +121 -0
- package/hooks/test-tier-lens-labeling.mjs +26 -58
- package/opencode-plugins/harness-gate/index.js +40 -5
- package/opencode-plugins/harness-stop/index.js +133 -10
- package/package.json +1 -1
- package/runtime-src/auth-middleware.mjs +251 -0
- package/runtime-src/codex-bridge.mjs +644 -0
- package/runtime-src/fleet-engine.mjs +231 -0
- package/runtime-src/harness-daemon.mjs +460 -0
- package/runtime-src/metering.mjs +100 -0
- package/runtime-src/onboarding-engine.mjs +89 -0
- package/runtime-src/plugin-engine.mjs +196 -0
- package/runtime-src/service.mjs +1140 -48
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +126 -20
- package/src/connectors/codex.ts +559 -10
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +117 -0
- package/src/connectors/opencode.ts +28 -9
- package/src/connectors/runtime.ts +241 -21
- package/src/connectors/shell.ts +78 -3
- package/dist/cli-0.2.0.tgz +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const STATE_DIR = join(__dirname, '..', 'state');
|
|
8
|
+
|
|
9
|
+
function ensureDir(p) {
|
|
10
|
+
if (!existsSync(p)) mkdirSync(p, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function workflowsPath(tenantId) {
|
|
14
|
+
return join(STATE_DIR, `workflows-${tenantId}.json`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const WORKFLOW_TEMPLATES = {
|
|
18
|
+
'lead-qualification-pipeline': {
|
|
19
|
+
id: 'lead-qualification-pipeline',
|
|
20
|
+
archetype: 'leadQualifier',
|
|
21
|
+
name: 'Lead Qualification Pipeline',
|
|
22
|
+
description: 'Receive inbound lead, classify, enrich data, score, and route to appropriate agent',
|
|
23
|
+
steps: [
|
|
24
|
+
{ name: 'receive_and_classify', type: 'receive', description: 'Receive lead input and classify by source and intent' },
|
|
25
|
+
{ name: 'enrich_data', type: 'enrich', description: 'Look up additional data points for the lead' },
|
|
26
|
+
{ name: 'score_and_route', type: 'score', description: 'Score the lead and determine routing' },
|
|
27
|
+
{ name: 'approval_gate', type: 'approve', description: 'Confirm routing decision (auto-approve if score above threshold)', autoApprove: { field: 'score', operator: '>=', value: 80 } },
|
|
28
|
+
{ name: 'execute_routing', type: 'execute', description: 'Route lead to assigned agent or pipeline' },
|
|
29
|
+
{ name: 'log_and_notify', type: 'log', description: 'Log the qualification result and notify relevant agents' },
|
|
30
|
+
],
|
|
31
|
+
escalation: { to: 'directorOfSales', condition: 'score >= 90' },
|
|
32
|
+
retryPolicy: { maxAttempts: 3, backoffMs: 5000 },
|
|
33
|
+
},
|
|
34
|
+
'deal-evaluation-pipeline': {
|
|
35
|
+
id: 'deal-evaluation-pipeline',
|
|
36
|
+
archetype: 'dealAnalyzer',
|
|
37
|
+
name: 'Deal Evaluation Pipeline',
|
|
38
|
+
description: 'Collect deal data, compare terms, assess risk, and provide recommendation',
|
|
39
|
+
steps: [
|
|
40
|
+
{ name: 'collect_deal_data', type: 'receive', description: 'Gather all deal parameters and context' },
|
|
41
|
+
{ name: 'compare_terms', type: 'enrich', description: 'Compare against historical deals and market benchmarks' },
|
|
42
|
+
{ name: 'risk_assessment', type: 'score', description: 'Evaluate financial, operational, and strategic risks' },
|
|
43
|
+
{ name: 'recommend', type: 'execute', description: 'Generate go/no-go recommendation with rationale' },
|
|
44
|
+
],
|
|
45
|
+
escalation: { to: 'directorOfFinance', condition: 'risk_level == high' },
|
|
46
|
+
retryPolicy: { maxAttempts: 2, backoffMs: 10000 },
|
|
47
|
+
},
|
|
48
|
+
'outreach-sequence': {
|
|
49
|
+
id: 'outreach-sequence',
|
|
50
|
+
archetype: 'outboundCloser',
|
|
51
|
+
name: 'Outreach Sequence',
|
|
52
|
+
description: 'Target selection, initial contact, follow-up, objection handling, and close',
|
|
53
|
+
steps: [
|
|
54
|
+
{ name: 'target_selection', type: 'receive', description: 'Identify target prospects based on criteria' },
|
|
55
|
+
{ name: 'initial_contact', type: 'execute', description: 'Send initial outreach message' },
|
|
56
|
+
{ name: 'follow_up', type: 'execute', description: 'Send follow-up based on prospect engagement' },
|
|
57
|
+
{ name: 'objection_handle', type: 'score', description: 'Address objections and refine approach' },
|
|
58
|
+
{ name: 'close', type: 'execute', description: 'Attempt to close or schedule next action' },
|
|
59
|
+
],
|
|
60
|
+
escalation: { to: 'directorOfSales', condition: 'stage == stalled > 3 days' },
|
|
61
|
+
retryPolicy: { maxAttempts: 5, backoffMs: 86400000 },
|
|
62
|
+
},
|
|
63
|
+
'content-calendar': {
|
|
64
|
+
id: 'content-calendar',
|
|
65
|
+
archetype: 'contentStrategist',
|
|
66
|
+
name: 'Content Calendar',
|
|
67
|
+
description: 'Plan content topics, draft, review, publish, and measure performance',
|
|
68
|
+
steps: [
|
|
69
|
+
{ name: 'plan_topics', type: 'receive', description: 'Generate content calendar based on strategy and trends' },
|
|
70
|
+
{ name: 'draft_content', type: 'execute', description: 'Create initial content draft' },
|
|
71
|
+
{ name: 'review', type: 'approve', description: 'Review for quality, brand alignment, and compliance' },
|
|
72
|
+
{ name: 'publish', type: 'execute', description: 'Publish approved content to designated channels' },
|
|
73
|
+
{ name: 'measure', type: 'score', description: 'Track performance metrics and feed back into planning' },
|
|
74
|
+
],
|
|
75
|
+
escalation: { to: 'directorOfMarketing', condition: 'engagement < threshold' },
|
|
76
|
+
retryPolicy: { maxAttempts: 2, backoffMs: 0 },
|
|
77
|
+
},
|
|
78
|
+
'deal-lifecycle': {
|
|
79
|
+
id: 'deal-lifecycle',
|
|
80
|
+
archetype: 'transactionCoordinator',
|
|
81
|
+
name: 'Deal Lifecycle',
|
|
82
|
+
description: 'Initiate deal, track milestones, coordinate stakeholders, verify, and close',
|
|
83
|
+
steps: [
|
|
84
|
+
{ name: 'initiate', type: 'receive', description: 'Create deal record and assign stakeholders' },
|
|
85
|
+
{ name: 'track_milestones', type: 'enrich', description: 'Monitor and update milestone progress' },
|
|
86
|
+
{ name: 'coordinate', type: 'execute', description: 'Coordinate between parties and resolve blockers' },
|
|
87
|
+
{ name: 'verify', type: 'score', description: 'Final verification of all requirements and documents' },
|
|
88
|
+
{ name: 'close', type: 'execute', description: 'Execute close and update records' },
|
|
89
|
+
],
|
|
90
|
+
escalation: { to: 'chiefOfStaff', condition: 'blocker unresolved > 48h' },
|
|
91
|
+
retryPolicy: { maxAttempts: 3, backoffMs: 3600000 },
|
|
92
|
+
},
|
|
93
|
+
'reporting-cycle': {
|
|
94
|
+
id: 'reporting-cycle',
|
|
95
|
+
archetype: 'financialAnalyst',
|
|
96
|
+
name: 'Reporting Cycle',
|
|
97
|
+
description: 'Collect financial data, reconcile, analyze, forecast, and produce reports',
|
|
98
|
+
steps: [
|
|
99
|
+
{ name: 'collect_data', type: 'receive', description: 'Gather financial data from all sources' },
|
|
100
|
+
{ name: 'reconcile', type: 'enrich', description: 'Cross-reference and reconcile discrepancies' },
|
|
101
|
+
{ name: 'analyze', type: 'score', description: 'Perform variance analysis and trend detection' },
|
|
102
|
+
{ name: 'forecast', type: 'enrich', description: 'Generate forward-looking projections' },
|
|
103
|
+
{ name: 'produce_report', type: 'execute', description: 'Compile and distribute report' },
|
|
104
|
+
],
|
|
105
|
+
escalation: { to: 'directorOfFinance', condition: 'variance > 10%' },
|
|
106
|
+
retryPolicy: { maxAttempts: 2, backoffMs: 0 },
|
|
107
|
+
},
|
|
108
|
+
'audit-trail': {
|
|
109
|
+
id: 'audit-trail',
|
|
110
|
+
archetype: 'compliance-guard',
|
|
111
|
+
name: 'Audit Trail',
|
|
112
|
+
description: 'Monitor activity, flag anomalies, investigate, remediate, and document',
|
|
113
|
+
steps: [
|
|
114
|
+
{ name: 'monitor', type: 'receive', description: 'Continuously monitor agent outputs and actions' },
|
|
115
|
+
{ name: 'flag', type: 'score', description: 'Flag potential compliance issues or anomalies' },
|
|
116
|
+
{ name: 'investigate', type: 'enrich', description: 'Investigate flagged items for root cause' },
|
|
117
|
+
{ name: 'remediate', type: 'execute', description: 'Apply corrective actions for confirmed violations' },
|
|
118
|
+
{ name: 'document', type: 'log', description: 'Document findings and update compliance records' },
|
|
119
|
+
],
|
|
120
|
+
escalation: { to: 'chiefOfStaff', condition: 'severity == critical' },
|
|
121
|
+
retryPolicy: { maxAttempts: 3, backoffMs: 5000 },
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export function listWorkflowTemplates() {
|
|
126
|
+
return Object.values(WORKFLOW_TEMPLATES).map(t => ({
|
|
127
|
+
id: t.id,
|
|
128
|
+
archetype: t.archetype,
|
|
129
|
+
name: t.name,
|
|
130
|
+
description: t.description,
|
|
131
|
+
stepCount: t.steps.length,
|
|
132
|
+
hasApprovalGate: t.steps.some(s => s.type === 'approve'),
|
|
133
|
+
escalation: t.escalation,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function getWorkflowTemplate(workflowId) {
|
|
138
|
+
return WORKFLOW_TEMPLATES[workflowId] || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function configureWorkflow(tenantId, workflowId, config = {}) {
|
|
142
|
+
const template = WORKFLOW_TEMPLATES[workflowId];
|
|
143
|
+
if (!template) return { ok: false, error: `Unknown workflow: ${workflowId}. Available: ${Object.keys(WORKFLOW_TEMPLATES).join(', ')}` };
|
|
144
|
+
const pp = workflowsPath(tenantId);
|
|
145
|
+
let state = existsSync(pp) ? JSON.parse(readFileSync(pp, 'utf-8')) : { configured: [], instances: [] };
|
|
146
|
+
const existing = state.configured.findIndex(w => w.id === workflowId);
|
|
147
|
+
const entry = {
|
|
148
|
+
id: workflowId,
|
|
149
|
+
templateId: workflowId,
|
|
150
|
+
config: {
|
|
151
|
+
agentId: config.agentId || null,
|
|
152
|
+
trigger: config.trigger || { type: 'manual' },
|
|
153
|
+
autoApproveThreshold: config.autoApproveThreshold ?? template.steps.find(s => s.type === 'approve')?.autoApprove?.value ?? 80,
|
|
154
|
+
customSteps: config.customSteps || null,
|
|
155
|
+
escalationOverride: config.escalationOverride || null,
|
|
156
|
+
...config,
|
|
157
|
+
},
|
|
158
|
+
configuredAt: new Date().toISOString(),
|
|
159
|
+
};
|
|
160
|
+
if (existing >= 0) state.configured[existing] = entry; else state.configured.push(entry);
|
|
161
|
+
ensureDir(dirname(pp));
|
|
162
|
+
writeFileSync(pp, JSON.stringify(state, null, 2));
|
|
163
|
+
return { ok: true, workflow: entry };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function startWorkflow(tenantId, workflowId, initialPayload = {}) {
|
|
167
|
+
const template = WORKFLOW_TEMPLATES[workflowId];
|
|
168
|
+
if (!template) return { ok: false, error: `Unknown workflow: ${workflowId}` };
|
|
169
|
+
const pp = workflowsPath(tenantId);
|
|
170
|
+
let state = existsSync(pp) ? JSON.parse(readFileSync(pp, 'utf-8')) : { configured: [], instances: [] };
|
|
171
|
+
const instance = {
|
|
172
|
+
instanceId: randomUUID(),
|
|
173
|
+
workflowId,
|
|
174
|
+
templateId: workflowId,
|
|
175
|
+
status: 'running',
|
|
176
|
+
currentStep: 0,
|
|
177
|
+
steps: template.steps.map((s, i) => ({
|
|
178
|
+
...s,
|
|
179
|
+
index: i,
|
|
180
|
+
status: i === 0 ? 'active' : 'pending',
|
|
181
|
+
startedAt: i === 0 ? new Date().toISOString() : null,
|
|
182
|
+
completedAt: null,
|
|
183
|
+
attempts: 0,
|
|
184
|
+
result: null,
|
|
185
|
+
})),
|
|
186
|
+
payload: initialPayload,
|
|
187
|
+
history: [{ event: 'started', at: new Date().toISOString(), step: 0 }],
|
|
188
|
+
createdAt: new Date().toISOString(),
|
|
189
|
+
updatedAt: new Date().toISOString(),
|
|
190
|
+
};
|
|
191
|
+
state.instances.push(instance);
|
|
192
|
+
ensureDir(dirname(pp));
|
|
193
|
+
writeFileSync(pp, JSON.stringify(state, null, 2));
|
|
194
|
+
return { ok: true, instance };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function advanceWorkflow(tenantId, instanceId, stepResult = {}) {
|
|
198
|
+
const pp = workflowsPath(tenantId);
|
|
199
|
+
if (!existsSync(pp)) return { ok: false, error: 'No workflows configured' };
|
|
200
|
+
let state = JSON.parse(readFileSync(pp, 'utf-8'));
|
|
201
|
+
const instance = (state.instances || []).find(i => i.instanceId === instanceId);
|
|
202
|
+
if (!instance) return { ok: false, error: `Instance ${instanceId} not found` };
|
|
203
|
+
if (instance.status !== 'running') return { ok: false, error: `Workflow is ${instance.status}` };
|
|
204
|
+
const current = instance.steps[instance.currentStep];
|
|
205
|
+
if (!current) return { ok: false, error: 'No current step' };
|
|
206
|
+
current.status = 'completed';
|
|
207
|
+
current.completedAt = new Date().toISOString();
|
|
208
|
+
current.result = stepResult;
|
|
209
|
+
instance.history.push({ event: 'step_completed', step: instance.currentStep, result: stepResult, at: new Date().toISOString() });
|
|
210
|
+
const template = WORKFLOW_TEMPLATES[instance.workflowId];
|
|
211
|
+
const nextIdx = instance.currentStep + 1;
|
|
212
|
+
if (nextIdx >= instance.steps.length) {
|
|
213
|
+
instance.status = 'completed';
|
|
214
|
+
instance.currentStep = -1;
|
|
215
|
+
instance.history.push({ event: 'workflow_completed', at: new Date().toISOString() });
|
|
216
|
+
} else {
|
|
217
|
+
const nextStep = instance.steps[nextIdx];
|
|
218
|
+
if (nextStep.type === 'approve') {
|
|
219
|
+
const autoApprove = nextStep.autoApprove || template?.steps[nextIdx]?.autoApprove;
|
|
220
|
+
const shouldAutoApprove = autoApprove && stepResult[autoApprove.field] !== undefined &&
|
|
221
|
+
((autoApprove.operator === '>=' && stepResult[autoApprove.field] >= autoApprove.value) ||
|
|
222
|
+
(autoApprove.operator === '>' && stepResult[autoApprove.field] > autoApprove.value) ||
|
|
223
|
+
(autoApprove.operator === '==' && stepResult[autoApprove.field] == autoApprove.value));
|
|
224
|
+
if (shouldAutoApprove) {
|
|
225
|
+
nextStep.status = 'completed';
|
|
226
|
+
nextStep.completedAt = new Date().toISOString();
|
|
227
|
+
nextStep.result = { autoApproved: true };
|
|
228
|
+
instance.history.push({ event: 'auto_approved', step: nextIdx, at: new Date().toISOString() });
|
|
229
|
+
const afterNext = nextIdx + 1;
|
|
230
|
+
if (afterNext < instance.steps.length) {
|
|
231
|
+
instance.currentStep = afterNext;
|
|
232
|
+
instance.steps[afterNext].status = 'active';
|
|
233
|
+
instance.steps[afterNext].startedAt = new Date().toISOString();
|
|
234
|
+
} else {
|
|
235
|
+
instance.status = 'completed';
|
|
236
|
+
instance.currentStep = -1;
|
|
237
|
+
instance.history.push({ event: 'workflow_completed', at: new Date().toISOString() });
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
nextStep.status = 'awaiting_approval';
|
|
241
|
+
nextStep.startedAt = new Date().toISOString();
|
|
242
|
+
instance.currentStep = nextIdx;
|
|
243
|
+
instance.status = 'awaiting_approval';
|
|
244
|
+
instance.history.push({ event: 'awaiting_approval', step: nextIdx, at: new Date().toISOString() });
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
instance.currentStep = nextIdx;
|
|
248
|
+
nextStep.status = 'active';
|
|
249
|
+
nextStep.startedAt = new Date().toISOString();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
instance.updatedAt = new Date().toISOString();
|
|
253
|
+
writeFileSync(pp, JSON.stringify(state, null, 2));
|
|
254
|
+
return { ok: true, instance };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function approveWorkflowStep(tenantId, instanceId, approved = true) {
|
|
258
|
+
const pp = workflowsPath(tenantId);
|
|
259
|
+
if (!existsSync(pp)) return { ok: false, error: 'No workflows configured' };
|
|
260
|
+
let state = JSON.parse(readFileSync(pp, 'utf-8'));
|
|
261
|
+
const instance = (state.instances || []).find(i => i.instanceId === instanceId);
|
|
262
|
+
if (!instance) return { ok: false, error: `Instance ${instanceId} not found` };
|
|
263
|
+
if (instance.status !== 'awaiting_approval') return { ok: false, error: 'Workflow is not awaiting approval' };
|
|
264
|
+
const current = instance.steps[instance.currentStep];
|
|
265
|
+
if (!current) return { ok: false, error: 'No current step' };
|
|
266
|
+
if (approved) {
|
|
267
|
+
current.status = 'completed';
|
|
268
|
+
current.completedAt = new Date().toISOString();
|
|
269
|
+
current.result = { approved: true, approvedAt: new Date().toISOString() };
|
|
270
|
+
instance.history.push({ event: 'approved', step: instance.currentStep, at: new Date().toISOString() });
|
|
271
|
+
instance.status = 'running';
|
|
272
|
+
const nextIdx = instance.currentStep + 1;
|
|
273
|
+
if (nextIdx < instance.steps.length) {
|
|
274
|
+
instance.currentStep = nextIdx;
|
|
275
|
+
instance.steps[nextIdx].status = 'active';
|
|
276
|
+
instance.steps[nextIdx].startedAt = new Date().toISOString();
|
|
277
|
+
} else {
|
|
278
|
+
instance.status = 'completed';
|
|
279
|
+
instance.currentStep = -1;
|
|
280
|
+
instance.history.push({ event: 'workflow_completed', at: new Date().toISOString() });
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
current.status = 'rejected';
|
|
284
|
+
current.completedAt = new Date().toISOString();
|
|
285
|
+
current.result = { approved: false, rejectedAt: new Date().toISOString() };
|
|
286
|
+
instance.status = 'rejected';
|
|
287
|
+
instance.history.push({ event: 'rejected', step: instance.currentStep, at: new Date().toISOString() });
|
|
288
|
+
}
|
|
289
|
+
instance.updatedAt = new Date().toISOString();
|
|
290
|
+
writeFileSync(pp, JSON.stringify(state, null, 2));
|
|
291
|
+
return { ok: true, instance };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function getWorkflowStatus(tenantId) {
|
|
295
|
+
const pp = workflowsPath(tenantId);
|
|
296
|
+
if (!existsSync(pp)) return { tenantId, configured: [], instances: [], summary: null };
|
|
297
|
+
const state = JSON.parse(readFileSync(pp, 'utf-8'));
|
|
298
|
+
const instances = state.instances || [];
|
|
299
|
+
const summary = {
|
|
300
|
+
total: instances.length,
|
|
301
|
+
running: instances.filter(i => i.status === 'running').length,
|
|
302
|
+
completed: instances.filter(i => i.status === 'completed').length,
|
|
303
|
+
awaitingApproval: instances.filter(i => i.status === 'awaiting_approval').length,
|
|
304
|
+
rejected: instances.filter(i => i.status === 'rejected').length,
|
|
305
|
+
};
|
|
306
|
+
return { tenantId, configured: state.configured || [], instances: instances.map(i => ({
|
|
307
|
+
instanceId: i.instanceId,
|
|
308
|
+
workflowId: i.workflowId,
|
|
309
|
+
status: i.status,
|
|
310
|
+
currentStep: i.currentStep >= 0 ? i.steps[i.currentStep]?.name : null,
|
|
311
|
+
progress: `${i.steps.filter(s => s.status === 'completed').length}/${i.steps.length}`,
|
|
312
|
+
createdAt: i.createdAt,
|
|
313
|
+
updatedAt: i.updatedAt,
|
|
314
|
+
})), summary };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function getWorkflowInstance(tenantId, instanceId) {
|
|
318
|
+
const pp = workflowsPath(tenantId);
|
|
319
|
+
if (!existsSync(pp)) return null;
|
|
320
|
+
const state = JSON.parse(readFileSync(pp, 'utf-8'));
|
|
321
|
+
return (state.instances || []).find(i => i.instanceId === instanceId) || null;
|
|
322
|
+
}
|
package/scripts/bundle-sdk.mjs
CHANGED
|
@@ -32,6 +32,7 @@ const RUNTIME_DISCIPLINE_DST = resolve(RUNTIME_DST, 'discipline');
|
|
|
32
32
|
const CONNECTOR_HOOKS_SRC = resolve(PKG_ROOT, 'hooks');
|
|
33
33
|
const OPENCODE_PLUGINS_SRC = resolve(PKG_ROOT, 'opencode-plugins');
|
|
34
34
|
const CLIENT_ONBOARDING_SRC = resolve(PKG_ROOT, 'CLIENT-ONBOARDING.md');
|
|
35
|
+
const DOCTRINE_TRIGGER_MAP_SRC = resolve(CONNECTOR_HOOKS_SRC, 'doctrine_trigger_map.json');
|
|
35
36
|
const REQUIRED_HOOK_FILES = [
|
|
36
37
|
'aria-harness-via-sdk.mjs',
|
|
37
38
|
'aria-pre-tool-gate.mjs',
|
|
@@ -287,6 +288,10 @@ if (existsSync(SDK_SKILLS_SRC)) {
|
|
|
287
288
|
if (existsSync(COGNITIVE_SKILLS_SRC)) {
|
|
288
289
|
copyTree(COGNITIVE_SKILLS_SRC, join(RUNTIME_DISCIPLINE_DST, 'skills', 'aria-cognition'));
|
|
289
290
|
}
|
|
291
|
+
if (existsSync(DOCTRINE_TRIGGER_MAP_SRC)) {
|
|
292
|
+
copyFileSync(DOCTRINE_TRIGGER_MAP_SRC, join(RUNTIME_DISCIPLINE_DST, 'doctrine_trigger_map.json'));
|
|
293
|
+
copyFileSync(DOCTRINE_TRIGGER_MAP_SRC, join(RUNTIME_DST, 'doctrine_trigger_map.json'));
|
|
294
|
+
}
|
|
290
295
|
if (existsSync(CONNECTOR_HOOKS_SRC)) {
|
|
291
296
|
copyTree(CONNECTOR_HOOKS_SRC, join(DIST_ASSETS_DST, 'hooks'));
|
|
292
297
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdirSync, readdirSync, statSync, lstatSync, realpathSync } from 'fs';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import type { AriaConfig } from '../config.js';
|
|
6
|
+
import { syncDoctrineTriggerMap } from './doctrine-trigger-map.js';
|
|
7
|
+
import { buildMustReadGuide } from './must-read.js';
|
|
8
|
+
import { connectShell } from './shell.js';
|
|
6
9
|
|
|
7
10
|
// ── Hooks shipped with this package ──────────────────────────────────
|
|
8
11
|
// The connector installs these into ~/.claude/hooks/ so Claude Code's
|
|
@@ -241,16 +244,27 @@ const HOOKS_BLOCK = {
|
|
|
241
244
|
}],
|
|
242
245
|
};
|
|
243
246
|
|
|
244
|
-
function
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
function discoverManagedHookSymlinkTargets(hooksTargetDir: string, logs: string[]): Map<string, string> {
|
|
248
|
+
const targets = new Map<string, string>();
|
|
249
|
+
for (const name of HOOK_FILES) {
|
|
250
|
+
const hookPath = path.join(hooksTargetDir, name);
|
|
251
|
+
if (!existsSync(hookPath)) continue;
|
|
252
|
+
try {
|
|
253
|
+
const stat = lstatSync(hookPath);
|
|
254
|
+
if (!stat.isSymbolicLink()) continue;
|
|
255
|
+
const targetPath = realpathSync(hookPath);
|
|
256
|
+
targets.set(name, targetPath);
|
|
257
|
+
logs.push(`Detected managed hook symlink: ${name} -> ${targetPath}`);
|
|
258
|
+
} catch {}
|
|
248
259
|
}
|
|
249
|
-
|
|
260
|
+
return targets;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function installHookHelpersToRoot(sourceDir: string, targetRoot: string, logs: string[]): void {
|
|
250
264
|
for (const dirName of HOOK_DIRS) {
|
|
251
265
|
const srcDir = path.join(sourceDir, dirName);
|
|
252
266
|
if (!existsSync(srcDir)) continue;
|
|
253
|
-
const dstDir = path.join(
|
|
267
|
+
const dstDir = path.join(targetRoot, dirName);
|
|
254
268
|
if (!existsSync(dstDir)) {
|
|
255
269
|
mkdirSync(dstDir, { recursive: true, mode: 0o700 });
|
|
256
270
|
}
|
|
@@ -263,42 +277,74 @@ function installHooks(claudeDir: string, logs: string[], opts: { force?: boolean
|
|
|
263
277
|
if (child.endsWith('.mjs') || child.endsWith('.js')) {
|
|
264
278
|
try { chmodSync(dst, 0o755); } catch {}
|
|
265
279
|
}
|
|
266
|
-
logs.push(`Installed hook helper: ${
|
|
280
|
+
logs.push(`Installed hook helper: ${path.relative(targetRoot, dst)}`);
|
|
267
281
|
}
|
|
268
282
|
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function installHookFilesToRoot(
|
|
286
|
+
sourceDir: string,
|
|
287
|
+
targetRoot: string,
|
|
288
|
+
logs: string[],
|
|
289
|
+
opts: { force?: boolean },
|
|
290
|
+
symlinkTargets: Map<string, string>,
|
|
291
|
+
preserveSymlinkStubs: boolean,
|
|
292
|
+
): void {
|
|
269
293
|
for (const name of HOOK_FILES) {
|
|
270
294
|
const src = path.join(sourceDir, name);
|
|
271
295
|
if (!existsSync(src)) {
|
|
272
296
|
logs.push(`⚠ hook source missing: ${src} (package may be incomplete)`);
|
|
273
297
|
continue;
|
|
274
298
|
}
|
|
275
|
-
const dst = path.join(
|
|
276
|
-
|
|
277
|
-
|
|
299
|
+
const dst = path.join(targetRoot, name);
|
|
300
|
+
if (preserveSymlinkStubs && symlinkTargets.has(name)) {
|
|
301
|
+
logs.push(`Preserved managed hook symlink: ${name}`);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
278
304
|
if (existsSync(dst) && !isSamePath(src, dst) && !opts.force) {
|
|
279
305
|
try {
|
|
280
306
|
const existingContent = readFileSync(dst, 'utf-8');
|
|
281
307
|
const newContent = readFileSync(src, 'utf-8');
|
|
282
308
|
if (existingContent === newContent) {
|
|
283
|
-
logs.push(`Hook unchanged: ${
|
|
309
|
+
logs.push(`Hook unchanged: ${path.relative(targetRoot, dst)}`);
|
|
284
310
|
continue;
|
|
285
311
|
}
|
|
286
312
|
} catch {/* fall through to backup+overwrite */}
|
|
287
313
|
}
|
|
288
|
-
// Back up any existing real file before overwriting (never silently
|
|
289
|
-
// clobber a user's customizations).
|
|
290
314
|
if (existsSync(dst) && !isSamePath(src, dst)) {
|
|
291
315
|
const backup = `${dst}.pre-aria-connect.${Date.now()}`;
|
|
292
|
-
try { copyFileSync(dst, backup); logs.push(`Backed up existing ${
|
|
316
|
+
try { copyFileSync(dst, backup); logs.push(`Backed up existing ${path.basename(dst)} → ${path.basename(backup)}`); } catch {}
|
|
293
317
|
}
|
|
294
318
|
copyFileSync(src, dst);
|
|
295
|
-
// Only the .mjs scripts need executable perms; data files (e.g. the
|
|
296
|
-
// doctrine_trigger_map.json shipped alongside hooks) get default 0o644.
|
|
297
319
|
if (name.endsWith('.mjs') || name.endsWith('.js')) {
|
|
298
320
|
try { chmodSync(dst, 0o755); } catch {}
|
|
299
321
|
}
|
|
300
|
-
logs.push(`Installed hook: ${
|
|
322
|
+
logs.push(`Installed hook: ${path.relative(targetRoot, dst)}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function installHooks(claudeDir: string, logs: string[], opts: { force?: boolean } = {}): void {
|
|
327
|
+
const hooksTargetDir = path.join(claudeDir, 'hooks');
|
|
328
|
+
if (!existsSync(hooksTargetDir)) {
|
|
329
|
+
mkdirSync(hooksTargetDir, { recursive: true, mode: 0o700 });
|
|
330
|
+
}
|
|
331
|
+
const sourceDir = packageHooksDir();
|
|
332
|
+
const symlinkTargets = discoverManagedHookSymlinkTargets(hooksTargetDir, logs);
|
|
333
|
+
const targetRoots = new Set<string>([hooksTargetDir]);
|
|
334
|
+
for (const targetPath of symlinkTargets.values()) {
|
|
335
|
+
targetRoots.add(path.dirname(targetPath));
|
|
336
|
+
}
|
|
337
|
+
for (const root of targetRoots) {
|
|
338
|
+
if (!existsSync(root)) {
|
|
339
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
340
|
+
}
|
|
341
|
+
installHookHelpersToRoot(sourceDir, root, logs);
|
|
342
|
+
installHookFilesToRoot(sourceDir, root, logs, opts, symlinkTargets, root === hooksTargetDir);
|
|
301
343
|
}
|
|
344
|
+
|
|
345
|
+
const mustReadPath = path.join(claudeDir, 'ARIA_MUST_READ.md');
|
|
346
|
+
writeFileSync(mustReadPath, buildMustReadGuide('claude'), { mode: 0o644 });
|
|
347
|
+
logs.push(`Installed Claude must-read guide → ${mustReadPath}`);
|
|
302
348
|
}
|
|
303
349
|
|
|
304
350
|
// Install bundled SDK to ~/.claude/aria-sdk/. Hooks dynamic-import from this
|
|
@@ -389,6 +435,8 @@ Before any tool call, Read the harness packet at: $ARIA_HARNESS_PACKET_PATH
|
|
|
389
435
|
(path is also in your environment as ARIA_HARNESS_PACKET_PATH variable)
|
|
390
436
|
Cite it in your first cognition block — reference at least one axiom + one frame + one memory class from the packet.
|
|
391
437
|
Without packet-citation in your first cognition block, your output will be rejected by aria-pre-tool-gate.mjs in your sub-process.
|
|
438
|
+
|
|
439
|
+
If you are a spawned sub-agent, also read the active Aria handoff file when present. Use its cognitionOperatingMethod to execute the task: substrate first, constraints second, action shape third, evidence return last. Your final response to the parent must include substrate used, decision delta, artifact impact, evidence, and unresolved risk.
|
|
392
440
|
[/ARIA_HARNESS_BINDING]
|
|
393
441
|
|
|
394
442
|
[ARIA_DISCOVERY_RECORDING — when you find a defect, doctrine violation, or notable observation during work]
|
|
@@ -397,6 +445,57 @@ Record findings to your session ledger via:
|
|
|
397
445
|
This makes findings visible to the parent session's ledger-merge hook + the auto-fix spawner.
|
|
398
446
|
[/ARIA_DISCOVERY_RECORDING]`;
|
|
399
447
|
|
|
448
|
+
const ARIA_GATE_PHASE_DIRECTIVE = `[ARIA_GATE_SHAPE — DO THIS EVERY TURN]
|
|
449
|
+
PRE-GATE before the first non-trivial tool call or non-trivial answer:
|
|
450
|
+
1. Read the harness packet and any [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] context already injected this turn.
|
|
451
|
+
2. Choose the action shape before acting: tool call, text-only answer, or replan.
|
|
452
|
+
3. Emit a visible <cognition> block using the required labels for this surface. Each lens must cite loaded substrate anchors and change the action, not merely describe values.
|
|
453
|
+
4. Emit <applied_cognition> for every non-trivial answer or tool action so the model proves what cognition changed.
|
|
454
|
+
5. If the action is destructive, deployment-related, or materially mutates state, emit a <verify> block before the tool call.
|
|
455
|
+
6. If the action claims or changes an outcome, emit an <expected> block with a measurable predicate before the tool call or final answer.
|
|
456
|
+
|
|
457
|
+
MID-GATE when a hook blocks or warns:
|
|
458
|
+
1. Do not repeat the same tool call or same draft unchanged.
|
|
459
|
+
2. Name the exact missing requirement in one line.
|
|
460
|
+
3. Re-emit the missing structure only: <cognition>, <verify>, <expected>, and/or <reflection>.
|
|
461
|
+
4. Change the plan before retrying. If recovering from a repeated block, do not put the retrying tool call in the same message as the diagnosis.
|
|
462
|
+
5. If the blocker is stale gate residue or stale ledger state, say that explicitly. Never invent proofOfFix or fake task resolution.
|
|
463
|
+
|
|
464
|
+
LOOP-RECOVERY after the second block of the same shape:
|
|
465
|
+
1. Emit [LOOP_RECOVERY gate=<name> cause=<one line>] in plain text.
|
|
466
|
+
2. Summarize what repeated, what changed, and why the next attempt is materially different.
|
|
467
|
+
3. Produce the corrected preflight structure first. Retry only after the structure is complete.
|
|
468
|
+
|
|
469
|
+
OUTPUT-GATE before final emission:
|
|
470
|
+
1. Keep user-visible cognition readable, but preserve the canonical lens labels. Readability comes from the prose inside each lens, never from replacing the lens names.
|
|
471
|
+
2. If Mizan warns, include <reflection> addressing the exact warning instead of repeating lens text.
|
|
472
|
+
3. Discoveries are atomic with their fixes. Either fix inline now or bind to a real tracked task with proof-shaped evidence.
|
|
473
|
+
[/ARIA_GATE_SHAPE]`;
|
|
474
|
+
|
|
475
|
+
const ARIA_STRUCTURAL_COGNITION_CONTRACT = `[ARIA_STRUCTURAL_COGNITION_CONTRACT — NON-NEGOTIABLE]
|
|
476
|
+
The harness is not a style guide. It is a runtime contract.
|
|
477
|
+
|
|
478
|
+
Aria cognition is a work method, not a footer. Use it to improve the input and output before gates fire:
|
|
479
|
+
1. Perceive the real substrate: user intent, repo/runtime state, loaded harness packet, memories, prior tool results, and uncertainty.
|
|
480
|
+
2. Infer constraints: what must be true, what must not be harmed, what prior doctrine changes, and what evidence is missing.
|
|
481
|
+
3. Shape the next action: choose the smallest useful tool call, edit, question, or answer that satisfies those constraints.
|
|
482
|
+
4. Improve the artifact: make the tool input, code change, review finding, or prose more specific because of the cognition.
|
|
483
|
+
5. Predict the observable result: define what would prove the action or answer succeeded before claiming it.
|
|
484
|
+
6. Report evidence: final output should distinguish observed fact, bounded inference, unresolved risk, and next real action.
|
|
485
|
+
|
|
486
|
+
For every non-trivial response or action:
|
|
487
|
+
1. <cognition> must use the canonical lens labels for the active tier and each lens must contain real substrate anchors: axiom:, frame:, memory:, doctrine:, packet:, or active language:.
|
|
488
|
+
2. The cognition must reference first_principle explicitly and only cite substrate that was loaded this turn.
|
|
489
|
+
3. Each lens must affect the action: if a lens reveals risk, missing evidence, or a better path, change the tool input/output accordingly.
|
|
490
|
+
4. <applied_cognition> must include all fields exactly: decision_delta, dominant_domain, binds_to, expected_predicate, artifact_change.
|
|
491
|
+
5. decision_delta must name what changed because cognition ran. Values like none, unchanged, no change, or n/a are rejected.
|
|
492
|
+
6. binds_to must name the exact answer, tool call, file mutation, deploy, review, or decision being shaped.
|
|
493
|
+
7. expected_predicate must be observable: numeric, boolean, state string, command result, endpoint result, or explicit unverified boundary.
|
|
494
|
+
8. artifact_change must describe the semantic effect on the artifact or output, not restate the task.
|
|
495
|
+
9. If a gate blocks, do not apologize-loop. Read the block reason, re-open the must-read guide, and re-author the missing structure before retrying.
|
|
496
|
+
10. Before long or risky final prose, dry-run the draft with the Claude pre-emit validator when available.
|
|
497
|
+
[/ARIA_STRUCTURAL_COGNITION_CONTRACT]`;
|
|
498
|
+
|
|
400
499
|
function buildAriaSystemBlock(config: AriaConfig): string {
|
|
401
500
|
const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
|
|
402
501
|
const schemaText = Object.entries(config.schemaImages)
|
|
@@ -410,6 +509,8 @@ You are augmented with Aria's cognitive harness. This provides:
|
|
|
410
509
|
- 8-lens cognition: multi-perspective analysis for every decision
|
|
411
510
|
|
|
412
511
|
${ARIA_HARNESS_BINDING_PREFIX}
|
|
512
|
+
${ARIA_GATE_PHASE_DIRECTIVE}
|
|
513
|
+
${ARIA_STRUCTURAL_COGNITION_CONTRACT}
|
|
413
514
|
|
|
414
515
|
[SELF-GATE PROTOCOL]
|
|
415
516
|
Before emitting any claim, verify against these hard constraints:
|
|
@@ -463,8 +564,10 @@ export async function connectClaudeCode(
|
|
|
463
564
|
? settings.systemPromptPrefix
|
|
464
565
|
: '';
|
|
465
566
|
|
|
466
|
-
|
|
467
|
-
|
|
567
|
+
const ariaBlockRx = /<!-- ARIA HARNESS.*?<!-- END ARIA HARNESS -->/s;
|
|
568
|
+
if (ariaBlockRx.test(existing)) {
|
|
569
|
+
settings.systemPromptPrefix = existing.replace(ariaBlockRx, ariaBlock);
|
|
570
|
+
logs.push('Refreshed Aria harness systemPromptPrefix block');
|
|
468
571
|
} else {
|
|
469
572
|
settings.systemPromptPrefix = existing
|
|
470
573
|
? `${ariaBlock}\n\n${existing}`
|
|
@@ -477,6 +580,7 @@ export async function connectClaudeCode(
|
|
|
477
580
|
// just a system-prompt addendum.
|
|
478
581
|
installHooks(claudeDir, logs, { force: opts.force });
|
|
479
582
|
installSdk(claudeDir, logs);
|
|
583
|
+
syncDoctrineTriggerMap(logs);
|
|
480
584
|
wireHooksBlock(settings, logs);
|
|
481
585
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
|
|
482
586
|
|
|
@@ -498,6 +602,8 @@ export async function connectClaudeCode(
|
|
|
498
602
|
}
|
|
499
603
|
}
|
|
500
604
|
|
|
605
|
+
logs.push(...await connectShell('claude', config));
|
|
606
|
+
|
|
501
607
|
return logs;
|
|
502
608
|
}
|
|
503
609
|
|