@skillstate/core 0.0.1 → 2.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 (88) hide show
  1. package/dist/atomic-write.d.ts +40 -0
  2. package/dist/atomic-write.d.ts.map +1 -0
  3. package/dist/atomic-write.js +97 -0
  4. package/dist/atomic-write.js.map +1 -0
  5. package/dist/clock.d.ts +27 -0
  6. package/dist/clock.d.ts.map +1 -0
  7. package/dist/clock.js +45 -0
  8. package/dist/clock.js.map +1 -0
  9. package/dist/config.d.ts +43 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +152 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/events.d.ts +59 -0
  14. package/dist/events.d.ts.map +1 -0
  15. package/dist/events.js +36 -0
  16. package/dist/events.js.map +1 -0
  17. package/dist/index.d.ts +20 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +28 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/instrumentation.d.ts +35 -0
  22. package/dist/instrumentation.d.ts.map +1 -0
  23. package/dist/instrumentation.js +41 -0
  24. package/dist/instrumentation.js.map +1 -0
  25. package/dist/logger.d.ts +34 -0
  26. package/dist/logger.d.ts.map +1 -0
  27. package/dist/logger.js +40 -0
  28. package/dist/logger.js.map +1 -0
  29. package/dist/migrations.d.ts +35 -0
  30. package/dist/migrations.d.ts.map +1 -0
  31. package/dist/migrations.js +26 -0
  32. package/dist/migrations.js.map +1 -0
  33. package/dist/prompt-transformer.d.ts +106 -0
  34. package/dist/prompt-transformer.d.ts.map +1 -0
  35. package/dist/prompt-transformer.js +265 -0
  36. package/dist/prompt-transformer.js.map +1 -0
  37. package/dist/provider.d.ts +62 -0
  38. package/dist/provider.d.ts.map +1 -0
  39. package/dist/provider.js +46 -0
  40. package/dist/provider.js.map +1 -0
  41. package/dist/redaction.d.ts +26 -0
  42. package/dist/redaction.d.ts.map +1 -0
  43. package/dist/redaction.js +38 -0
  44. package/dist/redaction.js.map +1 -0
  45. package/dist/resilience.d.ts +83 -0
  46. package/dist/resilience.d.ts.map +1 -0
  47. package/dist/resilience.js +171 -0
  48. package/dist/resilience.js.map +1 -0
  49. package/dist/runtime.d.ts +223 -0
  50. package/dist/runtime.d.ts.map +1 -0
  51. package/dist/runtime.js +362 -0
  52. package/dist/runtime.js.map +1 -0
  53. package/dist/schemas/index.d.ts +2 -0
  54. package/dist/schemas/index.d.ts.map +1 -0
  55. package/dist/schemas/index.js +3 -0
  56. package/dist/schemas/index.js.map +1 -0
  57. package/dist/schemas/intercode-ctf.d.ts +3 -0
  58. package/dist/schemas/intercode-ctf.d.ts.map +1 -0
  59. package/dist/schemas/intercode-ctf.js +53 -0
  60. package/dist/schemas/intercode-ctf.js.map +1 -0
  61. package/dist/shutdown.d.ts +20 -0
  62. package/dist/shutdown.d.ts.map +1 -0
  63. package/dist/shutdown.js +42 -0
  64. package/dist/shutdown.js.map +1 -0
  65. package/dist/state-manager.d.ts +23 -0
  66. package/dist/state-manager.d.ts.map +1 -0
  67. package/dist/state-manager.js +129 -0
  68. package/dist/state-manager.js.map +1 -0
  69. package/dist/state-store.d.ts +48 -0
  70. package/dist/state-store.d.ts.map +1 -0
  71. package/dist/state-store.js +101 -0
  72. package/dist/state-store.js.map +1 -0
  73. package/dist/token-tracker.d.ts +104 -0
  74. package/dist/token-tracker.d.ts.map +1 -0
  75. package/dist/token-tracker.js +204 -0
  76. package/dist/token-tracker.js.map +1 -0
  77. package/dist/types.d.ts +61 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +2 -0
  80. package/dist/types.js.map +1 -0
  81. package/dist/validate.d.ts +39 -0
  82. package/dist/validate.d.ts.map +1 -0
  83. package/dist/validate.js +180 -0
  84. package/dist/validate.js.map +1 -0
  85. package/package.json +23 -5
  86. package/LICENSE +0 -21
  87. package/README.md +0 -11
  88. package/index.js +0 -3
