specrails-core 5.6.0 → 6.0.1
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/README.md +27 -145
- package/bin/specrails-core.mjs +12 -554
- package/dist/agent-runtime/capabilities.js +0 -1
- package/dist/agent-runtime/change-scope.d.ts +63 -0
- package/dist/agent-runtime/change-scope.js +297 -0
- package/dist/agent-runtime/cli-executor.d.ts +1 -1
- package/dist/agent-runtime/cli-executor.js +1 -2
- package/dist/agent-runtime/cli-process.js +0 -1
- package/dist/agent-runtime/cli.js +4 -5
- package/dist/agent-runtime/codex-schema.js +0 -1
- package/dist/agent-runtime/compact/architect.d.ts +3 -272
- package/dist/agent-runtime/compact/architect.js +8 -9
- package/dist/agent-runtime/compact/chat-client.d.ts +3 -3
- package/dist/agent-runtime/compact/chat-client.js +1 -2
- package/dist/agent-runtime/compact/developer.d.ts +3 -70
- package/dist/agent-runtime/compact/developer.js +13 -14
- package/dist/agent-runtime/compact/environment.d.ts +16 -5
- package/dist/agent-runtime/compact/environment.js +32 -3
- package/dist/agent-runtime/compact/exit-code-honesty.d.ts +2 -1
- package/dist/agent-runtime/compact/exit-code-honesty.js +0 -1
- package/dist/agent-runtime/compact/guarded-loop.d.ts +2 -18
- package/dist/agent-runtime/compact/guarded-loop.js +7 -8
- package/dist/agent-runtime/compact/prompt-inputs.d.ts +0 -2
- package/dist/agent-runtime/compact/prompt-inputs.js +1 -2
- package/dist/agent-runtime/compact/reviewer.d.ts +0 -13
- package/dist/agent-runtime/compact/reviewer.js +5 -6
- package/dist/agent-runtime/compact/step.d.ts +0 -1
- package/dist/agent-runtime/compact/step.js +1 -2
- package/dist/agent-runtime/compact/test-reachability.js +6 -3
- package/dist/agent-runtime/config.js +0 -1
- package/dist/agent-runtime/core-host.d.ts +2 -2
- package/dist/agent-runtime/core-host.js +2 -3
- package/dist/agent-runtime/durable-store.d.ts +0 -1
- package/dist/agent-runtime/durable-store.js +1 -2
- package/dist/agent-runtime/efficiency-summary.d.ts +2 -2
- package/dist/agent-runtime/efficiency-summary.js +1 -2
- package/dist/agent-runtime/efficiency-types.js +0 -1
- package/dist/agent-runtime/efficiency.js +0 -1
- package/dist/agent-runtime/evaluation-corpus.js +0 -1
- package/dist/agent-runtime/evaluation.d.ts +2 -1
- package/dist/agent-runtime/evaluation.js +1 -2
- package/dist/agent-runtime/executor-types.d.ts +1 -1
- package/dist/agent-runtime/executor-types.js +0 -1
- package/dist/agent-runtime/executors.js +0 -1
- package/dist/agent-runtime/gemini-policy.js +0 -1
- package/dist/agent-runtime/graph/artifacts.d.ts +1 -1
- package/dist/agent-runtime/graph/artifacts.js +5 -3
- package/dist/agent-runtime/graph/convergence.d.ts +26 -0
- package/dist/agent-runtime/graph/convergence.js +62 -0
- package/dist/agent-runtime/graph/nodes.d.ts +1 -1
- package/dist/agent-runtime/graph/nodes.js +170 -24
- package/dist/agent-runtime/graph/review-policy.js +0 -1
- package/dist/agent-runtime/graph/roles.d.ts +2 -5
- package/dist/agent-runtime/graph/roles.js +2 -8
- package/dist/agent-runtime/graph/state.d.ts +6 -1
- package/dist/agent-runtime/graph/state.js +2 -1
- package/dist/agent-runtime/graph-checkpointer.d.ts +2 -1
- package/dist/agent-runtime/graph-checkpointer.js +0 -1
- package/dist/agent-runtime/guardrails.d.ts +3 -3
- package/dist/agent-runtime/guardrails.js +1 -2
- package/dist/agent-runtime/index.d.ts +1 -1
- package/dist/agent-runtime/index.js +1 -2
- package/dist/agent-runtime/kimi-acp.js +1 -2
- package/dist/agent-runtime/kimi-model.d.ts +6 -0
- package/dist/agent-runtime/kimi-model.js +12 -0
- package/dist/agent-runtime/openai-executor.d.ts +0 -9
- package/dist/agent-runtime/openai-executor.js +6 -7
- package/dist/agent-runtime/openspec-tool-server.js +1 -2
- package/dist/agent-runtime/openspec.d.ts +0 -2
- package/dist/agent-runtime/openspec.js +1 -6
- package/dist/agent-runtime/prompts.d.ts +11 -5
- package/dist/agent-runtime/prompts.js +46 -17
- package/dist/agent-runtime/provider-diagnostic.js +1 -2
- package/dist/agent-runtime/recovery.js +1 -2
- package/dist/agent-runtime/repository-context.d.ts +1 -1
- package/dist/agent-runtime/repository-context.js +19 -1
- package/dist/agent-runtime/review-context.d.ts +1 -1
- package/dist/agent-runtime/review-context.js +0 -1
- package/dist/agent-runtime/role-routing.js +0 -1
- package/dist/agent-runtime/role-state.js +0 -1
- package/dist/agent-runtime/runtime-identity.js +1 -2
- package/dist/agent-runtime/tool-event.js +0 -1
- package/dist/agent-runtime/verification-plan.d.ts +5 -4
- package/dist/agent-runtime/verification-plan.js +9 -5
- package/dist/agent-runtime/workflow-types.js +0 -1
- package/dist/agent-runtime/workflow.js +0 -1
- package/dist/agent-runtime/workspace-tools.d.ts +2 -2
- package/dist/agent-runtime/workspace-tools.js +1 -2
- package/dist/installer/cli.d.ts +3 -32
- package/dist/installer/cli.js +26 -101
- package/dist/installer/commands/framework.d.ts +4 -3
- package/dist/installer/commands/framework.js +0 -1
- package/dist/installer/commands/init.d.ts +4 -25
- package/dist/installer/commands/init.js +5 -8
- package/dist/installer/phases/framework-lifecycle.d.ts +3 -17
- package/dist/installer/phases/framework-lifecycle.js +2 -3
- package/dist/installer/phases/install-config.d.ts +7 -8
- package/dist/installer/phases/install-config.js +2 -7
- package/dist/installer/phases/manifest.d.ts +6 -7
- package/dist/installer/phases/manifest.js +2 -7
- package/dist/installer/phases/prereqs.d.ts +6 -7
- package/dist/installer/phases/prereqs.js +4 -6
- package/dist/installer/phases/provider-detect.d.ts +6 -7
- package/dist/installer/phases/provider-detect.js +2 -4
- package/dist/installer/phases/scaffold.d.ts +9 -10
- package/dist/installer/phases/scaffold.js +82 -805
- package/dist/installer/util/errors.js +0 -1
- package/dist/installer/util/exec.d.ts +3 -2
- package/dist/installer/util/exec.js +0 -1
- package/dist/installer/util/fs.d.ts +0 -10
- package/dist/installer/util/fs.js +1 -2
- package/dist/installer/util/git.js +0 -1
- package/dist/installer/util/install-transaction.d.ts +2 -2
- package/dist/installer/util/install-transaction.js +1 -2
- package/dist/installer/util/logger.d.ts +1 -3
- package/dist/installer/util/logger.js +0 -1
- package/dist/installer/util/paths.js +0 -1
- package/dist/installer/util/registry.d.ts +4 -8
- package/dist/installer/util/registry.js +2 -3
- package/dist/{installer/runtime → pipeline}/pipeline-state.d.ts +11 -0
- package/dist/{installer/runtime → pipeline}/pipeline-state.js +56 -5
- package/dist/shared/args.d.ts +18 -0
- package/dist/shared/args.js +53 -0
- package/integration-contract.json +11 -47
- package/package.json +3 -10
- package/templates/agents/sr-architect.md +15 -310
- package/templates/agents/sr-developer.md +15 -269
- package/templates/agents/sr-reviewer.md +13 -367
- package/templates/codex-skills/rails/sr-architect/SKILL.md +14 -308
- package/templates/codex-skills/rails/sr-developer/SKILL.md +14 -201
- package/templates/codex-skills/rails/sr-reviewer/SKILL.md +15 -289
- package/templates/kimi/specrails/run-skill.mjs +91 -2173
- package/bin/tui-installer.mjs +0 -430
- package/commands/doctor.md +0 -62
- package/dist/agent-runtime/capabilities.js.map +0 -1
- package/dist/agent-runtime/cli-executor.js.map +0 -1
- package/dist/agent-runtime/cli-process.js.map +0 -1
- package/dist/agent-runtime/cli.js.map +0 -1
- package/dist/agent-runtime/codex-schema.js.map +0 -1
- package/dist/agent-runtime/compact/architect.js.map +0 -1
- package/dist/agent-runtime/compact/chat-client.js.map +0 -1
- package/dist/agent-runtime/compact/developer.js.map +0 -1
- package/dist/agent-runtime/compact/environment.js.map +0 -1
- package/dist/agent-runtime/compact/exit-code-honesty.js.map +0 -1
- package/dist/agent-runtime/compact/guarded-loop.js.map +0 -1
- package/dist/agent-runtime/compact/prompt-inputs.js.map +0 -1
- package/dist/agent-runtime/compact/reviewer.js.map +0 -1
- package/dist/agent-runtime/compact/step.js.map +0 -1
- package/dist/agent-runtime/compact/test-reachability.js.map +0 -1
- package/dist/agent-runtime/config.js.map +0 -1
- package/dist/agent-runtime/core-host.js.map +0 -1
- package/dist/agent-runtime/durable-store.js.map +0 -1
- package/dist/agent-runtime/efficiency-summary.js.map +0 -1
- package/dist/agent-runtime/efficiency-types.js.map +0 -1
- package/dist/agent-runtime/efficiency.js.map +0 -1
- package/dist/agent-runtime/evaluation-corpus.js.map +0 -1
- package/dist/agent-runtime/evaluation.js.map +0 -1
- package/dist/agent-runtime/executor-types.js.map +0 -1
- package/dist/agent-runtime/executors.js.map +0 -1
- package/dist/agent-runtime/gemini-policy.js.map +0 -1
- package/dist/agent-runtime/graph/artifacts.js.map +0 -1
- package/dist/agent-runtime/graph/nodes.js.map +0 -1
- package/dist/agent-runtime/graph/review-policy.js.map +0 -1
- package/dist/agent-runtime/graph/roles.js.map +0 -1
- package/dist/agent-runtime/graph/state.js.map +0 -1
- package/dist/agent-runtime/graph-checkpointer.js.map +0 -1
- package/dist/agent-runtime/guardrails.js.map +0 -1
- package/dist/agent-runtime/index.js.map +0 -1
- package/dist/agent-runtime/kimi-acp.js.map +0 -1
- package/dist/agent-runtime/openai-executor.js.map +0 -1
- package/dist/agent-runtime/openspec-tool-server.js.map +0 -1
- package/dist/agent-runtime/openspec.js.map +0 -1
- package/dist/agent-runtime/prompts.js.map +0 -1
- package/dist/agent-runtime/provider-diagnostic.js.map +0 -1
- package/dist/agent-runtime/recovery.js.map +0 -1
- package/dist/agent-runtime/repository-context.js.map +0 -1
- package/dist/agent-runtime/review-context.js.map +0 -1
- package/dist/agent-runtime/role-routing.js.map +0 -1
- package/dist/agent-runtime/role-state.js.map +0 -1
- package/dist/agent-runtime/runtime-identity.js.map +0 -1
- package/dist/agent-runtime/tool-event.js.map +0 -1
- package/dist/agent-runtime/verification-plan.js.map +0 -1
- package/dist/agent-runtime/workflow-types.js.map +0 -1
- package/dist/agent-runtime/workflow.js.map +0 -1
- package/dist/agent-runtime/workspace-tools.js.map +0 -1
- package/dist/installer/cli.js.map +0 -1
- package/dist/installer/commands/doctor.d.ts +0 -27
- package/dist/installer/commands/doctor.js +0 -615
- package/dist/installer/commands/doctor.js.map +0 -1
- package/dist/installer/commands/framework.js.map +0 -1
- package/dist/installer/commands/init.js.map +0 -1
- package/dist/installer/commands/update.d.ts +0 -56
- package/dist/installer/commands/update.js +0 -257
- package/dist/installer/commands/update.js.map +0 -1
- package/dist/installer/commands/v5-migration.d.ts +0 -32
- package/dist/installer/commands/v5-migration.js +0 -133
- package/dist/installer/commands/v5-migration.js.map +0 -1
- package/dist/installer/phases/framework-lifecycle.js.map +0 -1
- package/dist/installer/phases/install-config.js.map +0 -1
- package/dist/installer/phases/manifest.js.map +0 -1
- package/dist/installer/phases/prereqs.js.map +0 -1
- package/dist/installer/phases/provider-detect.js.map +0 -1
- package/dist/installer/phases/scaffold.js.map +0 -1
- package/dist/installer/runtime/kimi.d.ts +0 -84
- package/dist/installer/runtime/kimi.js +0 -255
- package/dist/installer/runtime/kimi.js.map +0 -1
- package/dist/installer/runtime/pipeline-state.js.map +0 -1
- package/dist/installer/util/errors.js.map +0 -1
- package/dist/installer/util/exec.js.map +0 -1
- package/dist/installer/util/fs.js.map +0 -1
- package/dist/installer/util/git.js.map +0 -1
- package/dist/installer/util/install-transaction.js.map +0 -1
- package/dist/installer/util/logger.js.map +0 -1
- package/dist/installer/util/paths.js.map +0 -1
- package/dist/installer/util/prompts.d.ts +0 -23
- package/dist/installer/util/prompts.js +0 -49
- package/dist/installer/util/prompts.js.map +0 -1
- package/dist/installer/util/registry.js.map +0 -1
- package/dist/installer/util/template.d.ts +0 -23
- package/dist/installer/util/template.js +0 -60
- package/dist/installer/util/template.js.map +0 -1
- package/docs/README.md +0 -40
- package/docs/agent-runtime-efficiency.md +0 -65
- package/docs/agent-runtime.md +0 -393
- package/docs/agents.md +0 -315
- package/docs/changelog.md +0 -168
- package/docs/ci-cd.md +0 -60
- package/docs/concepts.md +0 -183
- package/docs/customization.md +0 -363
- package/docs/deployment.md +0 -234
- package/docs/getting-started.md +0 -122
- package/docs/installation.md +0 -395
- package/docs/local-tickets.md +0 -191
- package/docs/migration-guide.md +0 -140
- package/docs/playbook-oss-maintainer.md +0 -112
- package/docs/playbook-parallel-dev.md +0 -124
- package/docs/playbook-product-discovery.md +0 -115
- package/docs/plugin-architecture.md +0 -144
- package/docs/research/codex-compatibility-analysis.md +0 -365
- package/docs/research/mcp-feasibility-analysis.md +0 -382
- package/docs/testing/test-matrix-codex.md +0 -130
- package/docs/updating.md +0 -89
- package/docs/user-docs/cli-reference.md +0 -428
- package/docs/user-docs/codex-vs-claude-code.md +0 -23
- package/docs/user-docs/core-updates.md +0 -70
- package/docs/user-docs/faq.md +0 -146
- package/docs/user-docs/getting-started-codex.md +0 -194
- package/docs/user-docs/getting-started-kimi.md +0 -423
- package/docs/user-docs/installation.md +0 -237
- package/docs/user-docs/provider-pipelines.md +0 -96
- package/docs/user-docs/quick-start.md +0 -161
- package/docs/windows.md +0 -78
- package/docs/workflows.md +0 -463
- package/schemas/profile.v1.json +0 -207
- package/templates/claude-md/CLAUDE-quickstart.md +0 -54
- package/templates/claude-md/root.md +0 -50
- package/templates/codex-skills/batch-implement/SKILL.md +0 -38
- package/templates/codex-skills/implement/SKILL.md +0 -38
- package/templates/codex-skills/retry/SKILL.md +0 -16
- package/templates/commands/specrails/compat-check.md +0 -271
- package/templates/commands/specrails/doctor.md +0 -62
- package/templates/commands/specrails/explore-spec.md +0 -173
- package/templates/commands/specrails/health-check.md +0 -527
- package/templates/commands/specrails/memory-inspect.md +0 -261
- package/templates/commands/specrails/opsx-diff.md +0 -419
- package/templates/commands/specrails/propose-spec.md +0 -102
- package/templates/commands/specrails/refactor-recommender.md +0 -169
- package/templates/commands/specrails/telemetry.md +0 -553
- package/templates/commands/specrails/why.md +0 -96
- package/templates/gemini-commands/batch-implement.toml +0 -37
- package/templates/gemini-commands/implement.toml +0 -37
- package/templates/gemini-commands/retry.toml +0 -15
- package/templates/profiles/default.json +0 -14
- package/templates/profiles/kimi-default.json +0 -15
- package/templates/rules/layer.md +0 -8
- package/templates/runtime/provider-pipeline.md +0 -113
- package/templates/security/security-exemptions.yaml +0 -20
- package/templates/settings/confidence-config.json +0 -17
- package/templates/settings/settings.json +0 -15
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { guardrailEnabled } from '../guardrails.js';
|
|
2
2
|
import { addDeveloperChecks, bindPlan, expandedPlanCommands, initializeVerificationPlan, readVerificationPlan, validateProposedChecks } from '../verification-plan.js';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { OpenSpecTools } from '../openspec.js';
|
|
5
|
-
import { candidateManifest, fingerprintCandidate, frozenAcceptanceCriteria, recordAcceptance, transitionPipeline, validateAcceptanceReport, verifyPipeline, } from '../../
|
|
6
|
+
import { candidateManifest, fingerprintCandidate, frozenAcceptanceCriteria, inspectPipeline, recordAcceptance, transitionPipeline, validateAcceptanceReport, verifyPipeline, } from '../../pipeline/pipeline-state.js';
|
|
6
7
|
import { ARCHITECT_OUTPUT_SCHEMA, DEVELOPER_OUTPUT_SCHEMA, REVIEW_OUTPUT_SCHEMA, correctionInstructions, deepenInstructions, roleInstructions } from '../prompts.js';
|
|
7
8
|
import { archive, child, journal, object, parseArchitecture, proposedVerification, write, writeDesignConfidence } from './artifacts.js';
|
|
8
9
|
import { evaluateReview } from './review-policy.js';
|
|
9
|
-
import { installEnvironment, isEnvironmentFailure } from '../compact/environment.js';
|
|
10
|
+
import { hostPreconditionFailure, installEnvironment, isEnvironmentFailure } from '../compact/environment.js';
|
|
10
11
|
import { unreachedTestFiles, unreachedTestsReason } from '../compact/test-reachability.js';
|
|
11
12
|
import { exitCodeContradiction, exitHonestyReason } from '../compact/exit-code-honesty.js';
|
|
12
13
|
import { boundedReviewManifest, reviewChanges } from '../review-context.js';
|
|
14
|
+
import { describeFailure, divergence, outcomesSinceResume, unchangedFailure } from './convergence.js';
|
|
15
|
+
import { captureOutOfScope, changeBases, changeSet, describeDiscarded, discardOutOfScopeEdits, renderChangeSet } from '../change-scope.js';
|
|
13
16
|
/** Autonomous investigation passes before a low-confidence design asks or proceeds. */
|
|
14
17
|
export const MAX_DEEPEN_PASSES = 1;
|
|
15
18
|
const DEVELOPER_SUMMARY_LIMIT = 32_000;
|
|
@@ -63,6 +66,34 @@ export function developerRecord(output, text, result, provider) {
|
|
|
63
66
|
incomplete,
|
|
64
67
|
};
|
|
65
68
|
}
|
|
69
|
+
/** The change so far as prompt lines; a checkout git cannot measure yields no section instead of failing the role. */
|
|
70
|
+
function measuredChange(context, change) {
|
|
71
|
+
try {
|
|
72
|
+
return renderChangeSet(changeSet(context, change));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** The turn's record without the files Core undid: they are not part of the change, so they never count as progress. */
|
|
79
|
+
function withoutDiscarded(context, record, discarded) {
|
|
80
|
+
if (!discarded.length)
|
|
81
|
+
return record;
|
|
82
|
+
const undone = new Set(discarded.map(edit => edit.path));
|
|
83
|
+
const relative = (file) => {
|
|
84
|
+
let value = file.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
85
|
+
for (const repository of context.repositories) {
|
|
86
|
+
const root = repository.path.replace(/\\/g, '/');
|
|
87
|
+
if (value.startsWith(root + '/')) {
|
|
88
|
+
value = value.slice(root.length + 1);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
};
|
|
94
|
+
const kept = (files) => files.filter(file => !undone.has(relative(file)));
|
|
95
|
+
return { ...record, files: kept(record.files), tests: kept(record.tests), discarded: discarded.map(edit => (context.repositories.length > 1 ? edit.repositoryId + ':' : '') + edit.path).slice(0, 200) };
|
|
96
|
+
}
|
|
66
97
|
function architectNode(deps) {
|
|
67
98
|
const { context, config, change, invoke, note } = deps;
|
|
68
99
|
const accept = (output) => {
|
|
@@ -77,6 +108,8 @@ function architectNode(deps) {
|
|
|
77
108
|
effect: 'write', ends: ['developer'],
|
|
78
109
|
async run(state, step) {
|
|
79
110
|
transitionPipeline(context, 'architect', 'running');
|
|
111
|
+
// The change is measured from here: record each repository's base before any role edits.
|
|
112
|
+
changeBases(context);
|
|
80
113
|
// A resumed node collects the requester's answer first, so the pass that
|
|
81
114
|
// asked the question is never repeated.
|
|
82
115
|
const answers = [...state.answers];
|
|
@@ -164,7 +197,9 @@ async function implementationVisit(deps, state, step, mode) {
|
|
|
164
197
|
// instructions.
|
|
165
198
|
const fixer = fixing ? config.fixer : undefined;
|
|
166
199
|
const provider = (fixer ?? config.agents.developer).provider;
|
|
167
|
-
|
|
200
|
+
// What the run has changed so far, from git: a correction stays inside it.
|
|
201
|
+
const changes = state.development ? measuredChange(context, change) : undefined;
|
|
202
|
+
const full = roleInstructions('developer', context, change, { definition: fixing ? config.rolePrompts?.fixer : config.rolePrompts?.developer, ...(fixing ? { stance: 'fixer' } : {}), feedback, verification: state.plan, developer: state.development, ...(changes ? { changeSet: changes } : {}) }) + (config.efficiency?.acceptDeveloperChecks !== false ? '\nYou may propose verificationChecks in your JSON summary. These are additive required checks, never replacements for the baseline. Use kind command with structured command/args, or kind harness with entrypoint and 1–8 relative source files (64 KiB each, 256 KiB total). Core persists harnesses outside delivery, appends the absolute entrypoint to argv and supplies SPECRAILS_CHECK_REPO_ROOT. Stable keys revise your own checks; omission retains them. Do not claim a check passed until Core has executed it.' : '');
|
|
168
203
|
const previous = state.development;
|
|
169
204
|
// A CLI developer with a live session takes corrections and continuations
|
|
170
205
|
// as a short follow-up in that session; a configured fixer engine always
|
|
@@ -173,11 +208,13 @@ async function implementationVisit(deps, state, step, mode) {
|
|
|
173
208
|
const resumable = !fixer && previous?.sessionId !== undefined && previous.provider === provider && visits > 1;
|
|
174
209
|
if (fixer)
|
|
175
210
|
note('fixer', `Correction round on the fixer engine (${fixer.provider}/${fixer.model ?? 'provider default'}).`);
|
|
211
|
+
// Out-of-scope state before the turn: afterwards the host undoes exactly what the turn did outside the scope.
|
|
212
|
+
const guard = captureOutOfScope(context, change);
|
|
176
213
|
const outcome = await invoke('developer', step, {
|
|
177
214
|
kind: visits > 1 ? 'correction' : 'initial',
|
|
178
215
|
...(fixer ? { agentOverride: fixer } : {}), ...(fixing ? { stance: 'fixer' } : {}),
|
|
179
216
|
...(resumable
|
|
180
|
-
? { prompt: correctionInstructions('developer', feedback), resumeSessionId: previous.sessionId, fallbackPrompt: full }
|
|
217
|
+
? { prompt: correctionInstructions('developer', feedback, { ...(changes ? { changeSet: changes } : {}), developer: state.development }), resumeSessionId: previous.sessionId, fallbackPrompt: full }
|
|
181
218
|
: { prompt: full }),
|
|
182
219
|
structured: true, lenient: true, outputSchema: DEVELOPER_OUTPUT_SCHEMA,
|
|
183
220
|
}, (output, text, result) => {
|
|
@@ -188,6 +225,19 @@ async function implementationVisit(deps, state, step, mode) {
|
|
|
188
225
|
throw new Error('Developer verification proposals are disabled by the frozen policy');
|
|
189
226
|
return { ...developerRecord(output, text, result, provider), ...(checks.length ? { verificationChecks: checks } : {}) };
|
|
190
227
|
});
|
|
228
|
+
const who = fixing ? 'Fixer' : 'Developer';
|
|
229
|
+
let discarded;
|
|
230
|
+
try {
|
|
231
|
+
discarded = discardOutOfScopeEdits(context, guard);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
return { status: 'blocked', error: `${who} edited files outside the repository scope and Core could not undo them: ${error instanceof Error ? error.message : String(error)}. Restore those files in the worktree, then resume.` };
|
|
235
|
+
}
|
|
236
|
+
const multiple = context.repositories.length > 1;
|
|
237
|
+
if (discarded.length) {
|
|
238
|
+
const scopes = context.repositories.filter(repository => repository.scope?.length).map(repository => repository.scope.join(', ')).join('; ');
|
|
239
|
+
note(mode, `Undid ${plural(discarded.length, 'edit')} outside the repository scope (${scopes}): ${describeDiscarded(discarded, multiple)}. Only changes inside the scope are part of this change.`);
|
|
240
|
+
}
|
|
191
241
|
// A timeout is a budget, not a defect: the groups already finished are
|
|
192
242
|
// ticked in tasks.md and verified, and write_progress holds the handoff, so
|
|
193
243
|
// the run BLOCKS (resume continues from the next open group, or raise
|
|
@@ -195,10 +245,9 @@ async function implementationVisit(deps, state, step, mode) {
|
|
|
195
245
|
if (!outcome.ok)
|
|
196
246
|
return settle(outcome, 'Finished task groups are ticked and verified; resume to continue from the next open group, or raise limits.timeoutMs.');
|
|
197
247
|
new OpenSpecTools(deps.openspec.developer, step.signal).assertParticipation();
|
|
198
|
-
const record = outcome.value;
|
|
248
|
+
const record = withoutDiscarded(context, outcome.value, discarded);
|
|
199
249
|
const effective = addDeveloperChecks(context, record.verificationChecks ?? []);
|
|
200
250
|
bindPlan(context, effective);
|
|
201
|
-
const who = fixing ? 'Fixer' : 'Developer';
|
|
202
251
|
note(mode, record.structured
|
|
203
252
|
? `${who} finished: ${plural(record.files.length, 'file')} changed, ${plural(record.tests.length, 'test file')} touched${record.incomplete.length ? `, ${plural(record.incomplete.length, 'task')} left incomplete` : ''}.`
|
|
204
253
|
: `${who} finished without the structured summary; the prose summary is recorded instead.`);
|
|
@@ -237,8 +286,55 @@ function fixerNode(deps) {
|
|
|
237
286
|
},
|
|
238
287
|
};
|
|
239
288
|
}
|
|
289
|
+
/** Where a missing dependency install belongs: the nearest directory with a lockfile or manifest above the failing command, else the repository root. */
|
|
290
|
+
const INSTALL_MARKERS = ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lock', 'bun.lockb', 'requirements.txt', 'pyproject.toml', 'go.mod', 'Cargo.toml'];
|
|
291
|
+
function installRoots(context, commands) {
|
|
292
|
+
const roots = [];
|
|
293
|
+
for (const command of commands) {
|
|
294
|
+
const repository = context.repositories.find(repo => repo.id === command.repositoryId);
|
|
295
|
+
if (!repository)
|
|
296
|
+
continue;
|
|
297
|
+
let directory = command.cwd;
|
|
298
|
+
let chosen = repository.path;
|
|
299
|
+
for (;;) {
|
|
300
|
+
const relative = path.relative(repository.path, directory);
|
|
301
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
302
|
+
break;
|
|
303
|
+
if (INSTALL_MARKERS.some(name => existsSync(path.join(directory, name)))) {
|
|
304
|
+
chosen = directory;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
if (!relative)
|
|
308
|
+
break;
|
|
309
|
+
directory = path.dirname(directory);
|
|
310
|
+
}
|
|
311
|
+
roots.push(chosen);
|
|
312
|
+
}
|
|
313
|
+
// Every repository root stays a candidate: its plan is a no-op when it is already installed.
|
|
314
|
+
return [...new Set([...roots, ...context.repositories.map(repository => repository.path)])];
|
|
315
|
+
}
|
|
316
|
+
/** A directory as the log should name it: relative to the checkout that contains it. */
|
|
317
|
+
function checkoutRelative(context, directory) {
|
|
318
|
+
const repository = context.repositories.find(repo => { const relative = path.relative(repo.path, directory); return !relative.startsWith('..') && !path.isAbsolute(relative); });
|
|
319
|
+
return repository ? path.relative(repository.path, directory).split(path.sep).join('/') || '.' : directory;
|
|
320
|
+
}
|
|
321
|
+
function hostPreconditionMessage(reason, command, args, directory) {
|
|
322
|
+
return `Verification cannot run in this environment: ${reason} (\`${[command, ...args].join(' ')}\` in ${directory}). This is not a defect in the change, so no correction round was started. Make it available to Specrails (for example in the login shell profile Specrails loads, or with a refreshed registry token), then resume.`;
|
|
323
|
+
}
|
|
324
|
+
/** A failed check the host must unblock (credentials, registry access, a missing variable): no correction round can repair it. */
|
|
325
|
+
function preconditionBlock(context, receipt) {
|
|
326
|
+
for (const command of receipt.commands) {
|
|
327
|
+
if (command.exitCode === 0)
|
|
328
|
+
continue;
|
|
329
|
+
const reason = hostPreconditionFailure(command.output);
|
|
330
|
+
if (reason)
|
|
331
|
+
return hostPreconditionMessage(reason, command.command, command.args, checkoutRelative(context, command.cwd));
|
|
332
|
+
}
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
240
335
|
function verifyNode(deps) {
|
|
241
336
|
const { context, note, config } = deps;
|
|
337
|
+
const roots = context.repositories.map(repository => repository.path);
|
|
242
338
|
return {
|
|
243
339
|
effect: 'write', ends: ['reviewer', 'developer', 'fixer'],
|
|
244
340
|
async run(state, step) {
|
|
@@ -257,35 +353,85 @@ function verifyNode(deps) {
|
|
|
257
353
|
throw new Error('Verification plan is unavailable');
|
|
258
354
|
const plan = expandedPlanCommands(context, effective);
|
|
259
355
|
const uncovered = uncoveredRepositories(context, plan);
|
|
260
|
-
|
|
356
|
+
// A correction round that left the failed candidate exactly as it was
|
|
357
|
+
// cannot pass the same checks: stop instead of re-running them.
|
|
358
|
+
const recent = outcomesSinceResume(state.verifyHistory ?? [], step.checkpoint);
|
|
359
|
+
const last = recent.at(-1);
|
|
360
|
+
const rejected = last?.passed === true && state.review !== null && !state.review.approved;
|
|
361
|
+
if (recent.some(entry => !entry.passed) || rejected) {
|
|
362
|
+
const candidate = fingerprintCandidate(journal(context));
|
|
363
|
+
const unchanged = unchangedFailure(recent, candidate, effective.planHash);
|
|
364
|
+
if (unchanged) {
|
|
365
|
+
note('fixer', 'The correction round did not change the candidate that failed verification; stopping instead of re-running the same checks.');
|
|
366
|
+
return { status: 'blocked', error: `Correction loop stopped: the last correction round left the change exactly as it was when verification failed (${unchanged.summary}). Re-running the same checks cannot pass. If the failure lies outside this change (a pre-existing failure, another package or the environment), fix it there and resume.`, usage: NO_SPEND };
|
|
367
|
+
}
|
|
368
|
+
// The reviewer rejected exactly this candidate and the correction round changed nothing: the
|
|
369
|
+
// fixer disputes the request. Its evidence still holds, so the reviewer decides again without
|
|
370
|
+
// re-running the same checks; a second rejection of the same change ends the dispute.
|
|
371
|
+
if (rejected && last.candidateHash === candidate && last.planHash === effective.planHash) {
|
|
372
|
+
const reviews = recent.filter(entry => entry.passed && entry.candidateHash === candidate && entry.planHash === effective.planHash).length;
|
|
373
|
+
if (reviews >= 2) {
|
|
374
|
+
note('fixer', 'The reviewer rejected the same unchanged change twice; stopping instead of another round.');
|
|
375
|
+
return { status: 'blocked', error: `Correction loop stopped: the reviewer rejected the same change twice and the correction rounds did not change it (${state.review.issues.slice(0, 3).join('; ').slice(0, 600) || state.review.summary.slice(0, 300)}). Decide whether the review request applies to this change, then resume.`, usage: NO_SPEND };
|
|
376
|
+
}
|
|
377
|
+
if (inspectPipeline(context).verification.valid && state.verifyResult) {
|
|
378
|
+
note('developer', 'The correction round did not change the verified change; reusing its verification evidence and returning to the reviewer.');
|
|
379
|
+
transitionPipeline(context, 'developer', 'done');
|
|
380
|
+
return { status: 'succeeded', next: 'reviewer', update: { verifyResult: state.verifyResult, verifyHistory: [{ ...last, at: new Date().toISOString() }] }, output: state.verifyResult, usage: NO_SPEND };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const run = () => verifyPipeline(context, { kind: 'full', planHash: effective.planHash, commands: plan, ...(uncovered.length ? { unverified: true } : {}) }, deps.onVerificationOutput, step.signal, { ...(guardrailEnabled(config.guardrails, 'verify-idle-timeout') ? {} : { idleTimeoutMs: 0 }), onEvidence: async (kind, payload) => { await step.reportEfficiencyActivity?.(kind, payload); }, maxConcurrency: effective.executionPolicy.maxConcurrency, ...(step.remainingBudget().maxDurationMs === undefined ? {} : { deadline: Date.now() + step.remainingBudget().maxDurationMs }) });
|
|
385
|
+
let receipt = await run();
|
|
386
|
+
const hostBlocked = (reason) => {
|
|
387
|
+
note('fixer', 'Verification failed on the environment, not on the change; stopping for the host instead of starting a correction round.');
|
|
388
|
+
return { status: 'blocked', error: reason, usage: NO_SPEND };
|
|
389
|
+
};
|
|
390
|
+
let precondition = receipt.valid ? undefined : preconditionBlock(context, receipt);
|
|
391
|
+
if (precondition)
|
|
392
|
+
return hostBlocked(precondition);
|
|
261
393
|
// A failure that is really a missing toolchain or dependency (exit 127,
|
|
262
394
|
// "command not found", "Cannot find module"…) is the HOST's to fix, not
|
|
263
395
|
// the model's — for every developer: a fresh worktree has no
|
|
264
|
-
// node_modules whatever wrote the code. Install once
|
|
265
|
-
// a second failure becomes
|
|
396
|
+
// node_modules whatever wrote the code. Install once where the failing
|
|
397
|
+
// command runs and re-verify; only a second failure becomes feedback.
|
|
266
398
|
if (!receipt.valid && guardrailEnabled(config.guardrails, 'environment-repair') && receipt.commands.some(command => isEnvironmentFailure(command.exitCode, command.output))) {
|
|
267
|
-
const installs = installEnvironment(context.
|
|
399
|
+
const installs = installEnvironment(installRoots(context, receipt.commands.filter(command => command.exitCode !== 0)), { failureOutput: receipt.commands.map(command => command.output).join('\n'), lockfileRepair: guardrailEnabled(config.guardrails, 'lockfile-repair'), onEvent: event => note('developer', event.kind === 'text' ? event.text ?? '' : `[environment] ${event.tool ?? ''} ${event.detail ?? ''}`.trim()) });
|
|
400
|
+
const refused = installs.find(outcome => outcome.precondition);
|
|
401
|
+
if (refused)
|
|
402
|
+
return hostBlocked(hostPreconditionMessage(`installing its dependencies failed because ${refused.precondition}`, refused.command, refused.args, checkoutRelative(context, refused.root)));
|
|
268
403
|
if (installs.some(outcome => outcome.ok)) {
|
|
269
404
|
note('developer', 'Verification failed on the environment (missing dependencies or tools); the host installed them and is verifying again.');
|
|
270
|
-
receipt = await
|
|
405
|
+
receipt = await run();
|
|
406
|
+
precondition = receipt.valid ? undefined : preconditionBlock(context, receipt);
|
|
407
|
+
if (precondition)
|
|
408
|
+
return hostBlocked(precondition);
|
|
271
409
|
}
|
|
272
410
|
}
|
|
273
411
|
const evidence = {
|
|
274
412
|
valid: receipt.valid, ...(receipt.reason ? { reason: receipt.reason } : {}), receiptId: receipt.id, unverifiedRepositories: uncovered,
|
|
275
413
|
commands: receipt.commands.map(({ evidenceId, repositoryId, command, args, exitCode, output }) => ({ ...(evidenceId ? { evidenceId } : {}), repositoryId, command, args, exitCode, output: output.slice(-2000) })),
|
|
276
414
|
};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
415
|
+
const outcome = (passed, finding) => ({ at: new Date().toISOString(), candidateHash: receipt.candidateHash, planHash: effective.planHash, passed, ...(passed ? {} : describeFailure(receipt, roots, finding)) });
|
|
416
|
+
/** Hands a failure to the fixer unless the correction loop has stopped converging. */
|
|
417
|
+
const toFixer = (record, message, finding) => {
|
|
418
|
+
const failure = outcome(false, finding);
|
|
419
|
+
const loop = divergence(recent, failure);
|
|
420
|
+
if (loop) {
|
|
421
|
+
note('fixer', 'The correction loop is not converging; stopping instead of starting another round.');
|
|
422
|
+
return { status: 'blocked', error: `Correction loop stopped: ${loop} Inspect the evidence, fix what lies outside the change (or adjust the request), then resume.`, output: record, usage: NO_SPEND };
|
|
423
|
+
}
|
|
424
|
+
note('fixer', message);
|
|
425
|
+
return { status: 'succeeded', next: 'fixer', update: { verifyResult: record, verifyHistory: [failure] }, output: record, usage: NO_SPEND };
|
|
426
|
+
};
|
|
427
|
+
if (!receipt.valid)
|
|
428
|
+
return toFixer(evidence, `Verification failed (${receipt.reason ?? 'a command failed'}); handing the exact output to the fixer.`);
|
|
281
429
|
// Exit 0 with "3 failed" in the output is a broken harness, not a pass.
|
|
282
430
|
if (guardrailEnabled(config.guardrails, 'exit-code-honesty')) {
|
|
283
431
|
const findings = receipt.commands.flatMap(command => { const found = exitCodeContradiction(command.exitCode, command.output); return found ? [{ ...found, command: [command.command, ...command.args].join(' ') }] : []; });
|
|
284
432
|
if (findings.length) {
|
|
285
433
|
const reason = exitHonestyReason(findings);
|
|
286
|
-
|
|
287
|
-
const dishonest = { ...evidence, valid: false, reason };
|
|
288
|
-
return { status: 'succeeded', next: 'fixer', update: { verifyResult: dishonest }, output: dishonest, usage: NO_SPEND };
|
|
434
|
+
return toFixer({ ...evidence, valid: false, reason }, `Verification exited 0 but reported failures (${findings.map(item => item.sample).join(' | ')}); handing it to the fixer as a failure.`, reason);
|
|
289
435
|
}
|
|
290
436
|
}
|
|
291
437
|
// Every command exited 0 — but did any of them RUN the tests the
|
|
@@ -299,14 +445,12 @@ function verifyNode(deps) {
|
|
|
299
445
|
const unreached = context.repositories.flatMap(repository => unreachedTestFiles(repository.path, plan.filter(command => command.repositoryId === repository.id).map(({ command, args, cwd }) => ({ command, args, ...(cwd ? { cwd } : {}) })), reported).map(file => context.repositories.length > 1 ? `${repository.id}:${file}` : file));
|
|
300
446
|
if (unreached.length) {
|
|
301
447
|
const reason = unreachedTestsReason(unreached);
|
|
302
|
-
|
|
303
|
-
const incomplete = { ...evidence, valid: false, reason };
|
|
304
|
-
return { status: 'succeeded', next: 'fixer', update: { verifyResult: incomplete }, output: incomplete, usage: NO_SPEND };
|
|
448
|
+
return toFixer({ ...evidence, valid: false, reason }, `Verification incomplete: ${unreached.join(', ')} ${unreached.length === 1 ? 'is' : 'are'} not run by any verification command; handing it to the fixer.`, reason);
|
|
305
449
|
}
|
|
306
450
|
}
|
|
307
451
|
transitionPipeline(context, 'developer', 'done');
|
|
308
452
|
note('developer', plan.length ? `Verification passed: ${plural(plan.length, 'command')} exited 0.` : 'No verification commands available; relying on task completion and review.');
|
|
309
|
-
return { status: 'succeeded', next: 'reviewer', update: { verifyResult: evidence }, output: evidence, usage: NO_SPEND };
|
|
453
|
+
return { status: 'succeeded', next: 'reviewer', update: { verifyResult: evidence, verifyHistory: [outcome(true)] }, output: evidence, usage: NO_SPEND };
|
|
310
454
|
},
|
|
311
455
|
};
|
|
312
456
|
}
|
|
@@ -365,9 +509,12 @@ function reviewerNode(deps) {
|
|
|
365
509
|
: undefined;
|
|
366
510
|
if (reReview)
|
|
367
511
|
note('reviewer', `Re-review: ${plural(reReview.changes.length, 'file')} changed since the previous verdict; certifying the previous issues instead of re-reading the candidate.`);
|
|
368
|
-
|
|
512
|
+
// The reviewer judges the change itself, measured from git — not the whole checkout.
|
|
513
|
+
const changes = measuredChange(context, change);
|
|
514
|
+
const prompt = roleInstructions('reviewer', context, change, { definition: config.rolePrompts?.reviewer, feedback: feedbackFor(state), verification: state.plan, policy, criteria, developer: state.development, ...(changes ? { changeSet: changes } : {}), ...(reReview ? { reReview } : {}) });
|
|
369
515
|
const incremental = config.efficiency?.reviewMode !== 'full' && delta.mode === 'incremental' && state.review?.sessionId;
|
|
370
516
|
const followup = correctionInstructions('reviewer', feedbackFor(state)) + '\nChanges since YOUR previous reviewed candidate:\n' + JSON.stringify(delta.changes)
|
|
517
|
+
+ (changes ? '\nThe complete change under review (from git, against the run\'s base) — review these files only:\n' + changes.join('\n') : '')
|
|
371
518
|
+ '\nRecertify EVERY current acceptance criterion; previous met results are not current evidence:\n' + JSON.stringify(criteria)
|
|
372
519
|
+ '\nCurrent developer handoff:\n' + JSON.stringify(state.development);
|
|
373
520
|
const outcome = await invoke('reviewer', step, { kind: state.review ? 'correction' : 'initial', prompt: incremental ? followup : prompt, ...(incremental ? { resumeSessionId: state.review.sessionId, fallbackPrompt: prompt } : {}), structured: true, outputSchema: REVIEW_OUTPUT_SCHEMA }, output => {
|
|
@@ -425,4 +572,3 @@ function archiveNode(deps) {
|
|
|
425
572
|
export function coreNodes(deps) {
|
|
426
573
|
return { architect: architectNode(deps), developer: developerNode(deps), fixer: fixerNode(deps), verify: verifyNode(deps), reviewer: reviewerNode(deps), archive: archiveNode(deps) };
|
|
427
574
|
}
|
|
428
|
-
//# sourceMappingURL=nodes.js.map
|
|
@@ -29,4 +29,3 @@ export function evaluateReview(raw, policy) {
|
|
|
29
29
|
&& REVIEW_ASPECTS.every(name => scored[name] >= policy.aspects[name]);
|
|
30
30
|
return { record: { approved: raw.approved, summary: raw.summary, issues: raw.issues, score: raw.score, aspects: scored }, approved };
|
|
31
31
|
}
|
|
32
|
-
//# sourceMappingURL=review-policy.js.map
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { type InvocationKind } from '../role-routing.js';
|
|
2
|
-
import { type PipelineContext } from '../../
|
|
2
|
+
import { type PipelineContext } from '../../pipeline/pipeline-state.js';
|
|
3
3
|
import type { ExecutorRegistry } from '../executors.js';
|
|
4
|
-
import { type AgentEvent, type AgentEventRole, type AgentResult, type AgentRole, type
|
|
4
|
+
import { type AgentEvent, type AgentEventRole, type AgentResult, type AgentRole, type RuntimeConfig, type RuntimeAgentConfig } from '../executor-types.js';
|
|
5
5
|
import type { WorkflowStepContext } from '../workflow-types.js';
|
|
6
|
-
/** Executor failures that mean "this session cannot be continued", not "the role failed". */
|
|
7
|
-
export declare const SESSION_FALLBACK_CODES: Set<string>;
|
|
8
6
|
export interface InvokeOptions {
|
|
9
7
|
kind?: InvocationKind;
|
|
10
8
|
prompt: string;
|
|
@@ -41,7 +39,6 @@ export interface RoleInvokerDeps {
|
|
|
41
39
|
/** `fixer` labels the developer step's events during a correction round on the fixer stance, so a log reader sees who is acting. */
|
|
42
40
|
onAgentEvent?: (role: AgentEventRole, event: AgentEvent) => void;
|
|
43
41
|
}
|
|
44
|
-
export declare function sumUsage(a: AgentUsage, b: AgentUsage): AgentUsage;
|
|
45
42
|
/**
|
|
46
43
|
* One role turn against the configured executor: budget-aware request, live
|
|
47
44
|
* event forwarding with repository-relative paths, structured parsing with a
|
|
@@ -5,18 +5,13 @@ import { selectRoleRoute } from '../role-routing.js';
|
|
|
5
5
|
import { ROLE_SKILLS, OPENSPEC_VERSION, OpenSpecTools, OpenSpecParticipationError, openSpecRepairPrompt } from '../openspec.js';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { performance } from 'node:perf_hooks';
|
|
8
|
-
import { frozenAcceptanceCriteria, pipelineStateDirectory } from '../../
|
|
8
|
+
import { frozenAcceptanceCriteria, pipelineStateDirectory } from '../../pipeline/pipeline-state.js';
|
|
9
9
|
import { AgentExecutionError, unknownUsage } from '../executor-types.js';
|
|
10
|
-
import { sumCacheUsage } from '../efficiency-types.js';
|
|
11
10
|
import { ROLE_INSTRUCTIONS_VERSION, repairInstructions } from '../prompts.js';
|
|
12
11
|
import { parseAgentObject } from './artifacts.js';
|
|
13
12
|
/** Executor failures that mean "this session cannot be continued", not "the role failed". */
|
|
14
|
-
|
|
13
|
+
const SESSION_FALLBACK_CODES = new Set(['session_not_found', 'session_expired', 'session_unsupported']);
|
|
15
14
|
const REPAIRABLE = /Invalid|Expected|requires|Duplicate|malformed/i;
|
|
16
|
-
export function sumUsage(a, b) {
|
|
17
|
-
const add = (x, y) => x === null || y === null ? null : x + y;
|
|
18
|
-
return { inputTokens: add(a.inputTokens, b.inputTokens), outputTokens: add(a.outputTokens, b.outputTokens), costUsd: add(a.costUsd, b.costUsd), ...sumCacheUsage([a, b]) };
|
|
19
|
-
}
|
|
20
15
|
/**
|
|
21
16
|
* One role turn against the configured executor: budget-aware request, live
|
|
22
17
|
* event forwarding with repository-relative paths, structured parsing with a
|
|
@@ -195,4 +190,3 @@ export function createRoleInvoker(deps) {
|
|
|
195
190
|
}
|
|
196
191
|
};
|
|
197
192
|
}
|
|
198
|
-
//# sourceMappingURL=roles.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { VerificationCommand } from '../../
|
|
1
|
+
import type { VerificationCommand } from '../../pipeline/pipeline-state.js';
|
|
2
|
+
import type { VerifyOutcome } from './convergence.js';
|
|
2
3
|
export type DesignConfidence = 'high' | 'medium' | 'low';
|
|
3
4
|
export interface ArchitectureRecord {
|
|
4
5
|
planningDepth?: 'focused' | 'full';
|
|
@@ -28,6 +29,8 @@ export interface DeveloperRecord {
|
|
|
28
29
|
}>;
|
|
29
30
|
/** False when the provider returned prose instead of the structured summary. */
|
|
30
31
|
structured: boolean;
|
|
32
|
+
/** Edits outside the repository scope that Core undid after this turn (repository-qualified when several repositories are in scope). */
|
|
33
|
+
discarded?: string[];
|
|
31
34
|
}
|
|
32
35
|
export interface VerificationRecord {
|
|
33
36
|
valid: boolean;
|
|
@@ -76,6 +79,8 @@ export declare const CoreState: import("@langchain/langgraph").AnnotationRoot<{
|
|
|
76
79
|
/** Autonomous investigation passes the architect already spent on a low-confidence design. */
|
|
77
80
|
deepenPasses: import("@langchain/langgraph").BaseChannel<number, number | import("@langchain/langgraph").OverwriteValue<number>, unknown>;
|
|
78
81
|
archived: import("@langchain/langgraph").BaseChannel<ArchiveRecord | null, ArchiveRecord | import("@langchain/langgraph").OverwriteValue<ArchiveRecord | null> | null, unknown>;
|
|
82
|
+
/** Recent verify outcomes (bounded), so the host can tell a converging correction loop from a circular one. */
|
|
83
|
+
verifyHistory: import("@langchain/langgraph").BaseChannel<VerifyOutcome[], VerifyOutcome[] | import("@langchain/langgraph").OverwriteValue<VerifyOutcome[]>, unknown>;
|
|
79
84
|
}>;
|
|
80
85
|
export type CoreStateType = typeof CoreState.State;
|
|
81
86
|
export type CoreStateUpdate = typeof CoreState.Update;
|
|
@@ -18,7 +18,8 @@ export const CoreState = Annotation.Root({
|
|
|
18
18
|
/** Autonomous investigation passes the architect already spent on a low-confidence design. */
|
|
19
19
|
deepenPasses: Annotation({ ...replace(), default: () => 0 }),
|
|
20
20
|
archived: Annotation({ ...replace(), default: () => null }),
|
|
21
|
+
/** Recent verify outcomes (bounded), so the host can tell a converging correction loop from a circular one. */
|
|
22
|
+
verifyHistory: Annotation({ reducer: (previous, next) => [...previous, ...next].slice(-50), default: () => [] }),
|
|
21
23
|
});
|
|
22
24
|
/** Graph order. `fixer` is the correction node: verify (failed, tasks ticked) and reviewer (rejected) route there; it hands back to verify. */
|
|
23
25
|
export const CORE_NODE_ORDER = ['architect', 'developer', 'fixer', 'verify', 'reviewer', 'archive'];
|
|
24
|
-
//# sourceMappingURL=state.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseCheckpointSaver, type ChannelVersions, type Checkpoint, type CheckpointListOptions, type CheckpointMetadata, type CheckpointTuple, type PendingWrite } from '@langchain/langgraph-checkpoint';
|
|
2
2
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
3
3
|
/** One serialized LangGraph value: the serializer's type tag plus base64 bytes. */
|
|
4
|
-
|
|
4
|
+
interface SerializedBlob {
|
|
5
5
|
type: string;
|
|
6
6
|
data: string;
|
|
7
7
|
}
|
|
@@ -59,3 +59,4 @@ export declare class FileCheckpointSaver extends BaseCheckpointSaver {
|
|
|
59
59
|
putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
|
|
60
60
|
deleteThread(threadId: string): Promise<void>;
|
|
61
61
|
}
|
|
62
|
+
export {};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export declare const GUARDRAIL_IDS: readonly ["plan-validation", "genuine-blocking-question", "plan-language-hint", "frozen-plan-writes", "primary-language", "duplicate-sibling", "one-test-per-module", "empty-write", "evidence-gated-ticking", "inventory-retry", "synthetic-corrections", "silent-group-closure", "verify-per-group", "test-reachability", "exit-code-honesty", "environment-repair", "lockfile-repair", "verify-idle-timeout"];
|
|
2
2
|
export type GuardrailId = (typeof GUARDRAIL_IDS)[number];
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type GuardrailPhase = 'architect' | 'developer' | 'host';
|
|
4
|
+
interface GuardrailDescriptor {
|
|
5
5
|
id: GuardrailId;
|
|
6
6
|
phase: GuardrailPhase;
|
|
7
7
|
}
|
|
8
8
|
export type GuardrailSettings = Partial<Record<GuardrailId, boolean>>;
|
|
9
9
|
/** Stable catalog (phase order = pipeline order); labels/descriptions live in the host UI so they can be localized. */
|
|
10
10
|
export declare const GUARDRAIL_CATALOG: readonly GuardrailDescriptor[];
|
|
11
|
-
export declare function isGuardrailId(value: unknown): value is GuardrailId;
|
|
12
11
|
/** A guardrail is on unless the settings say `false` explicitly. */
|
|
13
12
|
export declare function guardrailEnabled(settings: GuardrailSettings | undefined, id: GuardrailId): boolean;
|
|
14
13
|
/** Validates a raw `guardrails` object: only known ids, only booleans. Returns the normalized settings (unset ⇒ omitted). */
|
|
15
14
|
export declare function validateGuardrailSettings(value: unknown, path?: string): GuardrailSettings;
|
|
15
|
+
export {};
|
|
@@ -52,7 +52,7 @@ export const GUARDRAIL_CATALOG = [
|
|
|
52
52
|
{ id: 'lockfile-repair', phase: 'host' },
|
|
53
53
|
{ id: 'verify-idle-timeout', phase: 'host' },
|
|
54
54
|
];
|
|
55
|
-
|
|
55
|
+
function isGuardrailId(value) {
|
|
56
56
|
return typeof value === 'string' && GUARDRAIL_IDS.includes(value);
|
|
57
57
|
}
|
|
58
58
|
/** A guardrail is on unless the settings say `false` explicitly. */
|
|
@@ -73,4 +73,3 @@ export function validateGuardrailSettings(value, path = 'guardrails') {
|
|
|
73
73
|
}
|
|
74
74
|
return out;
|
|
75
75
|
}
|
|
76
|
-
//# sourceMappingURL=guardrails.js.map
|
|
@@ -17,5 +17,5 @@ export { FileCheckpointSaver, type GraphStoreIO, type SerializedGraphStore } fro
|
|
|
17
17
|
export { ARCHITECT_OUTPUT_SCHEMA, DEVELOPER_OUTPUT_SCHEMA, REVIEW_OUTPUT_SCHEMA, ROLE_INSTRUCTIONS_VERSION, rolePromptDefaults, correctionInstructions, deepenInstructions, repairInstructions, roleInstructions, type FrozenCriterion, type RoleFeedback, type RoleInstructionOptions, } from './prompts.js';
|
|
18
18
|
export { OPENSPEC_VERSION, ROLE_SKILLS, type OpenSpecRoleContext } from './openspec.js';
|
|
19
19
|
export * from './efficiency-summary.js';
|
|
20
|
-
export { readVerificationEvidence, type VerificationEvidenceQuery } from '../
|
|
20
|
+
export { readVerificationEvidence, type VerificationEvidenceQuery } from '../pipeline/pipeline-state.js';
|
|
21
21
|
export { runRecovery, recoveryRequestSchema, type RecoveryRequest } from './recovery.js';
|
|
@@ -17,6 +17,5 @@ export { FileCheckpointSaver } from './graph-checkpointer.js';
|
|
|
17
17
|
export { ARCHITECT_OUTPUT_SCHEMA, DEVELOPER_OUTPUT_SCHEMA, REVIEW_OUTPUT_SCHEMA, ROLE_INSTRUCTIONS_VERSION, rolePromptDefaults, correctionInstructions, deepenInstructions, repairInstructions, roleInstructions, } from './prompts.js';
|
|
18
18
|
export { OPENSPEC_VERSION, ROLE_SKILLS } from './openspec.js';
|
|
19
19
|
export * from './efficiency-summary.js';
|
|
20
|
-
export { readVerificationEvidence } from '../
|
|
20
|
+
export { readVerificationEvidence } from '../pipeline/pipeline-state.js';
|
|
21
21
|
export { runRecovery, recoveryRequestSchema } from './recovery.js';
|
|
22
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toolEvent } from './tool-event.js';
|
|
2
|
-
import { normalizeKimiCliModel } from '
|
|
2
|
+
import { normalizeKimiCliModel } from './kimi-model.js';
|
|
3
3
|
import { AgentExecutionError, unknownUsage, validateAgentRequest } from './executor-types.js';
|
|
4
4
|
import { runCliProcess } from './cli-process.js';
|
|
5
5
|
import { parseStructuredText } from './openai-executor.js';
|
|
@@ -187,4 +187,3 @@ export async function executeKimiReadonlyAcp(request, options = {}) {
|
|
|
187
187
|
request.onEvent?.({ kind: 'usage', usage });
|
|
188
188
|
return { text, usage, sessionId, structured: parseStructuredText(text) };
|
|
189
189
|
}
|
|
190
|
-
//# sourceMappingURL=kimi-acp.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurations keep raw provider ids. At the CLI boundary only the managed
|
|
3
|
+
* Kimi aliases gain their documented `kimi-code/` namespace; custom and
|
|
4
|
+
* already-qualified ids stay byte-identical.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeKimiCliModel(model: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const OFFICIAL_SHORT_MODEL_IDS = new Set(['k3', 'kimi-for-coding', 'kimi-for-coding-highspeed']);
|
|
2
|
+
const SAFE_MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$/;
|
|
3
|
+
/**
|
|
4
|
+
* Configurations keep raw provider ids. At the CLI boundary only the managed
|
|
5
|
+
* Kimi aliases gain their documented `kimi-code/` namespace; custom and
|
|
6
|
+
* already-qualified ids stay byte-identical.
|
|
7
|
+
*/
|
|
8
|
+
export function normalizeKimiCliModel(model) {
|
|
9
|
+
if (!SAFE_MODEL_ID.test(model))
|
|
10
|
+
throw new Error('Kimi model id must be 1-128 characters and match [A-Za-z0-9][A-Za-z0-9._/:-]*');
|
|
11
|
+
return OFFICIAL_SHORT_MODEL_IDS.has(model) ? `kimi-code/${model}` : model;
|
|
12
|
+
}
|
|
@@ -9,15 +9,6 @@ export interface OpenAICompatibleOptions {
|
|
|
9
9
|
idleTimeoutMs?: number;
|
|
10
10
|
}
|
|
11
11
|
export declare function parseStructuredText(text: string): Record<string, unknown> | undefined;
|
|
12
|
-
/** The agent loop an openai-compatible provider runs: compact host-driven pipelines by default. */
|
|
13
|
-
/** The OpenAI `reasoning_effort` tiers a compatible endpoint accepts. */
|
|
14
|
-
export declare const OPENAI_EFFORTS: readonly ["low", "medium", "high"];
|
|
15
|
-
export declare function resolveAgentLoop(provider: ApiProvider): 'compact' | 'free';
|
|
16
|
-
/** Compact runs: abort when NOTHING happens for this long (no tool call, reply or usage) — the real hang detector now that the wall clock is per task group. A non-streaming 16k reply at 20 tok/s is ~13 min, hence the margin. */
|
|
17
|
-
export declare const IDLE_TIMEOUT_MS: number;
|
|
18
|
-
export declare function resolveContextWindow(provider: ApiProvider): number;
|
|
19
|
-
/** The per-turn output budget the connection declares; undefined keeps the loop's default. */
|
|
20
|
-
export declare function resolveOutputBudget(provider: ApiProvider): number | undefined;
|
|
21
12
|
/** No SDK, gateway, tracing backend, paid probe, or mandatory key. */
|
|
22
13
|
export declare class OpenAICompatibleExecutor implements AgentExecutor {
|
|
23
14
|
private readonly provider;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readVerificationEvidence } from '../
|
|
1
|
+
import { readVerificationEvidence } from '../pipeline/pipeline-state.js';
|
|
2
2
|
import { assertEffortSupported, unknownCapabilities } from './capabilities.js';
|
|
3
3
|
import { OpenSpecTools, OPENSPEC_TOOL_DEFINITION, openSpecPrompt } from './openspec.js';
|
|
4
4
|
import { AgentExecutionError, unknownUsage, validateAgentRequest } from './executor-types.js';
|
|
@@ -19,13 +19,13 @@ export function parseStructuredText(text) {
|
|
|
19
19
|
}
|
|
20
20
|
/** The agent loop an openai-compatible provider runs: compact host-driven pipelines by default. */
|
|
21
21
|
/** The OpenAI `reasoning_effort` tiers a compatible endpoint accepts. */
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const OPENAI_EFFORTS = ['low', 'medium', 'high'];
|
|
23
|
+
function resolveAgentLoop(provider) { return provider.agentLoop ?? 'compact'; }
|
|
24
24
|
/** Compact runs: abort when NOTHING happens for this long (no tool call, reply or usage) — the real hang detector now that the wall clock is per task group. A non-streaming 16k reply at 20 tok/s is ~13 min, hence the margin. */
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const IDLE_TIMEOUT_MS = 20 * 60_000;
|
|
26
|
+
function resolveContextWindow(provider) { return provider.contextWindowTokens ?? DEFAULT_CONTEXT_WINDOW_TOKENS; }
|
|
27
27
|
/** The per-turn output budget the connection declares; undefined keeps the loop's default. */
|
|
28
|
-
|
|
28
|
+
function resolveOutputBudget(provider) { return provider.maxOutputTokens; }
|
|
29
29
|
/** No SDK, gateway, tracing backend, paid probe, or mandatory key. */
|
|
30
30
|
export class OpenAICompatibleExecutor {
|
|
31
31
|
provider;
|
|
@@ -134,4 +134,3 @@ export class OpenAICompatibleExecutor {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
//# sourceMappingURL=openai-executor.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readVerificationEvidence } from '../
|
|
1
|
+
import { readVerificationEvidence } from '../pipeline/pipeline-state.js';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -34,4 +34,3 @@ if (context.evidenceScope && context.role !== 'architect')
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
await server.connect(new StdioServerTransport());
|
|
37
|
-
//# sourceMappingURL=openspec-tool-server.js.map
|
|
@@ -62,7 +62,6 @@ export declare const IMPLEMENTATION_PROGRESS_SCHEMA: z.ZodObject<{
|
|
|
62
62
|
}, z.core.$strict>>;
|
|
63
63
|
blockers: z.ZodArray<z.ZodString>;
|
|
64
64
|
}, z.core.$strict>;
|
|
65
|
-
export declare function renderProgressHandoff(context: OpenSpecRoleContext): string;
|
|
66
65
|
export declare function hash(text: string): string;
|
|
67
66
|
export declare function resolveOpenSpecCli(): string;
|
|
68
67
|
export declare function runOpenSpec(cli: string, root: string, args: string[], signal?: AbortSignal): Promise<string>;
|
|
@@ -98,7 +97,6 @@ export declare class OpenSpecTools {
|
|
|
98
97
|
assertParticipation(after?: number): void;
|
|
99
98
|
assertReady(): Promise<OpenSpecApply>;
|
|
100
99
|
}
|
|
101
|
-
export declare const OPENSPEC_BRIDGE_ENTRY: string;
|
|
102
100
|
export declare const OPENSPEC_TOOL_ACTIONS: readonly ["load_skill", "new", "status", "instructions", "validate", "write_artifact", "read_progress", "write_progress"];
|
|
103
101
|
export declare const OPENSPEC_TOOL_DEFINITION: {
|
|
104
102
|
type: string;
|