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,1211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const crypto = require("crypto");
|
|
6
|
+
const { ensureCcoDirs, nowIso, safeReadJson, safeWriteJson, safeWriteText } = require("./fsx");
|
|
7
|
+
const { appendProductLearningEvent } = require("./product-learning-events");
|
|
8
|
+
|
|
9
|
+
const SESSION_CONTEXT_CONTRACT = "avorelo.sessionContext.v1";
|
|
10
|
+
const SESSION_CONTEXT_SCHEMA_VERSION = 1;
|
|
11
|
+
const LATEST_CONTEXT_REL_PATH = ".claude/cco/orchestration/session-context/latest-context.json";
|
|
12
|
+
const LATEST_CONTEXT_MD_REL_PATH = ".claude/cco/orchestration/session-context/latest-context.md";
|
|
13
|
+
const CONTEXT_HISTORY_DIR_REL_PATH = ".claude/cco/orchestration/session-context/history";
|
|
14
|
+
const CONTEXT_EVENT_LOG_REL_PATH = ".claude/cco/events/session-context.jsonl";
|
|
15
|
+
|
|
16
|
+
const DEFAULT_MAX_CHARS = 6000;
|
|
17
|
+
const HARD_MAX_CHARS = 12000;
|
|
18
|
+
|
|
19
|
+
const SECRET_PATTERN = /(?:sk-[A-Za-z0-9_-]{16,}|ghp_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|(?:api[_ -]?key|token|password|secret|authorization)\s*[:=]\s*\S+)/i;
|
|
20
|
+
|
|
21
|
+
function redactIfSecret(text) {
|
|
22
|
+
if (!text) return text;
|
|
23
|
+
const s = String(text);
|
|
24
|
+
return SECRET_PATTERN.test(s) ? "[REDACTED]" : s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createContextId() {
|
|
28
|
+
return `sc-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function estimateTokens(chars) {
|
|
32
|
+
return Math.ceil(chars / 4);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Source receipt readers ────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
function readWorkControlReceipt(cwd) {
|
|
38
|
+
try {
|
|
39
|
+
const { readLatestWorkControlReceipt } = require("./work-control-receipts");
|
|
40
|
+
return readLatestWorkControlReceipt(cwd);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readProof(cwd) {
|
|
47
|
+
try {
|
|
48
|
+
const { readCurrentProof } = require("./orchestration");
|
|
49
|
+
return readCurrentProof(cwd);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readOrBuildWorkspaceMap(cwd, options = {}) {
|
|
56
|
+
try {
|
|
57
|
+
const { readWorkspaceMap, writeWorkspaceMap } = require("./workspace-map");
|
|
58
|
+
const existing = readWorkspaceMap(cwd);
|
|
59
|
+
if (existing && !options.refreshWorkspaceMap) return existing;
|
|
60
|
+
return writeWorkspaceMap(cwd, { task: options.task || "" });
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readWorkspaceRegistry(cwd) {
|
|
67
|
+
try {
|
|
68
|
+
const { readLatestRegistry } = require("./workspace-registry");
|
|
69
|
+
return readLatestRegistry(cwd);
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Section builders ──────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function buildObjectiveSection(options, wcReceipt, proof) {
|
|
78
|
+
const taskText = redactIfSecret(
|
|
79
|
+
options.task
|
|
80
|
+
|| wcReceipt?.task?.text
|
|
81
|
+
|| proof?.task
|
|
82
|
+
|| "Session Context / Continuation Optimization Foundation v1"
|
|
83
|
+
);
|
|
84
|
+
return String(taskText).slice(0, 300);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildCurrentStateSection(wcReceipt) {
|
|
88
|
+
if (!wcReceipt) {
|
|
89
|
+
return {
|
|
90
|
+
finalState: "missing",
|
|
91
|
+
receiptPath: null,
|
|
92
|
+
source: "no-work-control-receipt",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const wc = wcReceipt.workControl || {};
|
|
96
|
+
return {
|
|
97
|
+
finalState: wc.finalState || "missing",
|
|
98
|
+
receiptPath: ".claude/cco/orchestration/work-control/latest-receipt.json",
|
|
99
|
+
blockedCount: wc.blockedCount || 0,
|
|
100
|
+
approvalRequiredCount: wc.approvalRequiredCount || 0,
|
|
101
|
+
highRiskIntakeCount: wc.highRiskIntakeCount || 0,
|
|
102
|
+
unknownSourcesCount: wc.unknownSourcesCount || 0,
|
|
103
|
+
source: "work-control-receipt",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildNextSafestAction(wcReceipt) {
|
|
108
|
+
if (!wcReceipt) {
|
|
109
|
+
return "Run `node bin/avorelo work-control --json` to establish current state before proceeding.";
|
|
110
|
+
}
|
|
111
|
+
return wcReceipt.workControl?.nextSafestAction
|
|
112
|
+
|| "Continue with the smallest local scope. Re-run proof before next session.";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildAlreadyDone(wcReceipt, proof) {
|
|
116
|
+
const items = [];
|
|
117
|
+
if (wcReceipt?.workControl?.carryForward?.lastRoute) {
|
|
118
|
+
items.push(`Last route: ${wcReceipt.workControl.carryForward.lastRoute}`);
|
|
119
|
+
}
|
|
120
|
+
if (wcReceipt?.workControl?.carryForward?.lastTaskSummary) {
|
|
121
|
+
items.push(`Last task: ${String(wcReceipt.workControl.carryForward.lastTaskSummary).slice(0, 120)}`);
|
|
122
|
+
}
|
|
123
|
+
if (wcReceipt?.evidencePaths?.length) {
|
|
124
|
+
items.push(`Evidence receipts recorded: ${wcReceipt.evidencePaths.length}`);
|
|
125
|
+
}
|
|
126
|
+
if (proof?.runId) {
|
|
127
|
+
items.push(`Proof run: ${proof.runId}`);
|
|
128
|
+
}
|
|
129
|
+
if (proof?.route?.selectedRoute) {
|
|
130
|
+
items.push(`Route proven: ${proof.route.selectedRoute}`);
|
|
131
|
+
}
|
|
132
|
+
return items.length ? items : ["No prior carry-forward found — start from current work-control receipt."];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildDoNotRepeat(wcReceipt) {
|
|
136
|
+
const base = [
|
|
137
|
+
"Do not broad-scan the repo — use workspace map and named files first.",
|
|
138
|
+
"Do not paste huge JSON into context — use `avorelo work-control --json` for compact receipts.",
|
|
139
|
+
"Do not start over — read latest-context.md or work-control receipt to continue.",
|
|
140
|
+
];
|
|
141
|
+
const fromReceipt = wcReceipt?.workControl?.doNotRepeat || [];
|
|
142
|
+
const combined = [...new Set([...base, ...fromReceipt])];
|
|
143
|
+
return combined.slice(0, 8);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function buildActiveRisks(wcReceipt) {
|
|
147
|
+
const risks = [];
|
|
148
|
+
if (!wcReceipt) {
|
|
149
|
+
risks.push("No work-control receipt — run `avorelo work-control` before proceeding.");
|
|
150
|
+
return risks;
|
|
151
|
+
}
|
|
152
|
+
const intake = wcReceipt.controls?.installIntake;
|
|
153
|
+
const governance = wcReceipt.controls?.governance;
|
|
154
|
+
const safePath = wcReceipt.controls?.safePath;
|
|
155
|
+
|
|
156
|
+
if (intake?.highRiskItems > 0) {
|
|
157
|
+
risks.push(`Install intake: ${intake.highRiskItems} high-risk items — review before tool/MCP use.`);
|
|
158
|
+
}
|
|
159
|
+
if (intake?.unknownItems > 0) {
|
|
160
|
+
risks.push(`Install intake: ${intake.unknownItems} unknown-source items — do not execute without review.`);
|
|
161
|
+
}
|
|
162
|
+
if (governance?.subjectTrustStatus === "unknown") {
|
|
163
|
+
risks.push(`Governance: subject trust status is 'unknown' — requires review before proceeding.`);
|
|
164
|
+
}
|
|
165
|
+
if (governance?.decision === "block" || wcReceipt.workControl?.blockedCount > 0) {
|
|
166
|
+
risks.push(`Governance: blocked action recorded — preserve block, do not bypass.`);
|
|
167
|
+
}
|
|
168
|
+
if (safePath?.lastDecision === "recommend_reduced_scope") {
|
|
169
|
+
risks.push(`Safe Path: reduced scope recommended — use narrow local boundary only.`);
|
|
170
|
+
}
|
|
171
|
+
if (!governance?.latestReceiptPath) {
|
|
172
|
+
risks.push("Governance receipt missing — run `avorelo guard` to establish governance baseline.");
|
|
173
|
+
}
|
|
174
|
+
if (!safePath?.latestReceiptPath) {
|
|
175
|
+
risks.push("Safe Path receipt missing — run `avorelo guard` to establish safe-path baseline.");
|
|
176
|
+
}
|
|
177
|
+
return risks.length ? risks : ["No active risks detected — re-run `avorelo work-control` to refresh."];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function buildRelevantFiles(wcReceipt, workspaceMap, options = {}) {
|
|
181
|
+
const task = String(options.task || wcReceipt?.task?.text || "").toLowerCase();
|
|
182
|
+
const maxFiles = options.maxFiles || 20;
|
|
183
|
+
const files = [];
|
|
184
|
+
|
|
185
|
+
// Task-aware file selection from workspace map
|
|
186
|
+
if (workspaceMap) {
|
|
187
|
+
const taskKeywords = {
|
|
188
|
+
"session": ["scripts/lib/session-context-optimizer.js", "tests/session-context-optimization.test.js"],
|
|
189
|
+
"context": ["scripts/lib/session-context-optimizer.js", "scripts/lib/workspace-map.js"],
|
|
190
|
+
"brain": ["scripts/lib/brain-pack.js", "tests/brain-pack.test.js"],
|
|
191
|
+
"recipe": ["scripts/lib/brain-pack.js", "tests/brain-pack.test.js"],
|
|
192
|
+
"work-control": ["scripts/lib/work-control-receipts.js", "tests/work-control-receipts.test.js"],
|
|
193
|
+
"workspace": ["scripts/lib/workspace-map.js", "tests/workspace-map.test.js"],
|
|
194
|
+
"registry": ["scripts/lib/workspace-registry.js", "tests/workspace-registry.test.js"],
|
|
195
|
+
"capability": ["scripts/lib/plans.js", "scripts/lib/capability-registry.js", "scripts/lib/capability-packs.js"],
|
|
196
|
+
"skill": ["scripts/lib/skills-operating-layer.js", "scripts/lib/avorelo-skill-registry.js"],
|
|
197
|
+
"intake": ["scripts/lib/install-intake-risk.js", "tests/install-intake-risk.test.js"],
|
|
198
|
+
"safe-path": ["scripts/lib/safe-path-engine.js"],
|
|
199
|
+
"governance": ["scripts/lib/agent-access-governance.js", "scripts/lib/agent-enforcement.js"],
|
|
200
|
+
"product-learning": ["scripts/lib/product-learning-events.js", "docs/Product-Learning-Telemetry.md"],
|
|
201
|
+
"status": ["scripts/cco-status.js", "scripts/lib/dashboard.js"],
|
|
202
|
+
"cli": ["scripts/lib/public-cli.js", "bin/avorelo"],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
for (const [keyword, keyFiles] of Object.entries(taskKeywords)) {
|
|
206
|
+
if (task.includes(keyword)) {
|
|
207
|
+
files.push(...keyFiles);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Optionally enrich from workspace registry (read latest, do not rebuild)
|
|
213
|
+
if (options.useRegistry !== false) {
|
|
214
|
+
const registry = readWorkspaceRegistry(options.cwd || process.cwd());
|
|
215
|
+
if (registry && Array.isArray(registry.recommendations?.sessionContextRelevantFiles)) {
|
|
216
|
+
const regFiles = registry.recommendations.sessionContextRelevantFiles.slice(0, 10);
|
|
217
|
+
files.push(...regFiles);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Always include core files for this feature
|
|
222
|
+
const coreFiles = [
|
|
223
|
+
"scripts/lib/session-context-optimizer.js",
|
|
224
|
+
"scripts/lib/brain-pack.js",
|
|
225
|
+
"scripts/lib/work-control-receipts.js",
|
|
226
|
+
"scripts/lib/workspace-map.js",
|
|
227
|
+
"scripts/lib/public-cli.js",
|
|
228
|
+
"scripts/lib/plans.js",
|
|
229
|
+
"scripts/lib/product-learning-events.js",
|
|
230
|
+
"tests/session-context-optimization.test.js",
|
|
231
|
+
"docs/dogfood/session-context-optimization-dogfood.md",
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
const combined = [...new Set([...files, ...coreFiles])].slice(0, maxFiles);
|
|
235
|
+
return combined;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Dynamic commands (task/target-aware) ──────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
const TASK_COMMAND_MAP = [
|
|
241
|
+
{
|
|
242
|
+
signals: ["intake", "mcp", "package", "install", "dependency", "risk"],
|
|
243
|
+
commands: [
|
|
244
|
+
"node bin/avorelo intake --json",
|
|
245
|
+
"node bin/avorelo guard --action tool_call --target \"unknown-mcp-server\" --json",
|
|
246
|
+
"node --test tests/install-intake-risk.test.js",
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
signals: ["safe-path", "governance", "guard", "block", "agent"],
|
|
251
|
+
commands: [
|
|
252
|
+
"node bin/avorelo guard --action file_write --target \".env\" --json",
|
|
253
|
+
"node --test tests/avorelo-agent-enforcement.test.js",
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
signals: ["session", "context", "continuation", "handoff", "rediscovery"],
|
|
258
|
+
commands: [
|
|
259
|
+
"node bin/avorelo session-context --json",
|
|
260
|
+
"node bin/avorelo session-context --handoff",
|
|
261
|
+
"node --test tests/session-context-optimization.test.js",
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
signals: ["brain", "brain-pack", "brain pack", "context intelligence"],
|
|
266
|
+
commands: [
|
|
267
|
+
"node bin/avorelo brain-pack --json",
|
|
268
|
+
"node --test tests/brain-pack.test.js",
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
signals: ["skill", "recipe", "workflow", "source-backed"],
|
|
273
|
+
commands: [
|
|
274
|
+
"node bin/avorelo skills route --task \"<task>\"",
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
signals: ["frontend", "browser", "screenshot", "visual", "playwright"],
|
|
279
|
+
commands: [
|
|
280
|
+
"node bin/avorelo visual-qa --dry-run",
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
signals: ["release", "merge", "ci", "build", "validate", "pr", "pull request"],
|
|
285
|
+
commands: [
|
|
286
|
+
"node scripts/validate-operating-layer.js",
|
|
287
|
+
"cmd /c npm test",
|
|
288
|
+
"node bin/avorelo proof",
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
const BASE_COMMANDS = [
|
|
294
|
+
"node bin/avorelo work-control --json",
|
|
295
|
+
"node bin/avorelo session-context --json",
|
|
296
|
+
"node bin/avorelo session-context --handoff",
|
|
297
|
+
"node bin/avorelo proof",
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
function buildRelevantCommands(task, targetSurface, wcReceipt, options = {}) {
|
|
301
|
+
const taskStr = String(task || wcReceipt?.task?.text || "").toLowerCase();
|
|
302
|
+
const matched = [];
|
|
303
|
+
|
|
304
|
+
for (const entry of TASK_COMMAND_MAP) {
|
|
305
|
+
if (entry.signals.some((s) => taskStr.includes(s))) {
|
|
306
|
+
matched.push(...entry.commands);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Substitute <task> placeholder
|
|
311
|
+
const taskSlice = String(task || wcReceipt?.task?.text || "current task").slice(0, 60);
|
|
312
|
+
const substituted = matched.map((c) => c.replace(/<task>/g, taskSlice));
|
|
313
|
+
|
|
314
|
+
const combined = [...new Set([...BASE_COMMANDS, ...substituted])];
|
|
315
|
+
|
|
316
|
+
// Target-specific filtering
|
|
317
|
+
if (targetSurface === "local_model") {
|
|
318
|
+
// Local model: minimal — only the most essential
|
|
319
|
+
return combined.slice(0, 3);
|
|
320
|
+
}
|
|
321
|
+
if (targetSurface === "codex") {
|
|
322
|
+
// Codex: command/test oriented — drop explanation-heavy ones
|
|
323
|
+
return combined
|
|
324
|
+
.filter((c) => c.includes("--test") || c.includes("npm test") || c.includes("validate-operating-layer") || c.includes("work-control") || c.includes("proof"))
|
|
325
|
+
.slice(0, 6);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return combined.slice(0, 8);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── Skills route fallback ──────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
function buildSkillsRouteFallback(wcReceipt, task) {
|
|
334
|
+
const skills = wcReceipt?.controls?.skills;
|
|
335
|
+
const skillsReceiptMissing = !skills || skills.status === "missing" || !skills.latestRouteReceiptPath;
|
|
336
|
+
|
|
337
|
+
if (!skillsReceiptMissing) return null;
|
|
338
|
+
|
|
339
|
+
const taskStr = String(task || wcReceipt?.task?.text || "");
|
|
340
|
+
if (!taskStr) return null;
|
|
341
|
+
|
|
342
|
+
// Only recommend fallback for task types where skills route is useful
|
|
343
|
+
const skillsSignals = ["skill", "recipe", "workflow", "reference", "source-backed", "build", "implement", "refactor"];
|
|
344
|
+
const isRelevant = skillsSignals.some((s) => taskStr.toLowerCase().includes(s));
|
|
345
|
+
if (!isRelevant) return null;
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
fallbackCommand: `node bin/avorelo skills route --task "${taskStr.slice(0, 60)}"`,
|
|
349
|
+
reason: "No skills route receipt found — run this command to get source-backed workflow guidance.",
|
|
350
|
+
note: "This is a fallback only. Skills route is not run automatically.",
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function buildModelEffortRecommendation(wcReceipt, proof) {
|
|
355
|
+
if (!proof?.route) {
|
|
356
|
+
if (!wcReceipt) {
|
|
357
|
+
return {
|
|
358
|
+
source: "none",
|
|
359
|
+
recommendation: "Run `avorelo route <task>` to get model/effort recommendation.",
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
source: "work-control",
|
|
364
|
+
recommendation: wcReceipt.workControl?.nextSafestAction
|
|
365
|
+
? "Use smallest safe local scope per work-control guidance."
|
|
366
|
+
: "No route available — run `avorelo route <task>` for routing recommendation.",
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
const route = proof.route;
|
|
370
|
+
return {
|
|
371
|
+
source: "proof-route",
|
|
372
|
+
selectedRoute: route.selectedRoute || null,
|
|
373
|
+
chosenTool: route.chosenTool || null,
|
|
374
|
+
selectedModel: route.selectedModel || null,
|
|
375
|
+
chosenModelProfile: route.chosenModelProfile || null,
|
|
376
|
+
fallbackTool: route.fallbackTool || null,
|
|
377
|
+
recommendation: [
|
|
378
|
+
route.chosenTool ? `Use: ${route.chosenTool}` : null,
|
|
379
|
+
route.selectedModel ? `Model: ${route.selectedModel}` : null,
|
|
380
|
+
route.chosenModelProfile ? `Profile: ${route.chosenModelProfile}` : null,
|
|
381
|
+
(route.safetyNotes || []).slice(0, 2).join("; ") || null,
|
|
382
|
+
].filter(Boolean).join(" · ") || "Use medium scope, local-first.",
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function buildSkillsHints(wcReceipt) {
|
|
387
|
+
const skills = wcReceipt?.controls?.skills;
|
|
388
|
+
if (!skills || skills.status === "missing") {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
status: skills.status,
|
|
393
|
+
reviewedSources: skills.reviewedSources || 0,
|
|
394
|
+
deferredSources: skills.deferredSources || 0,
|
|
395
|
+
latestRouteReceiptPath: skills.latestRouteReceiptPath || null,
|
|
396
|
+
hint: skills.deferredSources > 0
|
|
397
|
+
? `${skills.deferredSources} skill sources deferred — do not use without intake review.`
|
|
398
|
+
: "Skill sources reviewed — use existing skill route receipt.",
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function buildReceiptPathSummary(wcReceipt) {
|
|
403
|
+
const paths = [];
|
|
404
|
+
paths.push("work-control: .claude/cco/orchestration/work-control/latest-receipt.json");
|
|
405
|
+
const cf = wcReceipt?.workControl?.carryForward;
|
|
406
|
+
if (cf?.receiptPaths?.length) {
|
|
407
|
+
paths.push(...cf.receiptPaths.slice(0, 5));
|
|
408
|
+
}
|
|
409
|
+
const unchecked = cf?.uncheckedItems || [];
|
|
410
|
+
return { checked: paths, unchecked };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ── Compact prompt builder ────────────────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
function buildCompactPrompt(sessionStart, targetSurface) {
|
|
416
|
+
const toolName = targetSurface === "codex"
|
|
417
|
+
? "Codex"
|
|
418
|
+
: targetSurface === "local_model"
|
|
419
|
+
? "your local model"
|
|
420
|
+
: "Claude";
|
|
421
|
+
|
|
422
|
+
const lines = [
|
|
423
|
+
`# Start here — do not rediscover repo`,
|
|
424
|
+
"",
|
|
425
|
+
`**Task:** ${sessionStart.objective}`,
|
|
426
|
+
"",
|
|
427
|
+
`**Current state:** ${sessionStart.currentState.finalState?.toUpperCase() || "UNKNOWN"}`,
|
|
428
|
+
`**Next safest action:** ${sessionStart.nextSafestAction}`,
|
|
429
|
+
"",
|
|
430
|
+
"## Do not repeat",
|
|
431
|
+
...(sessionStart.doNotRepeat || []).slice(0, 5).map((item) => `- ${item}`),
|
|
432
|
+
"",
|
|
433
|
+
"## Relevant files (start here)",
|
|
434
|
+
...(sessionStart.relevantFiles || []).slice(0, 12).map((f) => `- ${f}`),
|
|
435
|
+
"",
|
|
436
|
+
"## Commands to run first",
|
|
437
|
+
...(sessionStart.relevantCommands || []).slice(0, 5).map((c) => `- \`${c}\``),
|
|
438
|
+
"",
|
|
439
|
+
"## Active risks",
|
|
440
|
+
...(sessionStart.activeRisks || []).slice(0, 4).map((r) => `- ${r}`),
|
|
441
|
+
];
|
|
442
|
+
|
|
443
|
+
if (sessionStart.recommendedModelOrWorker?.recommendation) {
|
|
444
|
+
lines.push("", `**Model/effort:** ${sessionStart.recommendedModelOrWorker.recommendation}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (sessionStart.skillsHints) {
|
|
448
|
+
lines.push("", `**Skills:** ${sessionStart.skillsHints.hint}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (sessionStart.skillsRouteFallback) {
|
|
452
|
+
lines.push("", `**Skills fallback:** ${sessionStart.skillsRouteFallback.reason}`);
|
|
453
|
+
lines.push(` Command: \`${sessionStart.skillsRouteFallback.fallbackCommand}\``);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (sessionStart.capabilityRecommendation?.recommendedCapabilityPack) {
|
|
457
|
+
lines.push("", `**Capability:** ${sessionStart.capabilityRecommendation.recommendedCapabilityPackName || sessionStart.capabilityRecommendation.recommendedCapabilityPack}`);
|
|
458
|
+
lines.push(` Why: ${sessionStart.capabilityRecommendation.capabilityReason || ""}`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (sessionStart.nextRunContext?.compactText) {
|
|
462
|
+
lines.push("", sessionStart.nextRunContext.compactText);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
lines.push(
|
|
466
|
+
"",
|
|
467
|
+
`*Context generated by Avorelo session-context. Do not paste raw receipts — use \`avorelo work-control --json\` for live state.*`,
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
return lines.join("\n");
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ── Context budget guard ──────────────────────────────────────────────────────
|
|
474
|
+
|
|
475
|
+
function applyContextBudget(context, maxChars) {
|
|
476
|
+
const hard = Math.min(maxChars, HARD_MAX_CHARS);
|
|
477
|
+
const sections = context.sessionStart;
|
|
478
|
+
const prompt = buildCompactPrompt(sections, context.targetSurface || "generic");
|
|
479
|
+
|
|
480
|
+
if (prompt.length <= hard) {
|
|
481
|
+
return {
|
|
482
|
+
compactPrompt: prompt,
|
|
483
|
+
truncationApplied: false,
|
|
484
|
+
includedSections: Object.keys(sections),
|
|
485
|
+
excludedSections: [],
|
|
486
|
+
estimatedTokens: estimateTokens(prompt.length),
|
|
487
|
+
avoidedJsonDump: true,
|
|
488
|
+
avoidedBroadRediscovery: true,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Drop lowest priority sections to fit budget
|
|
493
|
+
const dropOrder = [
|
|
494
|
+
"alreadyDone",
|
|
495
|
+
"receiptPathSummary",
|
|
496
|
+
"skillsHints",
|
|
497
|
+
"recommendedModelOrWorker",
|
|
498
|
+
"relevantFiles",
|
|
499
|
+
"activeRisks",
|
|
500
|
+
];
|
|
501
|
+
|
|
502
|
+
const reduced = { ...sections };
|
|
503
|
+
const excluded = [];
|
|
504
|
+
|
|
505
|
+
for (const key of dropOrder) {
|
|
506
|
+
const test = buildCompactPrompt(reduced, context.targetSurface || "generic");
|
|
507
|
+
if (test.length <= hard) break;
|
|
508
|
+
if (key in reduced) {
|
|
509
|
+
delete reduced[key];
|
|
510
|
+
excluded.push(key);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const finalPrompt = buildCompactPrompt(reduced, context.targetSurface || "generic");
|
|
515
|
+
return {
|
|
516
|
+
compactPrompt: finalPrompt,
|
|
517
|
+
truncationApplied: excluded.length > 0,
|
|
518
|
+
includedSections: Object.keys(reduced),
|
|
519
|
+
excludedSections: excluded,
|
|
520
|
+
estimatedTokens: estimateTokens(finalPrompt.length),
|
|
521
|
+
avoidedJsonDump: true,
|
|
522
|
+
avoidedBroadRediscovery: true,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// ── Markdown handoff builder ──────────────────────────────────────────────────
|
|
527
|
+
|
|
528
|
+
function buildMarkdownHandoff(context, budgetResult) {
|
|
529
|
+
const targetLabel = {
|
|
530
|
+
claude: "Claude Code",
|
|
531
|
+
codex: "Codex",
|
|
532
|
+
local_model: "Local Model",
|
|
533
|
+
generic: "AI Assistant",
|
|
534
|
+
}[context.targetSurface || "generic"] || "AI Assistant";
|
|
535
|
+
|
|
536
|
+
const s = context.sessionStart;
|
|
537
|
+
const lines = [
|
|
538
|
+
`# Avorelo Session Context — ${targetLabel}`,
|
|
539
|
+
`> **Start here — do not rediscover repo**`,
|
|
540
|
+
`> Generated: ${context.createdAt}`,
|
|
541
|
+
`> Context ID: ${context.contextId}`,
|
|
542
|
+
"",
|
|
543
|
+
`## Task`,
|
|
544
|
+
s.objective,
|
|
545
|
+
"",
|
|
546
|
+
`## Current State`,
|
|
547
|
+
`- **Status:** ${s.currentState?.finalState?.toUpperCase() || "UNKNOWN"}`,
|
|
548
|
+
s.currentState?.blockedCount > 0 ? `- **Blocked actions:** ${s.currentState.blockedCount}` : null,
|
|
549
|
+
s.currentState?.highRiskIntakeCount > 0 ? `- **High-risk intake items:** ${s.currentState.highRiskIntakeCount}` : null,
|
|
550
|
+
`- **Receipt:** \`.claude/cco/orchestration/work-control/latest-receipt.json\``,
|
|
551
|
+
"",
|
|
552
|
+
`## Next Safest Action`,
|
|
553
|
+
s.nextSafestAction,
|
|
554
|
+
"",
|
|
555
|
+
`## Do Not Repeat`,
|
|
556
|
+
...(s.doNotRepeat || []).map((item) => `- ${item}`),
|
|
557
|
+
"",
|
|
558
|
+
`## Relevant Files`,
|
|
559
|
+
...(s.relevantFiles || []).map((f) => `- \`${f}\``),
|
|
560
|
+
"",
|
|
561
|
+
`## Commands to Run First`,
|
|
562
|
+
...(s.relevantCommands || []).map((c) => `- \`${c}\``),
|
|
563
|
+
"",
|
|
564
|
+
`## Active Risks`,
|
|
565
|
+
...(s.activeRisks || []).map((r) => `- ${r}`),
|
|
566
|
+
"",
|
|
567
|
+
].filter((line) => line !== null);
|
|
568
|
+
|
|
569
|
+
if (s.alreadyDone?.length) {
|
|
570
|
+
lines.push("## Already Done");
|
|
571
|
+
s.alreadyDone.forEach((item) => lines.push(`- ${item}`));
|
|
572
|
+
lines.push("");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (s.recommendedModelOrWorker?.recommendation) {
|
|
576
|
+
lines.push("## Model / Effort");
|
|
577
|
+
lines.push(s.recommendedModelOrWorker.recommendation);
|
|
578
|
+
lines.push("");
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (s.skillsHints) {
|
|
582
|
+
lines.push("## Skills Continuation");
|
|
583
|
+
lines.push(s.skillsHints.hint);
|
|
584
|
+
lines.push("");
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (s.skillsRouteFallback) {
|
|
588
|
+
lines.push("## Skills Route Fallback");
|
|
589
|
+
lines.push(s.skillsRouteFallback.reason);
|
|
590
|
+
lines.push(`\`${s.skillsRouteFallback.fallbackCommand}\``);
|
|
591
|
+
lines.push("");
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (s.capabilityRecommendation?.recommendedCapabilityPack) {
|
|
595
|
+
const cap = s.capabilityRecommendation;
|
|
596
|
+
lines.push("## Recommended Capability");
|
|
597
|
+
lines.push(`- Pack: **${cap.recommendedCapabilityPackName || cap.recommendedCapabilityPack}**`);
|
|
598
|
+
lines.push(`- Why: ${cap.capabilityReason || ""}`);
|
|
599
|
+
lines.push(`- Next: ${cap.capabilityNextAction || ""}`);
|
|
600
|
+
if (cap.additionalCapabilityPacks?.length) {
|
|
601
|
+
lines.push(`- Also relevant: ${cap.additionalCapabilityPacks.map((p) => p.name).join(", ")}`);
|
|
602
|
+
}
|
|
603
|
+
lines.push("");
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (s.brainPackSummary) {
|
|
607
|
+
const bp = s.brainPackSummary;
|
|
608
|
+
lines.push("## Brain Pack");
|
|
609
|
+
lines.push(`- Project: ${bp.projectName || ""}`);
|
|
610
|
+
lines.push(`- Stage: ${bp.currentRoadmapStage || ""}`);
|
|
611
|
+
if (bp.detectedRecipe) lines.push(`- Recipe: ${bp.detectedRecipe}`);
|
|
612
|
+
lines.push(`- Path: \`${bp.jsonPath}\``);
|
|
613
|
+
lines.push("");
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (s.targetSpecific) {
|
|
617
|
+
const ts = s.targetSpecific;
|
|
618
|
+
lines.push(`## Target: ${ts.targetLabel}`);
|
|
619
|
+
lines.push(ts.topNote);
|
|
620
|
+
if (ts.architectureNote) lines.push(`> ${ts.architectureNote}`);
|
|
621
|
+
if (ts.doNotStart?.length) {
|
|
622
|
+
ts.doNotStart.forEach((d) => lines.push(`- ${d}`));
|
|
623
|
+
}
|
|
624
|
+
lines.push("");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (s.nextRunContext?.compactText) {
|
|
628
|
+
lines.push("## Run Improvements · Next-Run Context");
|
|
629
|
+
lines.push(s.nextRunContext.compactText);
|
|
630
|
+
lines.push(`- Status: ${s.nextRunContext.status} · ${s.nextRunContext.defaultsCount} default(s)`);
|
|
631
|
+
lines.push(`- Artifact: \`${s.nextRunContext.artifactPath}\``);
|
|
632
|
+
lines.push("");
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
lines.push("## Context Budget");
|
|
636
|
+
lines.push(`- Estimated tokens: ${budgetResult.estimatedTokens}`);
|
|
637
|
+
lines.push(`- Truncation applied: ${budgetResult.truncationApplied}`);
|
|
638
|
+
if (budgetResult.excludedSections.length) {
|
|
639
|
+
lines.push(`- Excluded sections: ${budgetResult.excludedSections.join(", ")}`);
|
|
640
|
+
}
|
|
641
|
+
lines.push("");
|
|
642
|
+
lines.push("---");
|
|
643
|
+
lines.push("*Generated by `avorelo session-context`. Do not paste raw receipts — use `avorelo work-control --json` for live state.*");
|
|
644
|
+
|
|
645
|
+
return lines.join("\n");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// ── Main build function ───────────────────────────────────────────────────────
|
|
649
|
+
|
|
650
|
+
// ── Capability recommender integration ────────────────────────────────────────
|
|
651
|
+
|
|
652
|
+
function readCapabilityRecommendation(cwd) {
|
|
653
|
+
try {
|
|
654
|
+
const { recommendCapabilities } = require("./capability-recommender");
|
|
655
|
+
const rec = recommendCapabilities({ cwd, signals: {}, plan: "free" });
|
|
656
|
+
return {
|
|
657
|
+
recommendedCapabilityPack: rec.recommendedPack?.id || null,
|
|
658
|
+
recommendedCapabilityPackName: rec.recommendedPack?.name || null,
|
|
659
|
+
capabilityReason: rec.reason || null,
|
|
660
|
+
capabilityNextAction: rec.nextAction || null,
|
|
661
|
+
capabilityFirstCommand: rec.nextAction
|
|
662
|
+
? rec.nextAction.replace(/^Run\s+/, "").replace(/\s+to\s.*$/, "").replace(/[`'"]/g, "").trim()
|
|
663
|
+
: null,
|
|
664
|
+
additionalCapabilityPacks: (rec.additionalPacks || []).map((p) => ({
|
|
665
|
+
id: p.id,
|
|
666
|
+
name: p.name,
|
|
667
|
+
})),
|
|
668
|
+
};
|
|
669
|
+
} catch {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// ── Brain Pack reader (safe) ──────────────────────────────────────────────────
|
|
675
|
+
|
|
676
|
+
function readBrainPackForContext(cwd) {
|
|
677
|
+
try {
|
|
678
|
+
const { readLatestBrainPack } = require("./brain-pack");
|
|
679
|
+
const bp = readLatestBrainPack(cwd);
|
|
680
|
+
if (!bp) return null;
|
|
681
|
+
// Return a compact summary only — never the full brain pack
|
|
682
|
+
return {
|
|
683
|
+
brainPackId: bp.brainPackId,
|
|
684
|
+
projectName: bp.project?.name || null,
|
|
685
|
+
currentRoadmapStage: bp.project?.currentRoadmapStage || null,
|
|
686
|
+
contextRules: bp.contextRules || null,
|
|
687
|
+
lastWorkControlFinalState: bp.signals?.lastWorkControlFinalState || null,
|
|
688
|
+
capabilityRecommended: bp.capabilityRecommendation?.recommendedPackId || null,
|
|
689
|
+
recipesCount: (bp.recipes || []).length,
|
|
690
|
+
detectedRecipe: (bp.recipes || [])[0]?.detectedRecipe || null,
|
|
691
|
+
jsonPath: ".claude/cco/context/brain-pack/latest.json",
|
|
692
|
+
};
|
|
693
|
+
} catch {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ── Target-specific context additions ────────────────────────────────────────
|
|
699
|
+
|
|
700
|
+
function buildTargetSpecificGuidance(targetSurface, objective, capabilityRec) {
|
|
701
|
+
switch (targetSurface) {
|
|
702
|
+
case "claude":
|
|
703
|
+
return {
|
|
704
|
+
targetLabel: "Claude Code",
|
|
705
|
+
emphasis: "full context, architecture awareness, named files, bounded implementation",
|
|
706
|
+
topNote: "Do not rediscover repo. Read named files first. Inspect before coding. Keep scope bounded.",
|
|
707
|
+
architectureNote: "Capability Recommender is the single router — no duplicate routing tables.",
|
|
708
|
+
doNotStart: capabilityRec?.additionalCapabilityPacks?.map((p) => `Do not start ${p.name} work in this session`) || [],
|
|
709
|
+
};
|
|
710
|
+
case "codex":
|
|
711
|
+
return {
|
|
712
|
+
targetLabel: "Codex",
|
|
713
|
+
emphasis: "command/test oriented, short scope, exact validation commands",
|
|
714
|
+
topNote: "Use exact commands. Run tests to verify. Avoid broad explanation. Stay within bounded scope.",
|
|
715
|
+
architectureNote: null,
|
|
716
|
+
doNotStart: ["Do not do broad codebase explanation.", "Use exact test commands to verify changes."],
|
|
717
|
+
};
|
|
718
|
+
case "local_model":
|
|
719
|
+
return {
|
|
720
|
+
targetLabel: "Local Model",
|
|
721
|
+
emphasis: "shortest context, fewer files, simple tasks only",
|
|
722
|
+
topNote: "Minimal context. Simple bounded task only. Do not attempt architecture-heavy changes.",
|
|
723
|
+
architectureNote: null,
|
|
724
|
+
doNotStart: ["Do not attempt complex multi-file changes.", "Prefer single-file bounded edits."],
|
|
725
|
+
};
|
|
726
|
+
default:
|
|
727
|
+
return {
|
|
728
|
+
targetLabel: "AI Assistant",
|
|
729
|
+
emphasis: "neutral compact handoff",
|
|
730
|
+
topNote: "Start from current state. Do not rediscover repo. Use named files.",
|
|
731
|
+
architectureNote: null,
|
|
732
|
+
doNotStart: [],
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function buildSessionContext(cwd, options = {}) {
|
|
738
|
+
const maxChars = Math.min(
|
|
739
|
+
options.maxChars || DEFAULT_MAX_CHARS,
|
|
740
|
+
HARD_MAX_CHARS,
|
|
741
|
+
);
|
|
742
|
+
const targetSurface = options.targetSurface || "generic";
|
|
743
|
+
const task = options.task;
|
|
744
|
+
|
|
745
|
+
const wcReceipt = readWorkControlReceipt(cwd);
|
|
746
|
+
const proof = readProof(cwd);
|
|
747
|
+
const workspaceMap = readOrBuildWorkspaceMap(cwd, {
|
|
748
|
+
refreshWorkspaceMap: options.refreshWorkspaceMap || false,
|
|
749
|
+
task: task || wcReceipt?.task?.text || "",
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
const objective = buildObjectiveSection(options, wcReceipt, proof);
|
|
753
|
+
const currentState = buildCurrentStateSection(wcReceipt);
|
|
754
|
+
const nextSafestAction = buildNextSafestAction(wcReceipt);
|
|
755
|
+
const alreadyDone = buildAlreadyDone(wcReceipt, proof);
|
|
756
|
+
const doNotRepeat = buildDoNotRepeat(wcReceipt);
|
|
757
|
+
const activeRisks = buildActiveRisks(wcReceipt);
|
|
758
|
+
const relevantFiles = buildRelevantFiles(wcReceipt, workspaceMap, {
|
|
759
|
+
task: task || wcReceipt?.task?.text || "",
|
|
760
|
+
maxFiles: 20,
|
|
761
|
+
cwd,
|
|
762
|
+
});
|
|
763
|
+
const relevantCommands = buildRelevantCommands(task, targetSurface, wcReceipt, {});
|
|
764
|
+
const recommendedModelOrWorker = buildModelEffortRecommendation(wcReceipt, proof);
|
|
765
|
+
const skillsHints = buildSkillsHints(wcReceipt);
|
|
766
|
+
const skillsRouteFallback = buildSkillsRouteFallback(wcReceipt, task || wcReceipt?.task?.text);
|
|
767
|
+
const receiptPathSummary = buildReceiptPathSummary(wcReceipt);
|
|
768
|
+
|
|
769
|
+
// Next-run context (run improvements + learning) — compact injection only
|
|
770
|
+
let nextRunContext = null;
|
|
771
|
+
try {
|
|
772
|
+
const { loadNextRunContext, buildCompactContextText } = require("./next-run-context");
|
|
773
|
+
const nrc = loadNextRunContext(cwd);
|
|
774
|
+
if (nrc && (nrc.status === "prepared" || nrc.status === "applied" || nrc.status === "injected") && (nrc.appliedDefaults || []).length > 0) {
|
|
775
|
+
nextRunContext = {
|
|
776
|
+
status: nrc.status,
|
|
777
|
+
defaultsCount: (nrc.appliedDefaults || []).length,
|
|
778
|
+
topDefault: (nrc.appliedDefaults || [])[0]?.text || null,
|
|
779
|
+
compactText: buildCompactContextText(nrc),
|
|
780
|
+
artifactPath: ".claude/cco/state/next-run-context.json",
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
} catch {}
|
|
784
|
+
|
|
785
|
+
// Capability recommender integration (reuse, do not duplicate)
|
|
786
|
+
const capabilityRecommendation = readCapabilityRecommendation(cwd);
|
|
787
|
+
|
|
788
|
+
// Brain pack compact inclusion
|
|
789
|
+
const brainPackSummary = readBrainPackForContext(cwd);
|
|
790
|
+
|
|
791
|
+
// Target-specific guidance
|
|
792
|
+
const targetSpecific = buildTargetSpecificGuidance(targetSurface, objective, capabilityRecommendation);
|
|
793
|
+
|
|
794
|
+
const sessionStart = {
|
|
795
|
+
objective,
|
|
796
|
+
currentState,
|
|
797
|
+
nextSafestAction,
|
|
798
|
+
alreadyDone,
|
|
799
|
+
doNotRepeat,
|
|
800
|
+
activeRisks,
|
|
801
|
+
relevantFiles,
|
|
802
|
+
relevantCommands,
|
|
803
|
+
recommendedModelOrWorker,
|
|
804
|
+
skillsHints,
|
|
805
|
+
skillsRouteFallback,
|
|
806
|
+
receiptPathSummary,
|
|
807
|
+
contextBudget: { maxChars, hardMaxChars: HARD_MAX_CHARS },
|
|
808
|
+
capabilityRecommendation,
|
|
809
|
+
brainPackSummary,
|
|
810
|
+
targetSpecific,
|
|
811
|
+
nextRunContext,
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
const contextId = createContextId();
|
|
815
|
+
const createdAt = nowIso();
|
|
816
|
+
|
|
817
|
+
const baseContext = {
|
|
818
|
+
schemaVersion: SESSION_CONTEXT_SCHEMA_VERSION,
|
|
819
|
+
contract: SESSION_CONTEXT_CONTRACT,
|
|
820
|
+
contextType: "session_start",
|
|
821
|
+
contextId,
|
|
822
|
+
createdAt,
|
|
823
|
+
targetSurface,
|
|
824
|
+
sourceReceipts: {
|
|
825
|
+
workControl: wcReceipt ? ".claude/cco/orchestration/work-control/latest-receipt.json" : null,
|
|
826
|
+
proof: proof ? ".claude/cco/orchestration/current-proof.json" : null,
|
|
827
|
+
route: proof?.route ? "via-proof" : null,
|
|
828
|
+
intake: wcReceipt?.controls?.installIntake?.latestReceiptPath || null,
|
|
829
|
+
governance: wcReceipt?.controls?.governance?.latestReceiptPath || null,
|
|
830
|
+
safePath: wcReceipt?.controls?.safePath?.latestReceiptPath || null,
|
|
831
|
+
skills: wcReceipt?.controls?.skills?.latestRouteReceiptPath || null,
|
|
832
|
+
workspaceMap: workspaceMap?.artifactPath || null,
|
|
833
|
+
},
|
|
834
|
+
sessionStart,
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
const budgetResult = applyContextBudget(baseContext, maxChars);
|
|
838
|
+
const markdownHandoff = buildMarkdownHandoff(baseContext, budgetResult);
|
|
839
|
+
|
|
840
|
+
baseContext.sessionStart.compactPrompt = budgetResult.compactPrompt;
|
|
841
|
+
baseContext.sessionStart.markdownHandoff = markdownHandoff;
|
|
842
|
+
|
|
843
|
+
baseContext.tokenDiscipline = {
|
|
844
|
+
maxChars,
|
|
845
|
+
estimatedTokens: budgetResult.estimatedTokens,
|
|
846
|
+
avoidedJsonDump: budgetResult.avoidedJsonDump,
|
|
847
|
+
avoidedBroadRediscovery: budgetResult.avoidedBroadRediscovery,
|
|
848
|
+
includedSections: budgetResult.includedSections,
|
|
849
|
+
excludedSections: budgetResult.excludedSections,
|
|
850
|
+
truncationApplied: budgetResult.truncationApplied,
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// Brain Pack is now real — scaffold is superseded
|
|
854
|
+
baseContext.brainPackUsed = Boolean(brainPackSummary);
|
|
855
|
+
baseContext.brainPackSummary = brainPackSummary;
|
|
856
|
+
baseContext.targetSpecificOutput = targetSurface !== "generic";
|
|
857
|
+
baseContext.dynamicCommands = true;
|
|
858
|
+
|
|
859
|
+
baseContext.featureCoverageLedger = [
|
|
860
|
+
{ feature: "compact_session_start_context", status: "implemented_now" },
|
|
861
|
+
{ feature: "continuation_packet_from_work_control", status: "implemented_now" },
|
|
862
|
+
{ feature: "do_not_repeat_guidance", status: "implemented_now" },
|
|
863
|
+
{ feature: "branch_worktree_task_summary", status: "implemented_now" },
|
|
864
|
+
{ feature: "receipt_path_summary", status: "implemented_now" },
|
|
865
|
+
{ feature: "next_safest_action", status: "implemented_now" },
|
|
866
|
+
{ feature: "what_was_checked_not_checked", status: "implemented_now" },
|
|
867
|
+
{ feature: "relevant_files_from_workspace_map", status: "implemented_now" },
|
|
868
|
+
{ feature: "avoid_huge_json_dumps", status: "implemented_now" },
|
|
869
|
+
{ feature: "avoid_broad_repo_rediscovery", status: "implemented_now" },
|
|
870
|
+
{ feature: "context_budget_guard", status: "implemented_now" },
|
|
871
|
+
{ feature: "model_effort_recommendation_summary", status: "implemented_now" },
|
|
872
|
+
{ feature: "skills_aware_continuation_hints", status: "implemented_now" },
|
|
873
|
+
{ feature: "intake_governance_safe_path_risk_summary", status: "implemented_now" },
|
|
874
|
+
{ feature: "cli_command_session_context", status: "implemented_now" },
|
|
875
|
+
{ feature: "product_learning_events", status: "implemented_now" },
|
|
876
|
+
{ feature: "status_proof_dashboard_compact_surface", status: "implemented_now" },
|
|
877
|
+
{ feature: "dogfood_with_avorelo_on_this_pr", status: "implemented_now" },
|
|
878
|
+
{ feature: "brain_pack_basic", status: "implemented_now" },
|
|
879
|
+
{ feature: "recipe_detection_basic", status: "implemented_now" },
|
|
880
|
+
{ feature: "dynamic_relevant_commands", status: "implemented_now" },
|
|
881
|
+
{ feature: "target_specific_handoff", status: "implemented_now" },
|
|
882
|
+
{ feature: "skills_route_fallback", status: "implemented_now" },
|
|
883
|
+
{ feature: "capability_recommender_context_bridge", status: "implemented_now" },
|
|
884
|
+
{ feature: "dogfood_feedback_loop_structured", status: "implemented_now" },
|
|
885
|
+
{ feature: "task_context_pack_builder_shape", status: "scaffolded_now" },
|
|
886
|
+
{ feature: "post_run_proof_summary_hook", status: "scaffolded_now" },
|
|
887
|
+
{ feature: "tool_connector_gating_hook", status: "scaffolded_now" },
|
|
888
|
+
{ feature: "local_premium_routing_hints", status: "scaffolded_now" },
|
|
889
|
+
{ feature: "token_savings_estimate", status: "scaffolded_now" },
|
|
890
|
+
{ feature: "adapter_output_hook", status: "scaffolded_now" },
|
|
891
|
+
{ feature: "full_brain_pack_management", status: "documented_deferred" },
|
|
892
|
+
{ feature: "full_ai_workflow_discipline_system", status: "documented_deferred" },
|
|
893
|
+
{ feature: "workflow_intelligence_radar", status: "documented_deferred" },
|
|
894
|
+
{ feature: "ai_workspace_registry_asset_inventory", status: "documented_deferred" },
|
|
895
|
+
{ feature: "browser_proof_evidence", status: "documented_deferred" },
|
|
896
|
+
{ feature: "full_token_cost_analytics_dashboard", status: "documented_deferred" },
|
|
897
|
+
{ feature: "full_recipe_library", status: "documented_deferred" },
|
|
898
|
+
{ feature: "full_model_gateway_optillm", status: "documented_deferred" },
|
|
899
|
+
{ feature: "team_cloud_sync", status: "documented_deferred" },
|
|
900
|
+
{ feature: "pricing_website_plugin_launch", status: "documented_deferred" },
|
|
901
|
+
];
|
|
902
|
+
|
|
903
|
+
baseContext.productLearning = {
|
|
904
|
+
eventsWritten: 0,
|
|
905
|
+
suggestedEvents: [
|
|
906
|
+
"session_context_generated",
|
|
907
|
+
"session_context_handoff_generated",
|
|
908
|
+
"context_budget_applied",
|
|
909
|
+
"broad_rediscovery_avoided",
|
|
910
|
+
"huge_json_dump_avoided",
|
|
911
|
+
"continuation_packet_used",
|
|
912
|
+
"model_effort_recommendation_generated",
|
|
913
|
+
"brain_pack_used_in_session_context",
|
|
914
|
+
"dynamic_commands_generated",
|
|
915
|
+
"target_specific_handoff_generated",
|
|
916
|
+
"capability_recommendation_embedded",
|
|
917
|
+
"skills_route_fallback_recommended",
|
|
918
|
+
],
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
baseContext.redacted = true;
|
|
922
|
+
return baseContext;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function writeSessionContext(cwd, context, options = {}) {
|
|
926
|
+
ensureCcoDirs(cwd);
|
|
927
|
+
|
|
928
|
+
const fs2 = require("fs");
|
|
929
|
+
const path2 = require("path");
|
|
930
|
+
|
|
931
|
+
// Ensure session-context dir exists
|
|
932
|
+
const scDir = path2.join(cwd, ".claude", "cco", "orchestration", "session-context");
|
|
933
|
+
fs2.mkdirSync(scDir, { recursive: true });
|
|
934
|
+
|
|
935
|
+
// Write latest JSON (without markdownHandoff to keep it compact)
|
|
936
|
+
const jsonPayload = { ...context };
|
|
937
|
+
const { markdownHandoff, compactPrompt, ...sessionStartWithoutBulky } = context.sessionStart;
|
|
938
|
+
jsonPayload.sessionStart = {
|
|
939
|
+
...sessionStartWithoutBulky,
|
|
940
|
+
compactPromptLength: compactPrompt?.length || 0,
|
|
941
|
+
markdownHandoffLength: markdownHandoff?.length || 0,
|
|
942
|
+
markdownHandoffPath: LATEST_CONTEXT_MD_REL_PATH,
|
|
943
|
+
};
|
|
944
|
+
safeWriteJson(cwd, LATEST_CONTEXT_REL_PATH, jsonPayload);
|
|
945
|
+
|
|
946
|
+
// Write latest Markdown
|
|
947
|
+
safeWriteText(cwd, LATEST_CONTEXT_MD_REL_PATH, markdownHandoff || "");
|
|
948
|
+
|
|
949
|
+
// Write history (plan-gated)
|
|
950
|
+
const canExport = options.plan && options.plan !== "free";
|
|
951
|
+
if (canExport) {
|
|
952
|
+
const historyDir = path2.join(cwd, CONTEXT_HISTORY_DIR_REL_PATH);
|
|
953
|
+
fs2.mkdirSync(historyDir, { recursive: true });
|
|
954
|
+
safeWriteJson(cwd, `${CONTEXT_HISTORY_DIR_REL_PATH}/${context.contextId}.json`, jsonPayload);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Append to event log
|
|
958
|
+
const eventLine = {
|
|
959
|
+
contextId: context.contextId,
|
|
960
|
+
finalState: context.sessionStart?.currentState?.finalState || "missing",
|
|
961
|
+
estimatedTokens: context.tokenDiscipline?.estimatedTokens || 0,
|
|
962
|
+
truncationApplied: context.tokenDiscipline?.truncationApplied || false,
|
|
963
|
+
targetSurface: context.targetSurface || "generic",
|
|
964
|
+
createdAt: context.createdAt,
|
|
965
|
+
};
|
|
966
|
+
try {
|
|
967
|
+
const abs = path2.join(cwd, CONTEXT_EVENT_LOG_REL_PATH);
|
|
968
|
+
fs2.mkdirSync(path2.dirname(abs), { recursive: true });
|
|
969
|
+
fs2.appendFileSync(abs, `${JSON.stringify(eventLine)}\n`, "utf8");
|
|
970
|
+
} catch {}
|
|
971
|
+
|
|
972
|
+
// Write product learning events
|
|
973
|
+
let eventsWritten = 0;
|
|
974
|
+
const productEvents = [
|
|
975
|
+
{
|
|
976
|
+
eventName: "session_context_generated",
|
|
977
|
+
category: "session_context",
|
|
978
|
+
status: "completed",
|
|
979
|
+
payload: {
|
|
980
|
+
finalState: context.sessionStart?.currentState?.finalState || "missing",
|
|
981
|
+
estimatedTokens: context.tokenDiscipline?.estimatedTokens || 0,
|
|
982
|
+
truncationApplied: context.tokenDiscipline?.truncationApplied || false,
|
|
983
|
+
targetSurface: context.targetSurface || "generic",
|
|
984
|
+
avoidedJsonDump: context.tokenDiscipline?.avoidedJsonDump || false,
|
|
985
|
+
avoidedBroadRediscovery: context.tokenDiscipline?.avoidedBroadRediscovery || false,
|
|
986
|
+
},
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
eventName: "context_budget_applied",
|
|
990
|
+
category: "session_context",
|
|
991
|
+
status: "completed",
|
|
992
|
+
payload: {
|
|
993
|
+
maxChars: context.tokenDiscipline?.maxChars || DEFAULT_MAX_CHARS,
|
|
994
|
+
estimatedTokens: context.tokenDiscipline?.estimatedTokens || 0,
|
|
995
|
+
truncationApplied: context.tokenDiscipline?.truncationApplied || false,
|
|
996
|
+
excludedSectionsCount: (context.tokenDiscipline?.excludedSections || []).length,
|
|
997
|
+
},
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
eventName: "broad_rediscovery_avoided",
|
|
1001
|
+
category: "session_context",
|
|
1002
|
+
status: "completed",
|
|
1003
|
+
payload: {
|
|
1004
|
+
relevantFilesCount: (context.sessionStart?.relevantFiles || []).length,
|
|
1005
|
+
workspaceMapUsed: Boolean(context.sourceReceipts?.workspaceMap),
|
|
1006
|
+
},
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
eventName: "huge_json_dump_avoided",
|
|
1010
|
+
category: "session_context",
|
|
1011
|
+
status: "completed",
|
|
1012
|
+
payload: {
|
|
1013
|
+
avoidedJsonDump: true,
|
|
1014
|
+
estimatedTokens: context.tokenDiscipline?.estimatedTokens || 0,
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
eventName: "continuation_packet_used",
|
|
1019
|
+
category: "session_context",
|
|
1020
|
+
status: "completed",
|
|
1021
|
+
payload: {
|
|
1022
|
+
workControlReceiptAvailable: Boolean(context.sourceReceipts?.workControl),
|
|
1023
|
+
carryForwardAvailable: Boolean(context.sessionStart?.alreadyDone?.length),
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
1026
|
+
];
|
|
1027
|
+
|
|
1028
|
+
if (context.sessionStart?.recommendedModelOrWorker?.source !== "none") {
|
|
1029
|
+
productEvents.push({
|
|
1030
|
+
eventName: "model_effort_recommendation_generated",
|
|
1031
|
+
category: "session_context",
|
|
1032
|
+
status: "completed",
|
|
1033
|
+
payload: {
|
|
1034
|
+
source: context.sessionStart.recommendedModelOrWorker.source || "unknown",
|
|
1035
|
+
hasChosenTool: Boolean(context.sessionStart.recommendedModelOrWorker.chosenTool),
|
|
1036
|
+
},
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
if (context.brainPackUsed) {
|
|
1041
|
+
productEvents.push({
|
|
1042
|
+
eventName: "brain_pack_used_in_session_context",
|
|
1043
|
+
category: "brain_pack",
|
|
1044
|
+
status: "completed",
|
|
1045
|
+
payload: {
|
|
1046
|
+
brainPackId: context.brainPackSummary?.brainPackId || null,
|
|
1047
|
+
detectedRecipe: context.brainPackSummary?.detectedRecipe || null,
|
|
1048
|
+
},
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if (context.dynamicCommands) {
|
|
1053
|
+
productEvents.push({
|
|
1054
|
+
eventName: "dynamic_commands_generated",
|
|
1055
|
+
category: "session_context",
|
|
1056
|
+
status: "completed",
|
|
1057
|
+
payload: {
|
|
1058
|
+
commandsCount: (context.sessionStart?.relevantCommands || []).length,
|
|
1059
|
+
targetSurface: context.targetSurface || "generic",
|
|
1060
|
+
},
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (context.targetSpecificOutput) {
|
|
1065
|
+
productEvents.push({
|
|
1066
|
+
eventName: "target_specific_handoff_generated",
|
|
1067
|
+
category: "session_context",
|
|
1068
|
+
status: "completed",
|
|
1069
|
+
payload: {
|
|
1070
|
+
targetSurface: context.targetSurface || "generic",
|
|
1071
|
+
targetLabel: context.sessionStart?.targetSpecific?.targetLabel || null,
|
|
1072
|
+
},
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if (context.sessionStart?.capabilityRecommendation?.recommendedCapabilityPack) {
|
|
1077
|
+
productEvents.push({
|
|
1078
|
+
eventName: "capability_recommendation_embedded",
|
|
1079
|
+
category: "session_context",
|
|
1080
|
+
status: "completed",
|
|
1081
|
+
payload: {
|
|
1082
|
+
recommendedPackId: context.sessionStart.capabilityRecommendation.recommendedCapabilityPack,
|
|
1083
|
+
additionalPacksCount: (context.sessionStart.capabilityRecommendation.additionalCapabilityPacks || []).length,
|
|
1084
|
+
},
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (context.sessionStart?.skillsRouteFallback) {
|
|
1089
|
+
productEvents.push({
|
|
1090
|
+
eventName: "skills_route_fallback_recommended",
|
|
1091
|
+
category: "session_context",
|
|
1092
|
+
status: "completed",
|
|
1093
|
+
payload: {
|
|
1094
|
+
reason: context.sessionStart.skillsRouteFallback.reason || null,
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
productEvents.forEach((event) => {
|
|
1100
|
+
try {
|
|
1101
|
+
appendProductLearningEvent(cwd, event);
|
|
1102
|
+
eventsWritten += 1;
|
|
1103
|
+
} catch {}
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
context.productLearning.eventsWritten = eventsWritten;
|
|
1107
|
+
return LATEST_CONTEXT_REL_PATH;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function readLatestSessionContext(cwd) {
|
|
1111
|
+
return safeReadJson(cwd, LATEST_CONTEXT_REL_PATH, null);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function buildSessionContextSurface(cwd) {
|
|
1115
|
+
const latest = readLatestSessionContext(cwd);
|
|
1116
|
+
if (!latest) {
|
|
1117
|
+
return {
|
|
1118
|
+
status: "missing",
|
|
1119
|
+
latestContextPath: null,
|
|
1120
|
+
latestMarkdownPath: null,
|
|
1121
|
+
estimatedTokens: 0,
|
|
1122
|
+
avoidedJsonDump: false,
|
|
1123
|
+
avoidedBroadRediscovery: false,
|
|
1124
|
+
contextBudgetStatus: "no-context-generated",
|
|
1125
|
+
nextAction: "Run `avorelo session-context` to generate the first session context.",
|
|
1126
|
+
showInStatus: true,
|
|
1127
|
+
showInDashboard: true,
|
|
1128
|
+
statusLine: "Session Context: MISSING · no context generated yet",
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
const td = latest.tokenDiscipline || {};
|
|
1133
|
+
const budgetStatus = td.truncationApplied ? "truncated" : "within-budget";
|
|
1134
|
+
|
|
1135
|
+
return {
|
|
1136
|
+
status: "active",
|
|
1137
|
+
latestContextPath: LATEST_CONTEXT_REL_PATH,
|
|
1138
|
+
latestMarkdownPath: LATEST_CONTEXT_MD_REL_PATH,
|
|
1139
|
+
estimatedTokens: td.estimatedTokens || 0,
|
|
1140
|
+
avoidedJsonDump: td.avoidedJsonDump || false,
|
|
1141
|
+
avoidedBroadRediscovery: td.avoidedBroadRediscovery || false,
|
|
1142
|
+
contextBudgetStatus: budgetStatus,
|
|
1143
|
+
finalState: latest.sessionStart?.currentState?.finalState || "missing",
|
|
1144
|
+
brainPackUsed: latest.brainPackUsed || false,
|
|
1145
|
+
targetSpecificOutput: latest.targetSpecificOutput || false,
|
|
1146
|
+
dynamicCommands: latest.dynamicCommands || false,
|
|
1147
|
+
recommendedCapabilityPack: latest.sessionStart?.capabilityRecommendation?.recommendedCapabilityPack || null,
|
|
1148
|
+
nextAction: latest.sessionStart?.nextSafestAction
|
|
1149
|
+
|| "Continue from session context — run `avorelo session-context --handoff`.",
|
|
1150
|
+
showInStatus: true,
|
|
1151
|
+
showInDashboard: true,
|
|
1152
|
+
statusLine: `Session Context: ${budgetStatus.toUpperCase()} · ~${td.estimatedTokens || 0} tokens · latest ${LATEST_CONTEXT_REL_PATH}`,
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function formatSessionContextText(contextOrSurface, options = {}) {
|
|
1157
|
+
const isHandoff = options.handoff === true;
|
|
1158
|
+
const targetSurface = options.targetSurface || contextOrSurface?.targetSurface || "generic";
|
|
1159
|
+
|
|
1160
|
+
if (!contextOrSurface) {
|
|
1161
|
+
return "Session Context: MISSING · run `avorelo session-context` to generate.\n";
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (contextOrSurface.contract === SESSION_CONTEXT_CONTRACT) {
|
|
1165
|
+
const ctx = contextOrSurface;
|
|
1166
|
+
const s = ctx.sessionStart || {};
|
|
1167
|
+
|
|
1168
|
+
if (isHandoff) {
|
|
1169
|
+
return (s.markdownHandoff || buildMarkdownHandoff(ctx, { estimatedTokens: 0, truncationApplied: false, excludedSections: [] })) + "\n";
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const lines = [
|
|
1173
|
+
`Session Context: ${ctx.contextId}`,
|
|
1174
|
+
`State: ${s.currentState?.finalState?.toUpperCase() || "UNKNOWN"}`,
|
|
1175
|
+
`Target: ${ctx.targetSurface || "generic"}`,
|
|
1176
|
+
`Next: ${s.nextSafestAction || "Continue from context."}`,
|
|
1177
|
+
`Capability: ${s.capabilityRecommendation?.recommendedCapabilityPackName || "none"}`,
|
|
1178
|
+
`BrainPack: ${ctx.brainPackUsed ? "used" : "missing"}`,
|
|
1179
|
+
`Tokens: ~${ctx.tokenDiscipline?.estimatedTokens || 0}`,
|
|
1180
|
+
`Budget: ${ctx.tokenDiscipline?.truncationApplied ? "truncated" : "within-budget"}`,
|
|
1181
|
+
`Context: ${LATEST_CONTEXT_REL_PATH}`,
|
|
1182
|
+
`Handoff: ${LATEST_CONTEXT_MD_REL_PATH}`,
|
|
1183
|
+
];
|
|
1184
|
+
|
|
1185
|
+
return lines.join("\n") + "\n";
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Surface object
|
|
1189
|
+
const surface = contextOrSurface;
|
|
1190
|
+
const lines = [
|
|
1191
|
+
`Session Context: ${surface.contextBudgetStatus?.toUpperCase() || "MISSING"}`,
|
|
1192
|
+
`Tokens: ~${surface.estimatedTokens || 0}`,
|
|
1193
|
+
];
|
|
1194
|
+
if (surface.nextAction) lines.push(`Next: ${surface.nextAction}`);
|
|
1195
|
+
if (surface.latestContextPath) lines.push(`Context: ${surface.latestContextPath}`);
|
|
1196
|
+
return lines.join("\n") + "\n";
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
module.exports = {
|
|
1200
|
+
SESSION_CONTEXT_CONTRACT,
|
|
1201
|
+
SESSION_CONTEXT_SCHEMA_VERSION,
|
|
1202
|
+
LATEST_CONTEXT_REL_PATH,
|
|
1203
|
+
LATEST_CONTEXT_MD_REL_PATH,
|
|
1204
|
+
DEFAULT_MAX_CHARS,
|
|
1205
|
+
HARD_MAX_CHARS,
|
|
1206
|
+
buildSessionContext,
|
|
1207
|
+
writeSessionContext,
|
|
1208
|
+
readLatestSessionContext,
|
|
1209
|
+
buildSessionContextSurface,
|
|
1210
|
+
formatSessionContextText,
|
|
1211
|
+
};
|