@@ -0,0 +1,40 @@
1
+ /** Default stale-lock TTL for {@link acquireLock} (30s). */
2
+ export declare const DEFAULT_LOCK_TTL_MS = 30000;
3
+ /**
4
+ * A `{ root, name }` pair resolving to a state file inside `root`.
5
+ * Used by the adapter overloads so codegen can accept user-supplied names
6
+ * without path-traversal risk.
7
+ */
8
+ export interface StatePathRef {
9
+ root: string;
10
+ name: string;
11
+ }
12
+ /**
13
+ * Write `content` to `filePath` atomically: temp sibling + fsync + rename.
14
+ * Parent directories are created. A crash can leave a `.tmp.<pid>.*`
15
+ * sibling behind, but never a truncated `filePath`.
16
+ */
17
+ export declare function atomicWriteFile(filePath: string, content: string | Uint8Array): Promise<void>;
18
+ /** Handle returned by {@link acquireLock}; removes the lockfile. */
19
+ export interface LockHandle {
20
+ release: () => void;
21
+ }
22
+ /**
23
+ * Create an exclusive lockfile at `lockPath` (`O_EXCL` via `wx`).
24
+ *
25
+ * - Fresh path → lock acquired, returns a handle.
26
+ * - Existing lock younger than `ttlMs` (default {@link DEFAULT_LOCK_TTL_MS})
27
+ * → held by a live process, returns `null`.
28
+ * - Existing lock older than `ttlMs` → stale (crashed holder), removed and
29
+ * re-acquired.
30
+ * - Unreadable/missing locations (stat fails) → `null`, never throws.
31
+ */
32
+ export declare function acquireLock(lockPath: string, ttlMs?: number): Promise<LockHandle | null>;
33
+ /**
34
+ * Resolve `name` inside `root` and return the absolute path. Throws when
35
+ * the result escapes `root` (`..` traversal or an absolute outsider).
36
+ * `name` values that stay inside (`sub/dir.json`, `a/../b.json`) and `root`
37
+ * itself (`.` / `''`) are returned as-is.
38
+ */
39
+ export declare function resolveStatePath(root: string, name: string): string;
40
+ //# sourceMappingURL=atomic-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"AAkBA,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,UAAU,GAC3B,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAUD;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAwB5B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnE"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @non-paper atomic persistence + lockfiles + safe path resolution.
3
+ *
4
+ * The paper has no I/O layer; the @non-paper adapters persist SKILL.state
5
+ * to disk. These helpers make that persistence crash-safe:
6
+ *
7
+ * - {@link atomicWriteFile}: write to a temp sibling, `fsync`, then
8
+ * `rename` — readers never observe a half-written state file;
9
+ * - {@link acquireLock}: exclusive lockfile creation (`wx`) with stale-TTL
10
+ * takeover for crashed holders;
11
+ * - {@link resolveStatePath}: confine a state file name inside a root
12
+ * directory — `..` escapes and absolute outsiders throw.
13
+ *
14
+ * Zero dependencies, Node >= 20, ESM.
15
+ */
16
+ import * as fs from 'node:fs';
17
+ import * as path from 'node:path';
18
+ /** Default stale-lock TTL for {@link acquireLock} (30s). */
19
+ export const DEFAULT_LOCK_TTL_MS = 30_000;
20
+ /**
21
+ * Write `content` to `filePath` atomically: temp sibling + fsync + rename.
22
+ * Parent directories are created. A crash can leave a `.tmp.<pid>.*`
23
+ * sibling behind, but never a truncated `filePath`.
24
+ */
25
+ export async function atomicWriteFile(filePath, content) {
26
+ const dir = path.dirname(filePath);
27
+ await fs.promises.mkdir(dir, { recursive: true });
28
+ const tmp = `${filePath}.tmp.${process.pid}.${Math.random().toString(36).slice(2)}`;
29
+ const handle = await fs.promises.open(tmp, 'w');
30
+ try {
31
+ await handle.writeFile(content);
32
+ await handle.sync();
33
+ }
34
+ finally {
35
+ await handle.close();
36
+ }
37
+ await fs.promises.rename(tmp, filePath);
38
+ }
39
+ function makeHandle(lockPath) {
40
+ return {
41
+ release: () => {
42
+ fs.rmSync(lockPath, { force: true });
43
+ },
44
+ };
45
+ }
46
+ /**
47
+ * Create an exclusive lockfile at `lockPath` (`O_EXCL` via `wx`).
48
+ *
49
+ * - Fresh path → lock acquired, returns a handle.
50
+ * - Existing lock younger than `ttlMs` (default {@link DEFAULT_LOCK_TTL_MS})
51
+ * → held by a live process, returns `null`.
52
+ * - Existing lock older than `ttlMs` → stale (crashed holder), removed and
53
+ * re-acquired.
54
+ * - Unreadable/missing locations (stat fails) → `null`, never throws.
55
+ */
56
+ export async function acquireLock(lockPath, ttlMs) {
57
+ const ttl = ttlMs ?? DEFAULT_LOCK_TTL_MS;
58
+ try {
59
+ await fs.promises.writeFile(lockPath, String(process.pid), {
60
+ flag: 'wx',
61
+ });
62
+ return makeHandle(lockPath);
63
+ }
64
+ catch {
65
+ let mtimeMs = 0;
66
+ try {
67
+ const stat = await fs.promises.stat(lockPath);
68
+ mtimeMs = stat.mtimeMs;
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ if (Date.now() - mtimeMs <= ttl) {
74
+ return null;
75
+ }
76
+ await fs.promises.unlink(lockPath);
77
+ await fs.promises.writeFile(lockPath, String(process.pid), {
78
+ flag: 'wx',
79
+ });
80
+ return makeHandle(lockPath);
81
+ }
82
+ }
83
+ /**
84
+ * Resolve `name` inside `root` and return the absolute path. Throws when
85
+ * the result escapes `root` (`..` traversal or an absolute outsider).
86
+ * `name` values that stay inside (`sub/dir.json`, `a/../b.json`) and `root`
87
+ * itself (`.` / `''`) are returned as-is.
88
+ */
89
+ export function resolveStatePath(root, name) {
90
+ const base = path.resolve(root);
91
+ const target = path.resolve(base, name);
92
+ if (target !== base && !target.startsWith(base + path.sep)) {
93
+ throw new Error(`Path traversal blocked: ${name}`);
94
+ }
95
+ return target;
96
+ }
97
+ //# sourceMappingURL=atomic-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAY1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,OAA4B;IAE5B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,GAAG,QAAQ,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IACD,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAOD,SAAS,UAAU,CAAC,QAAgB;IAClC,OAAO;QACL,OAAO,EAAE,GAAS,EAAE;YAClB,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,KAAc;IAEd,MAAM,GAAG,GAAG,KAAK,IAAI,mBAAmB,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACzD,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC9C,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,IAAI,GAAG,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACzD,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAY;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @non-paper time/identity source. `SystemClock` is the production
3
+ * implementation; tests inject frozen/counter clocks for determinism.
4
+ */
5
+ export interface Clock {
6
+ /** Current unix-epoch millis (like `Date.now()`). */
7
+ now(): number;
8
+ /** A unique id (like `crypto.randomUUID()`). */
9
+ uuid(): string;
10
+ }
11
+ /**
12
+ * @non-paper production clock: `Date.now()` + `crypto.randomUUID()`.
13
+ * This is exactly what the runtime does by default when no `clock?` is
14
+ * passed — injecting it explicitly changes nothing.
15
+ */
16
+ export declare class SystemClock implements Clock {
17
+ now(): number;
18
+ uuid(): string;
19
+ }
20
+ /**
21
+ * @non-paper deep copy: `structuredClone` when the value is cloneable,
22
+ * otherwise a JSON round-trip. Values JSON cannot represent (a bare
23
+ * function, `undefined` inside the fallback) are returned as-is rather
24
+ * than throwing — persistence must never crash on exotic state.
25
+ */
26
+ export declare function clone<T>(value: T): T;
27
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,qDAAqD;IACrD,GAAG,IAAI,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,IAAI,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,WAAY,YAAW,KAAK;IACvC,GAAG,IAAI,MAAM,CAEZ;IAED,IAAI,IAAI,MAAM,CAEb;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAOpC"}
package/dist/clock.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @non-paper injectable clock + deep clone helper.
3
+ *
4
+ * The paper core calls `Date.now()` directly for observation/step
5
+ * timestamps. This module adds an OPTIONAL seam for deterministic tests and
6
+ * crash-recovery replays: pass a `Clock` into `SkillStateRuntime`
7
+ * (`clock?` option) and timestamps come from it instead. Unset = the
8
+ * paper-exact `Date.now()` path, byte-for-byte unchanged behavior.
9
+ *
10
+ * `clone` is a `structuredClone` deep copy with a JSON fallback for values
11
+ * the structured-clone algorithm rejects (functions, symbols). Used by the
12
+ * @non-paper persistence layer so saved/loaded states never alias memory.
13
+ *
14
+ * Zero dependencies, Node >= 20, ESM.
15
+ */
16
+ import { randomUUID } from 'node:crypto';
17
+ /**
18
+ * @non-paper production clock: `Date.now()` + `crypto.randomUUID()`.
19
+ * This is exactly what the runtime does by default when no `clock?` is
20
+ * passed — injecting it explicitly changes nothing.
21
+ */
22
+ export class SystemClock {
23
+ now() {
24
+ return Date.now();
25
+ }
26
+ uuid() {
27
+ return randomUUID();
28
+ }
29
+ }
30
+ /**
31
+ * @non-paper deep copy: `structuredClone` when the value is cloneable,
32
+ * otherwise a JSON round-trip. Values JSON cannot represent (a bare
33
+ * function, `undefined` inside the fallback) are returned as-is rather
34
+ * than throwing — persistence must never crash on exotic state.
35
+ */
36
+ export function clone(value) {
37
+ try {
38
+ return structuredClone(value);
39
+ }
40
+ catch {
41
+ const json = JSON.stringify(value);
42
+ return json === undefined ? value : JSON.parse(json);
43
+ }
44
+ }
45
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAazC;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACtB,GAAG;QACD,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,IAAI;QACF,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAI,KAAQ;IAC/B,IAAI,CAAC;QACH,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAO,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,43 @@
1
+ /** @non-paper resolved runtime configuration for the CLI. */
2
+ export interface SkillStateConfig {
3
+ /** Path to the procedural-spec JSON (or `@intercode-ctf` builtin). */
4
+ specPath: string;
5
+ /** Path to the persisted state envelope. */
6
+ statePath: string;
7
+ /** Path to the tracker report JSON. */
8
+ reportPath: string;
9
+ /** Default cap for `run()` steps. */
10
+ maxSteps: number;
11
+ /** Retries after the first failed validation attempt (§7). */
12
+ maxValidationRetries: number;
13
+ /** Per-call transport deadline in ms (unset = no timeout layer). */
14
+ timeoutMs?: number;
15
+ /** Cumulative char cap for `run()` (unset = no cap). */
16
+ maxChars?: number;
17
+ /** Tracker platform label. */
18
+ platform: 'claude' | 'opencode' | 'generic';
19
+ /** Tracker session name. */
20
+ sessionName: string;
21
+ }
22
+ /** @non-paper config file name read from `cwd` by {@link loadConfig}. */
23
+ export declare const CONFIG_FILE_NAME = "skillstate.json";
24
+ /** @non-paper defaults used when neither file nor env sets a value. */
25
+ export declare function defaultConfig(): SkillStateConfig;
26
+ /**
27
+ * @non-paper merge a parsed config object with `SKILLSTATE_*` env vars
28
+ * over {@link defaultConfig} (env wins). Unparseable values are ignored.
29
+ * Never throws.
30
+ */
31
+ export declare function mergeConfig(file: unknown): SkillStateConfig;
32
+ /**
33
+ * @non-paper load `skillstate.json` from `cwd` and overlay `SKILLSTATE_*`
34
+ * env vars (env wins). Never throws: a missing or corrupt file behaves
35
+ * like an empty one, and unparseable env values are ignored.
36
+ *
37
+ * Env map: `SKILLSTATE_SPEC_PATH`, `SKILLSTATE_STATE_PATH`,
38
+ * `SKILLSTATE_REPORT_PATH`, `SKILLSTATE_MAX_STEPS`,
39
+ * `SKILLSTATE_MAX_VALIDATION_RETRIES`, `SKILLSTATE_TIMEOUT_MS`,
40
+ * `SKILLSTATE_MAX_CHARS`, `SKILLSTATE_PLATFORM`, `SKILLSTATE_SESSION_NAME`.
41
+ */
42
+ export declare function loadConfig(cwd: string): SkillStateConfig;
43
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC5C,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAElD,uEAAuE;AACvE,wBAAgB,aAAa,IAAI,gBAAgB,CAUhD;AA4CD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,CAwE3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAExD"}
package/dist/config.js ADDED
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @non-paper file + env configuration (Wave 4 DX).
3
+ *
4
+ * The paper has no config file; this module adds an OPTIONAL, additive
5
+ * seam so the CLI can run without flags:
6
+ *
7
+ * - `skillstate.json` in `cwd` holds a partial `SkillStateConfig`;
8
+ * - `SKILLSTATE_*` environment variables override the file (env wins);
9
+ * - missing/corrupt files resolve to defaults (fresh start), never throw.
10
+ *
11
+ * Zero dependencies, Node >= 20, ESM.
12
+ */
13
+ import * as fs from 'node:fs';
14
+ import * as path from 'node:path';
15
+ /** @non-paper config file name read from `cwd` by {@link loadConfig}. */
16
+ export const CONFIG_FILE_NAME = 'skillstate.json';
17
+ /** @non-paper defaults used when neither file nor env sets a value. */
18
+ export function defaultConfig() {
19
+ return {
20
+ specPath: './skill-spec.json',
21
+ statePath: './.skillstate.json',
22
+ reportPath: './skillstate-report.json',
23
+ maxSteps: 100,
24
+ maxValidationRetries: 2,
25
+ platform: 'generic',
26
+ sessionName: 'skillstate',
27
+ };
28
+ }
29
+ function isRecord(value) {
30
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
31
+ }
32
+ function asNonEmptyString(value) {
33
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
34
+ }
35
+ function asNonNegativeInt(value) {
36
+ if (typeof value === 'number' && Number.isInteger(value) && value >= 0) {
37
+ return value;
38
+ }
39
+ if (typeof value === 'string' && value.trim() !== '') {
40
+ const parsed = Number(value);
41
+ if (Number.isInteger(parsed) && parsed >= 0) {
42
+ return parsed;
43
+ }
44
+ }
45
+ return undefined;
46
+ }
47
+ function asPlatform(value) {
48
+ return value === 'claude' || value === 'opencode' || value === 'generic'
49
+ ? value
50
+ : undefined;
51
+ }
52
+ function readConfigFile(cwd) {
53
+ let raw;
54
+ try {
55
+ raw = fs.readFileSync(path.join(cwd, CONFIG_FILE_NAME), 'utf-8');
56
+ }
57
+ catch {
58
+ return {};
59
+ }
60
+ try {
61
+ const parsed = JSON.parse(raw);
62
+ return isRecord(parsed) ? parsed : {};
63
+ }
64
+ catch {
65
+ return {};
66
+ }
67
+ }
68
+ /**
69
+ * @non-paper merge a parsed config object with `SKILLSTATE_*` env vars
70
+ * over {@link defaultConfig} (env wins). Unparseable values are ignored.
71
+ * Never throws.
72
+ */
73
+ export function mergeConfig(file) {
74
+ const base = defaultConfig();
75
+ const obj = isRecord(file) ? file : {};
76
+ const env = process.env;
77
+ const fileSpec = asNonEmptyString(obj['specPath']);
78
+ const fileState = asNonEmptyString(obj['statePath']);
79
+ const fileReport = asNonEmptyString(obj['reportPath']);
80
+ const fileSteps = asNonNegativeInt(obj['maxSteps']);
81
+ const fileRetries = asNonNegativeInt(obj['maxValidationRetries']);
82
+ const fileTimeout = asNonNegativeInt(obj['timeoutMs']);
83
+ const fileMaxChars = asNonNegativeInt((obj['maxChars'] ?? obj['tokenBudgetChars']));
84
+ const filePlatform = asPlatform(obj['platform']);
85
+ const fileSession = asNonEmptyString(obj['sessionName']);
86
+ const merged = {
87
+ specPath: fileSpec ?? base.specPath,
88
+ statePath: fileState ?? base.statePath,
89
+ reportPath: fileReport ?? base.reportPath,
90
+ maxSteps: fileSteps ?? base.maxSteps,
91
+ maxValidationRetries: fileRetries ?? base.maxValidationRetries,
92
+ platform: filePlatform ?? base.platform,
93
+ sessionName: fileSession ?? base.sessionName,
94
+ };
95
+ if (fileTimeout !== undefined) {
96
+ merged.timeoutMs = fileTimeout;
97
+ }
98
+ if (fileMaxChars !== undefined) {
99
+ merged.maxChars = fileMaxChars;
100
+ }
101
+ const envSpec = asNonEmptyString(env['SKILLSTATE_SPEC_PATH']);
102
+ const envState = asNonEmptyString(env['SKILLSTATE_STATE_PATH']);
103
+ const envReport = asNonEmptyString(env['SKILLSTATE_REPORT_PATH']);
104
+ const envSteps = asNonNegativeInt(env['SKILLSTATE_MAX_STEPS']);
105
+ const envRetries = asNonNegativeInt(env['SKILLSTATE_MAX_VALIDATION_RETRIES']);
106
+ const envTimeout = asNonNegativeInt(env['SKILLSTATE_TIMEOUT_MS']);
107
+ const envMaxChars = asNonNegativeInt(env['SKILLSTATE_MAX_CHARS']);
108
+ const envPlatform = asPlatform(env['SKILLSTATE_PLATFORM']);
109
+ const envSession = asNonEmptyString(env['SKILLSTATE_SESSION_NAME']);
110
+ if (envSpec !== undefined) {
111
+ merged.specPath = envSpec;
112
+ }
113
+ if (envState !== undefined) {
114
+ merged.statePath = envState;
115
+ }
116
+ if (envReport !== undefined) {
117
+ merged.reportPath = envReport;
118
+ }
119
+ if (envSteps !== undefined) {
120
+ merged.maxSteps = envSteps;
121
+ }
122
+ if (envRetries !== undefined) {
123
+ merged.maxValidationRetries = envRetries;
124
+ }
125
+ if (envTimeout !== undefined) {
126
+ merged.timeoutMs = envTimeout;
127
+ }
128
+ if (envMaxChars !== undefined) {
129
+ merged.maxChars = envMaxChars;
130
+ }
131
+ if (envPlatform !== undefined) {
132
+ merged.platform = envPlatform;
133
+ }
134
+ if (envSession !== undefined) {
135
+ merged.sessionName = envSession;
136
+ }
137
+ return merged;
138
+ }
139
+ /**
140
+ * @non-paper load `skillstate.json` from `cwd` and overlay `SKILLSTATE_*`
141
+ * env vars (env wins). Never throws: a missing or corrupt file behaves
142
+ * like an empty one, and unparseable env values are ignored.
143
+ *
144
+ * Env map: `SKILLSTATE_SPEC_PATH`, `SKILLSTATE_STATE_PATH`,
145
+ * `SKILLSTATE_REPORT_PATH`, `SKILLSTATE_MAX_STEPS`,
146
+ * `SKILLSTATE_MAX_VALIDATION_RETRIES`, `SKILLSTATE_TIMEOUT_MS`,
147
+ * `SKILLSTATE_MAX_CHARS`, `SKILLSTATE_PLATFORM`, `SKILLSTATE_SESSION_NAME`.
148
+ */
149
+ export function loadConfig(cwd) {
150
+ return mergeConfig(readConfigFile(cwd));
151
+ }
152
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAwBlC,yEAAyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAElD,uEAAuE;AACvE,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,QAAQ,EAAE,mBAAmB;QAC7B,SAAS,EAAE,oBAAoB;QAC/B,UAAU,EAAE,0BAA0B;QACtC,QAAQ,EAAE,GAAG;QACb,oBAAoB,EAAE,CAAC;QACvB,QAAQ,EAAE,SAAS;QACnB,WAAW,EAAE,YAAY;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,SAAS;QACtE,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAExB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,gBAAgB,CACnC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAY,CACxD,CAAC;IACF,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IAEzD,MAAM,MAAM,GAAqB;QAC/B,QAAQ,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ;QACnC,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS;QACtC,UAAU,EAAE,UAAU,IAAI,IAAI,CAAC,UAAU;QACzC,QAAQ,EAAE,SAAS,IAAI,IAAI,CAAC,QAAQ;QACpC,oBAAoB,EAAE,WAAW,IAAI,IAAI,CAAC,oBAAoB;QAC9D,QAAQ,EAAE,YAAY,IAAI,IAAI,CAAC,QAAQ;QACvC,WAAW,EAAE,WAAW,IAAI,IAAI,CAAC,WAAW;KAC7C,CAAC;IACF,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,SAAS,GAAG,WAAW,CAAC;IACjC,CAAC;IACD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,CAAC,QAAQ,GAAG,YAAY,CAAC;IACjC,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAEpE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC5B,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC9B,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IAChC,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,oBAAoB,GAAG,UAAU,CAAC;IAC3C,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC;IAChC,CAAC;IACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,QAAQ,GAAG,WAAW,CAAC;IAChC,CAAC;IACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,QAAQ,GAAG,WAAW,CAAC;IAChC,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;IAClC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @non-paper typed runtime event emitter + shared singleton.
3
+ *
4
+ * The paper defines no observability; Algorithm 1 just runs. This module
5
+ * adds an OPTIONAL, additive event seam the runtime emits into ONLY when a
6
+ * caller passes `events?` (unset = zero overhead, zero behavior change):
7
+ *
8
+ * - `step:start` — before the LLM call (`{ step, observation }`);
9
+ * - `step:end` — after a completed step (`{ step, action, invalidated }`);
10
+ * - `step:error` — validation-exhausted or transport-thrown steps
11
+ * (`{ step, error }`);
12
+ * - `budget:exceeded` — `run()` char-budget trip
13
+ * (`{ step, totalChars, maxChars }`).
14
+ *
15
+ * Zero dependencies, Node >= 20, ESM. Deliberately NOT `node:events`:
16
+ * a 30-line typed emitter keeps payloads type-safe without any import.
17
+ */
18
+ import type { Observation } from './types.js';
19
+ /** @non-paper runtime event names. */
20
+ export type RuntimeEventName = 'step:start' | 'step:end' | 'step:error' | 'budget:exceeded';
21
+ /** @non-paper payloads per runtime event. */
22
+ export interface RuntimeEventPayloads {
23
+ 'step:start': {
24
+ step: number;
25
+ observation: Observation;
26
+ };
27
+ 'step:end': {
28
+ step: number;
29
+ action: string;
30
+ invalidated: boolean;
31
+ };
32
+ 'step:error': {
33
+ step: number;
34
+ error: string;
35
+ };
36
+ 'budget:exceeded': {
37
+ step: number;
38
+ totalChars: number;
39
+ maxChars: number;
40
+ };
41
+ }
42
+ /** @non-paper listener for one runtime event. */
43
+ export type RuntimeEventListener<E extends RuntimeEventName> = (payload: RuntimeEventPayloads[E]) => void;
44
+ /**
45
+ * @non-paper minimal typed emitter. `on` returns an unsubscribe closure;
46
+ * `emit` to an event with no listeners is a no-op (never throws).
47
+ */
48
+ export declare class RuntimeEventEmitter {
49
+ private readonly listeners;
50
+ /** Subscribe; returns an unsubscribe function. */
51
+ on<E extends RuntimeEventName>(event: E, listener: RuntimeEventListener<E>): () => void;
52
+ /** Unsubscribe (no-op when absent — never throws). */
53
+ off<E extends RuntimeEventName>(event: E, listener: RuntimeEventListener<E>): void;
54
+ /** Deliver `payload` to a snapshot of the current listeners. */
55
+ emit<E extends RuntimeEventName>(event: E, payload: RuntimeEventPayloads[E]): void;
56
+ }
57
+ /** @non-paper process-wide runtime event bus (use explicitly, never magic). */
58
+ export declare const runtimeEvents: RuntimeEventEmitter;
59
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,sCAAsC;AACtC,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,iBAAiB,CAAC;AAEtB,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,CAAC;IACzD,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;IACnE,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAED,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAC7D,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAC7B,IAAI,CAAC;AAEV;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGtB;IAEJ,kDAAkD;IAClD,EAAE,CAAC,CAAC,SAAS,gBAAgB,EAC3B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC,MAAM,IAAI,CAUZ;IAED,sDAAsD;IACtD,GAAG,CAAC,CAAC,SAAS,gBAAgB,EAC5B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC,IAAI,CAEN;IAED,gEAAgE;IAChE,IAAI,CAAC,CAAC,SAAS,gBAAgB,EAC7B,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,IAAI,CAQN;CACF;AAED,+EAA+E;AAC/E,eAAO,MAAM,aAAa,qBAA4B,CAAC"}
package/dist/events.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @non-paper minimal typed emitter. `on` returns an unsubscribe closure;
3
+ * `emit` to an event with no listeners is a no-op (never throws).
4
+ */
5
+ export class RuntimeEventEmitter {
6
+ listeners = new Map();
7
+ /** Subscribe; returns an unsubscribe function. */
8
+ on(event, listener) {
9
+ let set = this.listeners.get(event);
10
+ if (set === undefined) {
11
+ set = new Set();
12
+ this.listeners.set(event, set);
13
+ }
14
+ set.add(listener);
15
+ return () => {
16
+ this.off(event, listener);
17
+ };
18
+ }
19
+ /** Unsubscribe (no-op when absent — never throws). */
20
+ off(event, listener) {
21
+ this.listeners.get(event)?.delete(listener);
22
+ }
23
+ /** Deliver `payload` to a snapshot of the current listeners. */
24
+ emit(event, payload) {
25
+ const set = this.listeners.get(event);
26
+ if (set === undefined) {
27
+ return;
28
+ }
29
+ for (const listener of [...set]) {
30
+ listener(payload);
31
+ }
32
+ }
33
+ }
34
+ /** @non-paper process-wide runtime event bus (use explicitly, never magic). */
35
+ export const runtimeEvents = new RuntimeEventEmitter();
36
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAuCA;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACb,SAAS,GAAG,IAAI,GAAG,EAGjC,CAAC;IAEJ,kDAAkD;IAClD,EAAE,CACA,KAAQ,EACR,QAAiC;QAEjC,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAAsC,CAAC,CAAC;QAChD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,GAAG,CACD,KAAQ,EACR,QAAiC;QAEjC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAsC,CAAC,CAAC;IAC5E,CAAC;IAED,gEAAgE;IAChE,IAAI,CACF,KAAQ,EACR,OAAgC;QAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAC/B,QAAoC,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,20 @@
1
+ export * from './types.js';
2
+ export * from './state-manager.js';
3
+ export * from './prompt-transformer.js';
4
+ export * from './token-tracker.js';
5
+ export * from './runtime.js';
6
+ export * from './instrumentation.js';
7
+ export * from './resilience.js';
8
+ export * from './validate.js';
9
+ export * from './redaction.js';
10
+ export * from './atomic-write.js';
11
+ export * from './clock.js';
12
+ export * from './migrations.js';
13
+ export * from './state-store.js';
14
+ export * from './events.js';
15
+ export * from './logger.js';
16
+ export * from './provider.js';
17
+ export * from './config.js';
18
+ export * from './shutdown.js';
19
+ export * from './schemas/index.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AAErC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAElC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAE5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ // Public API of the skillstate core.
2
+ //
3
+ // Paper-exact baseline (§3-§4): types, state-manager, prompt-transformer,
4
+ // token-tracker, runtime. `instrumentation` is an OPTIONAL @non-paper helper
5
+ // module (heuristics/estimates, not from the paper).
6
+ export * from './types.js';
7
+ export * from './state-manager.js';
8
+ export * from './prompt-transformer.js';
9
+ export * from './token-tracker.js';
10
+ export * from './runtime.js';
11
+ export * from './instrumentation.js';
12
+ // @non-paper Wave-2 reliability/security helpers (additive, opt-in).
13
+ export * from './resilience.js';
14
+ export * from './validate.js';
15
+ export * from './redaction.js';
16
+ export * from './atomic-write.js';
17
+ // @non-paper Wave-3 observability/persistence helpers (additive, opt-in).
18
+ export * from './clock.js';
19
+ export * from './migrations.js';
20
+ export * from './state-store.js';
21
+ export * from './events.js';
22
+ export * from './logger.js';
23
+ // @non-paper Wave-4 DX helpers (additive, opt-in).
24
+ export * from './provider.js';
25
+ export * from './config.js';
26
+ export * from './shutdown.js';
27
+ export * from './schemas/index.js';
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,qDAAqD;AACrD,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,qEAAqE;AACrE,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,0EAA0E;AAC1E,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,mDAAmD;AACnD,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @non-paper — OPTIONAL instrumentation helpers, NOT part of the paper.
3
+ *
4
+ * Nothing in this module appears in arXiv 2608.26263v3. The paper's §4.3
5
+ * methodology measures prompts in raw string chars (Average Prompt Size =
6
+ * mean char length per call, Total Token Cost = cumulative burn) and reports
7
+ * no tokenizer heuristic and no dollar pricing. Import from here only when
8
+ * you explicitly want a rough, clearly-labelled estimate outside the
9
+ * paper-exact baseline in `./token-tracker.js`.
10
+ */
11
+ /** Pluggable character-to-token estimator (heuristic, never exact). */
12
+ export interface TokenCounter {
13
+ /** Roughly estimate the tokens in `text`. */
14
+ count(text: string): number;
15
+ }
16
+ /**
17
+ * @non-paper legacy heuristic: 1 token ≈ 4 chars, rounded up.
18
+ *
19
+ * Kept for backward compatibility of ad-hoc estimates only. Do NOT use it
20
+ * for paper §4.3 metrics — those are measured in chars (see `TokenTracker`).
21
+ * Empty text costs zero.
22
+ */
23
+ export declare class CharDiv4Counter implements TokenCounter {
24
+ count(text: string): number;
25
+ }
26
+ /**
27
+ * @non-paper estimated dollar savings of state prompts vs the conversation
28
+ * baseline, from measured char counts (e.g. `TokenTracker.compareWithBaseline`).
29
+ *
30
+ * This is a back-of-the-envelope estimate, NOT a paper metric: the paper
31
+ * reports no pricing. `usdPerMillionChars` defaults to 3 (a placeholder
32
+ * rate, not a paper figure). Returns 0 when there is nothing to save.
33
+ */
34
+ export declare function estimateCostSavings(conversationChars: number, stateChars: number, usdPerMillionChars?: number): number;
35
+ //# sourceMappingURL=instrumentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1B;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,kBAAkB,SAAI,GACrB,MAAM,CAMR"}