@skillstate/core 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atomic-write.d.ts +17 -0
- package/dist/atomic-write.d.ts.map +1 -1
- package/dist/atomic-write.js +47 -0
- package/dist/atomic-write.js.map +1 -1
- package/dist/hook-runtime.d.ts +61 -1
- package/dist/hook-runtime.d.ts.map +1 -1
- package/dist/hook-runtime.js +144 -4
- package/dist/hook-runtime.js.map +1 -1
- package/dist/host-state.d.ts +3 -2
- package/dist/host-state.d.ts.map +1 -1
- package/dist/host-state.js +24 -11
- package/dist/host-state.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prompt-contract.d.ts +9 -0
- package/dist/prompt-contract.d.ts.map +1 -1
- package/dist/prompt-contract.js +14 -0
- package/dist/prompt-contract.js.map +1 -1
- package/dist/session-meta.d.ts +58 -0
- package/dist/session-meta.d.ts.map +1 -0
- package/dist/session-meta.js +101 -0
- package/dist/session-meta.js.map +1 -0
- package/package.json +1 -1
package/dist/atomic-write.d.ts
CHANGED
|
@@ -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"}
|
package/dist/atomic-write.js
CHANGED
|
@@ -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).
|
package/dist/atomic-write.js.map
CHANGED
|
@@ -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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"}
|
package/dist/hook-runtime.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/hook-runtime.js
CHANGED
|
@@ -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
|
-
|
|
69
|
-
|
|
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 `${
|
|
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');
|
package/dist/hook-runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../src/hook-runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/host-state.d.ts
CHANGED
|
@@ -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
|
package/dist/host-state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-state.d.ts","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/host-state.js
CHANGED
|
@@ -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`.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* the
|
|
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
|
-
|
|
27
|
-
|
|
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(
|
|
42
|
+
return path.join(bucket, 'skillstate.json');
|
|
30
43
|
}
|
|
31
44
|
//# sourceMappingURL=host-state.js.map
|
package/dist/host-state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-state.js","sourceRoot":"","sources":["../src/host-state.ts"],"names":[],"mappings":"AAAA
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
@@ -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,
|
|
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,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,CAwD/D"}
|
package/dist/prompt-contract.js
CHANGED
|
@@ -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
|
|
@@ -88,6 +97,9 @@ export function skillMdBody(options) {
|
|
|
88
97
|
'- watch every Bash tool result and merge a fenced ```json block carrying',
|
|
89
98
|
' a `state_patch` into the state file (`PostToolUse`).',
|
|
90
99
|
'',
|
|
100
|
+
'Sub-agents automatically get isolated state copies (agents/<session-id>);',
|
|
101
|
+
'merge them with `agent.merge` when their task completes.',
|
|
102
|
+
'',
|
|
91
103
|
'The injected state is authoritative — history is not reliable. Never',
|
|
92
104
|
'reconstruct execution context from the conversation.',
|
|
93
105
|
'',
|
|
@@ -109,6 +121,8 @@ export function skillMdBody(options) {
|
|
|
109
121
|
'- Put anything you need to survive into `state_patch`; never rely on',
|
|
110
122
|
' the conversation remembering it.',
|
|
111
123
|
'- `action` names what you will do next (e.g. "continue", "done").',
|
|
124
|
+
'- When the task is done, call state.finalize {status:"completed"} so the',
|
|
125
|
+
' orchestrator knows (state.finalize {status:"failed"} on failure).',
|
|
112
126
|
'',
|
|
113
127
|
].join('\n');
|
|
114
128
|
}
|
|
@@ -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":"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;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,4GAA4G,CAAC;AAE/G;;;;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,2EAA2E;QAC3E,0DAA0D;QAC1D,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,0EAA0E;QAC1E,qEAAqE;QACrE,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -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"}
|