@skillstate/opencode 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +196 -131
  2. package/dist/feedback.d.ts +178 -0
  3. package/dist/feedback.d.ts.map +1 -0
  4. package/dist/feedback.js +235 -0
  5. package/dist/feedback.js.map +1 -0
  6. package/dist/index.d.ts +33 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +27 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/mode.d.ts +97 -0
  11. package/dist/mode.d.ts.map +1 -0
  12. package/dist/mode.js +111 -0
  13. package/dist/mode.js.map +1 -0
  14. package/dist/opencode-adapter.d.ts +4 -3
  15. package/dist/opencode-adapter.d.ts.map +1 -1
  16. package/dist/opencode-adapter.js.map +1 -1
  17. package/dist/paper-mode.d.ts +421 -0
  18. package/dist/paper-mode.d.ts.map +1 -0
  19. package/dist/paper-mode.js +445 -0
  20. package/dist/paper-mode.js.map +1 -0
  21. package/dist/plugin.d.ts +158 -8
  22. package/dist/plugin.d.ts.map +1 -1
  23. package/dist/plugin.js +788 -21
  24. package/dist/plugin.js.map +1 -1
  25. package/dist/response-sink.d.ts +208 -0
  26. package/dist/response-sink.d.ts.map +1 -0
  27. package/dist/response-sink.js +243 -0
  28. package/dist/response-sink.js.map +1 -0
  29. package/dist/runtime.d.ts +203 -0
  30. package/dist/runtime.d.ts.map +1 -0
  31. package/dist/runtime.js +332 -0
  32. package/dist/runtime.js.map +1 -0
  33. package/dist/spec-loader.d.ts +81 -0
  34. package/dist/spec-loader.d.ts.map +1 -0
  35. package/dist/spec-loader.js +163 -0
  36. package/dist/spec-loader.js.map +1 -0
  37. package/dist/step-boundary.d.ts +91 -0
  38. package/dist/step-boundary.d.ts.map +1 -0
  39. package/dist/step-boundary.js +109 -0
  40. package/dist/step-boundary.js.map +1 -0
  41. package/dist/system-hint.d.ts +70 -3
  42. package/dist/system-hint.d.ts.map +1 -1
  43. package/dist/system-hint.js +90 -11
  44. package/dist/system-hint.js.map +1 -1
  45. package/dist/tools.d.ts +35 -1
  46. package/dist/tools.d.ts.map +1 -1
  47. package/dist/tools.js +16 -0
  48. package/dist/tools.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Resolving P — the procedural specification the paper's prompt is built on.
