avorelo 0.1.0
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/LICENSE +21 -0
- package/README.md +56 -0
- package/bin/avorelo +9 -0
- package/package.json +135 -0
- package/scripts/README.md +40 -0
- package/scripts/cco-dashboard.js +252 -0
- package/scripts/cco-status.js +430 -0
- package/scripts/lib/activation/account-state.js +37 -0
- package/scripts/lib/activation/activation-runner.js +546 -0
- package/scripts/lib/activation/activation-self-healing.js +480 -0
- package/scripts/lib/activation/activation-state.js +83 -0
- package/scripts/lib/activation/activation-summary.js +191 -0
- package/scripts/lib/activation/adapters/claude-code.js +77 -0
- package/scripts/lib/activation/adapters/codex-cli.js +52 -0
- package/scripts/lib/activation/adapters/cursor.js +37 -0
- package/scripts/lib/activation/adapters/github-agent.js +39 -0
- package/scripts/lib/activation/adapters/terminal.js +42 -0
- package/scripts/lib/activation/adapters/vscode.js +39 -0
- package/scripts/lib/activation/adapters/windsurf.js +37 -0
- package/scripts/lib/activation/ai-surface-detector.js +151 -0
- package/scripts/lib/activation/connect-account.js +145 -0
- package/scripts/lib/activation/detect-environment.js +75 -0
- package/scripts/lib/activation/detect-hosts.js +62 -0
- package/scripts/lib/activation/format-activation-output.js +109 -0
- package/scripts/lib/activation/next-action.js +43 -0
- package/scripts/lib/activation/repair-engine.js +219 -0
- package/scripts/lib/activation-distribution-readiness.js +507 -0
- package/scripts/lib/adapter-conformance.js +176 -0
- package/scripts/lib/adapter-readiness.js +417 -0
- package/scripts/lib/adapter-safety-boundaries.js +335 -0
- package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
- package/scripts/lib/agent-access-governance.js +455 -0
- package/scripts/lib/agent-enforcement.js +765 -0
- package/scripts/lib/agent-policy-profile.js +210 -0
- package/scripts/lib/agent-security/action-evaluator.js +507 -0
- package/scripts/lib/agent-security/adapter-registry.js +98 -0
- package/scripts/lib/agent-security/auto-policy.js +139 -0
- package/scripts/lib/agent-security/bounded-scan.js +93 -0
- package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
- package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
- package/scripts/lib/agent-security/file-write-adapter.js +183 -0
- package/scripts/lib/agent-security/file-write-rules.js +178 -0
- package/scripts/lib/agent-security/index.js +3342 -0
- package/scripts/lib/agent-security/instruction-risk.js +181 -0
- package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
- package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
- package/scripts/lib/agent-security/package-action-adapter.js +175 -0
- package/scripts/lib/agent-security/package-action-rules.js +233 -0
- package/scripts/lib/agent-security/performance.js +148 -0
- package/scripts/lib/agent-security/permission-minimizer.js +403 -0
- package/scripts/lib/agent-security/scan-cache.js +74 -0
- package/scripts/lib/agent-security/source-trust.js +146 -0
- package/scripts/lib/ai-install-prompt.js +288 -0
- package/scripts/lib/ai-workspace-hygiene.js +1499 -0
- package/scripts/lib/alpha-activation.js +520 -0
- package/scripts/lib/alpha-feedback.js +263 -0
- package/scripts/lib/alpha-readiness-gate.js +332 -0
- package/scripts/lib/anti-gaming.js +169 -0
- package/scripts/lib/artifact-health.js +431 -0
- package/scripts/lib/attribution.js +180 -0
- package/scripts/lib/audit.js +289 -0
- package/scripts/lib/avorelo-skill-registry.js +810 -0
- package/scripts/lib/batch-jobs.js +71 -0
- package/scripts/lib/brain-pack.js +578 -0
- package/scripts/lib/brand-boundary.js +424 -0
- package/scripts/lib/brand.js +74 -0
- package/scripts/lib/browser-capability.js +1048 -0
- package/scripts/lib/browser-proof-preflight.js +321 -0
- package/scripts/lib/cache-readiness.js +187 -0
- package/scripts/lib/canonical-reentry.js +162 -0
- package/scripts/lib/capability-packs.js +314 -0
- package/scripts/lib/capability-recommender.js +512 -0
- package/scripts/lib/capability-registry.js +1059 -0
- package/scripts/lib/carry-forward-surfacing.js +194 -0
- package/scripts/lib/ccusage-adapter.js +188 -0
- package/scripts/lib/company-loop.js +1149 -0
- package/scripts/lib/config.js +637 -0
- package/scripts/lib/context-acquisition-plan.js +287 -0
- package/scripts/lib/context-budget-guard.js +170 -0
- package/scripts/lib/context-budget-scanner.js +257 -0
- package/scripts/lib/context-optimizer.js +715 -0
- package/scripts/lib/context-reduction-plan.js +178 -0
- package/scripts/lib/context-safety.js +88 -0
- package/scripts/lib/context-savings-engine.js +158 -0
- package/scripts/lib/cost-evidence.js +254 -0
- package/scripts/lib/cross-host-install-plan.js +308 -0
- package/scripts/lib/cross-host-install-readiness.js +237 -0
- package/scripts/lib/cross-host-value-flow.js +268 -0
- package/scripts/lib/dashboard.js +900 -0
- package/scripts/lib/design-partner-feedback.js +346 -0
- package/scripts/lib/entitlements.js +100 -0
- package/scripts/lib/execution-packet.js +559 -0
- package/scripts/lib/experimentation-events.js +547 -0
- package/scripts/lib/external-capability-compliance.js +107 -0
- package/scripts/lib/external-user-simulation.js +166 -0
- package/scripts/lib/failure-recovery-readiness.js +81 -0
- package/scripts/lib/failure-recovery.js +419 -0
- package/scripts/lib/feedback-intelligence.js +537 -0
- package/scripts/lib/feedback-signals.js +205 -0
- package/scripts/lib/file-integrity.js +68 -0
- package/scripts/lib/fsx.js +127 -0
- package/scripts/lib/full-readiness-gate.js +451 -0
- package/scripts/lib/guidance-builder.js +174 -0
- package/scripts/lib/hook-apply.js +1019 -0
- package/scripts/lib/hook-baseline.js +310 -0
- package/scripts/lib/hook-config-preview.js +275 -0
- package/scripts/lib/hook-contracts.js +290 -0
- package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
- package/scripts/lib/host-capability-matrix.js +351 -0
- package/scripts/lib/host-support-context.js +254 -0
- package/scripts/lib/http-hook-action.js +538 -0
- package/scripts/lib/install-ai-readiness.js +84 -0
- package/scripts/lib/install-intake-risk.js +1037 -0
- package/scripts/lib/install-journey-intelligence.js +329 -0
- package/scripts/lib/intervention-guidance.js +57 -0
- package/scripts/lib/known-limitations.js +115 -0
- package/scripts/lib/l8-path-truth.js +146 -0
- package/scripts/lib/launch-hardening-gate.js +436 -0
- package/scripts/lib/launch-readiness.js +628 -0
- package/scripts/lib/learning-memory.js +686 -0
- package/scripts/lib/lifecycle-hooks.js +802 -0
- package/scripts/lib/local-package-smoke.js +423 -0
- package/scripts/lib/local-pricing.js +299 -0
- package/scripts/lib/mcp-enforcement.js +311 -0
- package/scripts/lib/mcp-least-privilege-policy.js +303 -0
- package/scripts/lib/mcp-tool-inventory.js +388 -0
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +335 -0
- package/scripts/lib/metrics.js +699 -0
- package/scripts/lib/micro-proof.js +133 -0
- package/scripts/lib/next-run-context.js +436 -0
- package/scripts/lib/operating-value.js +1648 -0
- package/scripts/lib/optimization-v3.js +122 -0
- package/scripts/lib/orchestration/adapters/_shared.js +49 -0
- package/scripts/lib/orchestration/adapters/aider.js +18 -0
- package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
- package/scripts/lib/orchestration/adapters/codex.js +35 -0
- package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
- package/scripts/lib/orchestration/adapters/git.js +25 -0
- package/scripts/lib/orchestration/adapters/index.js +31 -0
- package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
- package/scripts/lib/orchestration/adapters/ollama.js +18 -0
- package/scripts/lib/orchestration/adapters/opencode.js +18 -0
- package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
- package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
- package/scripts/lib/orchestration/cli.js +438 -0
- package/scripts/lib/orchestration/execution-manager.js +279 -0
- package/scripts/lib/orchestration/handoff.js +314 -0
- package/scripts/lib/orchestration/index.js +456 -0
- package/scripts/lib/orchestration/inventory.js +47 -0
- package/scripts/lib/orchestration/model-discovery.js +498 -0
- package/scripts/lib/orchestration/model-profiler.js +170 -0
- package/scripts/lib/orchestration/model-profiles.js +252 -0
- package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
- package/scripts/lib/orchestration/proof-writer.js +349 -0
- package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
- package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
- package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
- package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
- package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
- package/scripts/lib/orchestration/risk-classifier.js +130 -0
- package/scripts/lib/orchestration/routing-policy.js +486 -0
- package/scripts/lib/orchestration/settings.js +112 -0
- package/scripts/lib/orchestration/state.js +165 -0
- package/scripts/lib/orchestration/verification-manager.js +138 -0
- package/scripts/lib/output-profiles.js +146 -0
- package/scripts/lib/package-content-audit.js +368 -0
- package/scripts/lib/package-runtime.js +278 -0
- package/scripts/lib/plan-surface.js +53 -0
- package/scripts/lib/plans.js +2318 -0
- package/scripts/lib/policy-provider.js +27 -0
- package/scripts/lib/prelaunch-activation-readiness.js +409 -0
- package/scripts/lib/prelaunch-evidence-store.js +816 -0
- package/scripts/lib/prelaunch-intelligence.js +869 -0
- package/scripts/lib/pricing-experiment.js +118 -0
- package/scripts/lib/pro-moment-events.js +77 -0
- package/scripts/lib/pro-moment-state.js +227 -0
- package/scripts/lib/pro-moments.js +1216 -0
- package/scripts/lib/product-learning-events.js +629 -0
- package/scripts/lib/project-profile.js +555 -0
- package/scripts/lib/prompt-compiler.js +280 -0
- package/scripts/lib/prompt-lint.js +32 -0
- package/scripts/lib/prompt-suggestions.js +52 -0
- package/scripts/lib/proof-canonical.js +398 -0
- package/scripts/lib/proof-drilldown.js +383 -0
- package/scripts/lib/proof-events.js +342 -0
- package/scripts/lib/proof-history.js +243 -0
- package/scripts/lib/proof-metrics.js +296 -0
- package/scripts/lib/proof-outcome-evidence.js +134 -0
- package/scripts/lib/proof-receipt.js +335 -0
- package/scripts/lib/proof-record.js +461 -0
- package/scripts/lib/public-activation-distribution-gate.js +258 -0
- package/scripts/lib/public-cli.js +3891 -0
- package/scripts/lib/public-distribution-truth.js +211 -0
- package/scripts/lib/public-install-claim-checker.js +294 -0
- package/scripts/lib/publish-provenance-readiness.js +283 -0
- package/scripts/lib/readiness-delta.js +218 -0
- package/scripts/lib/readiness-evidence-closure.js +196 -0
- package/scripts/lib/reentry-memory-capture.js +241 -0
- package/scripts/lib/reentry-memory-retrieval.js +302 -0
- package/scripts/lib/reentry-memory-status.js +146 -0
- package/scripts/lib/reentry-memory-store.js +178 -0
- package/scripts/lib/reentry-state.js +66 -0
- package/scripts/lib/release-candidate-bundle.js +166 -0
- package/scripts/lib/remediation.js +81 -0
- package/scripts/lib/repo-map.js +391 -0
- package/scripts/lib/run-improvements-lifecycle.js +330 -0
- package/scripts/lib/run-improvements.js +789 -0
- package/scripts/lib/runtime-decision-policy.js +387 -0
- package/scripts/lib/safe-path-engine.js +705 -0
- package/scripts/lib/safe-run-controller.js +887 -0
- package/scripts/lib/score.js +262 -0
- package/scripts/lib/seamless-enforcement.js +329 -0
- package/scripts/lib/seamless-outcome.js +689 -0
- package/scripts/lib/seamless-reality-gate.js +5043 -0
- package/scripts/lib/security-risk-classifier.js +511 -0
- package/scripts/lib/security-scan.js +384 -0
- package/scripts/lib/session-context-optimizer.js +1211 -0
- package/scripts/lib/session-timing.js +315 -0
- package/scripts/lib/skill-hygiene.js +805 -0
- package/scripts/lib/skill-packs.js +161 -0
- package/scripts/lib/skills-operating-layer.js +580 -0
- package/scripts/lib/smart-work-routing.js +768 -0
- package/scripts/lib/source-catalog.js +700 -0
- package/scripts/lib/status-value-summary.js +32 -0
- package/scripts/lib/support-bundle.js +578 -0
- package/scripts/lib/task-continuation.js +440 -0
- package/scripts/lib/test-helpers.js +15 -0
- package/scripts/lib/tier.js +38 -0
- package/scripts/lib/token-context-quality-gate.js +370 -0
- package/scripts/lib/token-cost-capture.js +187 -0
- package/scripts/lib/token-cost-intelligence.js +358 -0
- package/scripts/lib/token-efficiency-evidence.js +213 -0
- package/scripts/lib/token-evidence.js +699 -0
- package/scripts/lib/tokenish.js +17 -0
- package/scripts/lib/tool-output-sandbox.js +304 -0
- package/scripts/lib/trust-audit.js +136 -0
- package/scripts/lib/unified-events.js +396 -0
- package/scripts/lib/upgrade-interruption-recovery.js +407 -0
- package/scripts/lib/usage-ledger.js +201 -0
- package/scripts/lib/value-ledger.js +130 -0
- package/scripts/lib/value-proof-calibration.js +531 -0
- package/scripts/lib/visual-qa.js +231 -0
- package/scripts/lib/voice-alpha.js +29 -0
- package/scripts/lib/work-aware-orchestration.js +976 -0
- package/scripts/lib/work-control-receipts.js +577 -0
- package/scripts/lib/work-ledger.js +1123 -0
- package/scripts/lib/work-panel-preview.js +352 -0
- package/scripts/lib/workflow-discipline.js +280 -0
- package/scripts/lib/workflow-signals.js +419 -0
- package/scripts/lib/workspace-map.js +281 -0
- package/scripts/lib/workspace-registry.js +1367 -0
- package/scripts/lib/workspace-resolver.js +480 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const crypto = require("node:crypto");
|
|
6
|
+
|
|
7
|
+
const CONTRACT = "avorelo.smartWorkRouting.v1";
|
|
8
|
+
const SCHEMA_VERSION = 1;
|
|
9
|
+
|
|
10
|
+
const ROUTE_DIR_REL = ".claude/cco/orchestration/smart-work-routing";
|
|
11
|
+
const LATEST_ROUTE_REL = `${ROUTE_DIR_REL}/latest-route.json`;
|
|
12
|
+
const HANDOFF_DIR_REL = ".claude/cco/orchestration/worker-handoff";
|
|
13
|
+
const LATEST_HANDOFF_REL = `${HANDOFF_DIR_REL}/latest-handoff.json`;
|
|
14
|
+
|
|
15
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const PATH_TYPES = Object.freeze([
|
|
18
|
+
"local_deterministic",
|
|
19
|
+
"local_or_cheap",
|
|
20
|
+
"strong_worker",
|
|
21
|
+
"handoff",
|
|
22
|
+
"a2a_delegate",
|
|
23
|
+
"acp_session",
|
|
24
|
+
"human_approval",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const DECISION_TYPES = Object.freeze([
|
|
28
|
+
"auto_execute",
|
|
29
|
+
"auto_prepare",
|
|
30
|
+
"delegate",
|
|
31
|
+
"approval_required",
|
|
32
|
+
"blocked",
|
|
33
|
+
"debug_only",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const MODES = Object.freeze(["automatic", "assisted", "manual_debug"]);
|
|
37
|
+
|
|
38
|
+
const REASON_CODES = Object.freeze({
|
|
39
|
+
SAFE_LOCAL_PATH_AVAILABLE: "SAFE_LOCAL_PATH_AVAILABLE",
|
|
40
|
+
CHEAP_WORKER_CAPABLE: "CHEAP_WORKER_CAPABLE",
|
|
41
|
+
STRONG_WORKER_REQUIRED_FOR_CRITICAL_STAGE: "STRONG_WORKER_REQUIRED_FOR_CRITICAL_STAGE",
|
|
42
|
+
PROOF_REQUIRED_FOR_CODE_CHANGE: "PROOF_REQUIRED_FOR_CODE_CHANGE",
|
|
43
|
+
SAME_SURFACE_PREFERRED: "SAME_SURFACE_PREFERRED",
|
|
44
|
+
CROSS_VENDOR_ROUTING_BLOCKED_BY_DEFAULT: "CROSS_VENDOR_ROUTING_BLOCKED_BY_DEFAULT",
|
|
45
|
+
ACP_NOT_NEEDED_CLI_PATH_SUFFICIENT: "ACP_NOT_NEEDED_CLI_PATH_SUFFICIENT",
|
|
46
|
+
ACP_COMPATIBLE_EDITOR_DETECTED: "ACP_COMPATIBLE_EDITOR_DETECTED",
|
|
47
|
+
A2A_NOT_NEEDED_LOCAL_OR_HANDOFF_SUFFICIENT: "A2A_NOT_NEEDED_LOCAL_OR_HANDOFF_SUFFICIENT",
|
|
48
|
+
A2A_REMOTE_AGENT_DETECTED: "A2A_REMOTE_AGENT_DETECTED",
|
|
49
|
+
A2A_OPAQUE_AGENT_DELEGATION_REQUIRED: "A2A_OPAQUE_AGENT_DELEGATION_REQUIRED",
|
|
50
|
+
EXTERNAL_AGENT_REQUIRES_APPROVAL: "EXTERNAL_AGENT_REQUIRES_APPROVAL",
|
|
51
|
+
REMOTE_AGENT_NO_IDENTITY_PROOF: "REMOTE_AGENT_NO_IDENTITY_PROOF",
|
|
52
|
+
REMOTE_AGENT_NO_CAPABILITY_CARD: "REMOTE_AGENT_NO_CAPABILITY_CARD",
|
|
53
|
+
REMOTE_AGENT_NO_COMPLETION_RECEIPT: "REMOTE_AGENT_NO_COMPLETION_RECEIPT",
|
|
54
|
+
SECRETS_EXCLUDED_FROM_CONTEXT: "SECRETS_EXCLUDED_FROM_CONTEXT",
|
|
55
|
+
DEPLOY_REQUIRES_APPROVAL: "DEPLOY_REQUIRES_APPROVAL",
|
|
56
|
+
DESTRUCTIVE_ACTION_BLOCKED: "DESTRUCTIVE_ACTION_BLOCKED",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ── Keyword matchers ──────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
function includesAny(text, patterns) {
|
|
62
|
+
const t = String(text || "").toLowerCase();
|
|
63
|
+
return patterns.some((p) => t.includes(p.toLowerCase()));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const REMOTE_AGENT_KEYWORDS = [
|
|
67
|
+
"remote agent", "external agent", "opaque agent", "delegate to", "delegate implementation",
|
|
68
|
+
"third-party agent", "openai agent", "openrouter", "a2a", "agent-to-agent",
|
|
69
|
+
];
|
|
70
|
+
const ACP_KEYWORDS = [
|
|
71
|
+
"acp", "acp-compatible", "editor agent", "editor integration", "session bridge",
|
|
72
|
+
"cursor", "vscode agent", "jetbrains agent",
|
|
73
|
+
];
|
|
74
|
+
const DESTRUCTIVE_KEYWORDS = [
|
|
75
|
+
"delete .git", "rm -rf", "drop table", "truncate", "destroy", "wipe repo",
|
|
76
|
+
"clean repo", "reset hard", "force push", "obliterate",
|
|
77
|
+
];
|
|
78
|
+
const DEPLOY_KEYWORDS = [
|
|
79
|
+
"deploy to production", "deploy to prod", "release to production", "ship to prod",
|
|
80
|
+
"push to production", "production deploy", "release build", "publish release",
|
|
81
|
+
];
|
|
82
|
+
const SECURITY_KEYWORDS = [
|
|
83
|
+
"auth", "token", "secret", "password", "credential", "oauth", "jwt",
|
|
84
|
+
"api key", "encryption", "tls", "ssl", "certificate",
|
|
85
|
+
];
|
|
86
|
+
const DOCS_KEYWORDS = [
|
|
87
|
+
"update docs", "update documentation", "write docs", "fix readme", "doc change",
|
|
88
|
+
"add comment", "summarize", "explain", "describe", "changelog",
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
function createRoutingId() {
|
|
94
|
+
return `route-${crypto.randomBytes(6).toString("hex")}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function createHandoffId() {
|
|
98
|
+
return `handoff-${crypto.randomBytes(6).toString("hex")}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function safeReadJson(filePath) {
|
|
102
|
+
try {
|
|
103
|
+
if (!fs.existsSync(filePath)) return null;
|
|
104
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getMode(cwd) {
|
|
111
|
+
try {
|
|
112
|
+
const { getSeamlessMode } = require("./seamless-outcome");
|
|
113
|
+
return getSeamlessMode({}) || "automatic";
|
|
114
|
+
} catch {
|
|
115
|
+
return "automatic";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Task classification ───────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
function classifyTaskForRouting(taskText, orchestrationPlan) {
|
|
122
|
+
// Reuse orchestration classification if available
|
|
123
|
+
if (orchestrationPlan && orchestrationPlan.task) {
|
|
124
|
+
return orchestrationPlan.task;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const { classifyWorkTask } = require("./work-aware-orchestration");
|
|
128
|
+
return classifyWorkTask(taskText);
|
|
129
|
+
} catch {
|
|
130
|
+
return {
|
|
131
|
+
summary: String(taskText || "").slice(0, 120),
|
|
132
|
+
taskType: "unknown",
|
|
133
|
+
riskLevel: "medium",
|
|
134
|
+
complexity: "medium",
|
|
135
|
+
contextPressure: "medium",
|
|
136
|
+
redacted: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Decision logic (Phase 4) ──────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
function decideExecutionPath(cwd, orchestrationPlan, executionPacket, options = {}) {
|
|
144
|
+
// Always classify from current taskText — stored plan task may be for a different task
|
|
145
|
+
const task = classifyTaskForRouting(options.taskText || "", null);
|
|
146
|
+
|
|
147
|
+
const { taskType, riskLevel } = task;
|
|
148
|
+
const taskText = options.taskText || task.summary || "";
|
|
149
|
+
|
|
150
|
+
const reasonCodes = [];
|
|
151
|
+
let decision = "auto_prepare";
|
|
152
|
+
let pathType = "local_or_cheap";
|
|
153
|
+
let selectedWorker = "local_or_cheap";
|
|
154
|
+
let selectedModelProfile = "local_summary";
|
|
155
|
+
let selectedTool = "avorelo_cli";
|
|
156
|
+
let sameSurfacePreferred = true;
|
|
157
|
+
let crossVendorAllowed = false;
|
|
158
|
+
let proofRequired = false;
|
|
159
|
+
let interrupt = false;
|
|
160
|
+
let interruptReason = null;
|
|
161
|
+
let safeNextAction = null;
|
|
162
|
+
|
|
163
|
+
// Remote/opaque agent delegation
|
|
164
|
+
if (includesAny(taskText, REMOTE_AGENT_KEYWORDS)) {
|
|
165
|
+
decision = "approval_required";
|
|
166
|
+
pathType = "a2a_delegate";
|
|
167
|
+
selectedWorker = "human_approval";
|
|
168
|
+
reasonCodes.push(REASON_CODES.A2A_REMOTE_AGENT_DETECTED);
|
|
169
|
+
reasonCodes.push(REASON_CODES.EXTERNAL_AGENT_REQUIRES_APPROVAL);
|
|
170
|
+
reasonCodes.push(REASON_CODES.REMOTE_AGENT_NO_IDENTITY_PROOF);
|
|
171
|
+
reasonCodes.push(REASON_CODES.REMOTE_AGENT_NO_CAPABILITY_CARD);
|
|
172
|
+
reasonCodes.push(REASON_CODES.REMOTE_AGENT_NO_COMPLETION_RECEIPT);
|
|
173
|
+
interrupt = true;
|
|
174
|
+
interruptReason = "Remote/opaque agent delegation requires explicit approval. No secrets or broad context will be sent.";
|
|
175
|
+
safeNextAction = "Review proposed delegation scope, confirm no secrets included, then approve explicitly.";
|
|
176
|
+
proofRequired = true;
|
|
177
|
+
return {
|
|
178
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
179
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
180
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Destructive command
|
|
185
|
+
if (includesAny(taskText, DESTRUCTIVE_KEYWORDS) || riskLevel === "high" && includesAny(taskText, ["delete", "rm -rf", "wipe", "destroy", "clean"])) {
|
|
186
|
+
decision = "blocked";
|
|
187
|
+
pathType = "human_approval";
|
|
188
|
+
selectedWorker = "human_approval";
|
|
189
|
+
reasonCodes.push(REASON_CODES.DESTRUCTIVE_ACTION_BLOCKED);
|
|
190
|
+
interrupt = true;
|
|
191
|
+
interruptReason = "Destructive command detected. Action blocked by default.";
|
|
192
|
+
safeNextAction = "Review the command carefully. Run a dry-run or preview first before proceeding.";
|
|
193
|
+
proofRequired = true;
|
|
194
|
+
return {
|
|
195
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
196
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
197
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Deploy/prod
|
|
202
|
+
if (includesAny(taskText, DEPLOY_KEYWORDS) || taskType === "deployment_release") {
|
|
203
|
+
decision = "approval_required";
|
|
204
|
+
pathType = "human_approval";
|
|
205
|
+
selectedWorker = "human_approval";
|
|
206
|
+
reasonCodes.push(REASON_CODES.DEPLOY_REQUIRES_APPROVAL);
|
|
207
|
+
interrupt = true;
|
|
208
|
+
interruptReason = "Deploy/production action requires explicit approval.";
|
|
209
|
+
safeNextAction = "Run tests, build, and preview first: avorelo execution-packet \"run build and tests\"";
|
|
210
|
+
proofRequired = true;
|
|
211
|
+
return {
|
|
212
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
213
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
214
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Security/auth/secrets
|
|
219
|
+
if (taskType === "security_sensitive" || riskLevel === "critical" || includesAny(taskText, SECURITY_KEYWORDS)) {
|
|
220
|
+
decision = "approval_required";
|
|
221
|
+
pathType = "strong_worker";
|
|
222
|
+
selectedWorker = "strong_model";
|
|
223
|
+
selectedModelProfile = "security_review";
|
|
224
|
+
reasonCodes.push(REASON_CODES.STRONG_WORKER_REQUIRED_FOR_CRITICAL_STAGE);
|
|
225
|
+
reasonCodes.push(REASON_CODES.SECRETS_EXCLUDED_FROM_CONTEXT);
|
|
226
|
+
reasonCodes.push(REASON_CODES.PROOF_REQUIRED_FOR_CODE_CHANGE);
|
|
227
|
+
interrupt = true;
|
|
228
|
+
interruptReason = "Security/auth/secrets task requires strong worker and approval. Secrets excluded from context.";
|
|
229
|
+
safeNextAction = "Review the security scope carefully. Use avorelo guard before any auth change.";
|
|
230
|
+
proofRequired = true;
|
|
231
|
+
crossVendorAllowed = false;
|
|
232
|
+
return {
|
|
233
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
234
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
235
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ACP-compatible editor request (metadata only)
|
|
240
|
+
if (includesAny(taskText, ACP_KEYWORDS)) {
|
|
241
|
+
decision = "auto_prepare";
|
|
242
|
+
pathType = "acp_session";
|
|
243
|
+
selectedWorker = "local_or_cheap";
|
|
244
|
+
reasonCodes.push(REASON_CODES.ACP_COMPATIBLE_EDITOR_DETECTED);
|
|
245
|
+
reasonCodes.push(REASON_CODES.ACP_NOT_NEEDED_CLI_PATH_SUFFICIENT);
|
|
246
|
+
safeNextAction = "ACP readiness metadata generated. Local CLI path is sufficient for v1.";
|
|
247
|
+
proofRequired = false;
|
|
248
|
+
return {
|
|
249
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
250
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
251
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Docs/summarization: auto_prepare or auto_execute, local/cheap
|
|
256
|
+
if (taskType === "documentation" || taskType === "summarization" ||
|
|
257
|
+
taskType === "repo_inspection" || taskType === "handoff" ||
|
|
258
|
+
includesAny(taskText, DOCS_KEYWORDS)) {
|
|
259
|
+
decision = riskLevel === "low" ? "auto_execute" : "auto_prepare";
|
|
260
|
+
pathType = "local_or_cheap";
|
|
261
|
+
selectedWorker = "local_or_cheap";
|
|
262
|
+
selectedModelProfile = "local_summary";
|
|
263
|
+
reasonCodes.push(REASON_CODES.SAFE_LOCAL_PATH_AVAILABLE);
|
|
264
|
+
reasonCodes.push(REASON_CODES.CHEAP_WORKER_CAPABLE);
|
|
265
|
+
reasonCodes.push(REASON_CODES.SAME_SURFACE_PREFERRED);
|
|
266
|
+
proofRequired = false;
|
|
267
|
+
return {
|
|
268
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
269
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
270
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Bug fix / implementation: auto_prepare, handoff/strong_worker for implementation
|
|
275
|
+
if (taskType === "bug_fix" || taskType === "implementation" || taskType === "refactor" || taskType === "test_or_verification") {
|
|
276
|
+
decision = "auto_prepare";
|
|
277
|
+
const hasStrongStage = orchestrationPlan &&
|
|
278
|
+
Array.isArray(orchestrationPlan.stages) &&
|
|
279
|
+
orchestrationPlan.stages.some((s) => s.workerSelection?.role === "strong_model");
|
|
280
|
+
|
|
281
|
+
if (hasStrongStage || riskLevel === "high") {
|
|
282
|
+
pathType = "strong_worker";
|
|
283
|
+
selectedWorker = "strong_model";
|
|
284
|
+
selectedModelProfile = "default_coding";
|
|
285
|
+
reasonCodes.push(REASON_CODES.STRONG_WORKER_REQUIRED_FOR_CRITICAL_STAGE);
|
|
286
|
+
} else {
|
|
287
|
+
pathType = "handoff";
|
|
288
|
+
selectedWorker = "local_or_cheap";
|
|
289
|
+
selectedModelProfile = "local_summary";
|
|
290
|
+
reasonCodes.push(REASON_CODES.CHEAP_WORKER_CAPABLE);
|
|
291
|
+
}
|
|
292
|
+
reasonCodes.push(REASON_CODES.SAME_SURFACE_PREFERRED);
|
|
293
|
+
reasonCodes.push(REASON_CODES.PROOF_REQUIRED_FOR_CODE_CHANGE);
|
|
294
|
+
reasonCodes.push(REASON_CODES.CROSS_VENDOR_ROUTING_BLOCKED_BY_DEFAULT);
|
|
295
|
+
proofRequired = true;
|
|
296
|
+
safeNextAction = "Worker handoff prepared. Review scope, then execute with target worker.";
|
|
297
|
+
return {
|
|
298
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
299
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
300
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Default: auto_prepare, local/cheap
|
|
305
|
+
reasonCodes.push(REASON_CODES.SAFE_LOCAL_PATH_AVAILABLE);
|
|
306
|
+
reasonCodes.push(REASON_CODES.SAME_SURFACE_PREFERRED);
|
|
307
|
+
reasonCodes.push(REASON_CODES.CROSS_VENDOR_ROUTING_BLOCKED_BY_DEFAULT);
|
|
308
|
+
return {
|
|
309
|
+
decision, pathType, selectedWorker, selectedModelProfile, selectedTool,
|
|
310
|
+
sameSurfacePreferred, crossVendorAllowed, reasonCodes,
|
|
311
|
+
interrupt, interruptReason, safeNextAction, proofRequired,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── Protocol readiness (Phase 6) ──────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
function buildProtocolReadiness(cwd, route, options = {}) {
|
|
318
|
+
const taskText = (route && route.task && route.task.summary) || "";
|
|
319
|
+
const pathType = route && route.executionPath && route.executionPath.pathType;
|
|
320
|
+
|
|
321
|
+
const acpNeeded = pathType === "acp_session" || includesAny(taskText, ACP_KEYWORDS);
|
|
322
|
+
const a2aNeeded = pathType === "a2a_delegate" || includesAny(taskText, REMOTE_AGENT_KEYWORDS);
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
acp: {
|
|
326
|
+
neededNow: acpNeeded,
|
|
327
|
+
adapterReady: false,
|
|
328
|
+
useWhen: "An ACP-compatible editor or session bridge is explicitly detected or requested.",
|
|
329
|
+
reason: acpNeeded
|
|
330
|
+
? "ACP-compatible editor or session bridge detected in task. Readiness metadata generated; no ACP implementation in v1."
|
|
331
|
+
: "Local CLI/handoff path sufficient for v1. ACP not needed.",
|
|
332
|
+
reasonCodes: acpNeeded
|
|
333
|
+
? [REASON_CODES.ACP_COMPATIBLE_EDITOR_DETECTED]
|
|
334
|
+
: [REASON_CODES.ACP_NOT_NEEDED_CLI_PATH_SUFFICIENT],
|
|
335
|
+
},
|
|
336
|
+
a2a: {
|
|
337
|
+
neededNow: a2aNeeded,
|
|
338
|
+
adapterReady: false,
|
|
339
|
+
useWhen: "Task explicitly requires remote/opaque independent agent delegation with identity proof, capability card, and completion receipt.",
|
|
340
|
+
reason: a2aNeeded
|
|
341
|
+
? "Remote agent delegation detected. Requires approval. No secrets or broad context will be sent. Identity proof, capability card, and completion receipt required."
|
|
342
|
+
: "Local or same-surface handoff path sufficient. A2A not needed.",
|
|
343
|
+
reasonCodes: a2aNeeded
|
|
344
|
+
? [
|
|
345
|
+
REASON_CODES.A2A_REMOTE_AGENT_DETECTED,
|
|
346
|
+
REASON_CODES.EXTERNAL_AGENT_REQUIRES_APPROVAL,
|
|
347
|
+
REASON_CODES.REMOTE_AGENT_NO_IDENTITY_PROOF,
|
|
348
|
+
REASON_CODES.REMOTE_AGENT_NO_CAPABILITY_CARD,
|
|
349
|
+
REASON_CODES.REMOTE_AGENT_NO_COMPLETION_RECEIPT,
|
|
350
|
+
]
|
|
351
|
+
: [REASON_CODES.A2A_NOT_NEEDED_LOCAL_OR_HANDOFF_SUFFICIENT],
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ── Worker handoff bundle (Phase 5) ──────────────────────────────────────────
|
|
357
|
+
|
|
358
|
+
function buildWorkerHandoff(cwd, route, options = {}) {
|
|
359
|
+
const handoffId = createHandoffId();
|
|
360
|
+
const pathType = route.executionPath && route.executionPath.pathType;
|
|
361
|
+
const selectedWorker = (route.executionPath && route.executionPath.selectedWorker) || "local_or_cheap";
|
|
362
|
+
const targetSurface = selectedWorker === "strong_model" ? "claude_code" : "local_or_cheap";
|
|
363
|
+
const sameSurfacePreferred = (route.executionPath && route.executionPath.sameSurfacePreferred) !== false;
|
|
364
|
+
|
|
365
|
+
// Cross-vendor only with explicit approval
|
|
366
|
+
const crossVendorEnabled = options.crossVendorEnabled === true;
|
|
367
|
+
const needsApproval = ["a2a_delegate", "human_approval"].includes(pathType) ||
|
|
368
|
+
(route.executionPath && !route.executionPath.sameSurfacePreferred && !crossVendorEnabled);
|
|
369
|
+
|
|
370
|
+
const allowedScopeSummary = [
|
|
371
|
+
"file_read", "file_write_with_diff", "run_tests", "run_lint",
|
|
372
|
+
"run_build", "run_type_check", "git_status", "git_diff", "git_log",
|
|
373
|
+
].join(", ");
|
|
374
|
+
|
|
375
|
+
const blockedScopeSummary = [
|
|
376
|
+
"secrets (.env, .key, credentials)", "auth/billing dirs", "deploy actions",
|
|
377
|
+
"remote agent execution", "broad context dumps", "raw code export",
|
|
378
|
+
].join("; ");
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
contract: "avorelo.workerHandoff.v1",
|
|
382
|
+
handoffId,
|
|
383
|
+
targetWorker: selectedWorker,
|
|
384
|
+
targetSurface,
|
|
385
|
+
sameSurfacePreferred,
|
|
386
|
+
crossVendorAllowed: crossVendorEnabled,
|
|
387
|
+
taskGoal: (route.task && route.task.summary) || "not_set",
|
|
388
|
+
executionPacketRef: ".claude/cco/orchestration/execution-packet/latest-packet.json",
|
|
389
|
+
contextPackRef: ".claude/cco/orchestration/context-optimizer/latest-pack.json",
|
|
390
|
+
allowedScopeSummary,
|
|
391
|
+
blockedScopeSummary,
|
|
392
|
+
proofRequired: (route.executionPath && route.executionPath.proofRequired) || false,
|
|
393
|
+
requiredBeforeWrite: ["read relevant files", "run avorelo guard", "confirm scope"],
|
|
394
|
+
requiredAfterWrite: route.executionPath && route.executionPath.proofRequired
|
|
395
|
+
? ["run tests", "run build", "write proof artifact"]
|
|
396
|
+
: ["git diff review"],
|
|
397
|
+
safeNextAction: (route.executionPath && route.executionPath.safeNextAction) ||
|
|
398
|
+
"Review handoff scope, then execute with target worker.",
|
|
399
|
+
continuation: {
|
|
400
|
+
type: needsApproval ? "require_user_approval" : "continue_with_handoff",
|
|
401
|
+
approvalRequired: needsApproval,
|
|
402
|
+
approvalReason: needsApproval ? "Remote/cross-vendor delegation requires explicit approval." : null,
|
|
403
|
+
},
|
|
404
|
+
outputExpectations: {
|
|
405
|
+
diffRequired: (route.executionPath && route.executionPath.proofRequired) || false,
|
|
406
|
+
receiptRequired: true,
|
|
407
|
+
testPassRequired: (route.executionPath && route.executionPath.proofRequired) || false,
|
|
408
|
+
},
|
|
409
|
+
redacted: true,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ── Value evidence ────────────────────────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
function buildValueEvidence(route, orchestrationPlan) {
|
|
416
|
+
const evidence = [];
|
|
417
|
+
const stages = (orchestrationPlan && orchestrationPlan.stages) || [];
|
|
418
|
+
const localStages = stages.filter((s) =>
|
|
419
|
+
s.workerSelection && (s.workerSelection.costTier === "free_local" || s.workerSelection.role === "local_or_cheap")
|
|
420
|
+
).length;
|
|
421
|
+
|
|
422
|
+
if (localStages > 0) {
|
|
423
|
+
evidence.push(`${localStages} local/cheap stage(s) from orchestration plan (evidence-backed)`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const pathType = route.executionPath && route.executionPath.pathType;
|
|
427
|
+
if (pathType === "local_or_cheap" || pathType === "local_deterministic") {
|
|
428
|
+
evidence.push("Same-surface local path selected — no premium escalation needed");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const blockedCodes = (route.executionPath && route.executionPath.reasonCodes || []);
|
|
432
|
+
if (blockedCodes.includes(REASON_CODES.DESTRUCTIVE_ACTION_BLOCKED) ||
|
|
433
|
+
blockedCodes.includes(REASON_CODES.DEPLOY_REQUIRES_APPROVAL)) {
|
|
434
|
+
evidence.push("Unsafe action blocked — approval gate applied");
|
|
435
|
+
}
|
|
436
|
+
if (blockedCodes.includes(REASON_CODES.SECRETS_EXCLUDED_FROM_CONTEXT)) {
|
|
437
|
+
evidence.push("Secrets excluded from context — exposure prevented");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
evidenceItems: evidence,
|
|
442
|
+
localStagesUsed: localStages,
|
|
443
|
+
caveats: [
|
|
444
|
+
"Savings are estimates based on receipt data, not exact billing amounts.",
|
|
445
|
+
"No guaranteed savings claimed.",
|
|
446
|
+
],
|
|
447
|
+
redacted: true,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ── Main builder: buildSmartWorkRoute ─────────────────────────────────────────
|
|
452
|
+
|
|
453
|
+
function buildSmartWorkRoute(cwd, taskText, options = {}) {
|
|
454
|
+
const routingId = createRoutingId();
|
|
455
|
+
const mode = getMode(cwd);
|
|
456
|
+
|
|
457
|
+
// Load orchestration plan if available
|
|
458
|
+
let orchestrationPlan = null;
|
|
459
|
+
try {
|
|
460
|
+
const planPath = path.join(cwd, ".claude/cco/orchestration/work-aware-orchestration/latest-plan.json");
|
|
461
|
+
const data = safeReadJson(planPath);
|
|
462
|
+
// Only use if it matches the current task (task summary overlap)
|
|
463
|
+
if (data && data.task) {
|
|
464
|
+
orchestrationPlan = data;
|
|
465
|
+
}
|
|
466
|
+
} catch { /* non-fatal */ }
|
|
467
|
+
|
|
468
|
+
// Load execution packet if available
|
|
469
|
+
let executionPacket = null;
|
|
470
|
+
try {
|
|
471
|
+
const packetPath = path.join(cwd, ".claude/cco/orchestration/execution-packet/latest-packet.json");
|
|
472
|
+
executionPacket = safeReadJson(packetPath);
|
|
473
|
+
} catch { /* non-fatal */ }
|
|
474
|
+
|
|
475
|
+
// Always classify from the current task text — do not reuse stored plan's task
|
|
476
|
+
const task = classifyTaskForRouting(taskText, null);
|
|
477
|
+
|
|
478
|
+
// Decide execution path
|
|
479
|
+
const pathDecision = decideExecutionPath(cwd, orchestrationPlan, executionPacket, {
|
|
480
|
+
taskText,
|
|
481
|
+
...options,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
const executionPath = {
|
|
485
|
+
pathType: pathDecision.pathType,
|
|
486
|
+
selectedWorker: pathDecision.selectedWorker,
|
|
487
|
+
selectedModelProfile: pathDecision.selectedModelProfile,
|
|
488
|
+
selectedTool: pathDecision.selectedTool,
|
|
489
|
+
sameSurfacePreferred: pathDecision.sameSurfacePreferred,
|
|
490
|
+
crossVendorAllowed: pathDecision.crossVendorAllowed,
|
|
491
|
+
reasonCodes: pathDecision.reasonCodes,
|
|
492
|
+
proofRequired: pathDecision.proofRequired,
|
|
493
|
+
interrupt: pathDecision.interrupt || false,
|
|
494
|
+
interruptReason: pathDecision.interruptReason || null,
|
|
495
|
+
safeNextAction: pathDecision.safeNextAction || null,
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Only reuse orchestration plan stages if the plan's task type matches current task
|
|
499
|
+
const planTaskType = orchestrationPlan && orchestrationPlan.task && orchestrationPlan.task.taskType;
|
|
500
|
+
const stagesFromPlan = planTaskType === task.taskType && Array.isArray(orchestrationPlan.stages)
|
|
501
|
+
? orchestrationPlan.stages.map((s) => ({
|
|
502
|
+
stageId: s.stageId,
|
|
503
|
+
lifecycleStage: s.lifecycleStage,
|
|
504
|
+
riskLevel: s.riskLevel,
|
|
505
|
+
recommendedWorker: s.recommendedWorker,
|
|
506
|
+
proofExpected: s.proofExpected,
|
|
507
|
+
redacted: true,
|
|
508
|
+
}))
|
|
509
|
+
: null;
|
|
510
|
+
|
|
511
|
+
// Derive minimal stage list from routing decision when plan stages are unavailable/mismatched
|
|
512
|
+
const derivedStages = (() => {
|
|
513
|
+
const d = pathDecision.decision;
|
|
514
|
+
const pt = pathDecision.pathType;
|
|
515
|
+
if (d === "blocked" || d === "approval_required") return [];
|
|
516
|
+
if (pt === "local_or_cheap" || pt === "local_deterministic") return [
|
|
517
|
+
{ stageId: "inspect", lifecycleStage: "inspect", riskLevel: "low", recommendedWorker: "local_or_cheap", proofExpected: false, redacted: true },
|
|
518
|
+
{ stageId: "handoff", lifecycleStage: "handoff", riskLevel: "low", recommendedWorker: "local_or_cheap", proofExpected: false, redacted: true },
|
|
519
|
+
];
|
|
520
|
+
return [
|
|
521
|
+
{ stageId: "inspect", lifecycleStage: "inspect", riskLevel: "low", recommendedWorker: "local_or_cheap", proofExpected: false, redacted: true },
|
|
522
|
+
{ stageId: "handoff", lifecycleStage: "handoff", riskLevel: "low", recommendedWorker: "local_or_cheap", proofExpected: false, redacted: true },
|
|
523
|
+
];
|
|
524
|
+
})();
|
|
525
|
+
|
|
526
|
+
const stages = stagesFromPlan || derivedStages;
|
|
527
|
+
|
|
528
|
+
// Gather context info
|
|
529
|
+
const context = {
|
|
530
|
+
contextPackRef: executionPacket && executionPacket.contextPackRef || null,
|
|
531
|
+
secretsExcluded: true,
|
|
532
|
+
broadContextExcluded: true,
|
|
533
|
+
estimatedTokens: 0,
|
|
534
|
+
localOnly: true,
|
|
535
|
+
redacted: true,
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// Safety summary
|
|
539
|
+
const safety = {
|
|
540
|
+
secretsExcluded: true,
|
|
541
|
+
broadRepoContextBlocked: true,
|
|
542
|
+
deployBlocked: pathDecision.decision === "approval_required" &&
|
|
543
|
+
pathDecision.reasonCodes.includes(REASON_CODES.DEPLOY_REQUIRES_APPROVAL),
|
|
544
|
+
destructiveBlocked: pathDecision.decision === "blocked",
|
|
545
|
+
remoteAgentApprovalRequired: pathDecision.reasonCodes.includes(REASON_CODES.EXTERNAL_AGENT_REQUIRES_APPROVAL),
|
|
546
|
+
crossVendorBlockedByDefault: !pathDecision.crossVendorAllowed,
|
|
547
|
+
interruptRequired: pathDecision.interrupt || false,
|
|
548
|
+
redacted: true,
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const route = {
|
|
552
|
+
contract: CONTRACT,
|
|
553
|
+
schemaVersion: SCHEMA_VERSION,
|
|
554
|
+
routingId,
|
|
555
|
+
task,
|
|
556
|
+
mode,
|
|
557
|
+
decision: pathDecision.decision,
|
|
558
|
+
executionPath,
|
|
559
|
+
stages,
|
|
560
|
+
context,
|
|
561
|
+
safety,
|
|
562
|
+
protocolReadiness: null, // populated below
|
|
563
|
+
workerHandoff: null, // populated below
|
|
564
|
+
proof: {
|
|
565
|
+
receiptPath: LATEST_ROUTE_REL,
|
|
566
|
+
proofRequired: pathDecision.proofRequired,
|
|
567
|
+
redacted: true,
|
|
568
|
+
},
|
|
569
|
+
valueEvidence: null, // populated below
|
|
570
|
+
receipts: {
|
|
571
|
+
routeReceiptPath: LATEST_ROUTE_REL,
|
|
572
|
+
handoffReceiptPath: LATEST_HANDOFF_REL,
|
|
573
|
+
orchestrationPlanRef: orchestrationPlan
|
|
574
|
+
? ".claude/cco/orchestration/work-aware-orchestration/latest-plan.json"
|
|
575
|
+
: null,
|
|
576
|
+
executionPacketRef: executionPacket
|
|
577
|
+
? ".claude/cco/orchestration/execution-packet/latest-packet.json"
|
|
578
|
+
: null,
|
|
579
|
+
},
|
|
580
|
+
redacted: true,
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// Build protocol readiness
|
|
584
|
+
route.protocolReadiness = buildProtocolReadiness(cwd, route, options);
|
|
585
|
+
|
|
586
|
+
// Build value evidence
|
|
587
|
+
route.valueEvidence = buildValueEvidence(route, orchestrationPlan);
|
|
588
|
+
|
|
589
|
+
// Build worker handoff (all paths except debug_only)
|
|
590
|
+
if (pathDecision.decision !== "debug_only") {
|
|
591
|
+
route.workerHandoff = buildWorkerHandoff(cwd, route, options);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return route;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ── Smart route surface ───────────────────────────────────────────────────────
|
|
598
|
+
|
|
599
|
+
function buildSmartRouteSurface(cwd, options = {}) {
|
|
600
|
+
const routePath = path.join(cwd, LATEST_ROUTE_REL);
|
|
601
|
+
const handoffPath = path.join(cwd, LATEST_HANDOFF_REL);
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
contract: CONTRACT,
|
|
605
|
+
status: fs.existsSync(routePath) ? "available" : "not_run",
|
|
606
|
+
latestRoutePath: LATEST_ROUTE_REL,
|
|
607
|
+
latestHandoffPath: fs.existsSync(handoffPath) ? LATEST_HANDOFF_REL : null,
|
|
608
|
+
nextAction: fs.existsSync(routePath)
|
|
609
|
+
? "Smart route receipt available. Run avorelo smart-route \"<task>\" to route a new task."
|
|
610
|
+
: "Run avorelo smart-route \"<task>\" to build a smart work route.",
|
|
611
|
+
redacted: true,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// ── Receipt writer ────────────────────────────────────────────────────────────
|
|
616
|
+
|
|
617
|
+
function writeSmartWorkRouteReceipt(cwd, route) {
|
|
618
|
+
const dir = path.join(cwd, ROUTE_DIR_REL);
|
|
619
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
620
|
+
const filePath = path.join(cwd, LATEST_ROUTE_REL);
|
|
621
|
+
fs.writeFileSync(filePath, JSON.stringify(route, null, 2), "utf8");
|
|
622
|
+
return LATEST_ROUTE_REL;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function writeWorkerHandoffReceipt(cwd, handoff) {
|
|
626
|
+
const dir = path.join(cwd, HANDOFF_DIR_REL);
|
|
627
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
628
|
+
const filePath = path.join(cwd, LATEST_HANDOFF_REL);
|
|
629
|
+
fs.writeFileSync(filePath, JSON.stringify(handoff, null, 2), "utf8");
|
|
630
|
+
return LATEST_HANDOFF_REL;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// ── Text formatter ────────────────────────────────────────────────────────────
|
|
634
|
+
|
|
635
|
+
function formatSmartRouteText(route, options = {}) {
|
|
636
|
+
const debug = options.debug === true;
|
|
637
|
+
const lines = [];
|
|
638
|
+
|
|
639
|
+
const { decision, executionPath, task, safety, protocolReadiness } = route;
|
|
640
|
+
|
|
641
|
+
if (decision === "blocked") {
|
|
642
|
+
lines.push("ACTION BLOCKED");
|
|
643
|
+
lines.push(`Reason: ${executionPath.interruptReason || "Destructive or unsafe action detected."}`);
|
|
644
|
+
lines.push(`Safe next action: ${executionPath.safeNextAction || "Review and reduce scope."}`);
|
|
645
|
+
lines.push(`Proof needed: Run avorelo guard for verification steps.`);
|
|
646
|
+
} else if (decision === "approval_required") {
|
|
647
|
+
lines.push("ACTION REQUIRED");
|
|
648
|
+
lines.push(`Reason: ${executionPath.interruptReason || "High-risk action requires approval."}`);
|
|
649
|
+
lines.push(`Safe next action: ${executionPath.safeNextAction || "Review scope before proceeding."}`);
|
|
650
|
+
lines.push(`Proof needed: Run avorelo execution-packet for proof requirements.`);
|
|
651
|
+
} else {
|
|
652
|
+
lines.push("Avorelo routed this safely.");
|
|
653
|
+
const stageSummary = (route.stages || []).map((s) => s.stageId).filter(Boolean).join(" → ") || "prepare → handoff";
|
|
654
|
+
lines.push(`Path: ${stageSummary}`);
|
|
655
|
+
lines.push(`Context: compact pack, secrets excluded`);
|
|
656
|
+
lines.push(`Proof: ${executionPath.proofRequired ? "required" : "optional"}`);
|
|
657
|
+
if (route.workerHandoff) {
|
|
658
|
+
lines.push(`Next: worker handoff prepared`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (debug) {
|
|
663
|
+
lines.push("");
|
|
664
|
+
lines.push("--- Debug: Routing Detail ---");
|
|
665
|
+
lines.push(`Routing ID: ${route.routingId}`);
|
|
666
|
+
lines.push(`Mode: ${route.mode}`);
|
|
667
|
+
lines.push(`Decision: ${decision}`);
|
|
668
|
+
lines.push(`Path type: ${executionPath.pathType}`);
|
|
669
|
+
lines.push(`Worker: ${executionPath.selectedWorker}`);
|
|
670
|
+
lines.push(`Model profile: ${executionPath.selectedModelProfile}`);
|
|
671
|
+
lines.push(`Same surface preferred: ${executionPath.sameSurfacePreferred}`);
|
|
672
|
+
lines.push(`Cross-vendor: ${executionPath.crossVendorAllowed ? "allowed" : "blocked by default"}`);
|
|
673
|
+
lines.push(`Reason codes: ${(executionPath.reasonCodes || []).join(", ")}`);
|
|
674
|
+
lines.push("");
|
|
675
|
+
lines.push("--- Debug: Task Classification ---");
|
|
676
|
+
lines.push(`Task type: ${task.taskType}`);
|
|
677
|
+
lines.push(`Risk: ${task.riskLevel}`);
|
|
678
|
+
lines.push(`Complexity: ${task.complexity}`);
|
|
679
|
+
lines.push("");
|
|
680
|
+
lines.push("--- Debug: Safety ---");
|
|
681
|
+
lines.push(`Secrets excluded: ${safety.secretsExcluded}`);
|
|
682
|
+
lines.push(`Broad context blocked: ${safety.broadRepoContextBlocked}`);
|
|
683
|
+
lines.push(`Deploy blocked: ${safety.deployBlocked}`);
|
|
684
|
+
lines.push(`Destructive blocked: ${safety.destructiveBlocked}`);
|
|
685
|
+
lines.push(`Cross-vendor blocked by default: ${safety.crossVendorBlockedByDefault}`);
|
|
686
|
+
lines.push(`Interrupt required: ${safety.interruptRequired}`);
|
|
687
|
+
lines.push("");
|
|
688
|
+
lines.push("--- Debug: Protocol Readiness ---");
|
|
689
|
+
lines.push(`ACP needed now: ${protocolReadiness.acp.neededNow} (${protocolReadiness.acp.reason})`);
|
|
690
|
+
lines.push(`A2A needed now: ${protocolReadiness.a2a.neededNow} (${protocolReadiness.a2a.reason})`);
|
|
691
|
+
if (route.valueEvidence) {
|
|
692
|
+
lines.push("");
|
|
693
|
+
lines.push("--- Debug: Value Evidence ---");
|
|
694
|
+
for (const item of route.valueEvidence.evidenceItems || []) {
|
|
695
|
+
lines.push(` - ${item}`);
|
|
696
|
+
}
|
|
697
|
+
for (const caveat of route.valueEvidence.caveats || []) {
|
|
698
|
+
lines.push(` Caveat: ${caveat}`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return lines.join("\n");
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ── Ledger entry reader ───────────────────────────────────────────────────────
|
|
707
|
+
|
|
708
|
+
function readSmartRouteLedgerEntry(cwd) {
|
|
709
|
+
const routePath = path.join(cwd, LATEST_ROUTE_REL);
|
|
710
|
+
if (!fs.existsSync(routePath)) return null;
|
|
711
|
+
|
|
712
|
+
const data = safeReadJson(routePath);
|
|
713
|
+
if (!data) return null;
|
|
714
|
+
|
|
715
|
+
const decision = data.decision || "unknown";
|
|
716
|
+
const pathType = (data.executionPath && data.executionPath.pathType) || "unknown";
|
|
717
|
+
const reasonCodes = (data.executionPath && data.executionPath.reasonCodes) || [];
|
|
718
|
+
|
|
719
|
+
const valueSignals = [];
|
|
720
|
+
const riskSignals = [];
|
|
721
|
+
|
|
722
|
+
if (pathType === "local_or_cheap" || pathType === "local_deterministic") {
|
|
723
|
+
valueSignals.push("local_or_cheap_path_selected");
|
|
724
|
+
}
|
|
725
|
+
if (reasonCodes.includes(REASON_CODES.DESTRUCTIVE_ACTION_BLOCKED)) {
|
|
726
|
+
riskSignals.push("destructive_action_blocked");
|
|
727
|
+
}
|
|
728
|
+
if (reasonCodes.includes(REASON_CODES.SECRETS_EXCLUDED_FROM_CONTEXT)) {
|
|
729
|
+
riskSignals.push("secrets_excluded");
|
|
730
|
+
}
|
|
731
|
+
if (reasonCodes.includes(REASON_CODES.DEPLOY_REQUIRES_APPROVAL)) {
|
|
732
|
+
riskSignals.push("deploy_approval_required");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const { normalizeLedgerEntry } = require("./work-ledger");
|
|
736
|
+
return normalizeLedgerEntry("smart_route", "smart_work_routing", data, {
|
|
737
|
+
status: decision === "blocked" ? "warn" : "pass",
|
|
738
|
+
summary: `Smart route: decision=${decision}, path=${pathType}.`,
|
|
739
|
+
evidencePath: LATEST_ROUTE_REL,
|
|
740
|
+
evidenceKind: "receipt",
|
|
741
|
+
valueSignals,
|
|
742
|
+
riskSignals,
|
|
743
|
+
reasonCodes: reasonCodes.slice(0, 5),
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
module.exports = {
|
|
748
|
+
CONTRACT,
|
|
749
|
+
SCHEMA_VERSION,
|
|
750
|
+
PATH_TYPES,
|
|
751
|
+
DECISION_TYPES,
|
|
752
|
+
MODES,
|
|
753
|
+
REASON_CODES,
|
|
754
|
+
LATEST_ROUTE_REL,
|
|
755
|
+
LATEST_HANDOFF_REL,
|
|
756
|
+
ROUTE_DIR_REL,
|
|
757
|
+
HANDOFF_DIR_REL,
|
|
758
|
+
buildSmartWorkRoute,
|
|
759
|
+
decideExecutionPath,
|
|
760
|
+
buildProtocolReadiness,
|
|
761
|
+
buildWorkerHandoff,
|
|
762
|
+
writeSmartWorkRouteReceipt,
|
|
763
|
+
writeWorkerHandoffReceipt,
|
|
764
|
+
buildSmartRouteSurface,
|
|
765
|
+
formatSmartRouteText,
|
|
766
|
+
readSmartRouteLedgerEntry,
|
|
767
|
+
classifyTaskForRouting,
|
|
768
|
+
};
|