@the-open-engine/zeroshot 5.4.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/CHANGELOG.md +539 -0
- package/LICENSE +21 -0
- package/README.md +508 -0
- package/cli/commands/inspect-render.js +135 -0
- package/cli/commands/inspect.js +294 -0
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +5431 -0
- package/cli/lib/first-run.js +211 -0
- package/cli/lib/update-checker.js +281 -0
- package/cli/message-formatter-utils.js +75 -0
- package/cli/message-formatters-normal.js +275 -0
- package/cli/message-formatters-watch.js +185 -0
- package/cluster-templates/base-templates/debug-workflow.json +422 -0
- package/cluster-templates/base-templates/full-workflow.json +727 -0
- package/cluster-templates/base-templates/heavy-validation.json +272 -0
- package/cluster-templates/base-templates/quick-validation.json +285 -0
- package/cluster-templates/base-templates/single-worker.json +71 -0
- package/cluster-templates/base-templates/worker-validator.json +230 -0
- package/cluster-templates/conductor-bootstrap.json +130 -0
- package/docker/zeroshot-cluster/Dockerfile +153 -0
- package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.js +181 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.js +151 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/common.js +71 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.js +176 -0
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/index.js +95 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.js +228 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-actions.js +140 -0
- package/lib/agent-cli-provider/contract-actions.js.map +1 -0
- package/lib/agent-cli-provider/contract-env.d.ts +11 -0
- package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-env.js +113 -0
- package/lib/agent-cli-provider/contract-env.js.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.js +52 -0
- package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
- package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
- package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-errors.js +71 -0
- package/lib/agent-cli-provider/contract-errors.js.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.js +102 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.js +89 -0
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
- package/lib/agent-cli-provider/contract-options.d.ts +3 -0
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-options.js +140 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -0
- package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-parse.js +81 -0
- package/lib/agent-cli-provider/contract-parse.js.map +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts +18 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-support.js +154 -0
- package/lib/agent-cli-provider/contract-support.js.map +1 -0
- package/lib/agent-cli-provider/contract.d.ts +13 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract.js +30 -0
- package/lib/agent-cli-provider/contract.js.map +1 -0
- package/lib/agent-cli-provider/env-safety.d.ts +11 -0
- package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
- package/lib/agent-cli-provider/env-safety.js +83 -0
- package/lib/agent-cli-provider/env-safety.js.map +1 -0
- package/lib/agent-cli-provider/errors.d.ts +5 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/errors.js +115 -0
- package/lib/agent-cli-provider/errors.js.map +1 -0
- package/lib/agent-cli-provider/executable.d.ts +3 -0
- package/lib/agent-cli-provider/executable.d.ts.map +1 -0
- package/lib/agent-cli-provider/executable.js +24 -0
- package/lib/agent-cli-provider/executable.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +8 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/index.js +31 -0
- package/lib/agent-cli-provider/index.js.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.js +17 -0
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
- package/lib/agent-cli-provider/json.d.ts +16 -0
- package/lib/agent-cli-provider/json.d.ts.map +1 -0
- package/lib/agent-cli-provider/json.js +110 -0
- package/lib/agent-cli-provider/json.js.map +1 -0
- package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
- package/lib/agent-cli-provider/log-prefix.js +22 -0
- package/lib/agent-cli-provider/log-prefix.js.map +1 -0
- package/lib/agent-cli-provider/process-runner.d.ts +17 -0
- package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/process-runner.js +89 -0
- package/lib/agent-cli-provider/process-runner.js.map +1 -0
- package/lib/agent-cli-provider/redaction.d.ts +9 -0
- package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
- package/lib/agent-cli-provider/redaction.js +227 -0
- package/lib/agent-cli-provider/redaction.js.map +1 -0
- package/lib/agent-cli-provider/schema.d.ts +5 -0
- package/lib/agent-cli-provider/schema.d.ts.map +1 -0
- package/lib/agent-cli-provider/schema.js +136 -0
- package/lib/agent-cli-provider/schema.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
- package/lib/agent-cli-provider/types.d.ts +194 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -0
- package/lib/agent-cli-provider/types.js +12 -0
- package/lib/agent-cli-provider/types.js.map +1 -0
- package/lib/completion.js +174 -0
- package/lib/detached-startup.js +220 -0
- package/lib/docker-config.js +220 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +55 -0
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/repo-settings.js +69 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +542 -0
- package/lib/start-cluster.js +321 -0
- package/lib/stream-json-parser.js +67 -0
- package/package.json +162 -0
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/test-install.sh +40 -0
- package/scripts/validate-templates.js +107 -0
- package/src/agent/agent-config.js +266 -0
- package/src/agent/agent-context-builder.js +189 -0
- package/src/agent/agent-context-sections.js +338 -0
- package/src/agent/agent-context-sources.js +147 -0
- package/src/agent/agent-hook-executor.js +721 -0
- package/src/agent/agent-input-injector.js +141 -0
- package/src/agent/agent-lifecycle.js +982 -0
- package/src/agent/agent-quality-gate-schema.js +93 -0
- package/src/agent/agent-quality-gates-context.js +51 -0
- package/src/agent/agent-stuck-detector.js +256 -0
- package/src/agent/agent-task-executor.js +2028 -0
- package/src/agent/agent-trigger-evaluator.js +67 -0
- package/src/agent/context-metrics.js +160 -0
- package/src/agent/context-pack-builder.js +367 -0
- package/src/agent/context-replay-policy.js +51 -0
- package/src/agent/guidance-queue.js +77 -0
- package/src/agent/output-extraction.js +367 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/pr-verification.js +653 -0
- package/src/agent/rate-limit-backoff.js +82 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent/validation-platform.js +35 -0
- package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
- package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
- package/src/agent-cli-provider/adapters/claude.ts +247 -0
- package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
- package/src/agent-cli-provider/adapters/codex.ts +217 -0
- package/src/agent-cli-provider/adapters/common.ts +124 -0
- package/src/agent-cli-provider/adapters/gemini.ts +243 -0
- package/src/agent-cli-provider/adapters/index.ts +126 -0
- package/src/agent-cli-provider/adapters/opencode.ts +286 -0
- package/src/agent-cli-provider/contract-actions.ts +150 -0
- package/src/agent-cli-provider/contract-env.ts +111 -0
- package/src/agent-cli-provider/contract-envelope.ts +110 -0
- package/src/agent-cli-provider/contract-errors.ts +66 -0
- package/src/agent-cli-provider/contract-fallback.ts +121 -0
- package/src/agent-cli-provider/contract-invoke.ts +104 -0
- package/src/agent-cli-provider/contract-options.ts +173 -0
- package/src/agent-cli-provider/contract-parse.ts +94 -0
- package/src/agent-cli-provider/contract-support.ts +167 -0
- package/src/agent-cli-provider/contract.ts +56 -0
- package/src/agent-cli-provider/env-safety.ts +82 -0
- package/src/agent-cli-provider/errors.ts +122 -0
- package/src/agent-cli-provider/executable.ts +24 -0
- package/src/agent-cli-provider/index.ts +83 -0
- package/src/agent-cli-provider/invoke-evidence.ts +18 -0
- package/src/agent-cli-provider/json.ts +114 -0
- package/src/agent-cli-provider/log-prefix.ts +20 -0
- package/src/agent-cli-provider/process-runner.ts +145 -0
- package/src/agent-cli-provider/redaction.ts +282 -0
- package/src/agent-cli-provider/schema.ts +115 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
- package/src/agent-cli-provider/types.ts +237 -0
- package/src/agent-wrapper.js +615 -0
- package/src/agents/git-pusher-template.js +705 -0
- package/src/attach/attach-client.js +438 -0
- package/src/attach/attach-server.js +543 -0
- package/src/attach/index.js +37 -0
- package/src/attach/protocol.js +220 -0
- package/src/attach/ring-buffer.js +121 -0
- package/src/attach/send-input.js +88 -0
- package/src/attach/socket-discovery.js +267 -0
- package/src/claude-task-runner.js +661 -0
- package/src/config-router.js +89 -0
- package/src/config-validator.js +2202 -0
- package/src/copy-worker.js +43 -0
- package/src/guidance-topics.js +10 -0
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +1734 -0
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +307 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +210 -0
- package/src/issue-providers/gitlab-provider.js +262 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +260 -0
- package/src/ledger.js +692 -0
- package/src/lib/gc.js +232 -0
- package/src/lib/safe-exec.js +88 -0
- package/src/logic-engine.js +201 -0
- package/src/message-buffer.js +81 -0
- package/src/message-bus-bridge.js +144 -0
- package/src/message-bus.js +256 -0
- package/src/name-generator.js +232 -0
- package/src/orchestrator.js +3924 -0
- package/src/preflight.js +712 -0
- package/src/process-metrics.js +608 -0
- package/src/providers/anthropic/index.js +3 -0
- package/src/providers/base-provider.js +355 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/index.js +3 -0
- package/src/providers/index.js +293 -0
- package/src/providers/openai/index.js +3 -0
- package/src/providers/opencode/index.js +3 -0
- package/src/quality-gates.js +143 -0
- package/src/schemas/sub-cluster.js +208 -0
- package/src/state-snapshot.js +398 -0
- package/src/state-snapshotter.js +142 -0
- package/src/status-footer.js +1026 -0
- package/src/sub-cluster-wrapper.js +693 -0
- package/src/task-runner.js +30 -0
- package/src/template-resolver.js +425 -0
- package/src/template-validation/index.js +338 -0
- package/src/template-validation/simulate-consensus-gates.js +324 -0
- package/src/template-validation/simulate-random-topology.js +541 -0
- package/src/template-validation/simulate-two-stage-validation.js +270 -0
- package/src/worktree-claude-config.js +135 -0
- package/src/worktree-tooling-env.js +150 -0
- package/task-lib/attachable-watcher.js +381 -0
- package/task-lib/commands/clean.js +50 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/get-log-path.js +23 -0
- package/task-lib/commands/kill.js +32 -0
- package/task-lib/commands/list.js +105 -0
- package/task-lib/commands/logs.js +439 -0
- package/task-lib/commands/resume.js +42 -0
- package/task-lib/commands/run.js +57 -0
- package/task-lib/commands/schedule.js +105 -0
- package/task-lib/commands/scheduler-cmd.js +96 -0
- package/task-lib/commands/schedules.js +148 -0
- package/task-lib/commands/status.js +44 -0
- package/task-lib/commands/unschedule.js +16 -0
- package/task-lib/completion.js +9 -0
- package/task-lib/config.js +11 -0
- package/task-lib/name-generator.js +230 -0
- package/task-lib/package.json +3 -0
- package/task-lib/provider-helper-runtime.js +29 -0
- package/task-lib/runner.js +190 -0
- package/task-lib/scheduler.js +252 -0
- package/task-lib/store.js +529 -0
- package/task-lib/watcher.js +305 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
const SNAPSHOT_VERSION = 1;
|
|
2
|
+
|
|
3
|
+
const LIMITS = {
|
|
4
|
+
errors: 5,
|
|
5
|
+
criteriaResults: 10,
|
|
6
|
+
acceptanceCriteria: 10,
|
|
7
|
+
filesAffected: 20,
|
|
8
|
+
blockers: 5,
|
|
9
|
+
nextSteps: 10,
|
|
10
|
+
rootCauses: 5,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const TEXT_LIMITS = {
|
|
14
|
+
task: 2000,
|
|
15
|
+
plan: 2500, // Slightly increased for actionable plans with embedded patterns (was 2000)
|
|
16
|
+
fixPlan: 1200,
|
|
17
|
+
summary: 300,
|
|
18
|
+
listItem: 200,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function toTimestamp(message) {
|
|
22
|
+
if (message && Number.isFinite(message.timestamp)) {
|
|
23
|
+
return message.timestamp;
|
|
24
|
+
}
|
|
25
|
+
return Date.now();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeText(value, maxLength, singleLine = false) {
|
|
29
|
+
if (value === undefined || value === null) return undefined;
|
|
30
|
+
let text = String(value);
|
|
31
|
+
if (singleLine) {
|
|
32
|
+
text = text.replace(/\s+/g, ' ').trim();
|
|
33
|
+
} else {
|
|
34
|
+
text = text.trim();
|
|
35
|
+
}
|
|
36
|
+
if (!text) return undefined;
|
|
37
|
+
if (maxLength && text.length > maxLength) {
|
|
38
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
39
|
+
}
|
|
40
|
+
return text;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeStringList(list, maxItems) {
|
|
44
|
+
if (!Array.isArray(list)) return undefined;
|
|
45
|
+
const normalized = list
|
|
46
|
+
.map((item) => normalizeText(item, TEXT_LIMITS.listItem, true))
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
if (normalized.length === 0) return undefined;
|
|
49
|
+
if (maxItems && normalized.length > maxItems) {
|
|
50
|
+
return normalized.slice(-maxItems);
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeAcceptanceCriteria(criteria) {
|
|
56
|
+
if (!Array.isArray(criteria)) return undefined;
|
|
57
|
+
const normalized = criteria
|
|
58
|
+
.map((item) => {
|
|
59
|
+
if (typeof item === 'string') {
|
|
60
|
+
return normalizeText(item, TEXT_LIMITS.listItem, true);
|
|
61
|
+
}
|
|
62
|
+
if (!item || typeof item !== 'object') return undefined;
|
|
63
|
+
const id = item.id ? String(item.id) : '';
|
|
64
|
+
const priority = item.priority ? ` (${item.priority})` : '';
|
|
65
|
+
const criterion = item.criterion || item.text || item.summary || '';
|
|
66
|
+
const label = id ? `${id}${priority}: ` : '';
|
|
67
|
+
const merged = `${label}${criterion}`.trim();
|
|
68
|
+
if (!merged) return undefined;
|
|
69
|
+
return normalizeText(merged, TEXT_LIMITS.listItem, true);
|
|
70
|
+
})
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
if (normalized.length === 0) return undefined;
|
|
73
|
+
if (normalized.length > LIMITS.acceptanceCriteria) {
|
|
74
|
+
return normalized.slice(-LIMITS.acceptanceCriteria);
|
|
75
|
+
}
|
|
76
|
+
return normalized;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeCriteriaEvidence(evidence) {
|
|
80
|
+
if (!evidence || typeof evidence !== 'object') return undefined;
|
|
81
|
+
const normalized = {};
|
|
82
|
+
if (evidence.command) {
|
|
83
|
+
const command = normalizeText(evidence.command, TEXT_LIMITS.listItem, true);
|
|
84
|
+
if (command) normalized.command = command;
|
|
85
|
+
}
|
|
86
|
+
if (Number.isFinite(evidence.exitCode)) {
|
|
87
|
+
normalized.exitCode = evidence.exitCode;
|
|
88
|
+
}
|
|
89
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeCriteriaResult(item) {
|
|
93
|
+
if (!item || typeof item !== 'object') return undefined;
|
|
94
|
+
const entry = {};
|
|
95
|
+
if (item.id) entry.id = String(item.id);
|
|
96
|
+
if (item.status) entry.status = String(item.status);
|
|
97
|
+
if (item.reason) {
|
|
98
|
+
const reason = normalizeText(item.reason, TEXT_LIMITS.listItem, true);
|
|
99
|
+
if (reason) entry.reason = reason;
|
|
100
|
+
}
|
|
101
|
+
const evidence = normalizeCriteriaEvidence(item.evidence);
|
|
102
|
+
if (evidence) entry.evidence = evidence;
|
|
103
|
+
return Object.keys(entry).length > 0 ? entry : undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeCriteriaResults(results) {
|
|
107
|
+
if (!Array.isArray(results)) return undefined;
|
|
108
|
+
const normalized = results.map(normalizeCriteriaResult).filter(Boolean);
|
|
109
|
+
if (normalized.length === 0) return undefined;
|
|
110
|
+
if (normalized.length > LIMITS.criteriaResults) {
|
|
111
|
+
return normalized.slice(-LIMITS.criteriaResults);
|
|
112
|
+
}
|
|
113
|
+
return normalized;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function normalizeErrors(data) {
|
|
117
|
+
if (!data || typeof data !== 'object') return undefined;
|
|
118
|
+
if (Array.isArray(data.errors)) {
|
|
119
|
+
return normalizeStringList(data.errors, LIMITS.errors);
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(data.issues)) {
|
|
122
|
+
const mapped = data.issues.map((issue) => {
|
|
123
|
+
if (typeof issue === 'string') return issue;
|
|
124
|
+
if (!issue || typeof issue !== 'object') return undefined;
|
|
125
|
+
return issue.bug || issue.message || issue.error || issue.summary || undefined;
|
|
126
|
+
});
|
|
127
|
+
return normalizeStringList(mapped, LIMITS.errors);
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeRootCauses(rootCauses) {
|
|
133
|
+
if (!Array.isArray(rootCauses)) return undefined;
|
|
134
|
+
const normalized = rootCauses
|
|
135
|
+
.map((cause) => {
|
|
136
|
+
if (typeof cause === 'string') {
|
|
137
|
+
return normalizeText(cause, TEXT_LIMITS.listItem, true);
|
|
138
|
+
}
|
|
139
|
+
if (!cause || typeof cause !== 'object') return undefined;
|
|
140
|
+
return normalizeText(
|
|
141
|
+
cause.cause || cause.summary || cause.description,
|
|
142
|
+
TEXT_LIMITS.listItem,
|
|
143
|
+
true
|
|
144
|
+
);
|
|
145
|
+
})
|
|
146
|
+
.filter(Boolean);
|
|
147
|
+
if (normalized.length === 0) return undefined;
|
|
148
|
+
if (normalized.length > LIMITS.rootCauses) {
|
|
149
|
+
return normalized.slice(-LIMITS.rootCauses);
|
|
150
|
+
}
|
|
151
|
+
return normalized;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function normalizeFilesAffected(filesAffected) {
|
|
155
|
+
return normalizeStringList(filesAffected, LIMITS.filesAffected);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeBoolean(value) {
|
|
159
|
+
if (typeof value === 'boolean') return value;
|
|
160
|
+
if (value === 'true') return true;
|
|
161
|
+
if (value === 'false') return false;
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function normalizeProgressStatus(data) {
|
|
166
|
+
if (!data || typeof data !== 'object') return undefined;
|
|
167
|
+
if (data.completionStatus && typeof data.completionStatus === 'object') {
|
|
168
|
+
return data.completionStatus;
|
|
169
|
+
}
|
|
170
|
+
const hasProgressFields =
|
|
171
|
+
Object.prototype.hasOwnProperty.call(data, 'canValidate') ||
|
|
172
|
+
Object.prototype.hasOwnProperty.call(data, 'percentComplete');
|
|
173
|
+
return hasProgressFields ? data : undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function buildBaseState(state, message) {
|
|
177
|
+
return {
|
|
178
|
+
version: SNAPSHOT_VERSION,
|
|
179
|
+
updatedAt: toTimestamp(message),
|
|
180
|
+
clusterId: message?.cluster_id || state?.clusterId || null,
|
|
181
|
+
sourceMessageId: message?.id || state?.sourceMessageId || null,
|
|
182
|
+
task: state?.task,
|
|
183
|
+
plan: state?.plan,
|
|
184
|
+
progress: state?.progress,
|
|
185
|
+
validation: state?.validation,
|
|
186
|
+
debug: state?.debug,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function pruneEmpty(value) {
|
|
191
|
+
if (Array.isArray(value)) {
|
|
192
|
+
const next = value.map(pruneEmpty).filter((item) => item !== undefined);
|
|
193
|
+
return next.length > 0 ? next : undefined;
|
|
194
|
+
}
|
|
195
|
+
if (value && typeof value === 'object') {
|
|
196
|
+
const next = {};
|
|
197
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
198
|
+
const pruned = pruneEmpty(entry);
|
|
199
|
+
if (pruned !== undefined) {
|
|
200
|
+
next[key] = pruned;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
204
|
+
}
|
|
205
|
+
if (value === undefined || value === null) return undefined;
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function finalizeState(state) {
|
|
210
|
+
const meta = {
|
|
211
|
+
version: state.version ?? SNAPSHOT_VERSION,
|
|
212
|
+
updatedAt: state.updatedAt ?? Date.now(),
|
|
213
|
+
clusterId: state.clusterId ?? null,
|
|
214
|
+
sourceMessageId: state.sourceMessageId ?? null,
|
|
215
|
+
};
|
|
216
|
+
const sections = pruneEmpty({
|
|
217
|
+
task: state.task,
|
|
218
|
+
plan: state.plan,
|
|
219
|
+
progress: state.progress,
|
|
220
|
+
validation: state.validation,
|
|
221
|
+
debug: state.debug,
|
|
222
|
+
});
|
|
223
|
+
return {
|
|
224
|
+
...meta,
|
|
225
|
+
...(sections || {}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function initStateFromIssue(issueMessage) {
|
|
230
|
+
const content = issueMessage?.content || {};
|
|
231
|
+
const data = content.data || {};
|
|
232
|
+
const task = {
|
|
233
|
+
raw: normalizeText(content.text, TEXT_LIMITS.task),
|
|
234
|
+
title: normalizeText(data.title, TEXT_LIMITS.summary, true),
|
|
235
|
+
issueNumber: data.issue_number ?? data.issueNumber,
|
|
236
|
+
source: issueMessage?.metadata?.source,
|
|
237
|
+
};
|
|
238
|
+
const base = buildBaseState(null, issueMessage);
|
|
239
|
+
base.task = pruneEmpty(task);
|
|
240
|
+
return finalizeState(base);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function applyIssueOpened(state, message) {
|
|
244
|
+
const base = buildBaseState(state, message);
|
|
245
|
+
const content = message?.content || {};
|
|
246
|
+
const data = content.data || {};
|
|
247
|
+
const task = {
|
|
248
|
+
raw: normalizeText(content.text, TEXT_LIMITS.task),
|
|
249
|
+
title: normalizeText(data.title, TEXT_LIMITS.summary, true),
|
|
250
|
+
issueNumber: data.issue_number ?? data.issueNumber,
|
|
251
|
+
source: message?.metadata?.source,
|
|
252
|
+
};
|
|
253
|
+
base.task = pruneEmpty(task);
|
|
254
|
+
return finalizeState(base);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function applyPlanReady(state, message) {
|
|
258
|
+
const base = buildBaseState(state, message);
|
|
259
|
+
const content = message?.content || {};
|
|
260
|
+
const data = content.data || {};
|
|
261
|
+
const plan = {
|
|
262
|
+
text: normalizeText(content.text, TEXT_LIMITS.plan),
|
|
263
|
+
summary: normalizeText(data.summary, TEXT_LIMITS.summary, true),
|
|
264
|
+
acceptanceCriteria: normalizeAcceptanceCriteria(data.acceptanceCriteria),
|
|
265
|
+
filesAffected: normalizeFilesAffected(data.filesAffected),
|
|
266
|
+
updatedAt: toTimestamp(message),
|
|
267
|
+
};
|
|
268
|
+
base.plan = pruneEmpty(plan);
|
|
269
|
+
return finalizeState(base);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function applyWorkerProgress(state, message) {
|
|
273
|
+
const base = buildBaseState(state, message);
|
|
274
|
+
const content = message?.content || {};
|
|
275
|
+
const status = normalizeProgressStatus(content.data || {});
|
|
276
|
+
if (!status) {
|
|
277
|
+
return finalizeState(base);
|
|
278
|
+
}
|
|
279
|
+
const progress = {
|
|
280
|
+
canValidate: normalizeBoolean(status.canValidate),
|
|
281
|
+
percentComplete: Number.isFinite(status.percentComplete) ? status.percentComplete : undefined,
|
|
282
|
+
blockers: normalizeStringList(status.blockers, LIMITS.blockers),
|
|
283
|
+
nextSteps: normalizeStringList(status.nextSteps, LIMITS.nextSteps),
|
|
284
|
+
lastSummary: normalizeText(content.text || status.summary, TEXT_LIMITS.summary, true),
|
|
285
|
+
updatedAt: toTimestamp(message),
|
|
286
|
+
};
|
|
287
|
+
base.progress = pruneEmpty(progress);
|
|
288
|
+
return finalizeState(base);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function applyImplementationReady(state, message) {
|
|
292
|
+
return applyWorkerProgress(state, message);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function applyValidationResult(state, message) {
|
|
296
|
+
const base = buildBaseState(state, message);
|
|
297
|
+
const content = message?.content || {};
|
|
298
|
+
const data = content.data || {};
|
|
299
|
+
const validation = {
|
|
300
|
+
approved: normalizeBoolean(data.approved),
|
|
301
|
+
errors: normalizeErrors(data),
|
|
302
|
+
criteriaResults: normalizeCriteriaResults(data.criteriaResults),
|
|
303
|
+
updatedAt: toTimestamp(message),
|
|
304
|
+
};
|
|
305
|
+
base.validation = pruneEmpty(validation);
|
|
306
|
+
return finalizeState(base);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function applyInvestigationComplete(state, message) {
|
|
310
|
+
const base = buildBaseState(state, message);
|
|
311
|
+
const content = message?.content || {};
|
|
312
|
+
const data = content.data || {};
|
|
313
|
+
const debug = {
|
|
314
|
+
fixPlan: normalizeText(content.text, TEXT_LIMITS.fixPlan),
|
|
315
|
+
successCriteria: normalizeText(data.successCriteria, TEXT_LIMITS.summary, true),
|
|
316
|
+
rootCauses: normalizeRootCauses(data.rootCauses),
|
|
317
|
+
updatedAt: toTimestamp(message),
|
|
318
|
+
};
|
|
319
|
+
base.debug = pruneEmpty(debug);
|
|
320
|
+
return finalizeState(base);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function buildTaskSummary(state) {
|
|
324
|
+
const taskTitle = normalizeText(state.task?.title || state.task?.raw, TEXT_LIMITS.summary, true);
|
|
325
|
+
return taskTitle ? `Task: ${taskTitle}` : undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function buildPlanSummary(state) {
|
|
329
|
+
const planSummary = normalizeText(
|
|
330
|
+
state.plan?.summary || state.plan?.text,
|
|
331
|
+
TEXT_LIMITS.summary,
|
|
332
|
+
true
|
|
333
|
+
);
|
|
334
|
+
return planSummary ? `Plan: ${planSummary}` : undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function buildProgressSummary(state) {
|
|
338
|
+
if (!state.progress) return undefined;
|
|
339
|
+
const parts = [];
|
|
340
|
+
if (Number.isFinite(state.progress.percentComplete)) {
|
|
341
|
+
parts.push(`${state.progress.percentComplete}%`);
|
|
342
|
+
}
|
|
343
|
+
if (typeof state.progress.canValidate === 'boolean') {
|
|
344
|
+
parts.push(`canValidate=${state.progress.canValidate}`);
|
|
345
|
+
}
|
|
346
|
+
const nextStepText = normalizeText(state.progress.nextSteps?.[0], TEXT_LIMITS.listItem, true);
|
|
347
|
+
if (nextStepText) {
|
|
348
|
+
parts.push(`next: ${nextStepText}`);
|
|
349
|
+
}
|
|
350
|
+
return parts.length > 0 ? `Progress: ${parts.join(' | ')}` : undefined;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function resolveValidationStatus(approved) {
|
|
354
|
+
if (approved === true) return 'approved';
|
|
355
|
+
if (approved === false) return 'rejected';
|
|
356
|
+
return 'pending';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function buildValidationSummary(state) {
|
|
360
|
+
if (!state.validation) return undefined;
|
|
361
|
+
const status = resolveValidationStatus(state.validation.approved);
|
|
362
|
+
const errorCount = state.validation.errors?.length || 0;
|
|
363
|
+
return `Validation: ${status}${errorCount ? ` (${errorCount} errors)` : ''}`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function buildDebugSummary(state) {
|
|
367
|
+
const debugSummary = normalizeText(
|
|
368
|
+
state.debug?.fixPlan || state.debug?.successCriteria,
|
|
369
|
+
TEXT_LIMITS.summary,
|
|
370
|
+
true
|
|
371
|
+
);
|
|
372
|
+
return debugSummary ? `Debug: ${debugSummary}` : undefined;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function renderStateSummary(state) {
|
|
376
|
+
if (!state || typeof state !== 'object') return '';
|
|
377
|
+
const lines = [
|
|
378
|
+
buildTaskSummary(state),
|
|
379
|
+
buildPlanSummary(state),
|
|
380
|
+
buildProgressSummary(state),
|
|
381
|
+
buildValidationSummary(state),
|
|
382
|
+
buildDebugSummary(state),
|
|
383
|
+
].filter(Boolean);
|
|
384
|
+
|
|
385
|
+
return lines.join('\n');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = {
|
|
389
|
+
SNAPSHOT_VERSION,
|
|
390
|
+
initStateFromIssue,
|
|
391
|
+
applyIssueOpened,
|
|
392
|
+
applyPlanReady,
|
|
393
|
+
applyWorkerProgress,
|
|
394
|
+
applyImplementationReady,
|
|
395
|
+
applyValidationResult,
|
|
396
|
+
applyInvestigationComplete,
|
|
397
|
+
renderStateSummary,
|
|
398
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const {
|
|
3
|
+
initStateFromIssue,
|
|
4
|
+
applyIssueOpened,
|
|
5
|
+
applyPlanReady,
|
|
6
|
+
applyWorkerProgress,
|
|
7
|
+
applyImplementationReady,
|
|
8
|
+
applyValidationResult,
|
|
9
|
+
applyInvestigationComplete,
|
|
10
|
+
renderStateSummary,
|
|
11
|
+
} = require('./state-snapshot');
|
|
12
|
+
|
|
13
|
+
const SNAPSHOT_TOPICS = [
|
|
14
|
+
'ISSUE_OPENED',
|
|
15
|
+
'PLAN_READY',
|
|
16
|
+
'WORKER_PROGRESS',
|
|
17
|
+
'IMPLEMENTATION_READY',
|
|
18
|
+
'VALIDATION_RESULT',
|
|
19
|
+
'INVESTIGATION_COMPLETE',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
class StateSnapshotter {
|
|
23
|
+
constructor({ messageBus, clusterId }) {
|
|
24
|
+
this.messageBus = messageBus;
|
|
25
|
+
this.clusterId = clusterId;
|
|
26
|
+
this.state = null;
|
|
27
|
+
this.lastHash = null;
|
|
28
|
+
this.unsubscribe = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
start() {
|
|
32
|
+
if (this.unsubscribe) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this._bootstrapFromLedger();
|
|
37
|
+
|
|
38
|
+
this.unsubscribe = this.messageBus.subscribeTopics(SNAPSHOT_TOPICS, (message) => {
|
|
39
|
+
if (message.cluster_id !== this.clusterId) return;
|
|
40
|
+
this._handleMessage(message);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
stop() {
|
|
45
|
+
if (!this.unsubscribe) return;
|
|
46
|
+
this.unsubscribe();
|
|
47
|
+
this.unsubscribe = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_bootstrapFromLedger() {
|
|
51
|
+
const existing = this.messageBus.findLast({
|
|
52
|
+
cluster_id: this.clusterId,
|
|
53
|
+
topic: 'STATE_SNAPSHOT',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (existing?.content?.data && typeof existing.content.data === 'object') {
|
|
57
|
+
this.state = existing.content.data;
|
|
58
|
+
this.lastHash = this._hashState(this.state);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const messages = SNAPSHOT_TOPICS.map((topic) =>
|
|
63
|
+
this.messageBus.findLast({ cluster_id: this.clusterId, topic })
|
|
64
|
+
).filter(Boolean);
|
|
65
|
+
|
|
66
|
+
if (messages.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
messages.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
|
|
71
|
+
|
|
72
|
+
let state = null;
|
|
73
|
+
for (const message of messages) {
|
|
74
|
+
state = this._applyMessage(state, message);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (state) {
|
|
78
|
+
this.state = state;
|
|
79
|
+
this._publishSnapshot(state);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_handleMessage(message) {
|
|
84
|
+
const nextState = this._applyMessage(this.state, message);
|
|
85
|
+
if (!nextState) return;
|
|
86
|
+
|
|
87
|
+
this.state = nextState;
|
|
88
|
+
this._publishSnapshot(nextState);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
_applyMessage(state, message) {
|
|
92
|
+
switch (message.topic) {
|
|
93
|
+
case 'ISSUE_OPENED':
|
|
94
|
+
return state ? applyIssueOpened(state, message) : initStateFromIssue(message);
|
|
95
|
+
case 'PLAN_READY':
|
|
96
|
+
return applyPlanReady(state, message);
|
|
97
|
+
case 'WORKER_PROGRESS':
|
|
98
|
+
return applyWorkerProgress(state, message);
|
|
99
|
+
case 'IMPLEMENTATION_READY':
|
|
100
|
+
return applyImplementationReady(state, message);
|
|
101
|
+
case 'VALIDATION_RESULT':
|
|
102
|
+
return applyValidationResult(state, message);
|
|
103
|
+
case 'INVESTIGATION_COMPLETE':
|
|
104
|
+
return applyInvestigationComplete(state, message);
|
|
105
|
+
default:
|
|
106
|
+
return state;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_publishSnapshot(state) {
|
|
111
|
+
const hash = this._hashState(state);
|
|
112
|
+
if (this._hashEquals(hash, this.lastHash)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.lastHash = hash;
|
|
116
|
+
|
|
117
|
+
this.messageBus.publish({
|
|
118
|
+
cluster_id: this.clusterId,
|
|
119
|
+
topic: 'STATE_SNAPSHOT',
|
|
120
|
+
sender: 'state-snapshotter',
|
|
121
|
+
receiver: 'broadcast',
|
|
122
|
+
content: {
|
|
123
|
+
text: renderStateSummary(state),
|
|
124
|
+
data: state,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_hashState(state) {
|
|
130
|
+
return crypto.createHash('sha256').update(JSON.stringify(state)).digest('hex');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_hashEquals(left, right) {
|
|
134
|
+
if (!left || !right) return false;
|
|
135
|
+
const leftBuffer = Buffer.from(left, 'utf8');
|
|
136
|
+
const rightBuffer = Buffer.from(right, 'utf8');
|
|
137
|
+
if (leftBuffer.length !== rightBuffer.length) return false;
|
|
138
|
+
return crypto.timingSafeEqual(leftBuffer, rightBuffer);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = StateSnapshotter;
|