@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/dist/sdk/BUNDLED.json
CHANGED
package/dist/sdk/index.d.ts
CHANGED
|
@@ -150,6 +150,11 @@ export interface ValidationResult {
|
|
|
150
150
|
severity: 'pass' | 'warn' | 'block';
|
|
151
151
|
rewritten?: string;
|
|
152
152
|
gateTriggers: string[];
|
|
153
|
+
appliedCognition?: {
|
|
154
|
+
ok: boolean;
|
|
155
|
+
contract?: string;
|
|
156
|
+
violations?: string[];
|
|
157
|
+
};
|
|
153
158
|
}
|
|
154
159
|
export interface ActionCheck {
|
|
155
160
|
allowed: boolean;
|
|
@@ -260,7 +265,14 @@ export declare class HTTPHarnessClient {
|
|
|
260
265
|
private cachedPacket;
|
|
261
266
|
private packetLastFetched;
|
|
262
267
|
private readonly packetTtlMs;
|
|
268
|
+
private preferredBaseUrl;
|
|
269
|
+
private readonly baseUrlCandidates;
|
|
263
270
|
constructor(config: HarnessClientConfig);
|
|
271
|
+
private buildBaseUrlCandidates;
|
|
272
|
+
private isRouteEligibleForBaseFailover;
|
|
273
|
+
private candidateUrlsFor;
|
|
274
|
+
private shouldFailoverResponse;
|
|
275
|
+
private promoteSuccessfulBase;
|
|
264
276
|
private extractHarnessText;
|
|
265
277
|
private normalizePlanDocs;
|
|
266
278
|
private collectPlanFilePaths;
|
package/dist/sdk/index.js
CHANGED
|
@@ -3,6 +3,32 @@ import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
|
3
3
|
import { homedir as _homedir } from 'node:os';
|
|
4
4
|
import { resolve, isAbsolute } from 'node:path';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
+
const CLOUD_RUN_SOUL_URL = 'https://arias-soul-6zp3gtk2ca-uc.a.run.app';
|
|
7
|
+
const PUBLIC_HARNESS_URL = 'https://harness.ariasos.com';
|
|
8
|
+
const LOCAL_HARNESS_CANDIDATES = [
|
|
9
|
+
'http://127.0.0.1:30080',
|
|
10
|
+
'http://192.168.4.25:30080',
|
|
11
|
+
];
|
|
12
|
+
const FAILOVER_STATUS_CODES = new Set([404, 405, 501, 502, 503, 504]);
|
|
13
|
+
function isMountedRuntimeBaseUrl(baseUrl) {
|
|
14
|
+
return /:\/\/(?:127\.0\.0\.1|localhost):4319$/i.test(String(baseUrl || '').replace(/\/+$/, ''));
|
|
15
|
+
}
|
|
16
|
+
function normalizeHarnessPacketPayload(payload) {
|
|
17
|
+
let current = payload;
|
|
18
|
+
for (let depth = 0; depth < 3; depth++) {
|
|
19
|
+
if (!current || typeof current !== 'object' || Array.isArray(current))
|
|
20
|
+
break;
|
|
21
|
+
const nested = current.packet;
|
|
22
|
+
if (!nested || typeof nested !== 'object' || Array.isArray(nested))
|
|
23
|
+
break;
|
|
24
|
+
if (typeof current.timestamp !== 'string' && typeof current.version !== 'string')
|
|
25
|
+
break;
|
|
26
|
+
current = nested;
|
|
27
|
+
}
|
|
28
|
+
return (current && typeof current === 'object' && !Array.isArray(current))
|
|
29
|
+
? current
|
|
30
|
+
: {};
|
|
31
|
+
}
|
|
6
32
|
// ── 8-Lens Cognition Block ─────────────────────────────────────────────────
|
|
7
33
|
const EIGHT_LENS_BLOCK = `[ARIA SHELL PROTOCOL — You are a controlled surface]
|
|
8
34
|
|
|
@@ -43,11 +69,17 @@ export class HTTPHarnessClient {
|
|
|
43
69
|
cachedPacket = null;
|
|
44
70
|
packetLastFetched = 0;
|
|
45
71
|
packetTtlMs = 60_000;
|
|
72
|
+
preferredBaseUrl;
|
|
73
|
+
baseUrlCandidates;
|
|
46
74
|
constructor(config) {
|
|
47
75
|
this.baseUrl = config.baseUrl.replace(/\/+$/, '');
|
|
48
76
|
this.apiKey = config.apiKey;
|
|
49
|
-
this.harnessPacketUrl = config.harnessPacketUrl ??
|
|
77
|
+
this.harnessPacketUrl = config.harnessPacketUrl ?? (isMountedRuntimeBaseUrl(this.baseUrl)
|
|
78
|
+
? `${this.baseUrl}/packet`
|
|
79
|
+
: `${this.baseUrl}/api/harness/codex`);
|
|
50
80
|
this.workspaceRoot = config.workspaceRoot ?? process.cwd();
|
|
81
|
+
this.preferredBaseUrl = this.baseUrl;
|
|
82
|
+
this.baseUrlCandidates = this.buildBaseUrlCandidates(config.baseUrl);
|
|
51
83
|
// Layer 3 — sub-agent disk-cache bootstrap (#84).
|
|
52
84
|
// If ARIA_HARNESS_PACKET_PATH is set (injected by aria-agent-handoff.mjs
|
|
53
85
|
// or spawnSubAgent), OR the owner-tier handoff JSON at
|
|
@@ -57,6 +89,65 @@ export class HTTPHarnessClient {
|
|
|
57
89
|
// TTL is 5 min (matches handoff TTL). Stale packets trigger normal fetch.
|
|
58
90
|
this._tryLoadDiskPacket();
|
|
59
91
|
}
|
|
92
|
+
buildBaseUrlCandidates(primaryBaseUrl) {
|
|
93
|
+
const envFallbacks = String(process.env.ARIA_HARNESS_FALLBACK_URLS || '')
|
|
94
|
+
.split(',')
|
|
95
|
+
.map((value) => value.trim())
|
|
96
|
+
.filter(Boolean);
|
|
97
|
+
const candidates = [
|
|
98
|
+
primaryBaseUrl,
|
|
99
|
+
process.env.ARIA_HIVE_RUNTIME_URL || '',
|
|
100
|
+
process.env.ARIA_HARNESS_BASE_URL || '',
|
|
101
|
+
process.env.ARIA_HARNESS_URL || '',
|
|
102
|
+
process.env.ARIA_SOUL_URL || '',
|
|
103
|
+
process.env.ARIAS_SOUL_URL || '',
|
|
104
|
+
process.env.ARIA_SOUL_BASE_URL || '',
|
|
105
|
+
...envFallbacks,
|
|
106
|
+
...LOCAL_HARNESS_CANDIDATES,
|
|
107
|
+
CLOUD_RUN_SOUL_URL,
|
|
108
|
+
PUBLIC_HARNESS_URL,
|
|
109
|
+
]
|
|
110
|
+
.map((value) => String(value || '').trim().replace(/\/+$/, ''))
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
return Array.from(new Set(candidates));
|
|
113
|
+
}
|
|
114
|
+
isRouteEligibleForBaseFailover(url) {
|
|
115
|
+
return (url.pathname.startsWith('/api/harness/') ||
|
|
116
|
+
url.pathname === '/api/garden/fire' ||
|
|
117
|
+
url.pathname.startsWith('/api/cognition/'));
|
|
118
|
+
}
|
|
119
|
+
candidateUrlsFor(targetUrl) {
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = new URL(targetUrl);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [targetUrl];
|
|
126
|
+
}
|
|
127
|
+
if (!this.isRouteEligibleForBaseFailover(parsed)) {
|
|
128
|
+
return [targetUrl];
|
|
129
|
+
}
|
|
130
|
+
const pathAndQuery = `${parsed.pathname}${parsed.search}`;
|
|
131
|
+
const orderedBases = [
|
|
132
|
+
this.preferredBaseUrl,
|
|
133
|
+
...this.baseUrlCandidates,
|
|
134
|
+
]
|
|
135
|
+
.map((value) => value.replace(/\/+$/, ''))
|
|
136
|
+
.filter(Boolean);
|
|
137
|
+
return Array.from(new Set(orderedBases)).map((baseUrl) => `${baseUrl}${pathAndQuery}`);
|
|
138
|
+
}
|
|
139
|
+
shouldFailoverResponse(response, url) {
|
|
140
|
+
if (!this.isRouteEligibleForBaseFailover(url))
|
|
141
|
+
return false;
|
|
142
|
+
return FAILOVER_STATUS_CODES.has(response.status);
|
|
143
|
+
}
|
|
144
|
+
promoteSuccessfulBase(targetUrl) {
|
|
145
|
+
try {
|
|
146
|
+
const parsed = new URL(targetUrl);
|
|
147
|
+
this.preferredBaseUrl = parsed.origin;
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
}
|
|
60
151
|
extractHarnessText(packet) {
|
|
61
152
|
const raw = packet?.harness;
|
|
62
153
|
return typeof raw === 'string' ? raw.trim() : '';
|
|
@@ -230,7 +321,7 @@ export class HTTPHarnessClient {
|
|
|
230
321
|
const raw = JSON.parse(readFileSync(packetPath, 'utf8'));
|
|
231
322
|
// Packet on disk may be the raw API response (has .packet wrapper) or
|
|
232
323
|
// a bare packet object. Normalise to HarnessPacket shape.
|
|
233
|
-
const packetData = (raw.packet ?? raw);
|
|
324
|
+
const packetData = normalizeHarnessPacketPayload(raw.packet ?? raw);
|
|
234
325
|
this.cachedPacket = {
|
|
235
326
|
packet: packetData,
|
|
236
327
|
timestamp: new Date().toISOString(),
|
|
@@ -297,7 +388,7 @@ export class HTTPHarnessClient {
|
|
|
297
388
|
}
|
|
298
389
|
const body = (await res.json());
|
|
299
390
|
this.cachedPacket = {
|
|
300
|
-
packet: (body.packet ?? body),
|
|
391
|
+
packet: normalizeHarnessPacketPayload(body.packet ?? body),
|
|
301
392
|
timestamp: new Date().toISOString(),
|
|
302
393
|
version: '1.0.0',
|
|
303
394
|
};
|
|
@@ -312,7 +403,7 @@ export class HTTPHarnessClient {
|
|
|
312
403
|
if (envPacket) {
|
|
313
404
|
try {
|
|
314
405
|
this.cachedPacket = {
|
|
315
|
-
packet: JSON.parse(envPacket),
|
|
406
|
+
packet: normalizeHarnessPacketPayload(JSON.parse(envPacket)),
|
|
316
407
|
timestamp: new Date().toISOString(),
|
|
317
408
|
version: '1.0.0',
|
|
318
409
|
};
|
|
@@ -768,7 +859,7 @@ export class HTTPHarnessClient {
|
|
|
768
859
|
resolvedPacketPath = packetPath;
|
|
769
860
|
// Also pre-load into this SDK instance's cache so subsequent
|
|
770
861
|
// getHarnessPacket() calls are instant within this process.
|
|
771
|
-
const packetPayload = (packetData.packet ?? packetData);
|
|
862
|
+
const packetPayload = normalizeHarnessPacketPayload(packetData.packet ?? packetData);
|
|
772
863
|
this.cachedPacket = {
|
|
773
864
|
packet: packetPayload,
|
|
774
865
|
timestamp: new Date().toISOString(),
|
|
@@ -1428,19 +1519,34 @@ export class HTTPHarnessClient {
|
|
|
1428
1519
|
// codes — caller decides what to do with non-2xx). No circuit breaker:
|
|
1429
1520
|
// we always try. Backoff: 250ms, 500ms, 1000ms (3 attempts total).
|
|
1430
1521
|
async fetchWithRetry(url, init, attempts = 3) {
|
|
1522
|
+
const candidateUrls = this.candidateUrlsFor(url);
|
|
1431
1523
|
let lastErr;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1524
|
+
let lastResponse = null;
|
|
1525
|
+
for (const candidateUrl of candidateUrls) {
|
|
1526
|
+
for (let i = 0; i < attempts; i++) {
|
|
1527
|
+
try {
|
|
1528
|
+
const response = await fetch(candidateUrl, init);
|
|
1529
|
+
lastResponse = response;
|
|
1530
|
+
const parsed = new URL(candidateUrl);
|
|
1531
|
+
if (this.shouldFailoverResponse(response, parsed) && candidateUrl !== candidateUrls[candidateUrls.length - 1]) {
|
|
1532
|
+
break;
|
|
1533
|
+
}
|
|
1534
|
+
if (response.ok) {
|
|
1535
|
+
this.promoteSuccessfulBase(candidateUrl);
|
|
1536
|
+
}
|
|
1537
|
+
return response;
|
|
1538
|
+
}
|
|
1539
|
+
catch (err) {
|
|
1540
|
+
lastErr = err;
|
|
1541
|
+
if (i < attempts - 1) {
|
|
1542
|
+
const delay = 250 * Math.pow(2, i);
|
|
1543
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1544
|
+
}
|
|
1441
1545
|
}
|
|
1442
1546
|
}
|
|
1443
1547
|
}
|
|
1548
|
+
if (lastResponse)
|
|
1549
|
+
return lastResponse;
|
|
1444
1550
|
throw lastErr instanceof Error
|
|
1445
1551
|
? lastErr
|
|
1446
1552
|
: new Error(`fetch failed after ${attempts} attempts: ${String(lastErr)}`);
|