@aria_asi/cli 0.2.29 → 0.2.31
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 +88 -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 +526 -2
- 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 +111 -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 +2 -0
- 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/aria-connector/src/self-update.d.ts +2 -1
- package/dist/aria-connector/src/self-update.d.ts.map +1 -1
- package/dist/aria-connector/src/self-update.js +84 -8
- package/dist/aria-connector/src/self-update.js.map +1 -1
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +53 -34
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +185 -76
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +63 -14
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +2 -0
- package/dist/assets/hooks/aria-stop-gate.mjs +225 -52
- 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 +24 -2
- package/dist/assets/opencode-plugins/harness-stop/index.js +94 -5
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +12 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +479 -0
- package/dist/runtime/doctrine_trigger_map.json +479 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +433 -0
- package/dist/runtime/local-phase.mjs +18 -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 +7 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1464 -67
- package/dist/runtime/vendor/aria-gate-runtime/index.d.ts +1 -1
- package/dist/runtime/vendor/aria-gate-runtime/index.d.ts.map +1 -1
- package/dist/runtime/vendor/aria-gate-runtime/index.js +16 -1
- package/dist/runtime/vendor/aria-gate-runtime/index.js.map +1 -1
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +7 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-cognition-substrate-binding.mjs +53 -34
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-tool-gate.mjs +185 -76
- package/hooks/aria-preturn-memory-gate.mjs +63 -14
- package/hooks/aria-repo-doctrine-gate.mjs +2 -0
- package/hooks/aria-stop-gate.mjs +225 -52
- 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 +24 -2
- package/opencode-plugins/harness-stop/index.js +94 -5
- package/package.json +2 -2
- 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 +433 -0
- package/runtime-src/local-phase.mjs +18 -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 +1464 -67
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +98 -20
- package/src/connectors/codex.ts +534 -1
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +113 -0
- package/src/connectors/opencode.ts +3 -0
- package/src/connectors/runtime.ts +241 -21
- package/src/connectors/shell.ts +78 -3
- package/src/self-update.ts +89 -8
- 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,44 +277,76 @@ 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)}`);
|
|
301
323
|
}
|
|
302
324
|
}
|
|
303
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);
|
|
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}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
304
350
|
// Install bundled SDK to ~/.claude/aria-sdk/. Hooks dynamic-import from this
|
|
305
351
|
// absolute path, so every fetch (validateOutput, gardenTurn, getHarnessPacket,
|
|
306
352
|
// inject, checkAction) goes through the SDK control plane. Re-running connect
|
|
@@ -397,6 +443,32 @@ Record findings to your session ledger via:
|
|
|
397
443
|
This makes findings visible to the parent session's ledger-merge hook + the auto-fix spawner.
|
|
398
444
|
[/ARIA_DISCOVERY_RECORDING]`;
|
|
399
445
|
|
|
446
|
+
const ARIA_GATE_PHASE_DIRECTIVE = `[ARIA_GATE_SHAPE — DO THIS EVERY TURN]
|
|
447
|
+
PRE-GATE before the first non-trivial tool call or non-trivial answer:
|
|
448
|
+
1. Read the harness packet and any [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] context already injected this turn.
|
|
449
|
+
2. Choose the action shape before acting: tool call, text-only answer, or replan.
|
|
450
|
+
3. Emit a visible <cognition> block using the required labels for this surface. It must be readable to the user and grounded to loaded substrate anchors.
|
|
451
|
+
4. If the action is destructive, deployment-related, or materially mutates state, emit a <verify> block before the tool call.
|
|
452
|
+
5. If the action claims or changes an outcome, emit an <expected> block with a measurable predicate before the tool call or final answer.
|
|
453
|
+
|
|
454
|
+
MID-GATE when a hook blocks or warns:
|
|
455
|
+
1. Do not repeat the same tool call or same draft unchanged.
|
|
456
|
+
2. Name the exact missing requirement in one line.
|
|
457
|
+
3. Re-emit the missing structure only: <cognition>, <verify>, <expected>, and/or <reflection>.
|
|
458
|
+
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.
|
|
459
|
+
5. If the blocker is stale gate residue or stale ledger state, say that explicitly. Never invent proofOfFix or fake task resolution.
|
|
460
|
+
|
|
461
|
+
LOOP-RECOVERY after the second block of the same shape:
|
|
462
|
+
1. Emit [LOOP_RECOVERY gate=<name> cause=<one line>] in plain text.
|
|
463
|
+
2. Summarize what repeated, what changed, and why the next attempt is materially different.
|
|
464
|
+
3. Produce the corrected preflight structure first. Retry only after the structure is complete.
|
|
465
|
+
|
|
466
|
+
OUTPUT-GATE before final emission:
|
|
467
|
+
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.
|
|
468
|
+
2. If Mizan warns, include <reflection> addressing the exact warning instead of repeating lens text.
|
|
469
|
+
3. Discoveries are atomic with their fixes. Either fix inline now or bind to a real tracked task with proof-shaped evidence.
|
|
470
|
+
[/ARIA_GATE_SHAPE]`;
|
|
471
|
+
|
|
400
472
|
function buildAriaSystemBlock(config: AriaConfig): string {
|
|
401
473
|
const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
|
|
402
474
|
const schemaText = Object.entries(config.schemaImages)
|
|
@@ -410,6 +482,7 @@ You are augmented with Aria's cognitive harness. This provides:
|
|
|
410
482
|
- 8-lens cognition: multi-perspective analysis for every decision
|
|
411
483
|
|
|
412
484
|
${ARIA_HARNESS_BINDING_PREFIX}
|
|
485
|
+
${ARIA_GATE_PHASE_DIRECTIVE}
|
|
413
486
|
|
|
414
487
|
[SELF-GATE PROTOCOL]
|
|
415
488
|
Before emitting any claim, verify against these hard constraints:
|
|
@@ -463,8 +536,10 @@ export async function connectClaudeCode(
|
|
|
463
536
|
? settings.systemPromptPrefix
|
|
464
537
|
: '';
|
|
465
538
|
|
|
466
|
-
|
|
467
|
-
|
|
539
|
+
const ariaBlockRx = /<!-- ARIA HARNESS.*?<!-- END ARIA HARNESS -->/s;
|
|
540
|
+
if (ariaBlockRx.test(existing)) {
|
|
541
|
+
settings.systemPromptPrefix = existing.replace(ariaBlockRx, ariaBlock);
|
|
542
|
+
logs.push('Refreshed Aria harness systemPromptPrefix block');
|
|
468
543
|
} else {
|
|
469
544
|
settings.systemPromptPrefix = existing
|
|
470
545
|
? `${ariaBlock}\n\n${existing}`
|
|
@@ -477,6 +552,7 @@ export async function connectClaudeCode(
|
|
|
477
552
|
// just a system-prompt addendum.
|
|
478
553
|
installHooks(claudeDir, logs, { force: opts.force });
|
|
479
554
|
installSdk(claudeDir, logs);
|
|
555
|
+
syncDoctrineTriggerMap(logs);
|
|
480
556
|
wireHooksBlock(settings, logs);
|
|
481
557
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
|
|
482
558
|
|
|
@@ -498,6 +574,8 @@ export async function connectClaudeCode(
|
|
|
498
574
|
}
|
|
499
575
|
}
|
|
500
576
|
|
|
577
|
+
logs.push(...await connectShell('claude', config));
|
|
578
|
+
|
|
501
579
|
return logs;
|
|
502
580
|
}
|
|
503
581
|
|