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
|
@@ -29,18 +29,18 @@ const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
29
29
|
const NON_SOURCE = new Set(['openspec', 'README.md', 'README', 'LICENSE', 'LICENSE.md', '.gitignore', '.gitattributes', '.editorconfig', 'CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.DS_Store']);
|
|
30
30
|
const HIDDEN = /^\./;
|
|
31
31
|
const stringList = { type: 'array', items: { type: 'string' } };
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const INVENTORY_SCHEMA = { type: 'object', additionalProperties: false, required: ['greenfield', 'languages', 'frameworks', 'keyFiles', 'tests', 'notes'], properties: { greenfield: { type: 'boolean' }, languages: stringList, frameworks: stringList, keyFiles: stringList, tests: stringList, notes: { type: 'string' } } };
|
|
33
|
+
const PROPOSAL_SCHEMA = { type: 'object', additionalProperties: false, required: ['why', 'whatChanges', 'capabilities', 'impact'], properties: { why: { type: 'string' }, whatChanges: stringList, capabilities: { type: 'object', additionalProperties: false, required: ['new', 'modified'], properties: { new: stringList, modified: stringList } }, impact: stringList } };
|
|
34
|
+
const DESIGN_SCHEMA = { type: 'object', additionalProperties: false, required: ['context', 'goals', 'nonGoals', 'decisions', 'risks'], properties: { context: { type: 'string' }, goals: stringList, nonGoals: stringList, decisions: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['title', 'choice', 'why'], properties: { title: { type: 'string' }, choice: { type: 'string' }, why: { type: 'string' } } } }, risks: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['risk', 'mitigation'], properties: { risk: { type: 'string' }, mitigation: { type: 'string' } } } } } };
|
|
35
|
+
const SPEC_SCHEMA = { type: 'object', additionalProperties: false, required: ['name', 'requirements'], properties: { name: { type: 'string' }, requirements: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['name', 'text', 'scenarios'], properties: { name: { type: 'string' }, text: { type: 'string' }, scenarios: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['name', 'when', 'then'], properties: { name: { type: 'string' }, when: { type: 'string' }, then: { type: 'string' } } } } } } } } };
|
|
36
36
|
/** Names the repository's primary language for the plan, so task file names use its extension (a greenfield repository has none yet). */
|
|
37
37
|
function languageLine(roots) {
|
|
38
38
|
const profile = languageProfile(roots);
|
|
39
39
|
return profile ? `\nPrimary language of the repository: ${profile.primary}${profile.others.length ? ` (also present: ${profile.others.join(', ')})` : ''} — name new source files with its extension.` : '';
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
const TASKS_SCHEMA = { type: 'object', additionalProperties: false, required: ['groups'], properties: { groups: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['title', 'tasks'], properties: { title: { type: 'string' }, tasks: stringList } } }, verification: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['repositoryId', 'command', 'args'], properties: { repositoryId: { type: 'string' }, command: { type: 'string' }, args: stringList } } }, blockingQuestion: { type: 'string' } } };
|
|
42
42
|
/** True when no repository holds anything but OpenSpec artifacts and repository boilerplate. */
|
|
43
|
-
|
|
43
|
+
function hostGreenfield(roots) {
|
|
44
44
|
return roots.every(root => {
|
|
45
45
|
if (!existsSync(root))
|
|
46
46
|
return true;
|
|
@@ -50,12 +50,12 @@ export function hostGreenfield(roots) {
|
|
|
50
50
|
function slug(value) { return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 60); }
|
|
51
51
|
function bullets(items, fallback) { return items.length ? items.map(item => `- ${item}`).join('\n') : `- ${fallback}`; }
|
|
52
52
|
/** Rendered with the exact headings `openspec instructions proposal` returns. */
|
|
53
|
-
|
|
53
|
+
function renderProposal(proposal, capabilityNames) {
|
|
54
54
|
return ['## Why', '', proposal.why, '', '## What Changes', '', bullets(proposal.whatChanges, 'See the requirements in the specs of this change.'), '', '## Capabilities', '', '### New Capabilities',
|
|
55
55
|
...(capabilityNames.new.length ? capabilityNames.new.map(name => `- \`${name}\`: ${name.replace(/-/g, ' ')}`) : ['- (none)']), '', '### Modified Capabilities',
|
|
56
56
|
...(capabilityNames.modified.length ? capabilityNames.modified.map(name => `- \`${name}\`: requirements extended by this change`) : ['- (none)']), '', '## Impact', '', bullets(proposal.impact, 'Limited to the repositories in scope.'), ''].join('\n');
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
function renderDesign(design, greenfield) {
|
|
59
59
|
return ['## Context', '', ...(greenfield ? ['The repository has no application code; the spec means building it from scratch.', ''] : []), design.context, '', '## Goals / Non-Goals', '', '**Goals:**', bullets(design.goals, 'Deliver the requested work.'), '', '**Non-Goals:**', bullets(design.nonGoals, 'Anything outside the frozen scope.'), '', '## Decisions', '',
|
|
60
60
|
...(design.decisions.length ? design.decisions.map(item => `- **${item.title}**: ${item.choice} — ${item.why}`) : ['- Follow the existing conventions of the repository.']), '', '## Risks / Trade-offs', '', ...(design.risks.length ? design.risks.map(item => `- [${item.risk}] → ${item.mitigation}`) : ['- [Scope creep] → implement only the listed tasks.']), '', '## Local reference patterns', '', greenfield ? 'No existing implementation to reference: the repository is empty.' : 'See the key files listed in the inventory of this change; no equivalent implementation was verified beyond them.', ''].join('\n');
|
|
61
61
|
}
|
|
@@ -368,4 +368,3 @@ export async function runCompactArchitect(env) {
|
|
|
368
368
|
env.onEvent?.({ kind: 'text', text: `Compact architect: ${specs.length} spec${specs.length === 1 ? '' : 's'}, ${tasks.groups.reduce((total, group) => total + group.tasks.length, 0)} tasks, ${inventoryLoop.toolCalls} inventory tool calls.` });
|
|
369
369
|
return { text: result, usage: env.client.usage, structured };
|
|
370
370
|
}
|
|
371
|
-
//# sourceMappingURL=architect.js.map
|
|
@@ -10,11 +10,11 @@ export type ChatMessage = Record<string, unknown>;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function ensurePatientDispatcher(): Promise<void>;
|
|
12
12
|
export declare function patientFetchInit(): Pick<RequestInit, 'dispatcher'>;
|
|
13
|
-
|
|
13
|
+
interface ChatCompletion {
|
|
14
14
|
message: Record<string, unknown>;
|
|
15
15
|
finishReason: unknown;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
interface ChatClientOptions {
|
|
18
18
|
endpoint: URL;
|
|
19
19
|
headers: Record<string, string>;
|
|
20
20
|
fetch: typeof globalThis.fetch;
|
|
@@ -80,4 +80,4 @@ export declare class ChatClient {
|
|
|
80
80
|
* non-streaming shape: `{ choices: [{ message, finish_reason }], usage }`.
|
|
81
81
|
*/
|
|
82
82
|
export declare function readCompletionResponse(response: Response): Promise<unknown>;
|
|
83
|
-
export
|
|
83
|
+
export {};
|
|
@@ -281,7 +281,7 @@ export async function readCompletionResponse(response) {
|
|
|
281
281
|
message.tool_calls = toolCalls;
|
|
282
282
|
return { choices: [{ message, finish_reason: finishReason ?? (toolCalls.length ? 'tool_calls' : 'stop') }], ...(usage ? { usage } : {}) };
|
|
283
283
|
}
|
|
284
|
-
|
|
284
|
+
async function readBoundedResponse(response) {
|
|
285
285
|
if (!response.body)
|
|
286
286
|
throw new Error('Missing response body');
|
|
287
287
|
const reader = response.body.getReader(), chunks = [];
|
|
@@ -304,4 +304,3 @@ export async function readBoundedResponse(response) {
|
|
|
304
304
|
}
|
|
305
305
|
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
306
306
|
}
|
|
307
|
-
//# sourceMappingURL=chat-client.js.map
|
|
@@ -1,50 +1,7 @@
|
|
|
1
1
|
import type { ChatMessage } from './chat-client.js';
|
|
2
2
|
import { type AgentResult } from '../executor-types.js';
|
|
3
3
|
import { type CompactEnv } from './step.js';
|
|
4
|
-
|
|
5
|
-
export declare const DEFAULT_TASK_TOOL_BUDGET = 25;
|
|
6
|
-
export declare const TASK_RESULT_SCHEMA: {
|
|
7
|
-
type: string;
|
|
8
|
-
additionalProperties: boolean;
|
|
9
|
-
required: string[];
|
|
10
|
-
properties: {
|
|
11
|
-
summary: {
|
|
12
|
-
type: string;
|
|
13
|
-
};
|
|
14
|
-
files: {
|
|
15
|
-
type: string;
|
|
16
|
-
items: {
|
|
17
|
-
type: string;
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
tests: {
|
|
21
|
-
type: string;
|
|
22
|
-
items: {
|
|
23
|
-
type: string;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
verification: {
|
|
27
|
-
type: string;
|
|
28
|
-
};
|
|
29
|
-
incomplete: {
|
|
30
|
-
type: string;
|
|
31
|
-
items: {
|
|
32
|
-
type: string;
|
|
33
|
-
additionalProperties: boolean;
|
|
34
|
-
required: string[];
|
|
35
|
-
properties: {
|
|
36
|
-
task: {
|
|
37
|
-
type: string;
|
|
38
|
-
};
|
|
39
|
-
reason: {
|
|
40
|
-
type: string;
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
export interface TaskGroup {
|
|
4
|
+
interface TaskGroup {
|
|
48
5
|
index: number;
|
|
49
6
|
title: string;
|
|
50
7
|
tasks: {
|
|
@@ -66,9 +23,7 @@ export declare function namedFiles(task: string): string[];
|
|
|
66
23
|
* Generic Node/Jest/TS and Python signatures; anything else passes through.
|
|
67
24
|
*/
|
|
68
25
|
export declare function diagnoseVerificationFailure(feedback: string, roots: readonly string[]): string[];
|
|
69
|
-
|
|
70
|
-
export declare function repositoryInventory(roots: readonly string[], limit?: number): string[];
|
|
71
|
-
export interface LanguageProfile {
|
|
26
|
+
interface LanguageProfile {
|
|
72
27
|
primary: string;
|
|
73
28
|
others: string[];
|
|
74
29
|
}
|
|
@@ -95,8 +50,6 @@ export declare function duplicateSibling(roots: readonly string[], file: string,
|
|
|
95
50
|
* adding .tsx is fine).
|
|
96
51
|
*/
|
|
97
52
|
export declare function foreignLanguageFile(roots: readonly string[], file: string, languages: LanguageProfile): string | undefined;
|
|
98
|
-
/** Existing test directories (relative, POSIX) among the conventional names, e.g. `tests`, `test`, `__tests__`, `spec`. */
|
|
99
|
-
export declare function existingTestDirs(roots: readonly string[]): string[];
|
|
100
53
|
/**
|
|
101
54
|
* A NEW test file outside the directory the repository already keeps its
|
|
102
55
|
* tests in (`src/hold.test.js` next to an existing `tests/`): the reason to
|
|
@@ -112,8 +65,6 @@ export declare function misplacedTestFile(roots: readonly string[], file: string
|
|
|
112
65
|
export declare function siblingTestFile(roots: readonly string[], file: string): string | undefined;
|
|
113
66
|
/** The absolute path a task-named file resolves to: the exact path, else the same stem in a sibling extension of the same language family. */
|
|
114
67
|
export declare function locateNamedFile(roots: readonly string[], file: string): string | undefined;
|
|
115
|
-
/** Extra tool calls a correction group gets on top of the task budget: it must read AND patch, and the feedback is long. */
|
|
116
|
-
export declare const CORRECTION_BUDGET_BONUS = 10;
|
|
117
68
|
/**
|
|
118
69
|
* Source excerpts around every `file:line` the feedback names (Jest/Babel
|
|
119
70
|
* `path/file.js: … (30:56)`, TS `file.ts(12,3)` / `file.ts:12:3`, mocha
|
|
@@ -125,27 +76,8 @@ export declare function feedbackExcerpts(feedback: string, roots: readonly strin
|
|
|
125
76
|
context?: number;
|
|
126
77
|
maxBytes?: number;
|
|
127
78
|
}): string;
|
|
128
|
-
/** An existing file this long is edited with apply_patch, never regenerated whole (observed: a 1.4k-line test rewritten twice in one group, ~25 minutes of generation). */
|
|
129
|
-
export declare const REWRITE_MAX_LINES = 150;
|
|
130
|
-
/** Above this many lines, `read_file` answers with an outline (head + index of definitions) instead of the whole file. */
|
|
131
|
-
export declare const OUTLINE_MIN_LINES = 400;
|
|
132
|
-
/**
|
|
133
|
-
* A large file as an outline: the first lines (imports, top-level state) and
|
|
134
|
-
* an index of definitions with their line numbers, so the model asks for the
|
|
135
|
-
* range it needs with read_lines instead of putting 15k tokens of test file
|
|
136
|
-
* into the transcript on every read (observed: 4-minute prefills).
|
|
137
|
-
*/
|
|
138
|
-
export declare function outlineLargeFile(file: string, content: string): string | undefined;
|
|
139
|
-
/** Files a model writes to narrate its own work; never part of a task, always noise for the reviewer (observed: fix_summary.json, task-summary.json in the repository root). */
|
|
140
|
-
export declare const NARRATION_FILE: RegExp;
|
|
141
79
|
/** Media/binary extensions a text-only tool can never produce correctly. */
|
|
142
80
|
export declare const BINARY_ASSET: RegExp;
|
|
143
|
-
/** Extra write-only calls a task group gets when it spent its whole budget reading. */
|
|
144
|
-
export declare const WRITE_EXTENSION_CALLS = 6;
|
|
145
|
-
/** Tool calls the in-place fix round after a failed group check may spend. */
|
|
146
|
-
export declare const GROUP_FIX_BUDGET = 20;
|
|
147
|
-
/** Test files among `reported` that the repository's own test command (per root) does not execute; empty when no command is detected. */
|
|
148
|
-
export declare function unreachedGroupTests(roots: readonly string[], reported: readonly string[]): string[];
|
|
149
81
|
/** A source file that exists but carries no implementation: a handful of lines, a placeholder import, or only TODOs. */
|
|
150
82
|
export declare function isStubFile(file: string, minLines?: number): boolean;
|
|
151
83
|
/** Ticks the given task ids; every other byte of tasks.md is preserved, as the developer write rule requires. */
|
|
@@ -158,3 +90,4 @@ export declare function tickTasks(markdown: string, ids: Set<string>): string;
|
|
|
158
90
|
export declare function runCompactDeveloper(env: CompactEnv, options?: {
|
|
159
91
|
taskToolBudget?: number;
|
|
160
92
|
}): Promise<AgentResult>;
|
|
93
|
+
export {};
|
|
@@ -4,16 +4,16 @@ import { unreachedTestFiles, unreachedTestsReason } from './test-reachability.js
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { AgentExecutionError } from '../executor-types.js';
|
|
6
6
|
import { artifactPath } from '../openspec.js';
|
|
7
|
-
import { readVerificationEvidence } from '../../
|
|
7
|
+
import { readVerificationEvidence } from '../../pipeline/pipeline-state.js';
|
|
8
8
|
import { bounded } from './prompt-inputs.js';
|
|
9
9
|
import { finalJson, on, openspecCall, strings, text, toolStep } from './step.js';
|
|
10
10
|
/** Tool calls one task group may spend before it must report. */
|
|
11
|
-
|
|
11
|
+
const DEFAULT_TASK_TOOL_BUDGET = 25;
|
|
12
12
|
/** Frozen planning artifacts: the developer edits application code, never the plan. */
|
|
13
13
|
const FROZEN_PATH = /(?:^|[\\/])openspec[\\/]/i;
|
|
14
14
|
const DEVELOPER_TOOLS = ['list_files', 'read_file', 'read_lines', 'search_text', 'get_diff', 'write_file', 'apply_patch'];
|
|
15
15
|
const stringList = { type: 'array', items: { type: 'string' } };
|
|
16
|
-
|
|
16
|
+
const TASK_RESULT_SCHEMA = { type: 'object', additionalProperties: false, required: ['summary', 'files', 'tests', 'verification', 'incomplete'], properties: { summary: { type: 'string' }, files: stringList, tests: stringList, verification: { type: 'string' }, incomplete: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['task', 'reason'], properties: { task: { type: 'string' }, reason: { type: 'string' } } } } } };
|
|
17
17
|
/** Parses the `## N. Title` / `- [ ] N.M text` layout the OpenSpec tasks template prescribes. */
|
|
18
18
|
export function parseTaskGroups(markdown) {
|
|
19
19
|
const groups = [];
|
|
@@ -133,7 +133,7 @@ function findFiles(root, base, depth = 0) {
|
|
|
133
133
|
return out;
|
|
134
134
|
}
|
|
135
135
|
/** Application source/test files under the roots (bounded), excluding plans, deps and dot dirs. */
|
|
136
|
-
|
|
136
|
+
function repositoryInventory(roots, limit = 60) {
|
|
137
137
|
const out = [];
|
|
138
138
|
const walk = (dir, rel, depth) => {
|
|
139
139
|
if (depth > 6 || out.length >= limit)
|
|
@@ -247,7 +247,7 @@ export function foreignLanguageFile(roots, file, languages) {
|
|
|
247
247
|
return `"${file}" is ${language}, but this repository is ${languages.primary}-only. Write the same behaviour in ${languages.primary} inside the existing modules; do not add a second language.`;
|
|
248
248
|
}
|
|
249
249
|
/** Existing test directories (relative, POSIX) among the conventional names, e.g. `tests`, `test`, `__tests__`, `spec`. */
|
|
250
|
-
|
|
250
|
+
function existingTestDirs(roots) {
|
|
251
251
|
const names = ['tests', 'test', '__tests__', 'spec', 'specs'];
|
|
252
252
|
return [...new Set(roots.flatMap(root => names.filter(name => { try {
|
|
253
253
|
return statSync(path.join(root, name)).isDirectory();
|
|
@@ -317,7 +317,7 @@ export function locateNamedFile(roots, file) {
|
|
|
317
317
|
return undefined;
|
|
318
318
|
}
|
|
319
319
|
/** Extra tool calls a correction group gets on top of the task budget: it must read AND patch, and the feedback is long. */
|
|
320
|
-
|
|
320
|
+
const CORRECTION_BUDGET_BONUS = 10;
|
|
321
321
|
/**
|
|
322
322
|
* Source excerpts around every `file:line` the feedback names (Jest/Babel
|
|
323
323
|
* `path/file.js: … (30:56)`, TS `file.ts(12,3)` / `file.ts:12:3`, mocha
|
|
@@ -369,9 +369,9 @@ export function feedbackExcerpts(feedback, roots, options = {}) {
|
|
|
369
369
|
return chunks.join('\n\n');
|
|
370
370
|
}
|
|
371
371
|
/** An existing file this long is edited with apply_patch, never regenerated whole (observed: a 1.4k-line test rewritten twice in one group, ~25 minutes of generation). */
|
|
372
|
-
|
|
372
|
+
const REWRITE_MAX_LINES = 150;
|
|
373
373
|
/** Above this many lines, `read_file` answers with an outline (head + index of definitions) instead of the whole file. */
|
|
374
|
-
|
|
374
|
+
const OUTLINE_MIN_LINES = 400;
|
|
375
375
|
const OUTLINE_HEAD_LINES = 120;
|
|
376
376
|
const DEFINITION_LINE = /^\s*(?:(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s*\w+|(?:export\s+)?class\s+\w+|(?:export\s+)?(?:const|let|var)\s+\w+\s*=\s*(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|\w+\s*=>)|(?:static\s+|async\s+|get\s+|set\s+)?[A-Za-z_$][\w$]*\s*\([^)]*\)\s*\{|(?:describe|it|test|suite|context)\s*\(|def\s+\w+|func\s+(?:\([^)]*\)\s*)?\w+|(?:pub\s+)?fn\s+\w+)/;
|
|
377
377
|
/**
|
|
@@ -380,7 +380,7 @@ const DEFINITION_LINE = /^\s*(?:(?:export\s+)?(?:default\s+)?(?:async\s+)?functi
|
|
|
380
380
|
* range it needs with read_lines instead of putting 15k tokens of test file
|
|
381
381
|
* into the transcript on every read (observed: 4-minute prefills).
|
|
382
382
|
*/
|
|
383
|
-
|
|
383
|
+
function outlineLargeFile(file, content) {
|
|
384
384
|
const lines = content.split('\n');
|
|
385
385
|
if (lines.length <= OUTLINE_MIN_LINES)
|
|
386
386
|
return undefined;
|
|
@@ -391,13 +391,13 @@ export function outlineLargeFile(file, content) {
|
|
|
391
391
|
return JSON.stringify({ outline: true, path: file, totalLines: lines.length, head: lines.slice(0, OUTLINE_HEAD_LINES).join('\n'), definitions: index, note: `${file} has ${lines.length} lines; this is its first ${OUTLINE_HEAD_LINES} lines plus an index of definitions. Read only the range you need with read_lines(path, startLine, endLine); do not read the whole file.` });
|
|
392
392
|
}
|
|
393
393
|
/** Files a model writes to narrate its own work; never part of a task, always noise for the reviewer (observed: fix_summary.json, task-summary.json in the repository root). */
|
|
394
|
-
|
|
394
|
+
const NARRATION_FILE = /(?:^|\/)(?:[\w.-]*(?:summary|progress|scratch|handoff)[\w.-]*)\.(?:json|md|txt)$/i;
|
|
395
395
|
/** Media/binary extensions a text-only tool can never produce correctly. */
|
|
396
396
|
export const BINARY_ASSET = /\.(?:wav|mp3|ogg|flac|m4a|aac|png|jpe?g|gif|webp|bmp|ico|woff2?|ttf|otf|eot|mp4|webm|mov|zip|gz|tar|pdf|wasm)$/i;
|
|
397
397
|
/** Extra write-only calls a task group gets when it spent its whole budget reading. */
|
|
398
|
-
|
|
398
|
+
const WRITE_EXTENSION_CALLS = 6;
|
|
399
399
|
/** Tool calls the in-place fix round after a failed group check may spend. */
|
|
400
|
-
|
|
400
|
+
const GROUP_FIX_BUDGET = 20;
|
|
401
401
|
/**
|
|
402
402
|
* One bounded correction round for a group whose check just failed: the exact
|
|
403
403
|
* output plus host-read excerpts around every reported file:line, the group's
|
|
@@ -465,7 +465,7 @@ async function fixGroupInPlace(env, group, output, budget, evidence, extraTools,
|
|
|
465
465
|
return [...new Set(written)];
|
|
466
466
|
}
|
|
467
467
|
/** Test files among `reported` that the repository's own test command (per root) does not execute; empty when no command is detected. */
|
|
468
|
-
|
|
468
|
+
function unreachedGroupTests(roots, reported) {
|
|
469
469
|
return roots.flatMap(root => { const check = detectCheckCommand(root); return check ? unreachedTestFiles(root, [check], reported) : []; });
|
|
470
470
|
}
|
|
471
471
|
/** A source file that exists but carries no implementation: a handful of lines, a placeholder import, or only TODOs. */
|
|
@@ -820,4 +820,3 @@ export async function runCompactDeveloper(env, options = {}) {
|
|
|
820
820
|
const structured = { summary: summaries.join('\n'), files: [...files], tests: [...tests], verification: verification.length ? verification.join('; ') : 'none', incomplete };
|
|
821
821
|
return { text: JSON.stringify(structured), usage: env.client.usage, structured };
|
|
822
822
|
}
|
|
823
|
-
//# sourceMappingURL=developer.js.map
|
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
import type { spawnSync } from 'node:child_process';
|
|
2
|
-
|
|
2
|
+
interface EnvironmentInstall {
|
|
3
3
|
ecosystem: 'node' | 'python' | 'go' | 'rust';
|
|
4
4
|
root: string;
|
|
5
5
|
command: string;
|
|
6
6
|
args: string[];
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
/** `precondition`: the install failed for a reason only the host can repair (see hostPreconditionFailure). */
|
|
9
|
+
interface InstallOutcome extends EnvironmentInstall {
|
|
9
10
|
ok: boolean;
|
|
10
11
|
detail: string;
|
|
12
|
+
precondition?: string;
|
|
11
13
|
}
|
|
12
14
|
/** True when a verification failure looks like a missing toolchain/dependency rather than a code defect. */
|
|
13
15
|
export declare function isEnvironmentFailure(exitCode: number | null | undefined, output: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Failures that no edit to the change can repair, because the verification
|
|
18
|
+
* process itself lacks something only the host can grant: an environment
|
|
19
|
+
* variable a configuration file references, registry credentials, registry
|
|
20
|
+
* network access, git credentials for a dependency. Feeding them to a fixer
|
|
21
|
+
* makes it edit package-manager configuration to route around the missing
|
|
22
|
+
* secret (observed: `.yarnrc.yml` rewritten, reverted by review, rewritten
|
|
23
|
+
* again). Returns a short host-facing reason, or undefined.
|
|
24
|
+
*/
|
|
25
|
+
export declare function hostPreconditionFailure(output: string): string | undefined;
|
|
14
26
|
/** Packages a failure output names explicitly (`Try \`npm i --save-dev @types/jest\``); the host installs exactly those. */
|
|
15
27
|
export declare function suggestedPackages(output: string): string[];
|
|
16
28
|
/** Dependencies package.json declares that node_modules does not hold (bounded). */
|
|
17
29
|
export declare function missingNodeDependencies(root: string): string[];
|
|
18
30
|
/** The installs a repository root needs, judged from its manifests and the absence of their install output. */
|
|
19
31
|
export declare function plannedInstalls(root: string, failureOutput?: string): EnvironmentInstall[];
|
|
20
|
-
/** Rewrites an exact, unpublished version in package.json to `^<major>.0.0`; true when something changed. */
|
|
21
|
-
export declare function relaxManifestPin(root: string, name: string, version: string): boolean;
|
|
22
32
|
/** Runs each planned install once, bounded; never throws. */
|
|
23
33
|
export declare function installEnvironment(roots: readonly string[], io?: {
|
|
24
34
|
spawn?: typeof spawnSync;
|
|
@@ -37,7 +47,7 @@ export declare function detectCheckCommand(root: string): {
|
|
|
37
47
|
command: string;
|
|
38
48
|
args: string[];
|
|
39
49
|
} | undefined;
|
|
40
|
-
|
|
50
|
+
interface GroupCheckOutcome {
|
|
41
51
|
ran: boolean;
|
|
42
52
|
ok: boolean;
|
|
43
53
|
command?: string;
|
|
@@ -60,3 +70,4 @@ export declare function runGroupCheck(roots: readonly string[], io?: {
|
|
|
60
70
|
text?: string;
|
|
61
71
|
}) => void;
|
|
62
72
|
}): GroupCheckOutcome;
|
|
73
|
+
export {};
|
|
@@ -19,6 +19,8 @@ const ENVIRONMENT_SIGNATURES = [
|
|
|
19
19
|
/jest: not found|vitest: not found|mocha: not found|tsc: not found|pytest: not found/i,
|
|
20
20
|
/TS2582|Do you need to install type definitions|Try `npm i --save-dev @types\//i,
|
|
21
21
|
/Module (?:[\w@./-]+) in the \w+ option was not found|Preset [\w@./-]+ not found|Cannot find module '(?:ts-jest|babel-jest|@swc\/jest|ts-node|tsx)'/i,
|
|
22
|
+
// Yarn Berry refuses to run scripts in a checkout that was never installed.
|
|
23
|
+
/doesn't seem to have been installed - running an install there might help|Couldn't find the node_modules state file - running an install might help/i,
|
|
22
24
|
];
|
|
23
25
|
/** True when a verification failure looks like a missing toolchain/dependency rather than a code defect. */
|
|
24
26
|
export function isEnvironmentFailure(exitCode, output) {
|
|
@@ -27,6 +29,33 @@ export function isEnvironmentFailure(exitCode, output) {
|
|
|
27
29
|
const tail = output.slice(-6000);
|
|
28
30
|
return ENVIRONMENT_SIGNATURES.some(pattern => pattern.test(tail));
|
|
29
31
|
}
|
|
32
|
+
const REGISTRY = String.raw `(?:registry|npmjs\.org|yarnpkg\.com|npm\.pkg\.github\.com|pkgs\.dev\.azure\.com|jfrog|artifactory|nexus|pypi\.org|crates\.io|proxy\.golang\.org)`;
|
|
33
|
+
/**
|
|
34
|
+
* Failures that no edit to the change can repair, because the verification
|
|
35
|
+
* process itself lacks something only the host can grant: an environment
|
|
36
|
+
* variable a configuration file references, registry credentials, registry
|
|
37
|
+
* network access, git credentials for a dependency. Feeding them to a fixer
|
|
38
|
+
* makes it edit package-manager configuration to route around the missing
|
|
39
|
+
* secret (observed: `.yarnrc.yml` rewritten, reverted by review, rewritten
|
|
40
|
+
* again). Returns a short host-facing reason, or undefined.
|
|
41
|
+
*/
|
|
42
|
+
export function hostPreconditionFailure(output) {
|
|
43
|
+
const text = output.replace(/\u001b\[[0-9;?]*[A-Za-z]/g, '').slice(-16_000);
|
|
44
|
+
const variable = /Environment variable not found \(([A-Za-z_][A-Za-z0-9_]*)\)/.exec(text);
|
|
45
|
+
if (variable)
|
|
46
|
+
return `the environment variable ${variable[1]} is not available to verification commands, and a package-manager configuration file requires it`;
|
|
47
|
+
if (/\bYN0041\b/.test(text) || /YN0035[\s\S]{0,400}?Response Code: 40[13]\b/.test(text) || /Response code 40[13] \((?:Unauthorized|Forbidden)\)/i.test(text)
|
|
48
|
+
|| /npm (?:ERR!|error) code (?:E401|E403|ENEEDAUTH)\b/.test(text) || /ERR_PNPM_FETCH_40[13]\b/.test(text)
|
|
49
|
+
|| new RegExp(String.raw `\b40[13] (?:Unauthorized|Forbidden)\b[^\n]{0,80}https?://[^\s]*${REGISTRY}`, 'i').test(text)
|
|
50
|
+
|| /\b(?:401|403) Client Error: (?:Unauthorized|Forbidden) for url/.test(text))
|
|
51
|
+
return 'the package registry rejected the credentials available to verification commands (HTTP 401/403)';
|
|
52
|
+
if (/npm (?:ERR!|error) code (?:ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT|ECONNRESET)\b/.test(text) || /ERR_PNPM_META_FETCH_FAIL\b/.test(text)
|
|
53
|
+
|| new RegExp(String.raw `\b(?:ENOTFOUND|EAI_AGAIN)\b[^\n]{0,200}${REGISTRY}|${REGISTRY}[^\n]{0,200}\b(?:ENOTFOUND|EAI_AGAIN)\b`, 'i').test(text))
|
|
54
|
+
return 'the package registry cannot be reached from the verification environment';
|
|
55
|
+
if (/fatal: could not read (?:Username|Password) for/.test(text) || /Permission denied \(publickey\)/.test(text))
|
|
56
|
+
return 'git credentials needed to fetch a dependency are not available to verification commands';
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
30
59
|
/** Packages a failure output names explicitly (`Try \`npm i --save-dev @types/jest\``); the host installs exactly those. */
|
|
31
60
|
export function suggestedPackages(output) {
|
|
32
61
|
const packages = new Set();
|
|
@@ -89,7 +118,7 @@ export function plannedInstalls(root, failureOutput = '') {
|
|
|
89
118
|
return plans;
|
|
90
119
|
}
|
|
91
120
|
/** Rewrites an exact, unpublished version in package.json to `^<major>.0.0`; true when something changed. */
|
|
92
|
-
|
|
121
|
+
function relaxManifestPin(root, name, version) {
|
|
93
122
|
const file = path.join(root, 'package.json');
|
|
94
123
|
let manifest;
|
|
95
124
|
try {
|
|
@@ -155,7 +184,8 @@ export function installEnvironment(roots, io = {}) {
|
|
|
155
184
|
const ok = result.status === 0;
|
|
156
185
|
const detail = ok ? `installed ${plan.ecosystem} dependencies in ${path.basename(root)}` : `${plan.command} ${plan.args.join(' ')} failed in ${path.basename(root)}: ${String(result.stderr || result.error?.message || `exit ${result.status}`).trim().slice(0, 400)}`;
|
|
157
186
|
io.onEvent?.({ kind: 'text', text: `Environment: ${detail}` });
|
|
158
|
-
|
|
187
|
+
const precondition = ok ? undefined : hostPreconditionFailure(`${result.stdout ?? ''}\n${result.stderr ?? ''}`);
|
|
188
|
+
outcomes.push({ ...plan, ok, detail, ...(precondition ? { precondition } : {}) });
|
|
159
189
|
}
|
|
160
190
|
}
|
|
161
191
|
return outcomes;
|
|
@@ -210,4 +240,3 @@ export function runGroupCheck(roots, io = {}) {
|
|
|
210
240
|
}
|
|
211
241
|
return { ran: false, ok: true, output: '' };
|
|
212
242
|
}
|
|
213
|
-
//# sourceMappingURL=environment.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface ExitHonestyFinding {
|
|
2
2
|
command: string;
|
|
3
3
|
failures: number | null;
|
|
4
4
|
sample: string;
|
|
@@ -6,3 +6,4 @@ export interface ExitHonestyFinding {
|
|
|
6
6
|
/** A failure count in a runner summary, or a failure-marker line, despite exit 0. */
|
|
7
7
|
export declare function exitCodeContradiction(exitCode: number | null, output: string): ExitHonestyFinding | null;
|
|
8
8
|
export declare function exitHonestyReason(findings: ReadonlyArray<ExitHonestyFinding>): string;
|
|
9
|
+
export {};
|
|
@@ -75,4 +75,3 @@ export function exitHonestyReason(findings) {
|
|
|
75
75
|
const parts = findings.map(item => `\`${item.command}\` exited 0 but its output reports ${item.failures !== null ? `${item.failures} failing test${item.failures === 1 ? '' : 's'}` : 'a failure'} (${JSON.stringify(item.sample)})`);
|
|
76
76
|
return `The verification command passed by exit code while reporting failures: ${parts.join('; ')}. A test runner that does not exit non-zero on failure hides broken tests: make the harness set \`process.exitCode = 1\` (or throw) when any test fails, then fix the failing tests.`;
|
|
77
77
|
}
|
|
78
|
-
//# sourceMappingURL=exit-code-honesty.js.map
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { type AgentEvent } from '../executor-types.js';
|
|
2
2
|
import { type ChatClient, type ChatMessage } from './chat-client.js';
|
|
3
3
|
export declare const DEFAULT_CONTEXT_WINDOW_TOKENS = 32768;
|
|
4
|
-
|
|
5
|
-
export declare const REPEAT_WARN_AT = 3;
|
|
6
|
-
/** Total identical calls that abort the step. */
|
|
7
|
-
export declare const REPEAT_ABORT_AT = 5;
|
|
8
|
-
export interface ToolLoopOptions {
|
|
4
|
+
interface ToolLoopOptions {
|
|
9
5
|
client: ChatClient;
|
|
10
6
|
/** Mutated in place: assistant, tool and nudge messages are appended. */
|
|
11
7
|
messages: ChatMessage[];
|
|
@@ -34,25 +30,12 @@ export interface ToolLoopOptions {
|
|
|
34
30
|
extraCalls: number;
|
|
35
31
|
};
|
|
36
32
|
}
|
|
37
|
-
/** Default output budget of one tool turn. A tool turn should emit a tool call or a short reply, so this is a runaway guard, not a ceiling for real work; a connection whose model thinks privately (that counts against `max_tokens`) raises it with `maxOutputTokens`. */
|
|
38
|
-
export declare const TOOL_TURN_MAX_OUTPUT = 8192;
|
|
39
|
-
/**
|
|
40
|
-
* `max_tokens` counts INSIDE the context window (prompt + output ≤ window), so
|
|
41
|
-
* a budget wider than what remains makes llama.cpp refuse the request
|
|
42
|
-
* ("exceeds the available context size") instead of helping. Bound every
|
|
43
|
-
* request to the room actually left; never below a minimal useful reply.
|
|
44
|
-
*/
|
|
45
|
-
export declare function boundedOutputBudget(requested: number, contextWindowTokens: number, messages: readonly ChatMessage[]): number;
|
|
46
|
-
/** Consecutive rewrites of one path (no other tool call between them) before the loop refuses the next one. */
|
|
47
|
-
export declare const REWRITE_WARN_AT = 3;
|
|
48
33
|
export interface ToolLoopResult {
|
|
49
34
|
text: string;
|
|
50
35
|
toolCalls: number;
|
|
51
36
|
}
|
|
52
37
|
/** Estimated tokens for the transcript: chars/4, which is what small local servers count against. */
|
|
53
38
|
export declare function estimateTokens(messages: ChatMessage[]): number;
|
|
54
|
-
/** One correct example the model can copy when its arguments were rejected. */
|
|
55
|
-
export declare function toolExample(name: string): string;
|
|
56
39
|
/** Schema-level argument repair: returns the error the model receives instead of executing. */
|
|
57
40
|
export declare function validateToolArguments(name: string, raw: string, available: Set<string>): {
|
|
58
41
|
args: Record<string, unknown>;
|
|
@@ -71,3 +54,4 @@ export declare function compactMessages(messages: ChatMessage[], contextWindowTo
|
|
|
71
54
|
* context compaction against the provider's window.
|
|
72
55
|
*/
|
|
73
56
|
export declare function runToolLoop(options: ToolLoopOptions): Promise<ToolLoopResult>;
|
|
57
|
+
export {};
|
|
@@ -4,15 +4,15 @@ import { AgentExecutionError } from '../executor-types.js';
|
|
|
4
4
|
import { record } from './chat-client.js';
|
|
5
5
|
export const DEFAULT_CONTEXT_WINDOW_TOKENS = 32_768;
|
|
6
6
|
/** Consecutive identical calls answered with an error instead of executing. */
|
|
7
|
-
|
|
7
|
+
const REPEAT_WARN_AT = 3;
|
|
8
8
|
/** Total identical calls that abort the step. */
|
|
9
|
-
|
|
9
|
+
const REPEAT_ABORT_AT = 5;
|
|
10
10
|
const COMPACTION_RATIO = 0.7;
|
|
11
11
|
const PROTECTED_TAIL = 4;
|
|
12
12
|
const PLACEHOLDER_QUERIES = new Set(['.', '*', '**', '.*', '..', '?', 'query', '<query>']);
|
|
13
13
|
const NUDGE = 'Your reply was empty. Reply with the final result now, as requested; do not call tools.';
|
|
14
14
|
/** Default output budget of one tool turn. A tool turn should emit a tool call or a short reply, so this is a runaway guard, not a ceiling for real work; a connection whose model thinks privately (that counts against `max_tokens`) raises it with `maxOutputTokens`. */
|
|
15
|
-
|
|
15
|
+
const TOOL_TURN_MAX_OUTPUT = 8192;
|
|
16
16
|
/** Headroom kept between the transcript and the context window when bounding an output budget. */
|
|
17
17
|
const OUTPUT_HEADROOM_TOKENS = 512;
|
|
18
18
|
/**
|
|
@@ -21,7 +21,7 @@ const OUTPUT_HEADROOM_TOKENS = 512;
|
|
|
21
21
|
* ("exceeds the available context size") instead of helping. Bound every
|
|
22
22
|
* request to the room actually left; never below a minimal useful reply.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
function boundedOutputBudget(requested, contextWindowTokens, messages) {
|
|
25
25
|
const remaining = contextWindowTokens - estimateTokens(messages) - OUTPUT_HEADROOM_TOKENS;
|
|
26
26
|
return Math.max(256, Math.min(requested, remaining));
|
|
27
27
|
}
|
|
@@ -29,7 +29,7 @@ const EFFORT_STEP_DOWN = { high: 'medium', medium: 'low' };
|
|
|
29
29
|
/** Tools whose result is a pure function of the workspace: safe to answer a repeated call with a pointer to the earlier identical result. */
|
|
30
30
|
const READ_TOOLS = new Set(['read_file', 'read_lines', 'search_text', 'get_diff', 'list_files']);
|
|
31
31
|
/** Consecutive rewrites of one path (no other tool call between them) before the loop refuses the next one. */
|
|
32
|
-
|
|
32
|
+
const REWRITE_WARN_AT = 3;
|
|
33
33
|
/** Whole-file rewrites only: iterating a file with apply_patch between reads is the normal edit flow. */
|
|
34
34
|
const WRITE_TOOLS = new Set(['write_file']);
|
|
35
35
|
/** The rewrite streak keys on the file NAME: a model alternating `tests/x.test.js` with an invented `repo/tests/x.test.js` (observed, 7 rewrites) is still rewriting one file. */
|
|
@@ -58,7 +58,7 @@ export function estimateTokens(messages) {
|
|
|
58
58
|
return Math.ceil(messages.reduce((total, message) => total + JSON.stringify(message).length, 0) / 4);
|
|
59
59
|
}
|
|
60
60
|
/** One correct example the model can copy when its arguments were rejected. */
|
|
61
|
-
|
|
61
|
+
function toolExample(name) {
|
|
62
62
|
switch (name) {
|
|
63
63
|
case 'search_text': return '{"path":".","query":"functionName"}';
|
|
64
64
|
case 'read_lines': return '{"path":"src/index.ts","startLine":1,"endLine":120}';
|
|
@@ -235,7 +235,7 @@ export async function runToolLoop(options) {
|
|
|
235
235
|
options.onToolCall?.();
|
|
236
236
|
calls.set(item.id, { name: item.name, args: item.arguments });
|
|
237
237
|
options.onEvent?.(toolEvent(item.name, item.arguments));
|
|
238
|
-
const key = item.name + '
|
|
238
|
+
const key = item.name + '\0' + item.arguments.replace(/\s+/g, '');
|
|
239
239
|
consecutive = key === previous ? consecutive + 1 : 1;
|
|
240
240
|
previous = key;
|
|
241
241
|
const total = (totals.get(key) ?? 0) + 1;
|
|
@@ -317,4 +317,3 @@ export async function runToolLoop(options) {
|
|
|
317
317
|
}
|
|
318
318
|
throw new AgentExecutionError(`Agent exhausted ${options.maxTurns} turns without a final result`, 'max_turns', client.usage);
|
|
319
319
|
}
|
|
320
|
-
//# sourceMappingURL=guarded-loop.js.map
|
|
@@ -36,7 +36,5 @@ export interface PromptInputs {
|
|
|
36
36
|
status: 'added' | 'changed' | 'deleted';
|
|
37
37
|
}>;
|
|
38
38
|
}
|
|
39
|
-
/** Text of one `## Heading` section up to the next heading (or terminator), without the heading line. */
|
|
40
|
-
export declare function section(prompt: string, heading: string): string;
|
|
41
39
|
export declare function bounded(text: string, limit: number): string;
|
|
42
40
|
export declare function extractPromptInputs(prompt: string): PromptInputs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RE_REVIEW_MARKER } from '../prompts.js';
|
|
2
2
|
const STOP = /^(## |Planning policy:|Current frozen acceptance obligations)/;
|
|
3
3
|
/** Text of one `## Heading` section up to the next heading (or terminator), without the heading line. */
|
|
4
|
-
|
|
4
|
+
function section(prompt, heading) {
|
|
5
5
|
const lines = prompt.split('\n');
|
|
6
6
|
const start = lines.findIndex(line => line.trim() === heading);
|
|
7
7
|
if (start < 0)
|
|
@@ -56,4 +56,3 @@ export function extractPromptInputs(prompt) {
|
|
|
56
56
|
reReviewChanges: parseReReviewChanges(prompt),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
//# sourceMappingURL=prompt-inputs.js.map
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import type { AgentResult } from '../executor-types.js';
|
|
2
2
|
import { type CompactEnv } from './step.js';
|
|
3
|
-
/** Tool calls the reviewer may spend inspecting beyond the supplied diff. */
|
|
4
|
-
export declare const DEFAULT_REVIEW_TOOL_BUDGET = 15;
|
|
5
|
-
/** A re-review only opens the changed files. */
|
|
6
|
-
export declare const RE_REVIEW_TOOL_BUDGET = 6;
|
|
7
|
-
/** Working-tree changes of every allowed root against HEAD, plus the content of new files, bounded. `only` restricts the diff and the new-file listing to those repository-relative paths (a re-review reads just what the fixer touched). */
|
|
8
|
-
export declare function collectDiff(roots: string[], only?: readonly string[]): string;
|
|
9
|
-
/** The gate thresholds the host rendered into the reviewer prompt (`score is at least N`, `aspect ≥ N`). */
|
|
10
|
-
export declare function reviewGateThresholds(gate: string): {
|
|
11
|
-
minScore: number;
|
|
12
|
-
aspects: Record<string, number>;
|
|
13
|
-
};
|
|
14
|
-
/** Files named by the previous review's issues (`path: what must change` lines under `## Previous review`). */
|
|
15
|
-
export declare function previousIssueFiles(feedback: string): string[];
|
|
16
3
|
/**
|
|
17
4
|
* The compact reviewer: the host supplies the diff and the frozen criteria, the
|
|
18
5
|
* model inspects read-only within a small budget, and the verdict is forced
|
|
@@ -8,14 +8,14 @@ import { hasBinaryAssets } from './architect.js';
|
|
|
8
8
|
import { bounded } from './prompt-inputs.js';
|
|
9
9
|
import { finalJson, openspecCall, strings, text, toolStep } from './step.js';
|
|
10
10
|
/** Tool calls the reviewer may spend inspecting beyond the supplied diff. */
|
|
11
|
-
|
|
11
|
+
const DEFAULT_REVIEW_TOOL_BUDGET = 15;
|
|
12
12
|
/** A re-review only opens the changed files. */
|
|
13
|
-
|
|
13
|
+
const RE_REVIEW_TOOL_BUDGET = 6;
|
|
14
14
|
const DIFF_LIMIT = 40 * 1024;
|
|
15
15
|
const UNTRACKED_LIMIT = 8 * 1024;
|
|
16
16
|
const MAX_UNTRACKED = 12;
|
|
17
17
|
/** Working-tree changes of every allowed root against HEAD, plus the content of new files, bounded. `only` restricts the diff and the new-file listing to those repository-relative paths (a re-review reads just what the fixer touched). */
|
|
18
|
-
|
|
18
|
+
function collectDiff(roots, only) {
|
|
19
19
|
// Pathspec magic stays enabled so openspec/ artifacts are excluded from the reviewed diff.
|
|
20
20
|
const env = { ...Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.toUpperCase().startsWith('GIT_'))), GIT_OPTIONAL_LOCKS: '0' };
|
|
21
21
|
const options = { encoding: 'utf8', env, windowsHide: true, timeout: 10_000, maxBuffer: 4 * 1024 * 1024 };
|
|
@@ -62,7 +62,7 @@ export function collectDiff(roots, only) {
|
|
|
62
62
|
return sections.join('\n\n');
|
|
63
63
|
}
|
|
64
64
|
/** The gate thresholds the host rendered into the reviewer prompt (`score is at least N`, `aspect ≥ N`). */
|
|
65
|
-
|
|
65
|
+
function reviewGateThresholds(gate) {
|
|
66
66
|
const minScore = Number(/score\` is at least (\d+)/.exec(gate)?.[1] ?? 0);
|
|
67
67
|
const aspects = {};
|
|
68
68
|
for (const match of gate.matchAll(/`([a-z_]+)` ≥ (\d+)/g))
|
|
@@ -70,7 +70,7 @@ export function reviewGateThresholds(gate) {
|
|
|
70
70
|
return { minScore, aspects };
|
|
71
71
|
}
|
|
72
72
|
/** Files named by the previous review's issues (`path: what must change` lines under `## Previous review`). */
|
|
73
|
-
|
|
73
|
+
function previousIssueFiles(feedback) {
|
|
74
74
|
const section = feedback.split('## Previous review')[1] ?? '';
|
|
75
75
|
return [...new Set([...section.matchAll(/(?:^|[\s-])((?:[\w.-]+\/)*[\w.-]+\.(?:[cm]?[jt]sx?|py|go|rs|java|kt|swift|cs|rb|php|c|cc|cpp|h|hpp|vue|svelte|html|css|scss|json|ya?ml|md))(?=[\s:,;)]|$)/g)].map(match => match[1].replace(/\\/g, '/')))];
|
|
76
76
|
}
|
|
@@ -175,4 +175,3 @@ export async function runCompactReviewer(env, options = {}) {
|
|
|
175
175
|
};
|
|
176
176
|
return { text: JSON.stringify(structured), usage: env.client.usage, structured };
|
|
177
177
|
}
|
|
178
|
-
//# sourceMappingURL=reviewer.js.map
|