pan-wizard 3.31.0 → 3.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -24
- package/agents/pan-conductor.md +2 -2
- package/agents/pan-design-checker.md +18 -0
- package/agents/pan-document_code.md +2 -2
- package/agents/pan-executor.md +3 -3
- package/agents/pan-experiment-runner.md +7 -10
- package/agents/pan-hardener.md +15 -15
- package/agents/pan-knowledge.md +1 -1
- package/agents/pan-meta-reviewer.md +2 -2
- package/agents/pan-optimizer.md +7 -3
- package/agents/pan-phase-researcher.md +2 -2
- package/agents/pan-plan-checker.md +29 -2
- package/agents/pan-planner.md +23 -10
- package/agents/pan-project-researcher.md +10 -67
- package/agents/pan-reviewer.md +19 -0
- package/agents/pan-roadmapper.md +5 -48
- package/agents/pan-verifier.md +49 -11
- package/bin/install-lib.cjs +170 -15
- package/bin/install.js +39 -14
- package/commands/pan/army.md +2 -2
- package/commands/pan/design-phase.md +8 -0
- package/commands/pan/exec-phase.md +1 -1
- package/commands/pan/experiment.md +2 -2
- package/commands/pan/focus-auto.md +12 -8
- package/commands/pan/focus-design.md +2 -0
- package/commands/pan/focus-exec.md +1 -1
- package/commands/pan/focus-scan.md +2 -7
- package/commands/pan/git.md +2 -2
- package/commands/pan/learn.md +2 -0
- package/commands/pan/map-codebase.md +4 -4
- package/commands/pan/mcp-bridge.md +1 -1
- package/commands/pan/milestone-done.md +1 -2
- package/commands/pan/optimize.md +14 -2
- package/commands/pan/phase-tests.md +2 -2
- package/commands/pan/profile.md +2 -2
- package/commands/pan/research-phase.md +2 -4
- package/commands/pan/review-deep.md +8 -8
- package/commands/pan/todo-add.md +2 -2
- package/commands/pan/todo-check.md +2 -2
- package/hooks/dist/pan-context-monitor.js +1 -1
- package/hooks/dist/pan-cost-logger.js +63 -11
- package/hooks/dist/pan-state-reinject.js +142 -17
- package/hooks/dist/pan-stop-guard.js +2 -2
- package/hooks/dist/pan-trace-logger.js +283 -18
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/agents-md.cjs +4 -1
- package/pan-wizard-core/bin/lib/commands.cjs +95 -2
- package/pan-wizard-core/bin/lib/config.cjs +110 -0
- package/pan-wizard-core/bin/lib/constants.cjs +39 -9
- package/pan-wizard-core/bin/lib/core.cjs +27 -7
- package/pan-wizard-core/bin/lib/cost.cjs +16 -6
- package/pan-wizard-core/bin/lib/findings.cjs +385 -0
- package/pan-wizard-core/bin/lib/focus.cjs +72 -19
- package/pan-wizard-core/bin/lib/foreign-planning.cjs +24 -6
- package/pan-wizard-core/bin/lib/hygiene.cjs +12 -2
- package/pan-wizard-core/bin/lib/init.cjs +9 -2
- package/pan-wizard-core/bin/lib/optimize.cjs +349 -21
- package/pan-wizard-core/bin/lib/phase.cjs +52 -2
- package/pan-wizard-core/bin/lib/review-deep.cjs +38 -2
- package/pan-wizard-core/bin/lib/state.cjs +10 -4
- package/pan-wizard-core/bin/lib/verdict.cjs +481 -0
- package/pan-wizard-core/bin/lib/verify-drift.cjs +14 -5
- package/pan-wizard-core/bin/lib/verify-preflight.cjs +4 -2
- package/pan-wizard-core/bin/lib/verify-scope.cjs +223 -0
- package/pan-wizard-core/bin/lib/verify.cjs +111 -8
- package/pan-wizard-core/bin/pan-tools.cjs +68 -6
- package/pan-wizard-core/learnings/README.md +10 -7
- package/pan-wizard-core/mcp/server.cjs +18 -9
- package/pan-wizard-core/mcp/tool-registry.cjs +2 -0
- package/pan-wizard-core/references/checkpoints.md +4 -2
- package/pan-wizard-core/references/verdict-contract.md +102 -0
- package/pan-wizard-core/templates/discovery.md +1 -1
- package/pan-wizard-core/templates/phase-prompt.md +2 -1
- package/pan-wizard-core/templates/verification-report.md +21 -0
- package/pan-wizard-core/workflows/exec-phase.md +73 -50
- package/pan-wizard-core/workflows/health.md +3 -0
- package/pan-wizard-core/workflows/learn.md +4 -2
- package/pan-wizard-core/workflows/milestone-audit.md +15 -3
- package/pan-wizard-core/workflows/new-project.md +1 -1
- package/pan-wizard-core/workflows/optimize.md +14 -1
- package/pan-wizard-core/workflows/plan-phase.md +20 -15
- package/pan-wizard-core/workflows/quick.md +6 -6
- package/pan-wizard-core/workflows/verify-phase.md +10 -15
- package/pan-zcode/README.md +3 -3
- package/scripts/build-agent-plugin.js +19 -5
- package/scripts/git-hooks/pre-commit +2 -1
- package/scripts/test-surface.cjs +4 -0
|
@@ -103,6 +103,102 @@ function buildConfigDefaults(hasBraveSearch, userDefaults) {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// What PAN reads beyond the keys buildConfigDefaults writes (market-ideas M26). The
|
|
107
|
+
// rest of the known set is derived from the defaults, so a new default is known the
|
|
108
|
+
// day it is added. Each entry has a reader: loadConfig's flat aliases and alias
|
|
109
|
+
// sections (`planning.*`, `git.*`), the stop guard (`mode`, `workflow.stop_guard`),
|
|
110
|
+
// the roadmapper and new-project (`depth`), memory-optimize (`memory`), and the
|
|
111
|
+
// workflows that config-get `workflow.auto_advance`. tests/config-keys in
|
|
112
|
+
// tests/config.test.cjs pins this list against the keys shipped code and prose use.
|
|
113
|
+
const CONFIG_KEYS_BEYOND_DEFAULTS = Object.freeze({
|
|
114
|
+
top: Object.freeze(['research', 'plan_checker', 'verifier', 'nyquist_validation', 'mode', 'depth', 'planning', 'git',
|
|
115
|
+
'focus', 'model_overrides', 'effort_overrides', 'cost', 'cache', 'build', 'verification', 'concurrency', 'memory']),
|
|
116
|
+
sections: Object.freeze({
|
|
117
|
+
workflow: Object.freeze(['auto_advance', 'stop_guard']),
|
|
118
|
+
planning: Object.freeze(['commit_docs', 'search_gitignored']),
|
|
119
|
+
git: Object.freeze(['branching_strategy', 'phase_branch_template', 'milestone_branch_template']),
|
|
120
|
+
}),
|
|
121
|
+
});
|
|
122
|
+
// Sections whose keys are the user's own (model ids, agent names, file lists,
|
|
123
|
+
// commands), so nothing below them is checked.
|
|
124
|
+
const OPEN_CONFIG_SECTIONS = Object.freeze(['focus', 'model_overrides', 'effort_overrides', 'cost', 'cache', 'build', 'verification', 'concurrency', 'memory']);
|
|
125
|
+
|
|
126
|
+
/** The known top-level keys, and the known keys one level inside each closed section. */
|
|
127
|
+
function knownConfigKeys() {
|
|
128
|
+
const defaults = buildConfigDefaults(false, {});
|
|
129
|
+
const top = new Set([...Object.keys(defaults), ...CONFIG_KEYS_BEYOND_DEFAULTS.top]);
|
|
130
|
+
const sections = new Map();
|
|
131
|
+
for (const [k, v] of Object.entries(defaults)) {
|
|
132
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) sections.set(k, new Set(Object.keys(v)));
|
|
133
|
+
}
|
|
134
|
+
for (const [k, list] of Object.entries(CONFIG_KEYS_BEYOND_DEFAULTS.sections)) {
|
|
135
|
+
sections.set(k, new Set([...(sections.get(k) || []), ...list]));
|
|
136
|
+
}
|
|
137
|
+
for (const k of OPEN_CONFIG_SECTIONS) sections.delete(k);
|
|
138
|
+
return { top, sections };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether PAN reads a dot-notation key path: the top-level key is known, and inside a
|
|
143
|
+
* closed section so is the second segment. Deeper segments are not checked.
|
|
144
|
+
* @param {string} keyPath
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
function isKnownConfigPath(keyPath) {
|
|
148
|
+
const [head, sub] = String(keyPath).split('.');
|
|
149
|
+
const { top, sections } = knownConfigKeys();
|
|
150
|
+
if (!top.has(head)) return false;
|
|
151
|
+
return sub === undefined || !sections.has(head) || sections.get(head).has(sub);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The key paths in a parsed config.json that PAN does not read (market-ideas M26):
|
|
156
|
+
* unknown top-level keys, and unknown keys one level inside a closed section.
|
|
157
|
+
* @param {Object} config
|
|
158
|
+
* @returns {string[]}
|
|
159
|
+
*/
|
|
160
|
+
function unknownConfigKeys(config) {
|
|
161
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) return [];
|
|
162
|
+
const { top, sections } = knownConfigKeys();
|
|
163
|
+
const unknown = [];
|
|
164
|
+
for (const [k, v] of Object.entries(config)) {
|
|
165
|
+
if (!top.has(k)) { unknown.push(k); continue; }
|
|
166
|
+
const known = sections.get(k);
|
|
167
|
+
if (!known || !v || typeof v !== 'object' || Array.isArray(v)) continue;
|
|
168
|
+
for (const sub of Object.keys(v)) if (!known.has(sub)) unknown.push(`${k}.${sub}`);
|
|
169
|
+
}
|
|
170
|
+
return unknown;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The known key path nearest an unknown one (edit distance 2 or less), or null. */
|
|
174
|
+
function nearestConfigKey(keyPath) {
|
|
175
|
+
const { top, sections } = knownConfigKeys();
|
|
176
|
+
const candidates = [...top];
|
|
177
|
+
for (const [k, subs] of sections) for (const s of subs) candidates.push(`${k}.${s}`);
|
|
178
|
+
const want = String(keyPath).split('.').slice(0, 2).join('.');
|
|
179
|
+
let best = null;
|
|
180
|
+
let bestD = 3;
|
|
181
|
+
for (const c of candidates) {
|
|
182
|
+
const d = editDistance(want, c);
|
|
183
|
+
if (d < bestD) { best = c; bestD = d; }
|
|
184
|
+
}
|
|
185
|
+
return best;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function editDistance(a, b) {
|
|
189
|
+
const row = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
190
|
+
for (let i = 1; i <= a.length; i++) {
|
|
191
|
+
let prev = row[0];
|
|
192
|
+
row[0] = i;
|
|
193
|
+
for (let j = 1; j <= b.length; j++) {
|
|
194
|
+
const tmp = row[j];
|
|
195
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
196
|
+
prev = tmp;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return row[b.length];
|
|
200
|
+
}
|
|
201
|
+
|
|
106
202
|
function cmdConfigEnsureSection(cwd, raw) {
|
|
107
203
|
const configPath = path.join(planningPath(cwd), CONFIG_FILE);
|
|
108
204
|
|
|
@@ -216,6 +312,14 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
216
312
|
try {
|
|
217
313
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
218
314
|
const result = { updated: true, key: keyPath, value: parsedValue };
|
|
315
|
+
// A key PAN does not read is still written (a newer PAN, or the user's own
|
|
316
|
+
// note, may want it), but never silently: a typo used to look like a setting
|
|
317
|
+
// that took effect (market-ideas M26).
|
|
318
|
+
if (!isKnownConfigPath(keyPath)) {
|
|
319
|
+
const near = nearestConfigKey(keyPath);
|
|
320
|
+
result.warning = `unknown config key "${keyPath}": PAN does not read it` + (near ? `; did you mean "${near}"?` : '');
|
|
321
|
+
process.stderr.write(`pan-tools: ${result.warning}\n`);
|
|
322
|
+
}
|
|
219
323
|
output(result, raw, `${keyPath}=${parsedValue}`);
|
|
220
324
|
} catch (err) {
|
|
221
325
|
// Config file could not be written — disk full, permissions, etc.
|
|
@@ -664,6 +768,12 @@ function cmdStandardsTools(cwd, standardId, raw) {
|
|
|
664
768
|
|
|
665
769
|
module.exports = {
|
|
666
770
|
buildConfigDefaults,
|
|
771
|
+
CONFIG_KEYS_BEYOND_DEFAULTS,
|
|
772
|
+
OPEN_CONFIG_SECTIONS,
|
|
773
|
+
knownConfigKeys,
|
|
774
|
+
isKnownConfigPath,
|
|
775
|
+
unknownConfigKeys,
|
|
776
|
+
nearestConfigKey,
|
|
667
777
|
cmdConfigEnsureSection,
|
|
668
778
|
cmdConfigSet,
|
|
669
779
|
cmdConfigGet,
|
|
@@ -207,16 +207,18 @@ const STANDARDS_CATALOG = {
|
|
|
207
207
|
level: 'foundational',
|
|
208
208
|
url: 'https://owasp.org/www-project-top-ten/',
|
|
209
209
|
checklist: [
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
'
|
|
213
|
-
'
|
|
214
|
-
'
|
|
215
|
-
'
|
|
210
|
+
// The 2025 list (top10.owasp.org): until 2026-10-03 this held the 2021 list
|
|
211
|
+
// under the 2025 name (prompt audit, market-ideas M34). SSRF is now part of A01.
|
|
212
|
+
'A01: Broken Access Control — verify authorization checks on all endpoints, and allowlists for server-side requests (SSRF)',
|
|
213
|
+
'A02: Security Misconfiguration — verify default credentials removed, headers set',
|
|
214
|
+
'A03: Software Supply Chain Failures — verify dependency scanning, no known CVEs, pinned and verified packages and build tooling',
|
|
215
|
+
'A04: Cryptographic Failures — verify sensitive data encryption at rest and in transit',
|
|
216
|
+
'A05: Injection — verify input validation and parameterized queries',
|
|
217
|
+
'A06: Insecure Design — verify threat modeling and secure design patterns',
|
|
216
218
|
'A07: Authentication Failures — verify MFA support, session management, credential storage',
|
|
217
|
-
'A08: Software
|
|
218
|
-
'A09: Logging and
|
|
219
|
-
'A10:
|
|
219
|
+
'A08: Software or Data Integrity Failures — verify CI/CD pipeline integrity, signed updates',
|
|
220
|
+
'A09: Security Logging and Alerting Failures — verify security events logged, alerts configured',
|
|
221
|
+
'A10: Mishandling of Exceptional Conditions — verify errors fail closed, are handled, and release resources',
|
|
220
222
|
],
|
|
221
223
|
},
|
|
222
224
|
'owasp-asvs-l1': {
|
|
@@ -541,6 +543,15 @@ const HEALTH_STATUS = { HEALTHY: 'healthy', DEGRADED: 'degraded', BROKEN: 'broke
|
|
|
541
543
|
/** Max JSON payload size before writing to tmpfile (bytes) */
|
|
542
544
|
const MAX_JSON_SIZE = 50000;
|
|
543
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Shape version of the machine-readable state JSON: `state` (load), `state json`,
|
|
548
|
+
* `progress` (json) and the MCP resources that wrap them (market item M17).
|
|
549
|
+
* Additive rule: fields may be added within a major version and are never renamed
|
|
550
|
+
* or removed; readers accept any 1.x and ignore fields they do not know. A rename
|
|
551
|
+
* or removal is a major bump ("2.0").
|
|
552
|
+
*/
|
|
553
|
+
const STATE_CONTRACT = '1.0';
|
|
554
|
+
|
|
544
555
|
/** Width of progress bar in status display (character count) */
|
|
545
556
|
const PROGRESS_BAR_WIDTH = 10;
|
|
546
557
|
|
|
@@ -724,6 +735,24 @@ const FOREIGN_PLANNING_MARKERS = Object.freeze({
|
|
|
724
735
|
'graphify.enabled', 'intel.enabled', 'hooks.workflow_guard', 'dynamic_routing', 'resolve_model_ids',
|
|
725
736
|
]),
|
|
726
737
|
}),
|
|
738
|
+
// planning-with-files (OthmanAdi/planning-with-files README, read 2026-09-28 at
|
|
739
|
+
// v3.21.0; market item M3 / MI-028). It writes INTO .planning/ beside whatever else
|
|
740
|
+
// lives there: `.active_plan`, `.attestation`, `sessions/`, `ledger-<agent>.jsonl`, and
|
|
741
|
+
// one `YYYY-MM-DD-slug/` directory per task holding task_plan.md / findings.md /
|
|
742
|
+
// progress.md. PAN writes none of those names. `coexists`: unlike gsd-core, a match
|
|
743
|
+
// means the tree is SHARED, not owned. PAN keeps working in it and says so, rather
|
|
744
|
+
// than refusing. planning-with-files gitignores .planning/ by default, which is what
|
|
745
|
+
// the I005 health check exists for.
|
|
746
|
+
planning_with_files: Object.freeze({
|
|
747
|
+
tool: 'planning-with-files',
|
|
748
|
+
coexists: true,
|
|
749
|
+
files: Object.freeze(['.active_plan', '.attestation']),
|
|
750
|
+
filePatterns: Object.freeze([/^ledger-[A-Za-z0-9._-]+\.jsonl$/]),
|
|
751
|
+
dirs: Object.freeze(['sessions']),
|
|
752
|
+
// A dated task directory is strong evidence only with the tool's plan file in it.
|
|
753
|
+
dirPatterns: Object.freeze([{ re: /^\d{4}-\d{2}-\d{2}-[A-Za-z0-9][A-Za-z0-9._-]*$/, requires: 'task_plan.md' }]),
|
|
754
|
+
configKeys: Object.freeze([]),
|
|
755
|
+
}),
|
|
727
756
|
});
|
|
728
757
|
|
|
729
758
|
module.exports = {
|
|
@@ -806,6 +835,7 @@ module.exports = {
|
|
|
806
835
|
HEALTH_STATUS,
|
|
807
836
|
FOREIGN_PLANNING_MARKERS,
|
|
808
837
|
MAX_JSON_SIZE,
|
|
838
|
+
STATE_CONTRACT,
|
|
809
839
|
PROGRESS_BAR_WIDTH,
|
|
810
840
|
MAX_SLUG_LENGTH,
|
|
811
841
|
FILLED_BLOCK,
|
|
@@ -39,8 +39,13 @@ const { planningPath, planningRel } = require('./utils.cjs');
|
|
|
39
39
|
// budget-profile project on Codex or OpenCode was told to spawn a model called
|
|
40
40
|
// "mid" (reality check R28).
|
|
41
41
|
// - openai: Codex's subagent docs (learn.chatgpt.com/docs/agent-configuration/
|
|
42
|
-
// subagents, read 2026-
|
|
43
|
-
//
|
|
42
|
+
// subagents, re-read 2026-10-03) now start demanding agents on gpt-6.1-sol "when
|
|
43
|
+
// your signed-in account or workspace has access", and keep gpt-6-luna as the
|
|
44
|
+
// "faster, lower-cost option". The mid tier stays gpt-6-sol: GPT-6.1 Sol's launch
|
|
45
|
+
// keeps it off on Enterprise and Edu until an administrator enables it
|
|
46
|
+
// (learn.chatgpt.com/docs/models, read 2026-10-03), so pinning it would hand
|
|
47
|
+
// those users a model they cannot run. The inherit tier runs Codex's own
|
|
48
|
+
// default, which is gpt-6.1-sol where available. gpt-6-astra is the flagship.
|
|
44
49
|
// - google: the newest stable Flash and Flash-Lite on ai.google.dev's models page
|
|
45
50
|
// (read 2026-09-23). The 2.5 family these rows used is "not deprecated" but
|
|
46
51
|
// limited to users who have used it before, so a new project may not reach it.
|
|
@@ -58,12 +63,14 @@ const PROVIDER_MODELS = {
|
|
|
58
63
|
// packages/web/src/content/docs/models.mdx and models.dev/api.json, both read
|
|
59
64
|
// 2026-09-26; every id below exists there under that provider). Same tiers as
|
|
60
65
|
// PROVIDER_MODELS, provider-qualified; `default` is Anthropic as it is there.
|
|
61
|
-
// Reality check R41.
|
|
66
|
+
// Reality check R41. The Anthropic mid tier follows the `sonnet` alias, which
|
|
67
|
+
// resolves to Sonnet 5.5 on the Anthropic API from Claude Code 2.1.284;
|
|
68
|
+
// models.dev lists `anthropic/claude-sonnet-5-5` (api.json, read 2026-10-03).
|
|
62
69
|
const OPENCODE_MODELS = {
|
|
63
|
-
anthropic: { reasoning: 'inherit', mid: 'anthropic/claude-sonnet-5', fast: 'anthropic/claude-haiku-4-5' },
|
|
64
|
-
openai: { reasoning: 'inherit', mid: 'openai/gpt-6-sol',
|
|
65
|
-
google: { reasoning: 'inherit', mid: 'google/gemini-3.8-flash',
|
|
66
|
-
default: { reasoning: 'inherit', mid: 'anthropic/claude-sonnet-5', fast: 'anthropic/claude-haiku-4-5' },
|
|
70
|
+
anthropic: { reasoning: 'inherit', mid: 'anthropic/claude-sonnet-5-5', fast: 'anthropic/claude-haiku-4-5' },
|
|
71
|
+
openai: { reasoning: 'inherit', mid: 'openai/gpt-6-sol', fast: 'openai/gpt-6-luna' },
|
|
72
|
+
google: { reasoning: 'inherit', mid: 'google/gemini-3.8-flash', fast: 'google/gemini-3.5-flash-lite' },
|
|
73
|
+
default: { reasoning: 'inherit', mid: 'anthropic/claude-sonnet-5-5', fast: 'anthropic/claude-haiku-4-5' },
|
|
67
74
|
};
|
|
68
75
|
|
|
69
76
|
/**
|
|
@@ -552,6 +559,18 @@ function escapeRegex(value) {
|
|
|
552
559
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
553
560
|
}
|
|
554
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Make free text safe for one markdown table cell: escape backslashes and pipes,
|
|
564
|
+
* and fold every run of line breaks into one space. A raw `|` splits the row into
|
|
565
|
+
* an extra column, and state.md's tables are appended to for the life of a
|
|
566
|
+
* project, so one bad row breaks every later read of the table (market-ideas M21).
|
|
567
|
+
* @param {*} value - Cell text; null and undefined become ''
|
|
568
|
+
* @returns {string}
|
|
569
|
+
*/
|
|
570
|
+
function escapeTableCell(value) {
|
|
571
|
+
return String(value ?? '').replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/[\r\n]+/g, ' ');
|
|
572
|
+
}
|
|
573
|
+
|
|
555
574
|
/**
|
|
556
575
|
* Normalize a phase identifier to zero-padded format.
|
|
557
576
|
* Examples: "1" → "01", "3A" → "03A", "12.1" → "12.1"
|
|
@@ -1454,6 +1473,7 @@ module.exports = {
|
|
|
1454
1473
|
isGitRepo,
|
|
1455
1474
|
execGit,
|
|
1456
1475
|
escapeRegex,
|
|
1476
|
+
escapeTableCell,
|
|
1457
1477
|
normalizePhaseName,
|
|
1458
1478
|
comparePhaseNum,
|
|
1459
1479
|
searchPhaseInDir,
|
|
@@ -59,7 +59,7 @@ const DEFAULT_RATES = {
|
|
|
59
59
|
// Anthropic — platform.claude.com/docs/en/about-claude/pricing, every row read
|
|
60
60
|
// 2026-09-23. Opus 4.6+/Opus 5 are $5/$25 (the old $15/$75 Opus pricing ended with
|
|
61
61
|
// the 4.5 generation). cache_write is the 5-minute write rate (1.25× input; the
|
|
62
|
-
// 1-hour write bills 2
|
|
62
|
+
// 1-hour write bills 2× and is priced from cache_write_1h, see above). Cache reads follow
|
|
63
63
|
// the 0.1× convention EXCEPT on three models the page footnotes: Fable 5.1 and
|
|
64
64
|
// Mythos 5.1 at 0.025× input, Opus 5.5 at 0.05×. Each needs its own row, because
|
|
65
65
|
// the family-prefix fallback would otherwise price it at its predecessor's reads.
|
|
@@ -91,6 +91,12 @@ const DEFAULT_RATES = {
|
|
|
91
91
|
// 2026-08-31 was made permanent and the scheduled rise to $3/$15 cancelled.
|
|
92
92
|
// Lesson: never write down a pre-announced price — this row carried the future
|
|
93
93
|
// rate for a month and over-billed by half.
|
|
94
|
+
// Sonnet 5.5 (claude-sonnet-5-5): the same prices as Sonnet 5 — $2/$10, 5-minute
|
|
95
|
+
// writes $2.50, 1-hour $4, hits $0.20 (pricing page, read 2026-10-03). Claude Code
|
|
96
|
+
// 2.1.284 points the `sonnet` alias at it on the Anthropic API (model-config, read
|
|
97
|
+
// 2026-10-03). The family prefix already priced it at Sonnet 5's identical row; its
|
|
98
|
+
// own row means a later price change to either model cannot misprice the other.
|
|
99
|
+
'claude-sonnet-5-5': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50, cache_write_1h: 4.0 },
|
|
94
100
|
'claude-sonnet-5': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50, cache_write_1h: 4.0 },
|
|
95
101
|
'claude-sonnet-4-6': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75, cache_write_1h: 6.0 },
|
|
96
102
|
// Sonnet 4.5 (dated id claude-sonnet-4-5-20250929): $3/$15/$0.30/$3.75 (R7).
|
|
@@ -105,10 +111,14 @@ const DEFAULT_RATES = {
|
|
|
105
111
|
// as plain input. Until 2026-09-22 this block said "no separate write charge" for
|
|
106
112
|
// every row, which had stopped being true — Terra and Luna writes were a fifth
|
|
107
113
|
// low (R27). Longest-prefix match lets each tier key win over a shorter one.
|
|
108
|
-
// GPT-6: Astra is the flagship; Sol and Luna were released 2026-09-22.
|
|
109
|
-
//
|
|
110
|
-
//
|
|
114
|
+
// GPT-6: Astra is the flagship; Sol and Luna were released 2026-09-22. GPT-6.1 Sol
|
|
115
|
+
// is Codex's default model from 0.159.1 (2026-09-29): `model = "gpt-6.1-sol"` in
|
|
116
|
+
// config-basic, and the subagent docs' starting model for demanding agents, with
|
|
117
|
+
// gpt-6-luna for lower-cost ones (learn.chatgpt.com/docs/config-file/config-basic,
|
|
118
|
+
// /agent-configuration/subagents, both read 2026-10-03). Its row was read from the
|
|
119
|
+
// pricing page on 2026-10-03: GPT-6 Sol's prices, except cached input at 0.05×.
|
|
111
120
|
'gpt-6-astra': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
|
|
121
|
+
'gpt-6.1-sol': { input: 2.0, output: 10.0, cache_read: 0.1, cache_write: 2.5 },
|
|
112
122
|
'gpt-6-sol': { input: 2.0, output: 10.0, cache_read: 0.2, cache_write: 2.5 },
|
|
113
123
|
'gpt-6-luna': { input: 0.10, output: 0.50, cache_read: 0.01, cache_write: 0.125 },
|
|
114
124
|
// GPT-5.6. The bare `gpt-5.6` id is an alias that "routes requests to GPT-5.6 Sol"
|
|
@@ -469,8 +479,8 @@ function readRecords(cwd) {
|
|
|
469
479
|
*/
|
|
470
480
|
// No single subagent runs for six hours — the longest native-workflow phase runs
|
|
471
481
|
// measured in the harness finish inside an hour — while a parent-transcript slice
|
|
472
|
-
// that spans a working day, or the idle night between two stops, does.
|
|
473
|
-
// the
|
|
482
|
+
// that spans a working day, or the idle night between two stops, does. The hooks
|
|
483
|
+
// write the span as measured; only this reader applies the rule. Calibrated on
|
|
474
484
|
// eleven field ledgers (2026-09): of the timed rows the old ratio rule flagged,
|
|
475
485
|
// 55 spanned under three hours and 12 spanned six to twenty-four — the latter all
|
|
476
486
|
// parent slices booked to a `general-purpose` or workflow subagent.
|