@skillstate/core 2.1.0 → 2.2.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.
@@ -30,6 +30,23 @@ export interface LockHandle {
30
30
  * - Unreadable/missing locations (stat fails) → `null`, never throws.
31
31
  */
32
32
  export declare function acquireLock(lockPath: string, ttlMs?: number): Promise<LockHandle | null>;
33
+ /** Default retry count for {@link withStateLock} (~10s at 50ms per try). */
34
+ export declare const DEFAULT_LOCK_RETRIES = 200;
35
+ /** Delay between {@link withStateLock} acquisition retries (50ms). */
36
+ export declare const LOCK_RETRY_DELAY_MS = 50;
37
+ /**
38
+ * Run `fn` while holding an exclusive cross-process lock on
39
+ * `statePath + '.lock'` ({@link acquireLock}, default TTL). Waits for a
40
+ * live holder: up to `retries` (default {@link DEFAULT_LOCK_RETRIES})
41
+ * attempts {@link LOCK_RETRY_DELAY_MS} apart, so 2-3 concurrent agent
42
+ * processes serialize instead of failing. The lock is acquired before
43
+ * `fn` starts and ALWAYS released in the `finally` block; the `fn`
44
+ * result (or failure) propagates after the release. Throws when the lock
45
+ * cannot be acquired within the retry budget. This is the async
46
+ * hot-path serialization primitive for the MCP server writes
47
+ * (patch/rollback/checkpoint/merge) and the host adapters.
48
+ */
49
+ export declare function withStateLock<T>(statePath: string, fn: () => Promise<T> | T, ttlMs?: number, retries?: number): Promise<T>;
33
50
  /**
34
51
  * Resolve `name` inside `root` and return the absolute path. Throws when
35
52
  * the result escapes `root` (`..` traversal or an absolute outsider).
@@ -1 +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"}
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,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACxB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,GAAE,MAA6B,GACrC,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAeD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnE"}
@@ -80,6 +80,53 @@ export async function acquireLock(lockPath, ttlMs) {
80
80
  return makeHandle(lockPath);
81
81
  }
82
82
  }
83
+ /** Default retry count for {@link withStateLock} (~10s at 50ms per try). */
84
+ export const DEFAULT_LOCK_RETRIES = 200;
85
+ /** Delay between {@link withStateLock} acquisition retries (50ms). */
86
+ export const LOCK_RETRY_DELAY_MS = 50;
87
+ /**
88
+ * Run `fn` while holding an exclusive cross-process lock on
89
+ * `statePath + '.lock'` ({@link acquireLock}, default TTL). Waits for a
90
+ * live holder: up to `retries` (default {@link DEFAULT_LOCK_RETRIES})
91
+ * attempts {@link LOCK_RETRY_DELAY_MS} apart, so 2-3 concurrent agent
92
+ * processes serialize instead of failing. The lock is acquired before
93
+ * `fn` starts and ALWAYS released in the `finally` block; the `fn`
94
+ * result (or failure) propagates after the release. Throws when the lock
95
+ * cannot be acquired within the retry budget. This is the async
96
+ * hot-path serialization primitive for the MCP server writes
97
+ * (patch/rollback/checkpoint/merge) and the host adapters.
98
+ */
99
+ export async function withStateLock(statePath, fn, ttlMs, retries = DEFAULT_LOCK_RETRIES) {
100
+ const lockPath = `${statePath}.lock`;
101
+ await fs.promises.mkdir(path.dirname(lockPath), { recursive: true });
102
+ let handle = await acquireLockSafely(lockPath, ttlMs);
103
+ for (let attempt = 0; handle === null && attempt < retries; attempt++) {
104
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS));
105
+ handle = await acquireLockSafely(lockPath, ttlMs);
106
+ }
107
+ if (!handle) {
108
+ throw new Error(`skillstate: could not acquire the state lock: ${lockPath}`);
109
+ }
110
+ try {
111
+ return await fn();
112
+ }
113
+ finally {
114
+ handle.release();
115
+ }
116
+ }
117
+ /**
118
+ * {@link acquireLock} with the stale-takeover race collapsed to `null`:
119
+ * between `unlink` and the re-`writeFile`, another waiter may recreate
120
+ * the lockfile (`EEXIST`) — that loser must retry, never crash.
121
+ */
122
+ async function acquireLockSafely(lockPath, ttlMs) {
123
+ try {
124
+ return await acquireLock(lockPath, ttlMs);
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
83
130
  /**
84
131
  * Resolve `name` inside `root` and return the absolute path. Throws when
85
132
  * the result escapes `root` (`..` traversal or an absolute outsider).
@@ -1 +1 @@
1
- {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../src/atomic-write.ts"],"names":[],"mappings":"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"}
@@ -26,12 +26,30 @@
26
26
  */
27
27
  /** True for plain (non-null, non-array) objects. */
28
28
  export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
29
+ /**
30
+ * Agent-id sanitization: keep `[A-Za-z0-9_-]` runs, collapse everything
31
+ * else into single `-`, trim edge dashes, cap at 64 chars. Garbage input
32
+ * sanitizes to `''` — callers treat that as "no agent" (the main state).
33
+ */
34
+ export declare function sanitizeAgentId(agentId: string): string;
35
+ /**
36
+ * Agent id from a host session id (Claude Code / Codex hook stdin carry
37
+ * `session_id`; opencode hooks carry `sessionID`): the short prefix — the
38
+ * first 8 characters — keeps agent directories bounded while remaining
39
+ * unique enough per session. Non-string/empty input yields `''`.
40
+ */
41
+ export declare function resolveAgentIdFromSession(sessionId: unknown): string;
29
42
  /**
30
43
  * Resolve the per-project state file for a working directory — the pure,
31
44
  * dependency-free mirror of `resolveHostStateForCwd`
32
45
  * (`<cwd>/.skillstate/skillstate.json`; the global bucket
33
46
  * `<home>/.skillstate/global/skillstate.json` when cwd equals home).
34
47
  *
48
+ * AGENT-SCOPED STATE: a non-empty `agentId` (sanitized via
49
+ * {@link sanitizeAgentId}) scopes the file under an isolated
50
+ * `agents/<agentId>/` copy — parallel sub-agents (hook sessions) never
51
+ * share the main state file.
52
+ *
35
53
  * String arithmetic only (POSIX): absolute paths are normalized like
36
54
  * `path.resolve` (empty/`.` segments dropped, `..` popped, trailing
37
55
  * slashes trimmed); relative inputs stay relative because there is no
@@ -40,7 +58,7 @@ export declare function isPlainObject(value: unknown): value is Record<string, u
40
58
  * that. `home` must be provided to detect the global bucket; when it is
41
59
  * omitted the project path is returned.
42
60
  */
43
- export declare function resolveStatePathForCwd(cwd: string, home?: string): string;
61
+ export declare function resolveStatePathForCwd(cwd: string, home?: string, agentId?: string): string;
44
62
  /**
45
63
  * Read the state file through the injected `readFile` (real `fs` in
46
64
  * generated scripts, mocks in tests). The on-disk envelope is
@@ -56,6 +74,39 @@ export declare function readStateEnvelope(statePath: string, readFile: (p: strin
56
74
  * plugin) or surface a `systemMessage` (PostToolUse hooks).
57
75
  */
58
76
  export declare function saveStateEnvelope(statePath: string, state: object, writeFile: (p: string, data: string) => void): void;
77
+ /**
78
+ * Minimal `node:fs` surface {@link lockStateWrite} needs. Injections keep
79
+ * the hook-runtime pure: real `node:fs` in generated scripts and the
80
+ * plugin, mocks in tests (the established hook-runtime dependency style).
81
+ */
82
+ export interface StateLockFs {
83
+ openSync(path: string, flags: string): number;
84
+ closeSync(fd: number): void;
85
+ statSync(path: string): {
86
+ mtimeMs: number;
87
+ };
88
+ unlinkSync(path: string): void;
89
+ mkdirSync(path: string): unknown;
90
+ }
91
+ /**
92
+ * Block the current thread for `ms` milliseconds — the sync sleep for the
93
+ * {@link lockStateWrite} retry loop. Prefers `Atomics.wait` on a shared
94
+ * integer (a real sleep); falls back to a busy spin when the primitive is
95
+ * unavailable. Plain JS — survives `fn.toString()` inlining.
96
+ */
97
+ export declare function sleepSync(ms: number): void;
98
+ /**
99
+ * Cross-process state lock for the SELF-CONTAINED hook scripts (sync,
100
+ * no imports): exclusive `O_EXCL` lockfile at `statePath + '.lock'`,
101
+ * stale-TTL takeover (10s — crashed hook holders), and a retry loop
102
+ * (50ms × 40 ≈ 2s) while a live holder runs its critical section. The
103
+ * injected `fs` (real `node:fs` in generated scripts and the plugin,
104
+ * mocks in tests) performs the lockfile I/O and creates the parent
105
+ * directory. The injected `fn` runs inside the lock; the lockfile is
106
+ * ALWAYS removed in the `finally` block. `fn` failures propagate AFTER
107
+ * the release; lock exhaustion throws.
108
+ */
109
+ export declare function lockStateWrite(statePath: string, fs: StateLockFs, fn: () => unknown): unknown;
59
110
  /**
60
111
  * Paper ⊕ merge: `null` deletes a key, nested plain objects merge
61
112
  * recursively, everything else replaces. Pure — neither `state` nor
@@ -92,6 +143,15 @@ export declare function findFencedPatch(text: string): PatchLookup;
92
143
  * holding a non-object is an invalid attempt.
93
144
  */
94
145
  export declare function findRawPatch(text: string): PatchLookup;
146
+ /**
147
+ * Read the session-meta sidecar's status (`<dir>/.session-meta.json`,
148
+ * sibling of the state file). Plain JS — inlined into the generated
149
+ * SessionStart hooks via `fn.toString()`. Returns the `status` string for
150
+ * a readable object-shaped sidecar, else `null` (missing/corrupt/other
151
+ * shape — no lifecycle marker). The `readFile` dependency is injected at
152
+ * the call site (real `node:fs` in hooks, mocks in tests).
153
+ */
154
+ export declare function readSessionMetaStatus(metaPath: string, readFile: (p: string) => string): string | null;
95
155
  /**
96
156
  * Assemble the CJS snippet embedded into every generated hook script:
97
157
  * all sibling functions of this module, source-verbatim via
@@ -1 +1 @@
1
- {"version":3,"file":"hook-runtime.d.ts","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBzE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAC9B,OAAO,CAaT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAC3C,IAAI,CAEN;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAQ1D;AAED,gFAAgF;AAChF,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAClC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAczD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA4BtD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C"}
1
+ {"version":3,"file":"hook-runtime.d.ts","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAGpE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CA+B3F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAC9B,OAAO,CAaT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAC3C,IAAI,CAEN;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAW1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CA8C7F;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAQ1D;AAED,gFAAgF;AAChF,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAClC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAczD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA4BtD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAkB3C"}
@@ -28,12 +28,39 @@
28
28
  export function isPlainObject(value) {
29
29
  return typeof value === 'object' && value !== null && !Array.isArray(value);
30
30
  }
31
+ /**
32
+ * Agent-id sanitization: keep `[A-Za-z0-9_-]` runs, collapse everything
33
+ * else into single `-`, trim edge dashes, cap at 64 chars. Garbage input
34
+ * sanitizes to `''` — callers treat that as "no agent" (the main state).
35
+ */
36
+ export function sanitizeAgentId(agentId) {
37
+ return agentId
38
+ .replace(/[^A-Za-z0-9_-]+/g, '-')
39
+ .replace(/^-+|-+$/g, '')
40
+ .slice(0, 64);
41
+ }
42
+ /**
43
+ * Agent id from a host session id (Claude Code / Codex hook stdin carry
44
+ * `session_id`; opencode hooks carry `sessionID`): the short prefix — the
45
+ * first 8 characters — keeps agent directories bounded while remaining
46
+ * unique enough per session. Non-string/empty input yields `''`.
47
+ */
48
+ export function resolveAgentIdFromSession(sessionId) {
49
+ if (typeof sessionId !== 'string' || sessionId.length === 0)
50
+ return '';
51
+ return sessionId.slice(0, 8);
52
+ }
31
53
  /**
32
54
  * Resolve the per-project state file for a working directory — the pure,
33
55
  * dependency-free mirror of `resolveHostStateForCwd`
34
56
  * (`<cwd>/.skillstate/skillstate.json`; the global bucket
35
57
  * `<home>/.skillstate/global/skillstate.json` when cwd equals home).
36
58
  *
59
+ * AGENT-SCOPED STATE: a non-empty `agentId` (sanitized via
60
+ * {@link sanitizeAgentId}) scopes the file under an isolated
61
+ * `agents/<agentId>/` copy — parallel sub-agents (hook sessions) never
62
+ * share the main state file.
63
+ *
37
64
  * String arithmetic only (POSIX): absolute paths are normalized like
38
65
  * `path.resolve` (empty/`.` segments dropped, `..` popped, trailing
39
66
  * slashes trimmed); relative inputs stay relative because there is no
@@ -42,7 +69,7 @@ export function isPlainObject(value) {
42
69
  * that. `home` must be provided to detect the global bucket; when it is
43
70
  * omitted the project path is returned.
44
71
  */
45
- export function resolveStatePathForCwd(cwd, home) {
72
+ export function resolveStatePathForCwd(cwd, home, agentId) {
46
73
  const normalize = (p) => {
47
74
  const isAbsolute = p.startsWith('/');
48
75
  const segments = [];
@@ -65,10 +92,14 @@ export function resolveStatePathForCwd(cwd, home) {
65
92
  const rootless = (p) => (p === '/' ? '' : p);
66
93
  const resolvedCwd = normalize(cwd);
67
94
  const resolvedHome = home === undefined || home === null ? '' : normalize(home);
68
- if (resolvedCwd === resolvedHome) {
69
- return `${rootless(resolvedHome)}/.skillstate/global/skillstate.json`;
95
+ const agent = typeof agentId === 'string' && agentId.length > 0 ? sanitizeAgentId(agentId) : '';
96
+ const base = resolvedCwd === resolvedHome
97
+ ? `${rootless(resolvedHome)}/.skillstate/global`
98
+ : `${rootless(resolvedCwd)}/.skillstate`;
99
+ if (agent.length > 0) {
100
+ return `${base}/agents/${agent}/skillstate.json`;
70
101
  }
71
- return `${rootless(resolvedCwd)}/.skillstate/skillstate.json`;
102
+ return `${base}/skillstate.json`;
72
103
  }
73
104
  /**
74
105
  * Read the state file through the injected `readFile` (real `fs` in
@@ -101,6 +132,90 @@ export function readStateEnvelope(statePath, readFile) {
101
132
  export function saveStateEnvelope(statePath, state, writeFile) {
102
133
  writeFile(statePath, `${JSON.stringify({ version: 1, state }, null, 2)}\n`);
103
134
  }
135
+ /**
136
+ * Block the current thread for `ms` milliseconds — the sync sleep for the
137
+ * {@link lockStateWrite} retry loop. Prefers `Atomics.wait` on a shared
138
+ * integer (a real sleep); falls back to a busy spin when the primitive is
139
+ * unavailable. Plain JS — survives `fn.toString()` inlining.
140
+ */
141
+ export function sleepSync(ms) {
142
+ try {
143
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
144
+ return;
145
+ }
146
+ catch (error) {
147
+ // Atomics.wait unavailable in this host — spin below.
148
+ }
149
+ const end = Date.now() + ms;
150
+ while (Date.now() < end) {
151
+ // Spin — hook scripts must stay synchronous.
152
+ }
153
+ }
154
+ /**
155
+ * Cross-process state lock for the SELF-CONTAINED hook scripts (sync,
156
+ * no imports): exclusive `O_EXCL` lockfile at `statePath + '.lock'`,
157
+ * stale-TTL takeover (10s — crashed hook holders), and a retry loop
158
+ * (50ms × 40 ≈ 2s) while a live holder runs its critical section. The
159
+ * injected `fs` (real `node:fs` in generated scripts and the plugin,
160
+ * mocks in tests) performs the lockfile I/O and creates the parent
161
+ * directory. The injected `fn` runs inside the lock; the lockfile is
162
+ * ALWAYS removed in the `finally` block. `fn` failures propagate AFTER
163
+ * the release; lock exhaustion throws.
164
+ */
165
+ export function lockStateWrite(statePath, fs, fn) {
166
+ const lockPath = `${statePath}.lock`;
167
+ const ttl = 10_000;
168
+ const retries = 40;
169
+ const dir = lockPath.replace(/\/[^/]+$/, '');
170
+ try {
171
+ fs.mkdirSync(dir);
172
+ }
173
+ catch (error) {
174
+ // Exists already — only the lockfile itself must be exclusive.
175
+ }
176
+ let acquired = false;
177
+ for (let attempt = 0; attempt < retries; attempt++) {
178
+ try {
179
+ fs.closeSync(fs.openSync(lockPath, 'wx'));
180
+ acquired = true;
181
+ break;
182
+ }
183
+ catch (error) {
184
+ let mtimeMs = 0;
185
+ try {
186
+ mtimeMs = fs.statSync(lockPath).mtimeMs;
187
+ }
188
+ catch (error) {
189
+ // Vanished between the failed create and the stat — retry at once.
190
+ }
191
+ if (Date.now() - mtimeMs > ttl) {
192
+ try {
193
+ fs.unlinkSync(lockPath);
194
+ }
195
+ catch (error) {
196
+ // Another waiter removed it first — retry from the top.
197
+ }
198
+ }
199
+ else {
200
+ sleepSync(50);
201
+ }
202
+ }
203
+ }
204
+ if (!acquired) {
205
+ throw new Error(`skillstate: could not acquire the state lock: ${lockPath}`);
206
+ }
207
+ try {
208
+ return fn();
209
+ }
210
+ finally {
211
+ try {
212
+ fs.unlinkSync(lockPath);
213
+ }
214
+ catch (error) {
215
+ // Lock already removed (stale takeover raced us) — nothing to release.
216
+ }
217
+ }
218
+ }
104
219
  /**
105
220
  * Paper ⊕ merge: `null` deletes a key, nested plain objects merge
106
221
  * recursively, everything else replaces. Pure — neither `state` nor
@@ -200,6 +315,26 @@ export function findRawPatch(text) {
200
315
  }
201
316
  return { absent: true };
202
317
  }
318
+ /**
319
+ * Read the session-meta sidecar's status (`<dir>/.session-meta.json`,
320
+ * sibling of the state file). Plain JS — inlined into the generated
321
+ * SessionStart hooks via `fn.toString()`. Returns the `status` string for
322
+ * a readable object-shaped sidecar, else `null` (missing/corrupt/other
323
+ * shape — no lifecycle marker). The `readFile` dependency is injected at
324
+ * the call site (real `node:fs` in hooks, mocks in tests).
325
+ */
326
+ export function readSessionMetaStatus(metaPath, readFile) {
327
+ try {
328
+ const parsed = JSON.parse(readFile(metaPath));
329
+ if (isPlainObject(parsed) && typeof parsed.status === 'string') {
330
+ return parsed.status;
331
+ }
332
+ }
333
+ catch (error) {
334
+ // Missing or corrupt sidecar — no lifecycle marker.
335
+ }
336
+ return null;
337
+ }
203
338
  /**
204
339
  * Assemble the CJS snippet embedded into every generated hook script:
205
340
  * all sibling functions of this module, source-verbatim via
@@ -210,6 +345,8 @@ export function findRawPatch(text) {
210
345
  export function hookRuntimeSnippet() {
211
346
  return [
212
347
  isPlainObject,
348
+ resolveAgentIdFromSession,
349
+ sanitizeAgentId,
213
350
  resolveStatePathForCwd,
214
351
  readStateEnvelope,
215
352
  saveStateEnvelope,
@@ -217,6 +354,9 @@ export function hookRuntimeSnippet() {
217
354
  readResponseText,
218
355
  findFencedPatch,
219
356
  findRawPatch,
357
+ sleepSync,
358
+ lockStateWrite,
359
+ readSessionMetaStatus,
220
360
  ]
221
361
  .map((fn) => fn.toString())
222
362
  .join('\n\n');
@@ -1 +1 @@
1
- {"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@
2
2
  * Resolve the per-project state file for a working directory —
3
3
  * `<cwd>/.skillstate/skillstate.json`, or the global bucket
4
4
  * `<home>/.skillstate/global/skillstate.json` when cwd equals home
5
- * (`home` defaults to `os.homedir()`).
5
+ * (`home` defaults to `os.homedir()`). A non-empty `agentId` scopes the
6
+ * file under `<bucket>/agents/<sanitized agentId>/skillstate.json`.
6
7
  */
7
- export declare function resolveHostStateForCwd(cwd: string, home?: string): string;
8
+ export declare function resolveHostStateForCwd(cwd: string, home?: string, agentId?: string): string;
8
9
  //# sourceMappingURL=host-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-state.d.ts","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAOzE"}
1
+ {"version":3,"file":"host-state.d.ts","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAa3F"}
@@ -1,31 +1,44 @@
1
1
  /**
2
2
  * @non-paper Wave-4 DX helper — the canonical per-project state resolver
3
3
  * shared by every host adapter (OpenCode plugin, Codex hooks/fork-trim,
4
- * Claude Code hooks).
4
+ * Claude Code hooks, MCP).
5
5
  *
6
6
  * Semantics: the state file for a working directory is
7
7
  * `<cwd>/.skillstate/skillstate.json`; a session opened directly in the
8
8
  * user's home resolves to the global bucket
9
- * `<home>/.skillstate/global/skillstate.json`. Pure path arithmetic — no
10
- * filesystem access. Host-embedded hook scripts (self-contained `.cjs`)
11
- * keep a byte-equivalent copy of this logic because they must run without
12
- * importing `@skillstate/*`; this function is the single source of truth
13
- * the copies mirror.
9
+ * `<home>/.skillstate/global/skillstate.json`. AGENT-SCOPED STATE: a
10
+ * non-empty `agentId` (sanitized `[A-Za-z0-9_-]`, ≤64 chars) scopes the
11
+ * file under an isolated copy — `agents/<agentId>/skillstate.json` inside
12
+ * the same bucket — so 2-3 parallel sub-agents (hook sessions) never
13
+ * last-writer-win over each other; the main agent keeps the unscoped
14
+ * path and merges sub-agent copies explicitly (MCP `agent.merge`).
15
+ *
16
+ * Pure path arithmetic — no filesystem access. Host-embedded hook scripts
17
+ * (self-contained `.cjs`) keep a behavior-equivalent copy of this logic in
18
+ * the hook-runtime `resolveStatePathForCwd` because they must run without
19
+ * importing `@skillstate/*`; that function is the parity mirror. The agent
20
+ * id sanitizer is shared verbatim from the hook-runtime module.
14
21
  */
15
22
  import * as os from 'node:os';
16
23
  import * as path from 'node:path';
24
+ import { sanitizeAgentId } from './hook-runtime.js';
17
25
  /**
18
26
  * Resolve the per-project state file for a working directory —
19
27
  * `<cwd>/.skillstate/skillstate.json`, or the global bucket
20
28
  * `<home>/.skillstate/global/skillstate.json` when cwd equals home
21
- * (`home` defaults to `os.homedir()`).
29
+ * (`home` defaults to `os.homedir()`). A non-empty `agentId` scopes the
30
+ * file under `<bucket>/agents/<sanitized agentId>/skillstate.json`.
22
31
  */
23
- export function resolveHostStateForCwd(cwd, home) {
32
+ export function resolveHostStateForCwd(cwd, home, agentId) {
24
33
  const resolvedCwd = path.resolve(cwd);
25
34
  const resolvedHome = path.resolve(home ?? os.homedir());
26
- if (resolvedCwd === resolvedHome) {
27
- return path.join(resolvedHome, '.skillstate', 'global', 'skillstate.json');
35
+ const bucket = resolvedCwd === resolvedHome
36
+ ? path.join(resolvedHome, '.skillstate', 'global')
37
+ : path.join(resolvedCwd, '.skillstate');
38
+ const agent = typeof agentId === 'string' && agentId.length > 0 ? sanitizeAgentId(agentId) : '';
39
+ if (agent.length > 0) {
40
+ return path.join(bucket, 'agents', agent, 'skillstate.json');
28
41
  }
29
- return path.join(resolvedCwd, '.skillstate', 'skillstate.json');
42
+ return path.join(bucket, 'skillstate.json');
30
43
  }
31
44
  //# sourceMappingURL=host-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-state.js","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,IAAa;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACxD,IAAI,WAAW,KAAK,YAAY,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;AAClE,CAAC"}
1
+ {"version":3,"file":"host-state.js","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,IAAa,EAAE,OAAgB;IACjF,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACxD,MAAM,MAAM,GACV,WAAW,KAAK,YAAY;QAC1B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,CAAC;QAClD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC5C,MAAM,KAAK,GACT,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAC9C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -20,5 +20,6 @@ export * from './hook-runtime.js';
20
20
  export * from './adapter-shared.js';
21
21
  export * from './prompt-contract.js';
22
22
  export * from './shutdown.js';
23
+ export * from './session-meta.js';
23
24
  export * from './schemas/index.js';
24
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
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,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAE9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -33,5 +33,7 @@ export * from './adapter-shared.js';
33
33
  // @non-paper Wave-5 deduplicated prompt texts (shared adapter vocabulary).
34
34
  export * from './prompt-contract.js';
35
35
  export * from './shutdown.js';
36
+ // @non-paper release-2.3.0 session lifecycle marker (orchestration meta).
37
+ export * from './session-meta.js';
36
38
  export * from './schemas/index.js';
37
39
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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,iBAAiB,CAAC;AAChC,uEAAuE;AACvE,yCAAyC;AACzC,cAAc,mBAAmB,CAAC;AAClC,wEAAwE;AACxE,wCAAwC;AACxC,cAAc,qBAAqB,CAAC;AACpC,2EAA2E;AAC3E,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC"}
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,iBAAiB,CAAC;AAChC,uEAAuE;AACvE,yCAAyC;AACzC,cAAc,mBAAmB,CAAC;AAClC,wEAAwE;AACxE,wCAAwC;AACxC,cAAc,qBAAqB,CAAC;AACpC,2EAA2E;AAC3E,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,0EAA0E;AAC1E,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
@@ -40,6 +40,15 @@ export declare const STATE_PATCH_CONTRACT: string;
40
40
  * hosts, so the Bash-carried patch option can no longer drift away.
41
41
  */
42
42
  export declare const HISTORY_UNRELIABLE_NOTE = "\nHistory is not reliable. Persist anything you need via the skillstate MCP tools (state.summary / state.patch) or a fenced ```json state_patch block.";
43
+ /**
44
+ * The interrupted-session hint injected by the SessionStart hooks
45
+ * (claude + codex alike) when the session-meta sidecar carries
46
+ * `status: "interrupted"` — a previous run of this session was killed
47
+ * (SIGINT/SIGTERM) mid-procedure. The hook appends the preserved state
48
+ * path so the agent can review progress/blockers before continuing.
49
+ * A fresh launch overwrites the status back to `running`.
50
+ */
51
+ export declare const INTERRUPTED_SESSION_NOTE = "\nPrevious session was interrupted; state preserved at <path>; review progress/blockers before continuing.";
43
52
  /**
44
53
  * Render a state schema as the shared `## Schema` markdown block used by
45
54
  * every prompt formatter and adapter `injectState`. Fields without a
@@ -48,14 +57,27 @@ export declare const HISTORY_UNRELIABLE_NOTE = "\nHistory is not reliable. Persi
48
57
  export declare function describeSchema(schema: StateSchema): string;
49
58
  /** Options for {@link skillMdBody}. */
50
59
  export interface SkillMdBodyOptions {
51
- /** Brand label woven into the hooks intro ("Claude Code", "Codex"). */
60
+ /** Brand label woven into the hooks intro ("Claude Code", "Codex", "OpenCode"). */
52
61
  hostLabel: string;
53
62
  /**
54
63
  * How the injected state reaches the model, as a predicate:
55
64
  * "injected into your context via hooks" (claude) /
56
- * "provided as developer context" (codex).
65
+ * "provided as developer context" (codex) /
66
+ * "injected into the message list before every model call" (opencode).
57
67
  */
58
68
  injectionPhrase: string;
69
+ /**
70
+ * The hook names this host wires, verbatim as they appear in the bullets:
71
+ * claude/codex use UserPromptSubmit + `SessionStart` + `PostToolUse`;
72
+ * opencode uses `messages.transform` + `session.compacting` +
73
+ * `tool.execute.after`. The merge-bullet is omitted when `patchHook` is
74
+ * undefined.
75
+ */
76
+ hooks: {
77
+ inject: string;
78
+ reInject: string;
79
+ patchHook?: string;
80
+ };
59
81
  /** The skill spec — name/instructions/version fill the frontmatter. */
60
82
  spec: ProceduralSpec;
61
83
  /** State path written into the frontmatter and the body (default `./.skillstate/skillstate.json`). */
@@ -68,6 +90,10 @@ export interface SkillMdBodyOptions {
68
90
  * and `injectionPhrase` (how the state reaches the model). OpenCode keeps
69
91
  * its own body — its history is trimmed by the plugin, not by hooks, so
70
92
  * its Execution Context and Process genuinely differ.
93
+ *
94
+ * HOOK-NEUTRAL: the injected bullets are driven by `options.hooks` so ANY
95
+ * harness (claude/codex hook-wiring, opencode plugin transforms) renders the
96
+ * same canonical Process without forking the body.
71
97
  */
72
98
  export declare function skillMdBody(options: SkillMdBodyOptions): string;
73
99
  //# sourceMappingURL=prompt-contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-contract.d.ts","sourceRoot":"","sources":["../src/prompt-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,oHAC2E,CAAC;AAEpH,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,QAOzB,CAAC;AAEb,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,oIACqG,CAAC;AAEpI;;;GAGG;AACH,eAAO,MAAM,wBAAwB,uGAC4D,CAAC;AAElG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAOrB,CAAC;AAEb;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,2JACsH,CAAC;AAE3J;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAQ1D;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,IAAI,EAAE,cAAc,CAAC;IACrB,sGAAsG;IACtG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAmD/D"}
1
+ {"version":3,"file":"prompt-contract.d.ts","sourceRoot":"","sources":["../src/prompt-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,oHAC2E,CAAC;AAEpH,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,QAOzB,CAAC;AAEb,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,oIACqG,CAAC;AAEpI;;;GAGG;AACH,eAAO,MAAM,wBAAwB,uGAC4D,CAAC;AAElG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAOrB,CAAC;AAEb;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,2JACsH,CAAC;AAE3J;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,+GACyE,CAAC;AAE/G;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAQ1D;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,uEAAuE;IACvE,IAAI,EAAE,cAAc,CAAC;IACrB,sGAAsG;IACtG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAyE/D"}
@@ -41,6 +41,15 @@ export const STATE_PATCH_CONTRACT = [
41
41
  * hosts, so the Bash-carried patch option can no longer drift away.
42
42
  */
43
43
  export const HISTORY_UNRELIABLE_NOTE = '\nHistory is not reliable. Persist anything you need via the skillstate MCP tools (state.summary / state.patch) or a fenced ```json state_patch block.';
44
+ /**
45
+ * The interrupted-session hint injected by the SessionStart hooks
46
+ * (claude + codex alike) when the session-meta sidecar carries
47
+ * `status: "interrupted"` — a previous run of this session was killed
48
+ * (SIGINT/SIGTERM) mid-procedure. The hook appends the preserved state
49
+ * path so the agent can review progress/blockers before continuing.
50
+ * A fresh launch overwrites the status back to `running`.
51
+ */
52
+ export const INTERRUPTED_SESSION_NOTE = '\nPrevious session was interrupted; state preserved at <path>; review progress/blockers before continuing.';
44
53
  /**
45
54
  * Render a state schema as the shared `## Schema` markdown block used by
46
55
  * every prompt formatter and adapter `injectState`. Fields without a
@@ -59,10 +68,15 @@ export function describeSchema(schema) {
59
68
  * and `injectionPhrase` (how the state reaches the model). OpenCode keeps
60
69
  * its own body — its history is trimmed by the plugin, not by hooks, so
61
70
  * its Execution Context and Process genuinely differ.
71
+ *
72
+ * HOOK-NEUTRAL: the injected bullets are driven by `options.hooks` so ANY
73
+ * harness (claude/codex hook-wiring, opencode plugin transforms) renders the
74
+ * same canonical Process without forking the body.
62
75
  */
63
76
  export function skillMdBody(options) {
64
77
  const resolvedStatePath = options.statePath ?? './.skillstate/skillstate.json';
65
- return [
78
+ const patchHook = options.hooks.patchHook;
79
+ const body = [
66
80
  '---',
67
81
  `name: ${JSON.stringify(options.spec.name)}`,
68
82
  `description: ${JSON.stringify(options.spec.instructions)}`,
@@ -80,36 +94,23 @@ export function skillMdBody(options) {
80
94
  '',
81
95
  `Your execution state lives at \`${resolvedStatePath}\` (per project; a`,
82
96
  'session started in $HOME uses `~/.skillstate/global/skillstate.json`).',
83
- `The skillstate ${options.hostLabel} hooks:`,
97
+ `The skillstate ${options.hostLabel} integration:`,
84
98
  '',
85
99
  `- the CURRENT state is ${options.injectionPhrase} on every prompt submit`,
86
- ' (`UserPromptSubmit`) and is re-injected after compaction',
87
- ' (`SessionStart` matcher `^compact$`);',
88
- '- watch every Bash tool result and merge a fenced ```json block carrying',
89
- ' a `state_patch` into the state file (`PostToolUse`).',
90
- '',
91
- 'The injected state is authoritative — history is not reliable. Never',
92
- 'reconstruct execution context from the conversation.',
93
- '',
94
- '## Process',
95
- '',
96
- '1. Orient yourself: read the injected state, or call the skillstate MCP',
97
- ' tools `state.summary` (compact) / `state.get` (full dump).',
98
- '2. Observe the result of your last action.',
99
- '3. Reason about what to do next, given the state and the observation.',
100
- '4. Persist progress with the skillstate MCP tool `state.patch` (sparse',
101
- ' patch; set a key to `null` to delete it; dry-run first with',
102
- ' `state.validate` when unsure; `state.diff` shows what changed since',
103
- ' your last look), and/or emit a fenced JSON block with exactly two',
104
- ' keys inside a Bash tool call so the `PostToolUse` hook merges it:',
105
- '',
106
- STATE_PATCH_EXAMPLE_JSON,
107
- '',
108
- `- ${STATE_PATCH_RULES}`,
109
- '- Put anything you need to survive into `state_patch`; never rely on',
110
- ' the conversation remembering it.',
111
- '- `action` names what you will do next (e.g. "continue", "done").',
112
- '',
113
- ].join('\n');
100
+ ` (\`${options.hooks.inject}\`) and is re-injected after compaction`,
101
+ ` (\`${options.hooks.reInject}\`);`,
102
+ ];
103
+ if (patchHook !== undefined) {
104
+ body.push('- watch every Bash tool result and merge a fenced ```json block carrying', ` a \`state_patch\` into the state file (\`${patchHook}\`).`);
105
+ }
106
+ body.push('', 'Sub-agents automatically get isolated state copies (agents/<session-id>);', 'merge them with `agent.merge` when their task completes.', '', 'The injected state is authoritative — history is not reliable. Never', 'reconstruct execution context from the conversation.', '', '## Process', '', '1. Orient yourself: read the injected state, or call the skillstate MCP', ' tools `state.summary` (compact) / `state.get` (full dump).', '2. Observe the result of your last action.', '3. Reason about what to do next, given the state and the observation.', '4. Persist progress with the skillstate MCP tool `state.patch` (sparse', ' patch; set a key to `null` to delete it; dry-run first with', ' `state.validate` when unsure; `state.diff` shows what changed since', ' your last look)');
107
+ if (patchHook !== undefined) {
108
+ body.push(' , and/or emit a fenced JSON block with exactly two', ' keys inside a Bash tool call so the `PostToolUse` hook merges it:', '', STATE_PATCH_EXAMPLE_JSON, '', `- ${STATE_PATCH_RULES}`);
109
+ }
110
+ else {
111
+ body.push(':', '', STATE_PATCH_EXAMPLE_JSON, '', `- ${STATE_PATCH_RULES}`);
112
+ }
113
+ body.push('- Put anything you need to survive into `state_patch`; never rely on', ' the conversation remembering it.', '- `action` names what you will do next (e.g. "continue", "done").', '- When the task is done, call state.finalize {status:"completed"} so the', ' orchestrator knows (state.finalize {status:"failed"} on failure).', '');
114
+ return body.join('\n');
114
115
  }
115
116
  //# sourceMappingURL=prompt-contract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-contract.js","sourceRoot":"","sources":["../src/prompt-contract.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,2BAA2B,GACtC,iHAAiH,CAAC;AAEpH,uEAAuE;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,SAAS;IACT,GAAG;IACH,gEAAgE;IAChE,gCAAgC;IAChC,GAAG;IACH,KAAK;CACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,mFAAmF;AACnF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,iIAAiI,CAAC;AAEpI;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,+FAA+F,CAAC;AAElG;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,+DAA+D;IAC/D,MAAM,2BAA2B,EAAE;IACnC,EAAE;IACF,wBAAwB;IACxB,EAAE;IACF,iBAAiB;CAClB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAClC,wJAAwJ,CAAC;AAE3J;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SAClC,GAAG,CACF,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAChB,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,WAAW,IAAI,gBAAgB,EAAE,CACxE;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,cAAc,MAAM,EAAE,CAAC;AAChC,CAAC;AAkBD;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,iBAAiB,GAAG,OAAO,CAAC,SAAS,IAAI,+BAA+B,CAAC;IAC/E,OAAO;QACL,KAAK;QACL,SAAS,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC5C,gBAAgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE;QAC3D,YAAY,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE;QAClC,oBAAoB;QACpB,iBAAiB,iBAAiB,EAAE;QACpC,gBAAgB;QAChB,KAAK;QACL,EAAE;QACF,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;QACxB,EAAE;QACF,OAAO,CAAC,IAAI,CAAC,YAAY;QACzB,EAAE;QACF,sBAAsB;QACtB,EAAE;QACF,mCAAmC,iBAAiB,oBAAoB;QACxE,wEAAwE;QACxE,kBAAkB,OAAO,CAAC,SAAS,SAAS;QAC5C,EAAE;QACF,0BAA0B,OAAO,CAAC,eAAe,yBAAyB;QAC1E,4DAA4D;QAC5D,yCAAyC;QACzC,0EAA0E;QAC1E,wDAAwD;QACxD,EAAE;QACF,sEAAsE;QACtE,sDAAsD;QACtD,EAAE;QACF,YAAY;QACZ,EAAE;QACF,yEAAyE;QACzE,+DAA+D;QAC/D,4CAA4C;QAC5C,uEAAuE;QACvE,wEAAwE;QACxE,gEAAgE;QAChE,wEAAwE;QACxE,sEAAsE;QACtE,sEAAsE;QACtE,EAAE;QACF,wBAAwB;QACxB,EAAE;QACF,KAAK,iBAAiB,EAAE;QACxB,sEAAsE;QACtE,oCAAoC;QACpC,mEAAmE;QACnE,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"prompt-contract.js","sourceRoot":"","sources":["../src/prompt-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /** Every lifecycle status the session meta sidecar may carry. */
2
+ export type SessionStatus = 'running' | 'interrupted' | 'completed' | 'merged' | 'failed';
3
+ /** All {@link SessionStatus} values. */
4
+ export declare const SESSION_STATUSES: readonly ["running", "interrupted", "completed", "merged", "failed"];
5
+ /** Sidecar file name living next to every state file. */
6
+ export declare const SESSION_META_FILE = ".session-meta.json";
7
+ /**
8
+ * A `running` session whose `lastActivityAt` is older than this (5 min)
9
+ * reports `stale` — the provider process died without a signal.
10
+ */
11
+ export declare const STALE_MS: number;
12
+ /** Session lifecycle metadata (the sidecar's JSON body). */
13
+ export interface SessionMeta {
14
+ status?: SessionStatus;
15
+ /** ISO timestamp of the last state write (debounced to 5s by MCP). */
16
+ lastActivityAt?: string;
17
+ /** ISO timestamp stamped by `launch()` when the session starts. */
18
+ startedAt?: string;
19
+ /** ISO timestamp stamped by `state.finalize`. */
20
+ finishedAt?: string;
21
+ /** ISO timestamp stamped by `agent.merge` on the sub-agent copy. */
22
+ mergedAt?: string;
23
+ /** Free-text result carried by `state.finalize`. */
24
+ result?: string;
25
+ /** Sanitized agent scope the session runs under (`''` = main). */
26
+ agentId?: string;
27
+ /** MCP protocol revision the server session speaks. */
28
+ protocolVersion?: string;
29
+ /** Tolerated: unknown extra fields survive read/merge round-trips. */
30
+ [key: string]: unknown;
31
+ }
32
+ /** Sidecar path for a state directory: `<dir>/.session-meta.json`. */
33
+ export declare function sessionMetaPath(dir: string): string;
34
+ /**
35
+ * Read the session meta sidecar for `dir`. Returns `null` when the file
36
+ * is missing, unreadable, corrupt, or not a JSON object — callers treat
37
+ * that as "no lifecycle marker" (staleness `orphan`), never a crash.
38
+ */
39
+ export declare function readSessionMeta(dir: string): SessionMeta | null;
40
+ /**
41
+ * Merge `patch` into the session meta sidecar for `dir` and persist it
42
+ * atomically under the meta lock (`<metaPath>.lock`). Creates the file
43
+ * (and the directory) when absent; unknown existing fields survive.
44
+ * Returns the merged meta as written.
45
+ */
46
+ export declare function writeSessionMeta(dir: string, patch: Partial<SessionMeta>): Promise<SessionMeta>;
47
+ /** Staleness verdict for an agent session (see the module doc). */
48
+ export type SessionStaleness = 'active' | 'stale' | 'orphan';
49
+ /**
50
+ * Classify a session's liveness from its meta:
51
+ * `orphan` — no/corrupt meta; `stale` — status `running` but the last
52
+ * activity (falling back to `startedAt`) is older than {@link STALE_MS};
53
+ * `active` — everything else (fresh running sessions AND terminal
54
+ * statuses: completed/failed/merged/interrupted runs are not stale, their
55
+ * `status` already says so).
56
+ */
57
+ export declare function sessionStaleness(meta: SessionMeta | null, nowMs?: number): SessionStaleness;
58
+ //# sourceMappingURL=session-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-meta.d.ts","sourceRoot":"","sources":["../src/session-meta.ts"],"names":[],"mappings":"AA8BA,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1F,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,sEAMgB,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,QAAQ,QAAgB,CAAC;AAEtC,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAU/D;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAC1B,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,KAAK,GAAE,MAAmB,GACzB,gBAAgB,CAWlB"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @non-paper Session lifecycle marker (release 2.3.0).
3
+ *
4
+ * The state envelope (`<stateDir>/skillstate.json`) belongs to the
5
+ * PROCEDURE (paper §3.2) — the agent's data, nothing else. The session
6
+ * lifecycle is ORCHESTRATION metadata and lives in a separate sidecar:
7
+ * `<stateDir>/.session-meta.json`, written with `atomicWriteFile` under
8
+ * the same cross-process `withStateLock` primitive (its own lock file,
9
+ * `<metaPath>.lock` — never the state lock, so meta writes cannot
10
+ * deadlock against state writes).
11
+ *
12
+ * Statuses:
13
+ * - `running` — a live session (MCP `launch()` stamps it at start);
14
+ * - `interrupted` — SIGINT/SIGTERM hit the server before a final status
15
+ * (the interrupt handler flushes this + the diff baseline);
16
+ * - `completed` / `failed` — the agent called MCP `state.finalize`;
17
+ * - `merged` — the orchestrator folded the sub-agent state into the main
18
+ * state via `agent.merge`.
19
+ *
20
+ * STALE DETECTION: a `running` meta whose `lastActivityAt` is older than
21
+ * {@link STALE_MS} (5 min) is `stale` (the provider died without a
22
+ * signal); missing/corrupt meta next to an existing state file is
23
+ * `orphan`. Everything else is `active`.
24
+ *
25
+ * Zero dependencies, Node >= 20, ESM.
26
+ */
27
+ import * as fs from 'node:fs';
28
+ import * as path from 'node:path';
29
+ import { atomicWriteFile, withStateLock } from './atomic-write.js';
30
+ /** All {@link SessionStatus} values. */
31
+ export const SESSION_STATUSES = [
32
+ 'running',
33
+ 'interrupted',
34
+ 'completed',
35
+ 'merged',
36
+ 'failed',
37
+ ];
38
+ /** Sidecar file name living next to every state file. */
39
+ export const SESSION_META_FILE = '.session-meta.json';
40
+ /**
41
+ * A `running` session whose `lastActivityAt` is older than this (5 min)
42
+ * reports `stale` — the provider process died without a signal.
43
+ */
44
+ export const STALE_MS = 5 * 60 * 1000;
45
+ /** Sidecar path for a state directory: `<dir>/.session-meta.json`. */
46
+ export function sessionMetaPath(dir) {
47
+ return path.join(dir, SESSION_META_FILE);
48
+ }
49
+ /**
50
+ * Read the session meta sidecar for `dir`. Returns `null` when the file
51
+ * is missing, unreadable, corrupt, or not a JSON object — callers treat
52
+ * that as "no lifecycle marker" (staleness `orphan`), never a crash.
53
+ */
54
+ export function readSessionMeta(dir) {
55
+ try {
56
+ const parsed = JSON.parse(fs.readFileSync(sessionMetaPath(dir), 'utf-8'));
57
+ if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
58
+ return parsed;
59
+ }
60
+ }
61
+ catch {
62
+ // Missing or corrupt sidecar — no lifecycle marker.
63
+ }
64
+ return null;
65
+ }
66
+ /**
67
+ * Merge `patch` into the session meta sidecar for `dir` and persist it
68
+ * atomically under the meta lock (`<metaPath>.lock`). Creates the file
69
+ * (and the directory) when absent; unknown existing fields survive.
70
+ * Returns the merged meta as written.
71
+ */
72
+ export async function writeSessionMeta(dir, patch) {
73
+ const metaPath = sessionMetaPath(dir);
74
+ return withStateLock(metaPath, async () => {
75
+ const current = readSessionMeta(dir);
76
+ const merged = { ...(current ?? {}), ...patch };
77
+ await atomicWriteFile(metaPath, `${JSON.stringify(merged, null, 2)}\n`);
78
+ return merged;
79
+ });
80
+ }
81
+ /**
82
+ * Classify a session's liveness from its meta:
83
+ * `orphan` — no/corrupt meta; `stale` — status `running` but the last
84
+ * activity (falling back to `startedAt`) is older than {@link STALE_MS};
85
+ * `active` — everything else (fresh running sessions AND terminal
86
+ * statuses: completed/failed/merged/interrupted runs are not stale, their
87
+ * `status` already says so).
88
+ */
89
+ export function sessionStaleness(meta, nowMs = Date.now()) {
90
+ if (meta === null) {
91
+ return 'orphan';
92
+ }
93
+ if (meta.status === 'running') {
94
+ const last = Date.parse(meta.lastActivityAt ?? meta.startedAt ?? '');
95
+ if (Number.isNaN(last) || nowMs - last > STALE_MS) {
96
+ return 'stale';
97
+ }
98
+ }
99
+ return 'active';
100
+ }
101
+ //# sourceMappingURL=session-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-meta.js","sourceRoot":"","sources":["../src/session-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKnE,wCAAwC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,SAAS;IACT,aAAa;IACb,WAAW;IACX,QAAQ;IACR,QAAQ;CACmC,CAAC;AAE9C,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAEtD;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAuBtC,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACnF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5E,OAAO,MAAqB,CAAC;QAC/B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,KAA2B;IAE3B,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,aAAa,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,MAAM,GAAgB,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;QAC7D,MAAM,eAAe,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAKD;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAwB,EACxB,KAAK,GAAW,IAAI,CAAC,GAAG,EAAE;IAE1B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,GAAG,QAAQ,EAAE,CAAC;YAClD,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillstate/core",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Paper-exact skillstate runtime core (A.4 byte-verbatim formatPaper, §4.3 token tracker).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",