gsd-pi 2.76.0-dev.82e249f7b → 2.76.0-dev.97807402
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/dist/claude-cli-check.js +32 -3
- package/dist/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
- package/dist/resources/extensions/gsd/auto/phases.js +14 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +27 -0
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-start.js +27 -18
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -48
- package/dist/resources/extensions/gsd/auto.js +13 -17
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +40 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +12 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/error-classifier.js +10 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gsd-db.js +115 -7
- package/dist/resources/extensions/gsd/guided-flow.js +189 -0
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +6 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -9
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -4
- package/dist/resources/extensions/gsd/safety/safety-harness.js +4 -0
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/dist/resources/skills/write-docs/SKILL.md +2 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +18 -1
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -1
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +19 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +84 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +3 -1
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +14 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +9 -5
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +25 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +16 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +36 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +9 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
- package/src/resources/extensions/gsd/auto/phases.ts +14 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +29 -0
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +15 -0
- package/src/resources/extensions/gsd/auto-start.ts +29 -19
- package/src/resources/extensions/gsd/auto-worktree.ts +34 -52
- package/src/resources/extensions/gsd/auto.ts +12 -17
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +42 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +13 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/error-classifier.ts +10 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gsd-db.ts +122 -7
- package/src/resources/extensions/gsd/guided-flow.ts +221 -0
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/journal.ts +2 -1
- package/src/resources/extensions/gsd/key-manager.ts +6 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +36 -10
- package/src/resources/extensions/gsd/preferences-types.ts +46 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -3
- package/src/resources/extensions/gsd/safety/safety-harness.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +152 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +234 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/src/resources/skills/write-docs/SKILL.md +2 -1
- /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → pI48IF3dgfs0CBrYi2bh_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → pI48IF3dgfs0CBrYi2bh_}/_ssgManifest.js +0 -0
|
@@ -140,6 +140,25 @@ import type { Request } from 'express';
|
|
|
140
140
|
assert.ok(packages.includes("typescript"));
|
|
141
141
|
assert.ok(!packages.includes("-D"));
|
|
142
142
|
});
|
|
143
|
+
|
|
144
|
+
// Regression tests for #4388: prose containing `from "..."` must not produce false-positive packages
|
|
145
|
+
test("does not treat prose 'from \"What's Next\"' as a package name (#4388)", () => {
|
|
146
|
+
const desc = 'Build the feature described from "What\'s Next" in the roadmap';
|
|
147
|
+
const packages = extractPackageReferences(desc);
|
|
148
|
+
assert.deepEqual(packages, [], `prose 'from "What\\'s Next"' must not produce package names, got: ${JSON.stringify(packages)}`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("does not treat prose \"from 'master'\" as a package name (#4388)", () => {
|
|
152
|
+
const desc = "Review changes from 'master' branch before merging";
|
|
153
|
+
const packages = extractPackageReferences(desc);
|
|
154
|
+
assert.deepEqual(packages, [], `prose "from 'master'" must not produce package names, got: ${JSON.stringify(packages)}`);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("still extracts import statements in code blocks after #4388 fix", () => {
|
|
158
|
+
const desc = "```typescript\nimport express from 'express';\nimport { Router } from 'express';\n```";
|
|
159
|
+
const packages = extractPackageReferences(desc);
|
|
160
|
+
assert.ok(packages.includes("express"), "import...from in code blocks must still be recognized");
|
|
161
|
+
});
|
|
143
162
|
});
|
|
144
163
|
|
|
145
164
|
// ─── File Path Consistency Tests ─────────────────────────────────────────────
|
|
@@ -1580,6 +1599,221 @@ describe("checkTaskOrdering directory inputs (#4446)", () => {
|
|
|
1580
1599
|
});
|
|
1581
1600
|
});
|
|
1582
1601
|
|
|
1602
|
+
// ─── Regression Tests: checkTaskOrdering false positive for pre-execution refs (#4071) ──
|
|
1603
|
+
|
|
1604
|
+
describe("checkTaskOrdering false positive for pre-execution refs (#4071)", () => {
|
|
1605
|
+
test("completed task at higher index does not trigger ordering violation for its outputs", () => {
|
|
1606
|
+
// Scenario: after a replan, a completed task at higher array index has already
|
|
1607
|
+
// created a file. A new earlier-sequence task reads that file. Since the
|
|
1608
|
+
// completed task already ran, its output is available regardless of disk state.
|
|
1609
|
+
// checkTaskOrdering must not flag this as a sequence violation.
|
|
1610
|
+
const tasks = [
|
|
1611
|
+
createTask({
|
|
1612
|
+
id: "T_NEW",
|
|
1613
|
+
sequence: 1,
|
|
1614
|
+
status: "pending",
|
|
1615
|
+
inputs: ["artifacts/setup.json"],
|
|
1616
|
+
expected_output: [],
|
|
1617
|
+
}),
|
|
1618
|
+
createTask({
|
|
1619
|
+
id: "T_SETUP",
|
|
1620
|
+
sequence: 5,
|
|
1621
|
+
status: "completed",
|
|
1622
|
+
inputs: [],
|
|
1623
|
+
expected_output: ["artifacts/setup.json"],
|
|
1624
|
+
}),
|
|
1625
|
+
];
|
|
1626
|
+
|
|
1627
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1628
|
+
assert.equal(
|
|
1629
|
+
results.length,
|
|
1630
|
+
0,
|
|
1631
|
+
"completed task outputs must not trigger ordering violations for earlier-sequence tasks that read them",
|
|
1632
|
+
);
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
test("pending task at higher index still triggers ordering violation", () => {
|
|
1636
|
+
// A PENDING task at higher index creating a file is a real violation.
|
|
1637
|
+
// Only completed tasks get the exemption.
|
|
1638
|
+
const tasks = [
|
|
1639
|
+
createTask({
|
|
1640
|
+
id: "T01",
|
|
1641
|
+
sequence: 1,
|
|
1642
|
+
status: "pending",
|
|
1643
|
+
inputs: ["artifacts/output.json"],
|
|
1644
|
+
expected_output: [],
|
|
1645
|
+
}),
|
|
1646
|
+
createTask({
|
|
1647
|
+
id: "T02",
|
|
1648
|
+
sequence: 5,
|
|
1649
|
+
status: "pending",
|
|
1650
|
+
inputs: [],
|
|
1651
|
+
expected_output: ["artifacts/output.json"],
|
|
1652
|
+
}),
|
|
1653
|
+
];
|
|
1654
|
+
|
|
1655
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1656
|
+
assert.equal(
|
|
1657
|
+
results.length,
|
|
1658
|
+
1,
|
|
1659
|
+
"pending task at higher index must still be flagged as ordering violation",
|
|
1660
|
+
);
|
|
1661
|
+
assert.equal(results[0].blocking, true);
|
|
1662
|
+
assert.ok(results[0].message.includes("T01"));
|
|
1663
|
+
assert.ok(results[0].message.includes("T02"));
|
|
1664
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
test("pending-first then completed-later: completed replaces pending in fileCreators (#4572)", () => {
|
|
1668
|
+
// Regression for CodeRabbit Major finding on PR #4572:
|
|
1669
|
+
// fileCreators only stored the FIRST task for a given path. If a PENDING task at
|
|
1670
|
+
// array index 1 was registered first, and a COMPLETED task at array index 2 also
|
|
1671
|
+
// declared the same output path, the completed entry was discarded. Line ~529 then
|
|
1672
|
+
// saw a pending creator with index > i and incorrectly fired a sequence violation
|
|
1673
|
+
// for the reader at array index 0.
|
|
1674
|
+
//
|
|
1675
|
+
// Scenario: path first declared by pending task (index 1), then by completed task
|
|
1676
|
+
// (index 2). Reader is at index 0. Without the fix a violation fires; with the fix
|
|
1677
|
+
// the completed entry replaces the pending entry and grants the exemption.
|
|
1678
|
+
const tasks = [
|
|
1679
|
+
// array index 0 — reads the shared path
|
|
1680
|
+
createTask({
|
|
1681
|
+
id: "T_READER",
|
|
1682
|
+
sequence: 1,
|
|
1683
|
+
status: "pending",
|
|
1684
|
+
inputs: ["shared/artifact.json"],
|
|
1685
|
+
expected_output: [],
|
|
1686
|
+
}),
|
|
1687
|
+
// array index 1 — pending producer (visited first during map build)
|
|
1688
|
+
createTask({
|
|
1689
|
+
id: "T_PENDING_PRODUCER",
|
|
1690
|
+
sequence: 5,
|
|
1691
|
+
status: "pending",
|
|
1692
|
+
inputs: [],
|
|
1693
|
+
expected_output: ["shared/artifact.json"],
|
|
1694
|
+
}),
|
|
1695
|
+
// array index 2 — completed producer (visited second; must replace pending entry)
|
|
1696
|
+
createTask({
|
|
1697
|
+
id: "T_COMPLETED_PRODUCER",
|
|
1698
|
+
sequence: 2,
|
|
1699
|
+
status: "completed",
|
|
1700
|
+
inputs: [],
|
|
1701
|
+
expected_output: ["shared/artifact.json"],
|
|
1702
|
+
}),
|
|
1703
|
+
];
|
|
1704
|
+
|
|
1705
|
+
// Without the fix: creator = T_PENDING_PRODUCER (index 1), !creator.completed && 1 > 0 → violation.
|
|
1706
|
+
// With the fix: creator = T_COMPLETED_PRODUCER (index 2), creator.completed → no violation.
|
|
1707
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1708
|
+
assert.equal(
|
|
1709
|
+
results.length,
|
|
1710
|
+
0,
|
|
1711
|
+
"completed producer must replace pending producer in fileCreators and suppress false violation",
|
|
1712
|
+
);
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
test("completed task output exemption applies regardless of whether file exists on disk", (t) => {
|
|
1716
|
+
// The completed-task exemption must work even when the file is not on disk
|
|
1717
|
+
// (e.g., the file was a temporary artifact that was cleaned up after the task ran).
|
|
1718
|
+
const tempDir = join(tmpdir(), `pre-exec-completed-task-${Date.now()}`);
|
|
1719
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1720
|
+
t.after(() => rmSync(tempDir, { recursive: true, force: true }));
|
|
1721
|
+
|
|
1722
|
+
// File deliberately NOT created on disk — completed task ran in a prior session
|
|
1723
|
+
const tasks = [
|
|
1724
|
+
createTask({
|
|
1725
|
+
id: "T_MAIN",
|
|
1726
|
+
sequence: 0,
|
|
1727
|
+
status: "pending",
|
|
1728
|
+
inputs: ["generated/config.json"],
|
|
1729
|
+
expected_output: [],
|
|
1730
|
+
}),
|
|
1731
|
+
createTask({
|
|
1732
|
+
id: "T_INIT",
|
|
1733
|
+
sequence: 10,
|
|
1734
|
+
status: "completed",
|
|
1735
|
+
inputs: [],
|
|
1736
|
+
expected_output: ["generated/config.json"],
|
|
1737
|
+
}),
|
|
1738
|
+
];
|
|
1739
|
+
|
|
1740
|
+
const results = checkTaskOrdering(tasks, tempDir);
|
|
1741
|
+
assert.equal(
|
|
1742
|
+
results.length,
|
|
1743
|
+
0,
|
|
1744
|
+
"completed task exemption must apply even when file is absent from disk",
|
|
1745
|
+
);
|
|
1746
|
+
});
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1749
|
+
describe("checkFilePathConsistency completed-task output exemption (#4071)", () => {
|
|
1750
|
+
test("completed task at higher index does not cause false positive for file it produced", (t) => {
|
|
1751
|
+
// Parallel to the checkTaskOrdering fix: checkFilePathConsistency also uses
|
|
1752
|
+
// getExpectedOutputsUpTo which historically only looked at prior-index tasks.
|
|
1753
|
+
// A completed task at a higher index has already run and its outputs are available.
|
|
1754
|
+
const tempDir = join(tmpdir(), `pre-exec-fc-completed-${Date.now()}`);
|
|
1755
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1756
|
+
t.after(() => rmSync(tempDir, { recursive: true, force: true }));
|
|
1757
|
+
|
|
1758
|
+
// File is NOT on disk — completed task ran in a prior session and file was cleaned
|
|
1759
|
+
const tasks = [
|
|
1760
|
+
createTask({
|
|
1761
|
+
id: "T_MAIN",
|
|
1762
|
+
sequence: 1,
|
|
1763
|
+
status: "pending",
|
|
1764
|
+
inputs: ["artifacts/config.json"],
|
|
1765
|
+
expected_output: [],
|
|
1766
|
+
}),
|
|
1767
|
+
createTask({
|
|
1768
|
+
id: "T_SETUP",
|
|
1769
|
+
sequence: 10,
|
|
1770
|
+
status: "completed",
|
|
1771
|
+
inputs: [],
|
|
1772
|
+
expected_output: ["artifacts/config.json"],
|
|
1773
|
+
}),
|
|
1774
|
+
];
|
|
1775
|
+
|
|
1776
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
1777
|
+
assert.equal(
|
|
1778
|
+
results.length,
|
|
1779
|
+
0,
|
|
1780
|
+
"completed task at higher index should satisfy inputs of pending tasks that read its outputs",
|
|
1781
|
+
);
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
test("pending task at higher index still causes a missing-file error", (t) => {
|
|
1785
|
+
const tempDir = join(tmpdir(), `pre-exec-fc-pending-${Date.now()}`);
|
|
1786
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1787
|
+
t.after(() => rmSync(tempDir, { recursive: true, force: true }));
|
|
1788
|
+
|
|
1789
|
+
const tasks = [
|
|
1790
|
+
createTask({
|
|
1791
|
+
id: "T01",
|
|
1792
|
+
sequence: 1,
|
|
1793
|
+
status: "pending",
|
|
1794
|
+
inputs: ["artifacts/output.json"],
|
|
1795
|
+
expected_output: [],
|
|
1796
|
+
}),
|
|
1797
|
+
createTask({
|
|
1798
|
+
id: "T02",
|
|
1799
|
+
sequence: 10,
|
|
1800
|
+
status: "pending",
|
|
1801
|
+
inputs: [],
|
|
1802
|
+
expected_output: ["artifacts/output.json"],
|
|
1803
|
+
}),
|
|
1804
|
+
];
|
|
1805
|
+
|
|
1806
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
1807
|
+
assert.equal(
|
|
1808
|
+
results.length,
|
|
1809
|
+
1,
|
|
1810
|
+
"pending task at higher index must still be flagged — the file is not available yet",
|
|
1811
|
+
);
|
|
1812
|
+
assert.equal(results[0].blocking, true);
|
|
1813
|
+
assert.equal(results[0].target, "artifacts/output.json");
|
|
1814
|
+
});
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1583
1817
|
describe("checkFilePathConsistency self-referential inputs (#4459)", () => {
|
|
1584
1818
|
test("input that is also in the same task's expected_output is not blocking when missing on disk", (t) => {
|
|
1585
1819
|
const tempDir = join(tmpdir(), `pre-exec-self-output-${Date.now()}`);
|
|
@@ -186,6 +186,45 @@ test("flat_rate_providers is a recognized preference key (no warning)", () => {
|
|
|
186
186
|
);
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
+
test("slice_parallel preferences validate and pass through", () => {
|
|
190
|
+
const { preferences, errors, warnings } = validatePreferences({
|
|
191
|
+
slice_parallel: { enabled: true, max_workers: 8 },
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
assert.equal(errors.length, 0);
|
|
195
|
+
assert.equal(warnings.filter(w => w.includes("slice_parallel")).length, 0);
|
|
196
|
+
assert.deepEqual(preferences.slice_parallel, { enabled: true, max_workers: 8 });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("slice_parallel rejects invalid values and warns on unknown keys", () => {
|
|
200
|
+
const { preferences, errors, warnings } = validatePreferences({
|
|
201
|
+
slice_parallel: {
|
|
202
|
+
enabled: "yes",
|
|
203
|
+
max_workers: 9,
|
|
204
|
+
future_mode: true,
|
|
205
|
+
},
|
|
206
|
+
} as any);
|
|
207
|
+
|
|
208
|
+
assert.ok(errors.some(e => e.includes("slice_parallel.enabled")), "should reject non-boolean enabled");
|
|
209
|
+
assert.ok(errors.some(e => e.includes("slice_parallel.max_workers")), "should reject max_workers outside 1..8");
|
|
210
|
+
assert.ok(warnings.some(w => w.includes('unknown slice_parallel key "future_mode"')));
|
|
211
|
+
assert.equal(preferences.slice_parallel, undefined);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("slice_parallel numeric max_workers is bounded to 1..8", () => {
|
|
215
|
+
const low = validatePreferences({ slice_parallel: { max_workers: 1 } });
|
|
216
|
+
const high = validatePreferences({ slice_parallel: { max_workers: 8 } });
|
|
217
|
+
const tooLow = validatePreferences({ slice_parallel: { max_workers: 0 } });
|
|
218
|
+
const tooHigh = validatePreferences({ slice_parallel: { max_workers: 9 } });
|
|
219
|
+
|
|
220
|
+
assert.equal(low.errors.length, 0);
|
|
221
|
+
assert.equal(low.preferences.slice_parallel?.max_workers, 1);
|
|
222
|
+
assert.equal(high.errors.length, 0);
|
|
223
|
+
assert.equal(high.preferences.slice_parallel?.max_workers, 8);
|
|
224
|
+
assert.ok(tooLow.errors.some(e => e.includes("slice_parallel.max_workers")));
|
|
225
|
+
assert.ok(tooHigh.errors.some(e => e.includes("slice_parallel.max_workers")));
|
|
226
|
+
});
|
|
227
|
+
|
|
189
228
|
test("valid values pass through correctly", () => {
|
|
190
229
|
const { preferences: p1 } = validatePreferences({ budget_enforcement: "halt" });
|
|
191
230
|
assert.equal(p1.budget_enforcement, "halt");
|
|
@@ -606,6 +645,44 @@ test("loadEffectiveGSDPreferences preserves experimental prefs across global+pro
|
|
|
606
645
|
}
|
|
607
646
|
});
|
|
608
647
|
|
|
648
|
+
test("loadEffectiveGSDPreferences exposes slice_parallel prefs to runtime callers", () => {
|
|
649
|
+
const originalCwd = process.cwd();
|
|
650
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
651
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-slice-parallel-project-"));
|
|
652
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-slice-parallel-home-"));
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
656
|
+
|
|
657
|
+
writeFileSync(
|
|
658
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
659
|
+
[
|
|
660
|
+
"---",
|
|
661
|
+
"version: 1",
|
|
662
|
+
"slice_parallel:",
|
|
663
|
+
" enabled: true",
|
|
664
|
+
" max_workers: 3",
|
|
665
|
+
"---",
|
|
666
|
+
].join("\n"),
|
|
667
|
+
"utf-8",
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
671
|
+
process.chdir(tempProject);
|
|
672
|
+
|
|
673
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
674
|
+
assert.notEqual(loaded, null);
|
|
675
|
+
assert.equal(loaded!.preferences.slice_parallel?.enabled, true);
|
|
676
|
+
assert.equal(loaded!.preferences.slice_parallel?.max_workers, 3);
|
|
677
|
+
} finally {
|
|
678
|
+
process.chdir(originalCwd);
|
|
679
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
680
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
681
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
682
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
609
686
|
test("preferences paths use canonical uppercase filenames", () => {
|
|
610
687
|
const originalCwd = process.cwd();
|
|
611
688
|
const originalGsdHome = process.env.GSD_HOME;
|
|
@@ -632,6 +709,39 @@ test("preferences paths use canonical uppercase filenames", () => {
|
|
|
632
709
|
}
|
|
633
710
|
});
|
|
634
711
|
|
|
712
|
+
test("explicit base path preference loading survives a deleted cwd (#4498)", (t) => {
|
|
713
|
+
const originalCwd = process.cwd();
|
|
714
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
715
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-base-project-"));
|
|
716
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-base-home-"));
|
|
717
|
+
const deletedCwd = mkdtempSync(join(tmpdir(), "gsd-prefs-deleted-cwd-"));
|
|
718
|
+
|
|
719
|
+
t.after(() => {
|
|
720
|
+
process.chdir(originalCwd);
|
|
721
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
722
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
723
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
724
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
725
|
+
rmSync(deletedCwd, { recursive: true, force: true });
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
729
|
+
writeFileSync(
|
|
730
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
731
|
+
"---\nversion: 1\nlanguage: Swedish\ngit:\n isolation: worktree\n---\n",
|
|
732
|
+
"utf-8",
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
736
|
+
process.chdir(deletedCwd);
|
|
737
|
+
rmSync(deletedCwd, { recursive: true, force: true });
|
|
738
|
+
|
|
739
|
+
const loaded = loadEffectiveGSDPreferences(tempProject);
|
|
740
|
+
assert.notEqual(loaded, null);
|
|
741
|
+
assert.equal(loaded!.preferences.language, "Swedish");
|
|
742
|
+
assert.equal(getIsolationMode(tempProject), "worktree");
|
|
743
|
+
});
|
|
744
|
+
|
|
635
745
|
test("uppercase PREFERENCES.md wins over legacy lowercase preferences.md", () => {
|
|
636
746
|
const originalCwd = process.cwd();
|
|
637
747
|
const originalGsdHome = process.env.GSD_HOME;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Guard test — every key in KNOWN_PREFERENCE_KEYS must be reachable from the
|
|
2
|
+
// /gsd prefs wizard. Without this guard, a new preference can be added to the
|
|
3
|
+
// schema without anyone wiring it into the TUI, silently re-creating the gap
|
|
4
|
+
// this test exists to prevent.
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
|
|
10
|
+
import { KNOWN_PREFERENCE_KEYS } from "../preferences-types.ts";
|
|
11
|
+
|
|
12
|
+
// Keys exposed via a dedicated command rather than the wizard. They're still
|
|
13
|
+
// reachable by the user, just not inside the category menu flow. If you add a
|
|
14
|
+
// new key here, add a comment explaining where it lives.
|
|
15
|
+
const EXPOSED_OUTSIDE_WIZARD = new Set<string>([
|
|
16
|
+
"version", // auto-managed by writePreferencesFile
|
|
17
|
+
"modelOverrides", // advanced routing — edit PREFERENCES.md directly (not in KNOWN_PREFERENCE_KEYS)
|
|
18
|
+
"context_mode", // advanced sandbox config (gsd_exec + compaction) — enabled by default; edit PREFERENCES.md directly to tune timeouts/caps. Wizard coverage tracked separately.
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
test("every KNOWN_PREFERENCE_KEYS entry is reachable from the wizard source", () => {
|
|
22
|
+
const src = readFileSync(
|
|
23
|
+
new URL("../commands-prefs-wizard.ts", import.meta.url),
|
|
24
|
+
"utf-8",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const missing: string[] = [];
|
|
28
|
+
for (const key of KNOWN_PREFERENCE_KEYS) {
|
|
29
|
+
if (EXPOSED_OUTSIDE_WIZARD.has(key)) continue;
|
|
30
|
+
// The key must appear somewhere in the wizard — either as a direct
|
|
31
|
+
// prefs[...] / pref reference, or in the orderedKeys serialization list.
|
|
32
|
+
// A plain substring match is enough because all prefs-wizard references
|
|
33
|
+
// use the exact key name.
|
|
34
|
+
if (!src.includes(`"${key}"`) && !src.includes(`.${key}`)) {
|
|
35
|
+
missing.push(key);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
assert.deepEqual(
|
|
40
|
+
missing,
|
|
41
|
+
[],
|
|
42
|
+
`These preference keys are in KNOWN_PREFERENCE_KEYS but are not referenced anywhere in the /gsd prefs wizard — they cannot be configured through the UI. Either add wizard coverage or add them to EXPOSED_OUTSIDE_WIZARD with an explanatory comment:\n${missing.join("\n")}`,
|
|
43
|
+
);
|
|
44
|
+
});
|
|
@@ -45,6 +45,13 @@ test("classifyError treats usage-limit phrasing as transient rate-limit (#4373)"
|
|
|
45
45
|
assert.equal(result.kind, "rate-limit");
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
test("classifyError treats extra-usage phrasing as transient rate-limit (#4397)", () => {
|
|
49
|
+
const result = classifyError("You are out of extra usage. Please wait before retrying.");
|
|
50
|
+
assert.ok(isTransient(result));
|
|
51
|
+
assert.equal(result.kind, "rate-limit");
|
|
52
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs === 60_000);
|
|
53
|
+
});
|
|
54
|
+
|
|
48
55
|
test("classifyError treats OpenRouter affordability errors as transient rate-limit class", () => {
|
|
49
56
|
const result = classifyError(
|
|
50
57
|
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
@@ -673,6 +680,47 @@ test("MAX_TRANSIENT_AUTO_RESUMES is at least 8 for sustained overload resilience
|
|
|
673
680
|
);
|
|
674
681
|
});
|
|
675
682
|
|
|
683
|
+
// ── Stream idle timeout / partial response (#4558) ──────────────────────────
|
|
684
|
+
|
|
685
|
+
test("classifyError: 'Stream idle timeout - partial response received' is transient network", () => {
|
|
686
|
+
const result = classifyError("API Error: Stream idle timeout - partial response received");
|
|
687
|
+
assert.ok(isTransient(result), "stream idle timeout must be transient");
|
|
688
|
+
assert.equal(result.kind, "network");
|
|
689
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs > 0);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("classifyError: 'stream idle timeout' (lowercase) is transient network", () => {
|
|
693
|
+
const result = classifyError("stream idle timeout");
|
|
694
|
+
assert.ok(isTransient(result), "lowercase stream idle timeout must be transient");
|
|
695
|
+
assert.equal(result.kind, "network");
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test("classifyError: 'partial response received' alone is transient network", () => {
|
|
699
|
+
const result = classifyError("partial response received");
|
|
700
|
+
assert.ok(isTransient(result), "partial response received must be transient");
|
|
701
|
+
assert.equal(result.kind, "network");
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// ── Context overflow / context window exceeded (#4528) ───────────────────────
|
|
705
|
+
|
|
706
|
+
test("classifyError: MiniMax context window error is transient server", () => {
|
|
707
|
+
const result = classifyError("400 invalid params, context window exceeds limit (2013)");
|
|
708
|
+
assert.ok(isTransient(result), "context window exceeded must be transient");
|
|
709
|
+
assert.equal(result.kind, "server");
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("classifyError: 'context length exceeded' is transient server", () => {
|
|
713
|
+
const result = classifyError("context length exceeded: max 128000 tokens");
|
|
714
|
+
assert.ok(isTransient(result), "context length exceeded must be transient");
|
|
715
|
+
assert.equal(result.kind, "server");
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
test("classifyError: 'context window' with 'exceed' is transient server", () => {
|
|
719
|
+
const result = classifyError("context window exceeded for this model");
|
|
720
|
+
assert.ok(isTransient(result), "context window exceeded must be transient");
|
|
721
|
+
assert.equal(result.kind, "server");
|
|
722
|
+
});
|
|
723
|
+
|
|
676
724
|
// ── agent-session retryable regex handles server_error (#1166) ──────────────
|
|
677
725
|
|
|
678
726
|
test("agent-session retryable error regex matches server_error (underscore)", () => {
|