@hegemonart/get-design-done 1.31.0 → 1.31.5
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +44 -0
- package/NOTICE +224 -0
- package/README.md +1 -1
- package/agents/design-authority-watcher.md +1 -1
- package/agents/perf-analyzer.md +2 -2
- package/bin/gdd-mcp +78 -0
- package/bin/gdd-sdk +34 -24
- package/bin/gdd-state-mcp +78 -0
- package/{README.de.md → docs/i18n/README.de.md} +1 -1
- package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
- package/{README.it.md → docs/i18n/README.it.md} +1 -1
- package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
- package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
- package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
- package/hooks/_hook-emit.js +1 -1
- package/hooks/budget-enforcer.ts +5 -5
- package/hooks/context-exhaustion.ts +2 -2
- package/hooks/gdd-precompact-snapshot.js +3 -3
- package/hooks/gdd-read-injection-scanner.ts +2 -2
- package/hooks/gdd-sessionstart-recap.js +1 -1
- package/hooks/gdd-turn-closeout.js +1 -1
- package/package.json +20 -9
- package/recipes/.gitkeep +0 -0
- package/reference/schemas/recipe.schema.json +33 -0
- package/scripts/cli/gdd-events.mjs +5 -5
- package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
- package/scripts/lib/cli/index.ts +22 -160
- package/scripts/lib/connection-probe/index.cjs +1 -1
- package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
- package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
- package/scripts/lib/error-classifier.cjs +24 -227
- package/scripts/lib/event-stream/index.ts +25 -193
- package/scripts/lib/gdd-errors/index.ts +24 -213
- package/scripts/lib/gdd-state/index.ts +23 -161
- package/scripts/lib/iteration-budget.cjs +23 -199
- package/scripts/lib/jittered-backoff.cjs +24 -107
- package/scripts/lib/lockfile.cjs +23 -195
- package/scripts/lib/logger/index.ts +1 -1
- package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
- package/scripts/lib/perf-analyzer/index.cjs +1 -1
- package/scripts/lib/pipeline-runner/index.ts +4 -4
- package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
- package/scripts/lib/prompt-dedup/index.cjs +1 -1
- package/scripts/lib/rate-guard.cjs +2 -2
- package/scripts/lib/recipe-loader.cjs +142 -0
- package/scripts/lib/session-runner/errors.ts +3 -3
- package/scripts/lib/session-runner/index.ts +3 -3
- package/scripts/lib/session-runner/transcript.ts +1 -1
- package/scripts/lib/tool-scoping/index.ts +1 -1
- package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
- package/scripts/mcp-servers/gdd-state/server.ts +28 -282
- package/sdk/README.md +45 -0
- package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
- package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
- package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
- package/sdk/cli/index.js +8091 -0
- package/sdk/cli/index.ts +172 -0
- package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
- package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
- package/sdk/errors/index.ts +218 -0
- package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
- package/sdk/event-stream/index.ts +197 -0
- package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
- package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
- package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
- package/sdk/index.ts +19 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
- package/sdk/mcp/gdd-mcp/server.js +1924 -0
- package/sdk/mcp/gdd-mcp/server.ts +325 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
- package/sdk/mcp/gdd-state/server.js +2790 -0
- package/sdk/mcp/gdd-state/server.ts +294 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
- package/sdk/primitives/error-classifier.cjs +232 -0
- package/sdk/primitives/iteration-budget.cjs +205 -0
- package/sdk/primitives/jittered-backoff.cjs +112 -0
- package/sdk/primitives/lockfile.cjs +201 -0
- package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
- package/sdk/state/index.ts +167 -0
- package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
- package/skills/quality-gate/SKILL.md +2 -2
- package/scripts/aggregate-agent-metrics.ts +0 -282
- package/scripts/bootstrap-manifest.txt +0 -3
- package/scripts/bootstrap.sh +0 -80
- package/scripts/build-distribution-bundles.cjs +0 -549
- package/scripts/build-intel.cjs +0 -486
- package/scripts/codegen-schema-types.ts +0 -149
- package/scripts/detect-stale-refs.cjs +0 -107
- package/scripts/e2e/run-headless.ts +0 -514
- package/scripts/extract-changelog-section.cjs +0 -58
- package/scripts/gsd-cleanup-incubator.cjs +0 -367
- package/scripts/injection-patterns.cjs +0 -58
- package/scripts/lint-agentskills-spec.cjs +0 -457
- package/scripts/release-smoke-test.cjs +0 -200
- package/scripts/rollback-release.sh +0 -42
- package/scripts/run-injection-scanner-ci.cjs +0 -83
- package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
- package/scripts/tests/test-authority-watcher-diff.sh +0 -113
- package/scripts/tests/test-motion-provenance.sh +0 -64
- package/scripts/validate-frontmatter.ts +0 -409
- package/scripts/validate-incubator-scope.cjs +0 -133
- package/scripts/validate-schemas.ts +0 -401
- package/scripts/validate-skill-length.cjs +0 -283
- package/scripts/verify-version-sync.cjs +0 -30
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
- /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
|
@@ -1,112 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Plan 20-14 — jittered exponential backoff primitive.
|
|
4
|
-
//
|
|
5
|
-
// Replaces fixed-interval retry sleeps across the codebase (update-check,
|
|
6
|
-
// watch-authorities, figma probes, connection probes, hook retry loops) with
|
|
7
|
-
// a deterministic, capped, jittered backoff curve.
|
|
8
|
-
//
|
|
9
|
-
// Formula:
|
|
10
|
-
// base = min(maxMs, baseMs * factor^attempt)
|
|
11
|
-
// delay = base * (1 + rand(-jitter, +jitter))
|
|
12
|
-
// clamp to [0, maxMs * (1 + jitter)]
|
|
13
|
-
//
|
|
14
|
-
// The zero-based `attempt` counter gives `baseMs` on the first call, then
|
|
15
|
-
// multiplies by `factor` per attempt up to the cap. Jitter is full-width
|
|
16
|
-
// symmetric (not AWS-style equal/decorrelated) because our consumers are
|
|
17
|
-
// single-threaded retry loops — there's no thundering-herd problem to
|
|
18
|
-
// smooth; the jitter is cosmetic protection against synchronized retries
|
|
19
|
-
// between siblings like the watcher + update-check running at the same
|
|
20
|
-
// session boundary.
|
|
1
|
+
'use strict';
|
|
2
|
+
// scripts/lib/jittered-backoff.cjs — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
|
|
21
3
|
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// attempt 9 → ~24s-30s (capped)
|
|
4
|
+
// Thin deprecation shim. The real implementation moved to
|
|
5
|
+
// sdk/primitives/jittered-backoff.cjs in Plan 31-5-04 (SDK consolidation).
|
|
6
|
+
// This file is re-created at the OLD path so undocumented EXTERNAL importers
|
|
7
|
+
// (anyone who reached into node_modules/@hegemonart/get-design-done/scripts/
|
|
8
|
+
// lib/jittered-backoff.cjs directly) keep working for one minor grace window.
|
|
28
9
|
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
10
|
+
// REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
|
|
11
|
+
// 1.32.0 still has them → 1.33.0 removes them. Internal callers already use
|
|
12
|
+
// the sdk/ path (Plan 31-5-04/05) — this shim is external-only and 31-5-10's
|
|
13
|
+
// no-stale-internal-refs guard excludes files carrying the GDD-DEPRECATION-SHIM
|
|
14
|
+
// marker above.
|
|
33
15
|
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
factor: 2,
|
|
46
|
-
jitter: 0.2,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Compute the jittered delay in milliseconds for a given attempt number.
|
|
51
|
-
*
|
|
52
|
-
* @param {number} attempt zero-based attempt counter (0 = first retry)
|
|
53
|
-
* @param {object} [opts]
|
|
54
|
-
* @param {number} [opts.baseMs] initial delay before any jitter. Default 100.
|
|
55
|
-
* @param {number} [opts.maxMs] maximum un-jittered base. Default 30_000.
|
|
56
|
-
* @param {number} [opts.factor] per-attempt multiplier. Default 2.
|
|
57
|
-
* @param {number} [opts.jitter] symmetric jitter fraction in [0, 1). Default 0.2.
|
|
58
|
-
* @returns {number} delay in ms. Never negative. May exceed `maxMs` by up
|
|
59
|
-
* to `jitter * maxMs` on the high side.
|
|
60
|
-
*
|
|
61
|
-
* Invariants:
|
|
62
|
-
* - `delayMs(n, opts) >= 0` for every non-negative `n`.
|
|
63
|
-
* - `delayMs(n, opts) <= maxMs * (1 + jitter)` for every non-negative `n`.
|
|
64
|
-
* - The distribution has nonzero stddev whenever `jitter > 0`.
|
|
65
|
-
*/
|
|
66
|
-
function delayMs(attempt, opts) {
|
|
67
|
-
const baseMs = (opts && Number.isFinite(opts.baseMs)) ? opts.baseMs : DEFAULTS.baseMs;
|
|
68
|
-
const maxMs = (opts && Number.isFinite(opts.maxMs)) ? opts.maxMs : DEFAULTS.maxMs;
|
|
69
|
-
const factor = (opts && Number.isFinite(opts.factor)) ? opts.factor : DEFAULTS.factor;
|
|
70
|
-
const jitter = (opts && Number.isFinite(opts.jitter)) ? opts.jitter : DEFAULTS.jitter;
|
|
71
|
-
|
|
72
|
-
// Guard against nonsense inputs — callers that pass garbage shouldn't
|
|
73
|
-
// cause NaN or Infinity to propagate into setTimeout.
|
|
74
|
-
const a = Math.max(0, Number.isFinite(attempt) ? Math.floor(attempt) : 0);
|
|
75
|
-
const safeBase = Math.max(0, baseMs);
|
|
76
|
-
const safeMax = Math.max(safeBase, maxMs);
|
|
77
|
-
const safeFactor = factor > 0 ? factor : DEFAULTS.factor;
|
|
78
|
-
// Clamp jitter to [0, 1) — full-range (>=1) would allow negative values
|
|
79
|
-
// after subtraction, which we want to forbid by invariant.
|
|
80
|
-
const safeJitter = Math.min(0.999, Math.max(0, jitter));
|
|
81
|
-
|
|
82
|
-
// Exponential growth capped at safeMax.
|
|
83
|
-
// Math.pow with a huge attempt count can overflow to Infinity; the
|
|
84
|
-
// Math.min picks up safeMax before Infinity escapes.
|
|
85
|
-
const unjittered = Math.min(safeMax, safeBase * Math.pow(safeFactor, a));
|
|
86
|
-
|
|
87
|
-
// Symmetric jitter in [-jitter, +jitter).
|
|
88
|
-
// Math.random() returns [0, 1); 2r - 1 maps to [-1, 1); scale by jitter.
|
|
89
|
-
const noise = (Math.random() * 2 - 1) * safeJitter;
|
|
90
|
-
const delay = unjittered * (1 + noise);
|
|
91
|
-
|
|
92
|
-
// Floor at zero (noise could in theory go slightly negative due to FP
|
|
93
|
-
// precision even with safeJitter < 1, for very small unjittered values).
|
|
94
|
-
return Math.max(0, delay);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Sleep for a jittered backoff interval. Convenience wrapper around
|
|
99
|
-
* `delayMs` + `setTimeout`.
|
|
100
|
-
*
|
|
101
|
-
* @param {number} attempt zero-based attempt counter
|
|
102
|
-
* @param {object} [opts] see {@link delayMs}
|
|
103
|
-
* @returns {Promise<number>} resolves to the actual delay that was applied
|
|
104
|
-
*/
|
|
105
|
-
function sleep(attempt, opts) {
|
|
106
|
-
const ms = delayMs(attempt, opts);
|
|
107
|
-
return new Promise((resolve) => {
|
|
108
|
-
setTimeout(() => resolve(ms), ms);
|
|
109
|
-
});
|
|
16
|
+
// Emits a DeprecationWarning exactly ONCE per process: the module-level
|
|
17
|
+
// `warned` flag plus Node's module cache (this file is evaluated once per
|
|
18
|
+
// process regardless of how many times it is required).
|
|
19
|
+
|
|
20
|
+
let warned = false;
|
|
21
|
+
if (!warned) {
|
|
22
|
+
warned = true;
|
|
23
|
+
process.emitWarning(
|
|
24
|
+
'scripts/lib/jittered-backoff.cjs is deprecated; import sdk/primitives/jittered-backoff instead. Removed in v1.33.0.',
|
|
25
|
+
'DeprecationWarning',
|
|
26
|
+
);
|
|
110
27
|
}
|
|
111
28
|
|
|
112
|
-
module.exports =
|
|
29
|
+
module.exports = require('../../sdk/primitives/jittered-backoff.cjs');
|
package/scripts/lib/lockfile.cjs
CHANGED
|
@@ -1,201 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Plan 20-14 — PID+timestamp sibling lockfile for `.cjs` consumers.
|
|
1
|
+
'use strict';
|
|
2
|
+
// scripts/lib/lockfile.cjs — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
|
|
4
3
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// than `staleMs` OR unparseable payload
|
|
11
|
-
// Release: unlink; ENOENT is not an error; idempotent
|
|
4
|
+
// Thin deprecation shim. The real implementation moved to
|
|
5
|
+
// sdk/primitives/lockfile.cjs in Plan 31-5-04 (SDK consolidation).
|
|
6
|
+
// This file is re-created at the OLD path so undocumented EXTERNAL importers
|
|
7
|
+
// (anyone who reached into node_modules/@hegemonart/get-design-done/scripts/
|
|
8
|
+
// lib/lockfile.cjs directly) keep working for one minor grace window.
|
|
12
9
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
10
|
+
// REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
|
|
11
|
+
// 1.32.0 still has them → 1.33.0 removes them. Internal callers already use
|
|
12
|
+
// the sdk/ path (Plan 31-5-04/05) — this shim is external-only and 31-5-10's
|
|
13
|
+
// no-stale-internal-refs guard excludes files carrying the GDD-DEPRECATION-SHIM
|
|
14
|
+
// marker above.
|
|
16
15
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// gdd-state MCP graph along. Hence this standalone .cjs port instead of
|
|
21
|
-
// calling the .ts version.
|
|
22
|
-
|
|
23
|
-
'use strict';
|
|
24
|
-
|
|
25
|
-
const fs = require('node:fs');
|
|
26
|
-
const os = require('node:os');
|
|
27
|
-
|
|
28
|
-
const DEFAULT_STALE_MS = 60_000;
|
|
29
|
-
const DEFAULT_MAX_WAIT_MS = 5_000;
|
|
30
|
-
const DEFAULT_POLL_MS = 50;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Acquire an advisory lock at `${path}.lock`. Returns an idempotent
|
|
34
|
-
* async release function.
|
|
35
|
-
*
|
|
36
|
-
* @param {string} path path being locked (we append `.lock`)
|
|
37
|
-
* @param {object} [opts]
|
|
38
|
-
* @param {number} [opts.staleMs] ms after which an existing lock is stale. Default 60_000.
|
|
39
|
-
* @param {number} [opts.maxWaitMs] total ms to wait before throwing. Default 5_000.
|
|
40
|
-
* @param {number} [opts.pollMs] ms between retry attempts. Default 50.
|
|
41
|
-
* @returns {Promise<() => Promise<void>>} release function
|
|
42
|
-
* @throws {Error} with name === 'LockAcquisitionError' when maxWaitMs elapses
|
|
43
|
-
*/
|
|
44
|
-
async function acquire(path, opts) {
|
|
45
|
-
const o = opts || {};
|
|
46
|
-
const staleMs = Number.isFinite(o.staleMs) ? o.staleMs : DEFAULT_STALE_MS;
|
|
47
|
-
const maxWaitMs = Number.isFinite(o.maxWaitMs) ? o.maxWaitMs : DEFAULT_MAX_WAIT_MS;
|
|
48
|
-
const pollMs = Number.isFinite(o.pollMs) ? o.pollMs : DEFAULT_POLL_MS;
|
|
49
|
-
|
|
50
|
-
if (staleMs < 0 || maxWaitMs < 0 || pollMs < 0) {
|
|
51
|
-
throw new Error(
|
|
52
|
-
`lockfile.acquire: invalid options (staleMs=${staleMs}, maxWaitMs=${maxWaitMs}, pollMs=${pollMs})`,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const lockPath = `${path}.lock`;
|
|
57
|
-
const payload = JSON.stringify({
|
|
58
|
-
pid: process.pid,
|
|
59
|
-
host: os.hostname(),
|
|
60
|
-
acquired_at: new Date().toISOString(),
|
|
61
|
-
});
|
|
62
|
-
const startedAt = Date.now();
|
|
63
|
-
|
|
64
|
-
while (true) {
|
|
65
|
-
try {
|
|
66
|
-
fs.writeFileSync(lockPath, payload, { flag: 'wx', encoding: 'utf8' });
|
|
67
|
-
return makeRelease(lockPath);
|
|
68
|
-
} catch (err) {
|
|
69
|
-
const code = err && typeof err === 'object' ? err.code : undefined;
|
|
70
|
-
if (code !== 'EEXIST' && code !== 'EPERM' && code !== 'EBUSY') {
|
|
71
|
-
throw err;
|
|
72
|
-
}
|
|
73
|
-
// Try to read the current holder; if it vanished between EEXIST and
|
|
74
|
-
// read, loop immediately.
|
|
75
|
-
const existing = readLockSafe(lockPath);
|
|
76
|
-
if (existing === null) continue;
|
|
77
|
-
|
|
78
|
-
const parsed = parseLock(existing);
|
|
79
|
-
// Only clear when we're confident the lock is stale: the payload
|
|
80
|
-
// parses AND the PID/age check says so. An unparseable payload is
|
|
81
|
-
// treated as fresh — on Windows, AV/indexer can transiently deny
|
|
82
|
-
// reads (EACCES/EPERM/EBUSY), and clearing under that condition
|
|
83
|
-
// would let two writers race and lose increments.
|
|
84
|
-
if (parsed !== null && isStale(parsed, staleMs)) {
|
|
85
|
-
// Clear stale lock; race-tolerant — if it's already gone we get
|
|
86
|
-
// ENOENT, no-op.
|
|
87
|
-
try { fs.unlinkSync(lockPath); } catch { /* ignore */ }
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (Date.now() - startedAt >= maxWaitMs) {
|
|
92
|
-
const e = new Error(
|
|
93
|
-
`lockfile: failed to acquire ${lockPath} within ${maxWaitMs}ms (held by ${existing})`,
|
|
94
|
-
);
|
|
95
|
-
e.name = 'LockAcquisitionError';
|
|
96
|
-
e.lockPath = lockPath;
|
|
97
|
-
e.holder = existing;
|
|
98
|
-
e.waitedMs = Date.now() - startedAt;
|
|
99
|
-
throw e;
|
|
100
|
-
}
|
|
101
|
-
await sleep(pollMs);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function makeRelease(lockPath) {
|
|
107
|
-
let released = false;
|
|
108
|
-
return async function release() {
|
|
109
|
-
if (released) return;
|
|
110
|
-
released = true;
|
|
111
|
-
try {
|
|
112
|
-
fs.unlinkSync(lockPath);
|
|
113
|
-
} catch (err) {
|
|
114
|
-
const code = err && typeof err === 'object' ? err.code : undefined;
|
|
115
|
-
if (code === 'ENOENT') return; // idempotent — already gone
|
|
116
|
-
if (code === 'EPERM' || code === 'EBUSY') {
|
|
117
|
-
// Windows AV/indexer: retry once.
|
|
118
|
-
await sleep(50);
|
|
119
|
-
try {
|
|
120
|
-
if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath);
|
|
121
|
-
} catch { /* give up; stale-detection will reclaim */ }
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
// Any other errno: swallow. Best-effort cleanup; stale-age check
|
|
125
|
-
// will eventually reclaim the lock.
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function readLockSafe(p) {
|
|
131
|
-
try {
|
|
132
|
-
return fs.readFileSync(p, 'utf8');
|
|
133
|
-
} catch (err) {
|
|
134
|
-
const code = err && typeof err === 'object' ? err.code : undefined;
|
|
135
|
-
if (code === 'ENOENT') return null;
|
|
136
|
-
return '<unreadable>';
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function parseLock(raw) {
|
|
141
|
-
try {
|
|
142
|
-
const obj = JSON.parse(raw);
|
|
143
|
-
if (
|
|
144
|
-
obj && typeof obj === 'object' &&
|
|
145
|
-
typeof obj.pid === 'number' &&
|
|
146
|
-
typeof obj.host === 'string' &&
|
|
147
|
-
typeof obj.acquired_at === 'string'
|
|
148
|
-
) {
|
|
149
|
-
return obj;
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
} catch {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function isStale(payload, staleMs) {
|
|
158
|
-
if (!isPidAlive(payload.pid, payload.host)) return true;
|
|
159
|
-
const t = Date.parse(payload.acquired_at);
|
|
160
|
-
if (!Number.isFinite(t)) return true;
|
|
161
|
-
return Date.now() - t > staleMs;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function isPidAlive(pid, host) {
|
|
165
|
-
if (host !== os.hostname()) return true; // can't introspect other hosts
|
|
166
|
-
if (pid === process.pid) return true;
|
|
167
|
-
try {
|
|
168
|
-
process.kill(pid, 0); // signal 0 = validate, don't deliver
|
|
169
|
-
return true;
|
|
170
|
-
} catch (err) {
|
|
171
|
-
const code = err && typeof err === 'object' ? err.code : undefined;
|
|
172
|
-
if (code === 'ESRCH') return false;
|
|
173
|
-
// EPERM / EACCES: process exists but is unsignalable; treat as alive.
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function sleep(ms) {
|
|
179
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
180
|
-
}
|
|
16
|
+
// Emits a DeprecationWarning exactly ONCE per process: the module-level
|
|
17
|
+
// `warned` flag plus Node's module cache (this file is evaluated once per
|
|
18
|
+
// process regardless of how many times it is required).
|
|
181
19
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
*/
|
|
190
|
-
async function renameWithRetry(from, to) {
|
|
191
|
-
try {
|
|
192
|
-
fs.renameSync(from, to);
|
|
193
|
-
} catch (err) {
|
|
194
|
-
const code = err && typeof err === 'object' ? err.code : undefined;
|
|
195
|
-
if (code !== 'EPERM' && code !== 'EBUSY' && code !== 'EACCES') throw err;
|
|
196
|
-
await sleep(50);
|
|
197
|
-
fs.renameSync(from, to);
|
|
198
|
-
}
|
|
20
|
+
let warned = false;
|
|
21
|
+
if (!warned) {
|
|
22
|
+
warned = true;
|
|
23
|
+
process.emitWarning(
|
|
24
|
+
'scripts/lib/lockfile.cjs is deprecated; import sdk/primitives/lockfile instead. Removed in v1.33.0.',
|
|
25
|
+
'DeprecationWarning',
|
|
26
|
+
);
|
|
199
27
|
}
|
|
200
28
|
|
|
201
|
-
module.exports =
|
|
29
|
+
module.exports = require('../../sdk/primitives/lockfile.cjs');
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// process.stderr); the logger contract never propagates event-stream
|
|
30
30
|
// failures back to the caller.
|
|
31
31
|
|
|
32
|
-
import { appendEvent } from '
|
|
32
|
+
import { appendEvent } from '../../../sdk/event-stream/index.ts';
|
|
33
33
|
import { ConsoleSink, JsonlSink, MultiSink } from './sinks.ts';
|
|
34
34
|
import {
|
|
35
35
|
LEVEL_ORDER,
|
|
@@ -59,7 +59,7 @@ function getAppendEvent() {
|
|
|
59
59
|
// with --experimental-strip-types (or Node 24 built-in TS) can
|
|
60
60
|
// require it. If require fails (e.g., older runtime, missing
|
|
61
61
|
// module), fall through to the no-op.
|
|
62
|
-
const m = require('
|
|
62
|
+
const m = require('../../../sdk/event-stream');
|
|
63
63
|
if (m && typeof m.appendEvent === 'function') return m.appendEvent;
|
|
64
64
|
} catch {
|
|
65
65
|
// Swallow — best-effort telemetry. Losing one verdict is
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `.design/telemetry/trajectories/<cycle>.jsonl` files (agent trace
|
|
7
7
|
* lines per Phase 22).
|
|
8
8
|
*
|
|
9
|
-
* JSONL discipline (same as
|
|
9
|
+
* JSONL discipline (same as sdk/event-stream/reader.ts):
|
|
10
10
|
* - One JSON object per line.
|
|
11
11
|
* - Blank lines / whitespace-only lines ignored silently.
|
|
12
12
|
* - Malformed lines tolerated — counted in skipped_count, NOT thrown.
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
//
|
|
24
24
|
// NEVER throws — every failure becomes a `PipelineResult`.
|
|
25
25
|
|
|
26
|
-
import { appendEvent } from '
|
|
27
|
-
import type { BaseEvent } from '
|
|
26
|
+
import { appendEvent } from '../../../sdk/event-stream/index.ts';
|
|
27
|
+
import type { BaseEvent } from '../../../sdk/event-stream/index.ts';
|
|
28
28
|
import { getLogger } from '../logger/index.ts';
|
|
29
|
-
import { transition as defaultTransition, TransitionGateFailed } from '
|
|
30
|
-
import { ValidationError } from '
|
|
29
|
+
import { transition as defaultTransition, TransitionGateFailed } from '../../../sdk/state/index.ts';
|
|
30
|
+
import { ValidationError } from '../../../sdk/errors/index.ts';
|
|
31
31
|
|
|
32
32
|
import {
|
|
33
33
|
STAGE_ORDER,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// name is in the set. Unknown stage names in `skipStages` are
|
|
16
16
|
// tolerated (no-op) — the filter is a membership check.
|
|
17
17
|
|
|
18
|
-
import { ValidationError } from '
|
|
18
|
+
import { ValidationError } from '../../../sdk/errors/index.ts';
|
|
19
19
|
import type { Stage } from './types.ts';
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -27,7 +27,7 @@ const DEFAULT_THRESHOLD = 3; // D-11 — '>= 3 agents'
|
|
|
27
27
|
*/
|
|
28
28
|
function getAppendEvent() {
|
|
29
29
|
try {
|
|
30
|
-
const m = require('
|
|
30
|
+
const m = require('../../../sdk/event-stream');
|
|
31
31
|
if (m && typeof m.appendEvent === 'function') return m.appendEvent;
|
|
32
32
|
} catch { /* swallow — event-stream not on path */ }
|
|
33
33
|
return function noopAppend(_ev) {};
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// `resetAt` wins. This matches the D-01 rule in the plan.
|
|
21
21
|
//
|
|
22
22
|
// State-file writes are atomic (temp + rename) and coordinated by
|
|
23
|
-
//
|
|
23
|
+
// sdk/primitives/lockfile.cjs so two child processes hitting `ingestHeaders`
|
|
24
24
|
// concurrently can never corrupt the file. The lock is scoped to the
|
|
25
25
|
// state file, not to the provider directory, so different providers can
|
|
26
26
|
// update in parallel.
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
const fs = require('node:fs');
|
|
31
31
|
const path = require('node:path');
|
|
32
32
|
|
|
33
|
-
const { acquire, renameWithRetry } = require('
|
|
33
|
+
const { acquire, renameWithRetry } = require('../../sdk/primitives/lockfile.cjs');
|
|
34
34
|
|
|
35
35
|
const STATE_DIR_REL = path.join('.design', 'rate-limits');
|
|
36
36
|
const LOCK_MAX_WAIT_MS = 3_000;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recipe-loader.cjs — recipes/ scaffold loader (Plan 31-5-03, RECIPE-01 / SC#14).
|
|
3
|
+
*
|
|
4
|
+
* The top-level recipes/ directory ships EMPTY of recipes; it is populated
|
|
5
|
+
* downstream by Phase 32 (skill-trigger recipes), Phase 33.6 (per-provider),
|
|
6
|
+
* Phase 26 (per-runtime/per-model), and Phase 23.5 (bandit-arm shape). This
|
|
7
|
+
* module fixes the loading contract those phases build against so each does not
|
|
8
|
+
* reinvent it. Modelled on Storybloq's src/autonomous/recipes/ loader.ts.
|
|
9
|
+
*
|
|
10
|
+
* Contract:
|
|
11
|
+
* loadRecipe(name, opts?) -> Recipe
|
|
12
|
+
* name : recipe stem; resolves <repoRoot>/recipes/<name>.json
|
|
13
|
+
* opts : { recipesDir, schemaPath, fs } — all injectable for tests
|
|
14
|
+
* returns : the validated, parsed Recipe object
|
|
15
|
+
* throws : Error('recipe not found: <name>') if the file is absent
|
|
16
|
+
* Error('recipe <name> failed schema validation: …') if invalid
|
|
17
|
+
*
|
|
18
|
+
* Cache (SC#14 "caches by SHA"):
|
|
19
|
+
* Keyed by name + ':' + sha256(fileBytes). Each call reads the file once to
|
|
20
|
+
* compute the content hash; on a HIT (unchanged bytes) it returns the cached
|
|
21
|
+
* object WITHOUT re-parsing / re-validating. On a MISS (changed bytes) it
|
|
22
|
+
* parses + validates + stores. Keying by content SHA — not just name — means
|
|
23
|
+
* an edited recipe is correctly re-validated.
|
|
24
|
+
*
|
|
25
|
+
* Empty-dir safety: this module requires cleanly when recipes/ is empty, and a
|
|
26
|
+
* directory listing of an empty (just-.gitkeep) dir does not throw. loadRecipe
|
|
27
|
+
* of a missing name throws the clear not-found error — distinct from "the empty
|
|
28
|
+
* dir is broken" (it is a valid scaffold state).
|
|
29
|
+
*
|
|
30
|
+
* Uses the repo's existing `ajv` dependency (package.json "ajv": "^8.18.0") —
|
|
31
|
+
* no new dependency. The schema is compiled once per schemaPath (singleton).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
'use strict';
|
|
35
|
+
|
|
36
|
+
const fs = require('node:fs');
|
|
37
|
+
const path = require('node:path');
|
|
38
|
+
const crypto = require('node:crypto');
|
|
39
|
+
const Ajv = require('ajv');
|
|
40
|
+
|
|
41
|
+
// recipe-loader.cjs lives in scripts/lib/ → two levels up is the repo root.
|
|
42
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
43
|
+
const DEFAULT_RECIPES_DIR = path.join(REPO_ROOT, 'recipes');
|
|
44
|
+
const DEFAULT_SCHEMA_PATH = path.join(REPO_ROOT, 'reference', 'schemas', 'recipe.schema.json');
|
|
45
|
+
|
|
46
|
+
// Ajv 8 CJS: require('ajv') is (or wraps) the constructor.
|
|
47
|
+
const AjvCtor = Ajv.default || Ajv;
|
|
48
|
+
|
|
49
|
+
// SHA-keyed cache: `name + ':' + sha256(bytes)` → parsed Recipe object.
|
|
50
|
+
const cache = new Map();
|
|
51
|
+
|
|
52
|
+
// Compiled-validator singletons, keyed by resolved schemaPath. The schema is a
|
|
53
|
+
// small trusted local file, so allErrors:true (full error list → clear message)
|
|
54
|
+
// is safe here — unlike the untrusted-HTTP receiver which fails fast.
|
|
55
|
+
const validators = new Map();
|
|
56
|
+
|
|
57
|
+
// Test-observability counter: increments once per parse+validate (i.e. per
|
|
58
|
+
// cache MISS). A cache HIT does not bump it. Tests assert on the delta to prove
|
|
59
|
+
// SHA-keyed hit/miss semantics without depending on raw read counts (a hit must
|
|
60
|
+
// still read the file once to hash it).
|
|
61
|
+
let _validations = 0;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get (compiling once) the validator for a schema path.
|
|
65
|
+
* @param {string} schemaPath
|
|
66
|
+
* @param {typeof fs} fsImpl
|
|
67
|
+
* @returns {import('ajv').ValidateFunction}
|
|
68
|
+
*/
|
|
69
|
+
function getValidator(schemaPath, fsImpl) {
|
|
70
|
+
const resolved = path.resolve(schemaPath);
|
|
71
|
+
let validate = validators.get(resolved);
|
|
72
|
+
if (validate) return validate;
|
|
73
|
+
|
|
74
|
+
const schema = JSON.parse(fsImpl.readFileSync(resolved, 'utf8'));
|
|
75
|
+
const ajv = new AjvCtor({ allErrors: true, strict: false });
|
|
76
|
+
validate = ajv.compile(schema);
|
|
77
|
+
validators.set(resolved, validate);
|
|
78
|
+
return validate;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load + validate a recipe by name.
|
|
83
|
+
* @param {string} name - recipe stem; resolves <recipesDir>/<name>.json
|
|
84
|
+
* @param {{ recipesDir?: string, schemaPath?: string, fs?: typeof fs }} [opts]
|
|
85
|
+
* @returns {Record<string, unknown>} the validated, parsed Recipe object
|
|
86
|
+
*/
|
|
87
|
+
function loadRecipe(name, opts = {}) {
|
|
88
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
89
|
+
throw new Error('recipe name must be a non-empty string');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const fsImpl = opts.fs || fs;
|
|
93
|
+
const dir = opts.recipesDir || DEFAULT_RECIPES_DIR;
|
|
94
|
+
const schemaPath = opts.schemaPath || DEFAULT_SCHEMA_PATH;
|
|
95
|
+
const file = path.join(dir, name + '.json');
|
|
96
|
+
|
|
97
|
+
if (!fsImpl.existsSync(file)) {
|
|
98
|
+
throw new Error('recipe not found: ' + name);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Read once per call to compute the content hash (the cache key).
|
|
102
|
+
const bytes = fsImpl.readFileSync(file);
|
|
103
|
+
const sha = crypto.createHash('sha256').update(bytes).digest('hex');
|
|
104
|
+
const key = name + ':' + sha;
|
|
105
|
+
|
|
106
|
+
// Cache HIT: unchanged bytes → return cached object, skip parse + validate.
|
|
107
|
+
if (cache.has(key)) {
|
|
108
|
+
return cache.get(key);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Cache MISS: parse + validate + store.
|
|
112
|
+
let parsed;
|
|
113
|
+
try {
|
|
114
|
+
parsed = JSON.parse(bytes.toString('utf8'));
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error('recipe ' + name + ' is not valid JSON: ' + err.message);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const validate = getValidator(schemaPath, fsImpl);
|
|
120
|
+
_validations += 1;
|
|
121
|
+
const ok = validate(parsed) === true;
|
|
122
|
+
if (!ok) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'recipe ' + name + ' failed schema validation: ' + JSON.stringify(validate.errors || []),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
cache.set(key, parsed);
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Test hook: clear the SHA cache (does not drop compiled validators). */
|
|
133
|
+
function _clearCache() {
|
|
134
|
+
cache.clear();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Test hook: introspect parse+validate counts (one per cache MISS). */
|
|
138
|
+
function _stats() {
|
|
139
|
+
return { validations: _validations, cacheSize: cache.size };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = { loadRecipe, _clearCache, _stats };
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
OperationFailedError,
|
|
39
39
|
StateConflictError,
|
|
40
40
|
type GDDError,
|
|
41
|
-
} from '
|
|
41
|
+
} from '../../../sdk/errors/index.ts';
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Build an absolute path to a repo-root-relative file. We can't use
|
|
@@ -68,7 +68,7 @@ const REPO_ROOT = findRepoRoot();
|
|
|
68
68
|
*/
|
|
69
69
|
const nodeRequire = createRequire(join(REPO_ROOT, 'package.json'));
|
|
70
70
|
const transportClassifier = nodeRequire(
|
|
71
|
-
resolve(REPO_ROOT, '
|
|
71
|
+
resolve(REPO_ROOT, 'sdk/primitives/error-classifier.cjs'),
|
|
72
72
|
) as {
|
|
73
73
|
classify: (err: unknown) => {
|
|
74
74
|
reason: string;
|
|
@@ -375,7 +375,7 @@ export function mapSdkError(err: unknown): MappedSdkError {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
// 9. Transport-layer classification (ECONNRESET, ETIMEDOUT, etc.).
|
|
378
|
-
// Delegate to
|
|
378
|
+
// Delegate to sdk/primitives/error-classifier.cjs which knows the errno
|
|
379
379
|
// vocabulary. Only trust its `retryable` flag for transient network
|
|
380
380
|
// classes — other classes were already handled above.
|
|
381
381
|
const classified = transportClassifier.classify(err);
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
// (always; payload status mirrors SessionResult.status). Optional
|
|
27
27
|
// `session.budget_exceeded` emitted when the budget trips.
|
|
28
28
|
|
|
29
|
-
import { appendEvent } from '
|
|
30
|
-
import type { BaseEvent } from '
|
|
29
|
+
import { appendEvent } from '../../../sdk/event-stream/index.ts';
|
|
30
|
+
import type { BaseEvent } from '../../../sdk/event-stream/index.ts';
|
|
31
31
|
import { sanitize as defaultSanitize } from '../prompt-sanitizer/index.ts';
|
|
32
32
|
|
|
33
33
|
import { mapSdkError } from './errors.ts';
|
|
@@ -64,7 +64,7 @@ function _findRepoRoot(): string {
|
|
|
64
64
|
const _REPO_ROOT = _findRepoRoot();
|
|
65
65
|
const _nodeRequire = createRequire(_join(_REPO_ROOT, 'package.json'));
|
|
66
66
|
const jitteredBackoff = _nodeRequire(
|
|
67
|
-
_resolve(_REPO_ROOT, '
|
|
67
|
+
_resolve(_REPO_ROOT, 'sdk/primitives/jittered-backoff.cjs'),
|
|
68
68
|
) as {
|
|
69
69
|
delayMs: (attempt: number, opts?: { baseMs?: number; maxMs?: number; factor?: number; jitter?: number }) => number;
|
|
70
70
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// transcript writer for Phase 21 headless Agent SDK sessions
|
|
3
3
|
// (Plan 21-01 Task 4).
|
|
4
4
|
//
|
|
5
|
-
// Design mirrors
|
|
5
|
+
// Design mirrors sdk/event-stream/writer.ts but is scoped to one
|
|
6
6
|
// session per file rather than the global telemetry stream. Each session
|
|
7
7
|
// owns a dedicated `.design/sessions/<ISO>-<stage>.jsonl` file; the
|
|
8
8
|
// filename is stable for the full run and survives retries (retries
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// * Plan 21-01 `session-runner` — computes `allowedTools` for each session.
|
|
22
22
|
// * Plan 21-05 `pipeline-runner` — picks the correct scope per stage.
|
|
23
23
|
|
|
24
|
-
import { ValidationError } from '
|
|
24
|
+
import { ValidationError } from '../../../sdk/errors/index.ts';
|
|
25
25
|
import type { Scope, ScopeInput, ScopeViolation, Stage } from './types.ts';
|
|
26
26
|
import {
|
|
27
27
|
NATIVE_TOOLS,
|