3
+ *
4
+ * ── Why the plugin needs a spec at all ────────────────────────────────────
5
+ *
6
+ * A.4 is `Format(P, Σₜ, Oₜ)`, and P is the first argument. Paper mode
7
+ * therefore cannot assemble a paper-conformant prompt without one, even
8
+ * though the store ({@link ProjectStateStore}) is deliberately schema-free:
9
+ * notes mode never shows the model a schema, so it never needs P.
10
+ *
11
+ * ── Resolution order ─────────────────────────────────────────────────────
12
+ *
13
+ * 1. `<project>/skill-spec.json` — the file `skillstate init` writes and the
14
+ * one the CLI's `--spec` already points at (`config.ts` default
15
+ * `specPath: './skill-spec.json'`). A project that has customised its
16
+ * procedure gets that procedure.
17
+ * 2. {@link GENERIC_PROCEDURE_SPEC} — the built-in, domain-neutral default.
18
+ *
19
+ * ── A malformed spec file must not reach the model ────────────────────────
20
+ *
21
+ * A half-written or hand-mangled `skill-spec.json` is exactly the kind of
22
+ * input that produced the v1 failure, where the spec's own instructions
23
+ * ("You are an autonomous CTF agent … find the flag") overrode the user. So
24
+ * the file is VALIDATED before it is trusted, field by field, and anything
25
+ * that does not typecheck falls back to the built-in spec with the reason
26
+ * recorded. There is no code path that feeds an unvalidated P to the model.
27
+ *
28
+ * Reads are cached per directory for the lifetime of the resolver: the
29
+ * `context` hook runs on every model request, and re-reading and re-validating
30
+ * a file that cannot change without the user editing it is pure overhead on
31
+ * the agent loop's hot path. {@link SpecResolver.invalidate} drops the cache
32
+ * for callers that do edit it.
33
+ */
34
+ import * as fs from 'node:fs';
35
+ import * as path from 'node:path';
36
+ import { GENERIC_PROCEDURE_SPEC, isPlainObject } from '@skillstate/core';
37
+ /** The file a project keeps its procedural spec in. */
38
+ export const SPEC_FILE_NAME = 'skill-spec.json';
39
+ const SCHEMA_TYPES = new Set([
40
+ 'string',
41
+ 'number',
42
+ 'boolean',
43
+ 'array',
44
+ 'object',
45
+ ]);
46
+ function validateSchema(value, at) {
47
+ if (!isPlainObject(value))
48
+ return `${at} is not an object`;
49
+ const schema = {};
50
+ for (const [key, raw] of Object.entries(value)) {
51
+ if (!isPlainObject(raw))
52
+ return `${at}.${key} is not an object`;
53
+ const type = raw['type'];
54
+ if (typeof type !== 'string' || !SCHEMA_TYPES.has(type)) {
55
+ return `${at}.${key}.type must be one of ${[...SCHEMA_TYPES].join(', ')}`;
56
+ }
57
+ if (!('default' in raw))
58
+ return `${at}.${key}.default is required`;
59
+ const description = raw['description'];
60
+ if (description !== undefined && typeof description !== 'string') {
61
+ return `${at}.${key}.description must be a string`;
62
+ }
63
+ const field = { type: type, default: raw['default'] };
64
+ if (typeof description === 'string')
65
+ field.description = description;
66
+ schema[key] = field;
67
+ }
68
+ return schema;
69
+ }
70
+ /**
71
+ * Validate a parsed spec document. Returns the spec, or a sentence naming
72
+ * the first field that failed. Pure.
73
+ *
74
+ * The checks are deliberately structural only — they reject a document that
75
+ * could not be rendered or that would misdescribe Σₜ, and nothing subtler.
76
+ * Semantic review of an instructions string is not something a type check can
77
+ * do, and pretending otherwise would be worse than not checking.
78
+ */
79
+ export function parseSpec(value) {
80
+ if (!isPlainObject(value))
81
+ return 'spec is not an object';
82
+ const id = value['id'];
83
+ if (typeof id !== 'string' || id.trim() === '')
84
+ return 'id must be a non-empty string';
85
+ const name = value['name'];
86
+ if (typeof name !== 'string' || name.trim() === '')
87
+ return 'name must be a non-empty string';
88
+ const version = value['version'];
89
+ if (typeof version !== 'string' || version.trim() === '') {
90
+ return 'version must be a non-empty string';
91
+ }
92
+ const instructions = value['instructions'];
93
+ if (typeof instructions !== 'string' || instructions.trim() === '') {
94
+ return 'instructions must be a non-empty string';
95
+ }
96
+ const schema = validateSchema(value['schema'], 'schema');
97
+ if (typeof schema === 'string')
98
+ return schema;
99
+ return { id, name, version, instructions, schema };
100
+ }
101
+ /**
102
+ * Per-project spec resolution with a cache.
103
+ *
104
+ * One instance per plugin setup. The cache key is the resolved project
105
+ * directory, so a single OpenCode server serving several checkouts gets a
106
+ * different spec per checkout without the caller managing that.
107
+ */
108
+ export class SpecResolver {
109
+ cache = new Map();
110
+ /**
111
+ * The spec for `directory`, reading and validating the file at most once
112
+ * per {@link invalidate}.
113
+ */
114
+ resolve(directory) {
115
+ const key = path.resolve(directory);
116
+ const cached = this.cache.get(key);
117
+ if (cached !== undefined)
118
+ return cached;
119
+ const resolution = this.load(key);
120
+ this.cache.set(key, resolution);
121
+ return resolution;
122
+ }
123
+ /** Drop the cached spec for `directory` (or for every directory). */
124
+ invalidate(directory) {
125
+ if (directory === undefined)
126
+ this.cache.clear();
127
+ else
128
+ this.cache.delete(path.resolve(directory));
129
+ }
130
+ load(directory) {
131
+ const file = path.join(directory, SPEC_FILE_NAME);
132
+ let raw;
133
+ try {
134
+ raw = fs.readFileSync(file, 'utf-8');
135
+ }
136
+ catch {
137
+ return { spec: GENERIC_PROCEDURE_SPEC, source: 'builtin' };
138
+ }
139
+ let parsed;
140
+ try {
141
+ parsed = JSON.parse(raw);
142
+ }
143
+ catch (error) {
144
+ return {
145
+ spec: GENERIC_PROCEDURE_SPEC,
146
+ source: 'builtin',
147
+ path: file,
148
+ rejected: `invalid JSON: ${String(error)}`,
149
+ };
150
+ }
151
+ const validated = parseSpec(parsed);
152
+ if (typeof validated === 'string') {
153
+ return {
154
+ spec: GENERIC_PROCEDURE_SPEC,
155
+ source: 'builtin',
156
+ path: file,
157
+ rejected: validated,
158
+ };
159
+ }
160
+ return { spec: validated, source: 'file', path: file };
161
+ }
162
+ }
163
+ //# sourceMappingURL=spec-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-loader.js","sourceRoot":"","sources":["../src/spec-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The step boundary — §5.1's one observation per step, enforced in code.
3
+ *
4
+ * ── The divergence this fixes ─────────────────────────────────────────────
5
+ *
6
+ * The paper's Algorithm 1 alternates, once per step:
7
+ *
8
+ * Aₜ ← Format(P, Σₜ, Oₜ); resp ← llm(Aₜ); Σₜ₊₁ ← Σₜ ⊕ ΔΣₜ; Oₜ₊₁ ← execute(aₜ)
9
+ *
10
+ * One prompt, one patch, one action, one observation. The model is never
11
+ * given a loop of its own, so it has no opportunity to run twenty things
12
+ * inside one step, and the state cannot lag the work.
13
+ *
14
+ * This integration cannot own the model call or the tool execution — the
15
+ * OpenCode plugin API exposes neither, and that is a real limit rather than a
16
+ * design choice. What it can do is own the *boundary*, and that is the half
17
+ * that was missing. Delegating execution to the host's agent loop handed the
18
+ * model an unbounded inner loop, and it used it: 21 tool calls, 3 text
19
+ * blocks, one state patch written at the end from whatever observation was
20
+ * current. Two models, repeated runs. The state lagged the work, and a model
21
+ * whose running total lags cannot accumulate.
22
+ *
23
+ * The fix uses a capability that is present and unused: `SessionContext.tools`
24
+ * is handed to the `context` hook on *every* model request. So requests
25
+ * alternate. One may act — tools present, the host executes, the observation
26
+ * lands in Oₜ. The next is given no tools at all, and a model that has just
27
+ * acted and is asked again with nothing to call can only answer in text, which
28
+ * is exactly where `state_patch` lives.
29
+ *
30
+ * That is §5.1's alternation with the host standing in for both `llm` and
31
+ * `execute`. It is not a simulation of the loop: the host really runs the
32
+ * action, and the state really moves between steps.
33
+ *
34
+ * ── What it does not do ───────────────────────────────────────────────────
35
+ *
36
+ * It cannot make the model write a *good* patch, only make it answer. The
37
+ * merge, the validation and the retry-with-rollback are unchanged, and a
38
+ * patch that fails validation is still refused. This buys the paper's
39
+ * one-observation-per-step; it does not buy correctness of reasoning.
40
+ */
41
+ export declare class StepBoundary {
42
+ #private;
43
+ /**
44
+ * Decide what a request may do, and remember the answer.
45
+ *
46
+ * Returns true when the model may act. A session starts in `act`, so the
47
+ * first request can do real work; every request after an action is a report.
48
+ */
49
+ mayAct(sessionID: string): boolean;
50
+ /**
51
+ * Record that an action ran, so the next request must report.
52
+ *
53
+ * Called when the host executed a tool on this session's behalf. There is
54
+ * no event that says "a tool finished" in a form the plugin can trust for
55
+ * this, so the boundary is advanced from the patch instead — see
56
+ * {@link reportRequired}. Kept as a separate method so the trigger can
57
+ * change without the cycle changing.
58
+ */
59
+ actionTaken(sessionID: string): void;
60
+ /**
61
+ * Whether this request must be answered with a state patch.
62
+ *
63
+ * The single question the context hook needs. It is also the whole point:
64
+ * the model gets one turn to act and one turn to account for it.
65
+ */
66
+ reportRequired(sessionID: string): boolean;
67
+ /**
68
+ * A patch was applied, so the next request may act again.
69
+ *
70
+ * Only an *applied* patch clears the phase. A rejected one leaves the model
71
+ * in `report`, which is what the retry-with-rollback in §6.3 needs: it is
72
+ * re-asked for the same step rather than being let off the hook to act
73
+ * before it has recorded anything.
74
+ */
75
+ patchApplied(sessionID: string): void;
76
+ /** Reset a session, on teardown or a fresh run. */
77
+ reset(sessionID: string): void;
78
+ /** Forget everything. A plugin unload must not leak a phase map. */
79
+ clear(): void;
80
+ /** How many sessions are mid-cycle, for diagnostics and tests. */
81
+ get size(): number;
82
+ }
83
+ /**
84
+ * Whether an action ends the procedure.
85
+ *
86
+ * Shared with the runtime so the two agree on what "finished" means. They
87
+ * answer the same question from the same string, and two copies of that would
88
+ * be two definitions to keep in step.
89
+ */
90
+ export declare function isTerminalAction(action: string): boolean;
91
+ //# sourceMappingURL=step-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-boundary.d.ts","sourceRoot":"","sources":["../src/step-boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAQH,qBAAa,YAAY;;IAGvB;;;;;OAKG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEjC;IAED;;;;;;;;OAQG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEnC;IAED;;;;;OAKG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzC;IAED;;;;;;;OAOG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEpC;IAED,mDAAmD;IACnD,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,oEAAoE;IACpE,KAAK,IAAI,IAAI,CAEZ;IAED,kEAAkE;IAClE,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The step boundary — §5.1's one observation per step, enforced in code.
3
+ *
4
+ * ── The divergence this fixes ─────────────────────────────────────────────
5
+ *
6
+ * The paper's Algorithm 1 alternates, once per step:
7
+ *
8
+ * Aₜ ← Format(P, Σₜ, Oₜ); resp ← llm(Aₜ); Σₜ₊₁ ← Σₜ ⊕ ΔΣₜ; Oₜ₊₁ ← execute(aₜ)
9
+ *
10
+ * One prompt, one patch, one action, one observation. The model is never
11
+ * given a loop of its own, so it has no opportunity to run twenty things
12
+ * inside one step, and the state cannot lag the work.
13
+ *
14
+ * This integration cannot own the model call or the tool execution — the
15
+ * OpenCode plugin API exposes neither, and that is a real limit rather than a
16
+ * design choice. What it can do is own the *boundary*, and that is the half
17
+ * that was missing. Delegating execution to the host's agent loop handed the
18
+ * model an unbounded inner loop, and it used it: 21 tool calls, 3 text
19
+ * blocks, one state patch written at the end from whatever observation was
20
+ * current. Two models, repeated runs. The state lagged the work, and a model
21
+ * whose running total lags cannot accumulate.
22
+ *
23
+ * The fix uses a capability that is present and unused: `SessionContext.tools`
24
+ * is handed to the `context` hook on *every* model request. So requests
25
+ * alternate. One may act — tools present, the host executes, the observation
26
+ * lands in Oₜ. The next is given no tools at all, and a model that has just
27
+ * acted and is asked again with nothing to call can only answer in text, which
28
+ * is exactly where `state_patch` lives.
29
+ *
30
+ * That is §5.1's alternation with the host standing in for both `llm` and
31
+ * `execute`. It is not a simulation of the loop: the host really runs the
32
+ * action, and the state really moves between steps.
33
+ *
34
+ * ── What it does not do ───────────────────────────────────────────────────
35
+ *
36
+ * It cannot make the model write a *good* patch, only make it answer. The
37
+ * merge, the validation and the retry-with-rollback are unchanged, and a
38
+ * patch that fails validation is still refused. This buys the paper's
39
+ * one-observation-per-step; it does not buy correctness of reasoning.
40
+ */
41
+ /** Actions that end the procedure rather than continuing it. */
42
+ const TERMINAL = new Set(['done', 'complete', 'completed', 'finished', 'stop', 'end', '']);
43
+ export class StepBoundary {
44
+ #phase = new Map();
45
+ /**
46
+ * Decide what a request may do, and remember the answer.
47
+ *
48
+ * Returns true when the model may act. A session starts in `act`, so the
49
+ * first request can do real work; every request after an action is a report.
50
+ */
51
+ mayAct(sessionID) {
52
+ return (this.#phase.get(sessionID) ?? 'act') === 'act';
53
+ }
54
+ /**
55
+ * Record that an action ran, so the next request must report.
56
+ *
57
+ * Called when the host executed a tool on this session's behalf. There is
58
+ * no event that says "a tool finished" in a form the plugin can trust for
59
+ * this, so the boundary is advanced from the patch instead — see
60
+ * {@link reportRequired}. Kept as a separate method so the trigger can
61
+ * change without the cycle changing.
62
+ */
63
+ actionTaken(sessionID) {
64
+ this.#phase.set(sessionID, 'report');
65
+ }
66
+ /**
67
+ * Whether this request must be answered with a state patch.
68
+ *
69
+ * The single question the context hook needs. It is also the whole point:
70
+ * the model gets one turn to act and one turn to account for it.
71
+ */
72
+ reportRequired(sessionID) {
73
+ return (this.#phase.get(sessionID) ?? 'act') === 'report';
74
+ }
75
+ /**
76
+ * A patch was applied, so the next request may act again.
77
+ *
78
+ * Only an *applied* patch clears the phase. A rejected one leaves the model
79
+ * in `report`, which is what the retry-with-rollback in §6.3 needs: it is
80
+ * re-asked for the same step rather than being let off the hook to act
81
+ * before it has recorded anything.
82
+ */
83
+ patchApplied(sessionID) {
84
+ this.#phase.set(sessionID, 'act');
85
+ }
86
+ /** Reset a session, on teardown or a fresh run. */
87
+ reset(sessionID) {
88
+ this.#phase.delete(sessionID);
89
+ }
90
+ /** Forget everything. A plugin unload must not leak a phase map. */
91
+ clear() {
92
+ this.#phase.clear();
93
+ }
94
+ /** How many sessions are mid-cycle, for diagnostics and tests. */
95
+ get size() {
96
+ return this.#phase.size;
97
+ }
98
+ }
99
+ /**
100
+ * Whether an action ends the procedure.
101
+ *
102
+ * Shared with the runtime so the two agree on what "finished" means. They
103
+ * answer the same question from the same string, and two copies of that would
104
+ * be two definitions to keep in step.
105
+ */
106
+ export function isTerminalAction(action) {
107
+ return TERMINAL.has(action.trim().toLowerCase());
108
+ }
109
+ //# sourceMappingURL=step-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-boundary.js","sourceRoot":"","sources":["../src/step-boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAKH,gEAAgE;AAChE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AAE3F,MAAM,OAAO,YAAY;IACd,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE3C;;;;;OAKG;IACH,MAAM,CAAC,SAAiB;QACtB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;IACzD,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW,CAAC,SAAiB;QAC3B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,SAAiB;QAC9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,QAAQ,CAAC;IAC5D,CAAC;IAED;;;;;;;OAOG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,SAAiB;QACrB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAED,oEAAoE;IACpE,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,kEAAkE;IAClE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACnD,CAAC"}
@@ -34,11 +34,35 @@ export declare const ADVERTISED_TOOLS: readonly ['skillstate_read', 'skillstate_
34
34
  *
35
35
  * Small documents are inlined verbatim (the model sees what is already
36
36
  * saved without a tool round-trip). Documents past
37
- * {@link MAX_INLINE_STATE_CHARS} are summarized as a key list plus a byte
38
- * count and a pointer to `skillstate_read` — an unbounded state file must
39
- * not be able to grow the system prompt without limit.
37
+ * {@link MAX_INLINE_STATE_CHARS} are summarized as a key list plus a count and
38
+ * a pointer to `skillstate_read` — an unbounded state file must not be able to
39
+ * grow the system prompt without limit.
40
+ *
41
+ * The reported count is in the SAME UNIT as the limit, and it is labelled with
42
+ * that unit's name. The first version reported `bytes` next to a limit expressed
43
+ * in characters, which is this project's whole recurring mistake in one line: a
44
+ * state of 4,003 Cyrillic characters is 3,003 over the limit and 7,993 bytes, so
45
+ * the model reading the summary was handed two numbers in two units and a limit
46
+ * in a third. §4.3 is explicit that sizes here are raw string CHARS.
40
47
  */
41
48
  export declare function renderStateForHint(state: Record<string, unknown>): string;
49
+ /**
50
+ * How many MODEL REQUESTS may pass with the state untouched before the drift
51
+ * notice fires.
52
+ *
53
+ * **"Requests", not "turns".** Measured on the weakest model in the
54
+ * catalogue: 20 file reads took 6 requests, 40 reads took 12, 70 reads took
55
+ * 27. The model batches tool calls, so a request is worth several file reads
56
+ * and a threshold described in "turns" is off by a factor of three. The
57
+ * number here is the unit the hook can actually count.
58
+ *
59
+ * 12 is chosen from the corpus rather than taste: the average run in the
60
+ * host's own store showed the model ~29,900 prompt tokens per request, so a
61
+ * dozen silent requests is roughly 350k tokens re-sent for a record that
62
+ * never moved. High enough that an agent reading five files in a row is not
63
+ * nagged.
64
+ */
65
+ export declare const DRIFT_NOTICE_AFTER_TURNS = 12;
42
66
  /** Options for {@link buildStateHint}. */
43
67
  export interface StateHintOptions {
44
68
  /** The state document this session has saved. */
@@ -50,7 +74,50 @@ export interface StateHintOptions {
50
74
  * hint names the merge step so the main session can fold its notes back.
51
75
  */
52
76
  scope?: string;
77
+ /**
78
+ * The fields a project `skill-spec.json` declares, when one is present.
79
+ *
80
+ * Notes mode does not ENFORCE a schema — §4.1 scopes the schema to a spec P,
81
+ * and notes mode has no P because it never formats an A.4 prompt. What it
82
+ * must not do is stay silent, and it was: a project shipped a schema, a model
83
+ * read it, ignored it, and wrote thirty files under a namespace it invented
84
+ * while the declared fields sat at their defaults. Nothing said so, and the
85
+ * state looked populated to a reader checking the wrong key.
86
+ *
87
+ * So the fields are stated, not enforced. A model that follows them writes
88
+ * the state where the spec says; one that does not has been told where the
89
+ * spec says, which is the difference between an override and an oversight.
90
+ */
91
+ declaredFields?: readonly string[];
92
+ /**
93
+ * True when the project is INITIALIZED — a state file exists for it.
94
+ *
95
+ * This is the whole difference between an optional side channel and a
96
+ * record, and the two must not be phrased the same way. Telling a model to
97
+ * "skip these tools when the work needs no memory" is correct advice for a
98
+ * scratch project and an invitation to walk away in a real one, where the
99
+ * user initialized skillstate precisely because the work does need it.
100
+ */
101
+ initialized?: boolean;
102
+ /**
103
+ * Turns taken since the state last changed. Set by the caller, which
104
+ * counts; see {@link DRIFT_NOTICE_AFTER_TURNS}.
105
+ */
106
+ turnsSinceWrite?: number;
53
107
  }
108
+ /**
109
+ * The notice shown once when the state has not moved for a long stretch.
110
+ *
111
+ * This is feedback, not an instruction, and the distinction is the whole
112
+ * design. It states a measured fact — this many turns, no write — and
113
+ * nothing about what the model ought to do. That keeps the v1 failure
114
+ * impossible (nothing here can displace the user's task) while still
115
+ * telling a drifting agent that the user initialized this for a reason.
116
+ *
117
+ * It fires once per silence, not every turn: a notice that repeats forever
118
+ * is wallpaper, and after the second copy nobody reads it.
119
+ */
120
+ export declare function driftNotice(turnsSinceWrite: number): string;
54
121
  /**
55
122
  * Build the system-prompt fragment.
56
123
  *
@@ -1 +1 @@
1
- {"version":3,"file":"system-hint.d.ts","sourceRoot":"","sources":["../src/system-hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,YAC3B,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,CACV,CAAC;AAEX;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAezE;AAED,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAwBhE"}
1
+ {"version":3,"file":"system-hint.d.ts","sourceRoot":"","sources":["../src/system-hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,YAC3B,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,CACV,CAAC;AAEX;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAczE;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAkDhE"}
@@ -38,23 +38,77 @@ export const ADVERTISED_TOOLS = [
38
38
  *
39
39
  * Small documents are inlined verbatim (the model sees what is already
40
40
  * saved without a tool round-trip). Documents past
41
- * {@link MAX_INLINE_STATE_CHARS} are summarized as a key list plus a byte
42
- * count and a pointer to `skillstate_read` — an unbounded state file must
43
- * not be able to grow the system prompt without limit.
41
+ * {@link MAX_INLINE_STATE_CHARS} are summarized as a key list plus a count and
42
+ * a pointer to `skillstate_read` — an unbounded state file must not be able to
43
+ * grow the system prompt without limit.
44
+ *
45
+ * The reported count is in the SAME UNIT as the limit, and it is labelled with
46
+ * that unit's name. The first version reported `bytes` next to a limit expressed
47
+ * in characters, which is this project's whole recurring mistake in one line: a
48
+ * state of 4,003 Cyrillic characters is 3,003 over the limit and 7,993 bytes, so
49
+ * the model reading the summary was handed two numbers in two units and a limit
50
+ * in a third. §4.3 is explicit that sizes here are raw string CHARS.
44
51
  */
45
52
  export function renderStateForHint(state) {
46
53
  const json = JSON.stringify(state, null, 2);
47
54
  if (json.length <= MAX_INLINE_STATE_CHARS)
48
55
  return json;
49
56
  const keys = Object.keys(state).sort();
50
- const bytes = Buffer.byteLength(json, 'utf-8');
51
57
  return `${JSON.stringify({
52
58
  _truncated: true,
53
- bytes,
59
+ chars: json.length,
54
60
  keys,
55
61
  hint: 'Saved state is large — call skillstate_read to load it.',
56
62
  }, null, 2)}`;
57
63
  }
64
+ /**
65
+ * How many MODEL REQUESTS may pass with the state untouched before the drift
66
+ * notice fires.
67
+ *
68
+ * **"Requests", not "turns".** Measured on the weakest model in the
69
+ * catalogue: 20 file reads took 6 requests, 40 reads took 12, 70 reads took
70
+ * 27. The model batches tool calls, so a request is worth several file reads
71
+ * and a threshold described in "turns" is off by a factor of three. The
72
+ * number here is the unit the hook can actually count.
73
+ *
74
+ * 12 is chosen from the corpus rather than taste: the average run in the
75
+ * host's own store showed the model ~29,900 prompt tokens per request, so a
76
+ * dozen silent requests is roughly 350k tokens re-sent for a record that
77
+ * never moved. High enough that an agent reading five files in a row is not
78
+ * nagged.
79
+ */
80
+ export const DRIFT_NOTICE_AFTER_TURNS = 12;
81
+ /**
82
+ * The line that says what the state IS, which depends on whether the project
83
+ * was initialized.
84
+ *
85
+ * Neither version is an imperative. Both are statements about the setup, and
86
+ * the difference is that one describes a record the user asked for and the
87
+ * other describes an optional convenience. A model told to skip the tools
88
+ * when it judges them unnecessary will eventually judge a long task
89
+ * unnecessary at exactly the wrong moment; a model told the state is the
90
+ * project's record has a fact to work with.
91
+ */
92
+ function purposeLine(initialized, statePath) {
93
+ return initialized
94
+ ? `This project has an execution state at \`${statePath}\`. It is the project's record: what has been established, decided, and left to do, kept across a context reset or compaction. The conversation is not that record, and it is not kept — the state file is.`
95
+ : `Notes for this project are saved at \`${statePath}\` and survive a context reset or compaction.`;
96
+ }
97
+ /**
98
+ * The notice shown once when the state has not moved for a long stretch.
99
+ *
100
+ * This is feedback, not an instruction, and the distinction is the whole
101
+ * design. It states a measured fact — this many turns, no write — and
102
+ * nothing about what the model ought to do. That keeps the v1 failure
103
+ * impossible (nothing here can displace the user's task) while still
104
+ * telling a drifting agent that the user initialized this for a reason.
105
+ *
106
+ * It fires once per silence, not every turn: a notice that repeats forever
107
+ * is wallpaper, and after the second copy nobody reads it.
108
+ */
109
+ export function driftNotice(turnsSinceWrite) {
110
+ return `Note: this state file has not changed across the last ${turnsSinceWrite} steps of work.`;
111
+ }
58
112
  /**
59
113
  * Build the system-prompt fragment.
60
114
  *
@@ -74,14 +128,39 @@ export function buildStateHint(options) {
74
128
  const mergeLine = scope === ''
75
129
  ? ''
76
130
  : '\nThis is a sub-agent session. When you finish, the main session folds your notes back with `skillstate_merge`; write them as if someone else will read them.';
77
- return [
131
+ const initialized = options.initialized === true;
132
+ const turns = options.turnsSinceWrite ?? 0;
133
+ const declared = options.declaredFields ?? [];
134
+ // Only speak when the state and the spec actually DISAGREE. A line naming
135
+ // the declared fields on every turn of every project would cost prompt budget
136
+ // to say something that is true, and the hint has a standing test that it
137
+ // must stay under a tenth of the conversation it rides along with. The case
138
+ // worth spending characters on is the one measured: a model that wrote
139
+ // everything under a namespace it invented while the declared fields sat at
140
+ // their defaults.
141
+ const undeclared = Object.keys(state).filter((k) => !declared.some((d) => d.startsWith(`${k} `)));
142
+ const schemaLine = declared.length === 0 || undeclared.length === 0
143
+ ? ''
144
+ : `\nThis project\'s \`skill-spec.json\` declares the state fields ${declared
145
+ .map((f) => `\`${f}\``)
146
+ .join(', ')}. The state currently holds ${undeclared
147
+ .slice(0, 6)
148
+ .map((k) => `\`${k}\``)
149
+ .join(', ')}, which the spec does not declare. Write declared fields at the TOP LEVEL under their own names; a later reader looking for \`${declared[0].split(' ')[0]}\` will not find it one level down.`;
150
+ const lines = [
78
151
  '<skillstate-project-notes>',
79
- `Notes for this project are saved at \`${statePath}\` and survive a context reset or compaction.`,
152
+ purposeLine(initialized, statePath),
80
153
  '',
81
154
  renderStateForHint(state),
82
- `${toolLine}${mergeLine}`,
83
- 'Use them only to carry facts across turns — plans already made, decisions already taken, file paths, and what is left to do. The notes are a side channel, not the task: keep doing what the user asked, and skip these tools entirely when the work needs no cross-turn memory.',
84
- '</skillstate-project-notes>',
85
- ].join('\n');
155
+ `${toolLine}${mergeLine}${schemaLine}`,
156
+ initialized
157
+ ? 'Record what you establish here as you go — plans made, decisions taken, file paths, values read, and what is left — so it is still here after a reset. The notes are not the task: keep doing what the user asked.'
158
+ : 'Use them only to carry facts across turns — plans already made, decisions already taken, file paths, and what is left to do. The notes are a side channel, not the task: keep doing what the user asked, and skip these tools entirely when the work needs no cross-turn memory.',
159
+ ];
160
+ if (initialized && turns >= DRIFT_NOTICE_AFTER_TURNS) {
161
+ lines.push('', driftNotice(turns));
162
+ }
163
+ lines.push('</skillstate-project-notes>');
164
+ return lines.join('\n');
86
165
  }
87
166
  //# sourceMappingURL=system-hint.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-hint.js","sourceRoot":"","sources":["../src/system-hint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,yEAAyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;CACV,CAAC;AAEX;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA8B;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5C,IAAI,IAAI,CAAC,MAAM,IAAI,sBAAsB;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,GAAG,IAAI,CAAC,SAAS,CACtB;QACE,UAAU,EAAE,IAAI;QAChB,KAAK;QACL,IAAI;QACJ,IAAI,EAAE,yDAAyD;KAChE,EACD,IAAI,EACJ,CAAC,CACF,EAAE,CAAC;AACN,CAAC;AAeD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAyB;IACtD,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE/C,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;IAC7F,MAAM,QAAQ,GAAG,YAAY,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAE9E,MAAM,SAAS,GACb,KAAK,KAAK,EAAE;QACV,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,+JAA+J,CAAC;IAEtK,OAAO;QACL,4BAA4B;QAC5B,yCAAyC,SAAS,+CAA+C;QACjG,EAAE;QACF,kBAAkB,CAAC,KAAK,CAAC;QACzB,GAAG,QAAQ,GAAG,SAAS,EAAE;QACzB,kRAAkR;QAClR,6BAA6B;KAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"system-hint.js","sourceRoot":"","sources":["../src/system-hint.ts"],"names":[],"mappings":"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"}