clinkx 1.1.2 → 1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +63 -12
  3. package/clinkx-workflows/dist/allowed-roots.js +2 -0
  4. package/clinkx-workflows/dist/allowed-roots.js.map +1 -1
  5. package/clinkx-workflows/dist/child-env.d.ts +3 -1
  6. package/clinkx-workflows/dist/child-env.js +6 -2
  7. package/clinkx-workflows/dist/child-env.js.map +1 -1
  8. package/clinkx-workflows/dist/config.d.ts +28 -5
  9. package/clinkx-workflows/dist/config.js +137 -20
  10. package/clinkx-workflows/dist/config.js.map +1 -1
  11. package/clinkx-workflows/dist/contained-file.js +9 -0
  12. package/clinkx-workflows/dist/contained-file.js.map +1 -1
  13. package/clinkx-workflows/dist/dashboard/session-log-identity.js +2 -1
  14. package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -1
  15. package/clinkx-workflows/dist/root-path-validation.d.ts +12 -0
  16. package/clinkx-workflows/dist/root-path-validation.js +63 -0
  17. package/clinkx-workflows/dist/root-path-validation.js.map +1 -0
  18. package/clinkx-workflows/dist/rosters.d.ts +1 -0
  19. package/clinkx-workflows/dist/rosters.js +16 -6
  20. package/clinkx-workflows/dist/rosters.js.map +1 -1
  21. package/clinkx-workflows/dist/server.js +5 -2
  22. package/clinkx-workflows/dist/server.js.map +1 -1
  23. package/clinkx-workflows/dist/state-schema.d.ts +13 -0
  24. package/clinkx-workflows/dist/state.d.ts +7 -1
  25. package/clinkx-workflows/dist/state.js +74 -13
  26. package/clinkx-workflows/dist/state.js.map +1 -1
  27. package/clinkx-workflows/dist/workflow-completion.d.ts +83 -0
  28. package/clinkx-workflows/dist/workflow-completion.js +250 -0
  29. package/clinkx-workflows/dist/workflow-completion.js.map +1 -0
  30. package/clinkx-workflows/dist/workflow-tools.d.ts +4 -3
  31. package/clinkx-workflows/dist/workflow-tools.js +341 -34
  32. package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
  33. package/dist/allowed-roots.d.ts +1 -1
  34. package/dist/allowed-roots.js +124 -14
  35. package/dist/allowed-roots.js.map +1 -1
  36. package/dist/context-builder/config.d.ts +1 -0
  37. package/dist/context-builder/config.js +21 -9
  38. package/dist/context-builder/config.js.map +1 -1
  39. package/dist/context-builder/mutation-lock.d.ts +4 -0
  40. package/dist/context-builder/mutation-lock.js +7 -1
  41. package/dist/context-builder/mutation-lock.js.map +1 -1
  42. package/dist/context-builder/storage.d.ts +10 -2
  43. package/dist/context-builder/storage.js +213 -16
  44. package/dist/context-builder/storage.js.map +1 -1
  45. package/dist/dashboard-cli.d.ts +6 -0
  46. package/dist/dashboard-cli.js +8 -29
  47. package/dist/dashboard-cli.js.map +1 -1
  48. package/dist/registry.d.ts +1 -1
  49. package/dist/registry.js +5 -1
  50. package/dist/registry.js.map +1 -1
  51. package/dist/server.js +12 -5
  52. package/dist/server.js.map +1 -1
  53. package/docs/configuration.md +157 -12
  54. package/docs/security.md +3 -0
  55. package/package.json +1 -1
  56. package/scripts/provider-doctor-core.mjs +21 -5
@@ -0,0 +1,250 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { readContainedRelativeFile } from "./contained-file.js";
4
+ import { DEFAULT_DASHBOARD_MAX_STATE_BYTES } from "./config.js";
5
+ import { readSessionLogGeneration } from "./dashboard/session-log-identity.js";
6
+ import { WorkflowCancellationError } from "./errors.js";
7
+ import { logger } from "./logger.js";
8
+ export const WORKFLOW_WAIT_BUDGET_MS = 30_000;
9
+ export const SETTLED_LIMIT = 128;
10
+ /** sha256 over the exact bytes read from state.json (never a decoded string). */
11
+ function fingerprintStateBytes(bytes) {
12
+ return createHash("sha256").update(bytes).digest("hex");
13
+ }
14
+ /**
15
+ * Provenance only: result bodies and unfinished promise callbacks are never cached.
16
+ * An execution exists here only once its owner has bound a created/resumed
17
+ * store, so a request rejected during admission can never displace one.
18
+ */
19
+ export class WorkflowCompletionRegistry {
20
+ latest = new Map();
21
+ byId = new Map();
22
+ minted = new WeakSet();
23
+ settledIds = [];
24
+ bind(runId, runDir, generation) {
25
+ const listeners = new Set();
26
+ const record = {
27
+ executionId: randomUUID(),
28
+ runId,
29
+ runDir: resolve(runDir),
30
+ generation,
31
+ publication: undefined,
32
+ settled: false,
33
+ stamped: false,
34
+ observers: 0,
35
+ listeners,
36
+ get listenerCount() { return listeners.size; },
37
+ };
38
+ this.minted.add(record);
39
+ this.byId.set(record.executionId, record);
40
+ this.latest.set(record.runDir, record);
41
+ return record;
42
+ }
43
+ owner(runId, runDir) {
44
+ let entry;
45
+ let boundStore;
46
+ return {
47
+ get entry() { return entry; },
48
+ bind: (store) => {
49
+ if (entry !== undefined) {
50
+ if (boundStore !== store)
51
+ throw new Error(`workflow run ${runId} is already bound to a different state store`);
52
+ return entry;
53
+ }
54
+ entry = this.bind(runId, runDir, store.sessionLogGeneration);
55
+ boundStore = store;
56
+ return entry;
57
+ },
58
+ };
59
+ }
60
+ select(runDir, expected) {
61
+ const latest = this.latest.get(resolve(runDir));
62
+ if (expected === undefined)
63
+ return latest;
64
+ const entry = this.byId.get(expected);
65
+ if (entry === undefined)
66
+ return "identity_unavailable";
67
+ if (entry.runDir !== resolve(runDir) || latest !== entry)
68
+ return "identity_changed";
69
+ return entry;
70
+ }
71
+ /**
72
+ * Capture the terminal publication while ownership still prevents local
73
+ * re-admission. Only a terminal state of the bound directory incarnation counts.
74
+ */
75
+ async recordPublication(entry) {
76
+ const record = this.own(entry);
77
+ if (record.settled || record.generation === undefined)
78
+ return;
79
+ const publication = await inspectPublication(record.runDir);
80
+ if (publication?.generation === record.generation && publication.terminal) {
81
+ record.publication = publication;
82
+ }
83
+ }
84
+ markStamped(entry) {
85
+ this.own(entry).stamped = true;
86
+ }
87
+ settle(entry) {
88
+ const record = this.own(entry);
89
+ if (record.settled)
90
+ return;
91
+ record.settled = true;
92
+ this.settledIds.push(record.executionId);
93
+ this.prune();
94
+ for (const listener of [...record.listeners])
95
+ listener();
96
+ }
97
+ /** Protect provenance through the handler's final filesystem rereads. */
98
+ retain(entry) {
99
+ const record = this.own(entry);
100
+ record.observers += 1;
101
+ return () => {
102
+ record.observers -= 1;
103
+ this.prune();
104
+ };
105
+ }
106
+ /**
107
+ * Execution fence around a projection. `observedDigest` is the fingerprint of
108
+ * the state bytes the projection actually parsed (null: unreadable); pass it
109
+ * only when the entry was already settled before that projection began.
110
+ * Without it, only provenance and directory incarnation are checked, which
111
+ * is correct for an active execution that legitimately republishes state.
112
+ */
113
+ async check(entry, observedDigest) {
114
+ const record = this.own(entry);
115
+ const before = this.selectionFailure(record);
116
+ if (before !== undefined)
117
+ return before;
118
+ if (record.generation === undefined)
119
+ return "identity_unavailable";
120
+ const generation = await readSessionLogGeneration(record.runDir);
121
+ // Recheck after the I/O: a replacement may bind while the read is pending.
122
+ const after = this.selectionFailure(record);
123
+ if (after !== undefined)
124
+ return after;
125
+ if (generation.kind !== "available")
126
+ return "identity_unavailable";
127
+ if (generation.sessionLogGeneration !== record.generation)
128
+ return "identity_changed";
129
+ if (record.settled && record.publication === undefined)
130
+ return "identity_unavailable";
131
+ if (observedDigest !== undefined) {
132
+ if (record.publication === undefined || observedDigest === null)
133
+ return "identity_unavailable";
134
+ if (observedDigest !== record.publication.fingerprint)
135
+ return "identity_changed";
136
+ }
137
+ return undefined;
138
+ }
139
+ wait(entry, signal) {
140
+ const record = this.own(entry);
141
+ if (signal.aborted)
142
+ return Promise.reject(new WorkflowCancellationError());
143
+ if (record.settled)
144
+ return Promise.resolve("settled");
145
+ return new Promise((resolveWait, reject) => {
146
+ let timer;
147
+ const cleanup = () => {
148
+ if (timer !== undefined)
149
+ clearTimeout(timer);
150
+ record.listeners.delete(onComplete);
151
+ signal.removeEventListener("abort", onAbort);
152
+ };
153
+ const onComplete = () => {
154
+ cleanup();
155
+ resolveWait("settled");
156
+ };
157
+ const onAbort = () => {
158
+ cleanup();
159
+ reject(new WorkflowCancellationError());
160
+ };
161
+ record.listeners.add(onComplete);
162
+ signal.addEventListener("abort", onAbort, { once: true });
163
+ timer = setTimeout(() => {
164
+ cleanup();
165
+ resolveWait("budget_elapsed");
166
+ }, WORKFLOW_WAIT_BUDGET_MS);
167
+ // A pending observation must not keep an otherwise idle process alive.
168
+ timer.unref?.();
169
+ // Inspect after registration, so a fast completion cannot strand a waiter.
170
+ if (record.settled)
171
+ onComplete();
172
+ else if (signal.aborted)
173
+ onAbort();
174
+ });
175
+ }
176
+ selectionFailure(record) {
177
+ const selected = this.select(record.runDir, record.executionId);
178
+ if (typeof selected === "string")
179
+ return selected;
180
+ return selected === record ? undefined : "identity_unavailable";
181
+ }
182
+ own(entry) {
183
+ if (!this.minted.has(entry))
184
+ throw new Error("workflow completion was not bound by this registry");
185
+ return entry;
186
+ }
187
+ prune() {
188
+ while (this.settledIds.length > SETTLED_LIMIT) {
189
+ const index = this.settledIds.findIndex((id) => this.byId.get(id)?.observers === 0);
190
+ if (index === -1)
191
+ return;
192
+ const id = this.settledIds.splice(index, 1)[0];
193
+ const retired = this.byId.get(id);
194
+ this.byId.delete(id);
195
+ if (retired !== undefined && this.latest.get(retired.runDir) === retired) {
196
+ this.latest.delete(retired.runDir);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ function isTerminalStatus(status) {
202
+ return status === "succeeded" || status === "failed" || status === "cancelled";
203
+ }
204
+ /**
205
+ * Persisted provenance for a pin this process retains no record for. Only a
206
+ * terminal state stamped with exactly that id, whose run.lock is released
207
+ * (`stale_or_absent`), counts as its settlement: a live or unknown lock means
208
+ * an owner may still be inside its cleanup fence, and a non-terminal match has
209
+ * no local owner whose fence could be witnessed.
210
+ */
211
+ export function persistedIdentityFailure(state, lock, runId, expected) {
212
+ if (typeof state.executionId !== "string" || state.runId !== runId)
213
+ return "identity_unavailable";
214
+ if (state.executionId !== expected)
215
+ return "identity_changed";
216
+ if (!isTerminalStatus(state.status) || lock !== "stale_or_absent")
217
+ return "identity_unavailable";
218
+ return typeof state.completedAt === "string" && state.executionCompletedAt === state.completedAt
219
+ ? undefined
220
+ : "identity_unavailable";
221
+ }
222
+ export async function inspectPublication(runDir) {
223
+ try {
224
+ const before = await readSessionLogGeneration(runDir);
225
+ if (before.kind !== "available")
226
+ return undefined;
227
+ const file = await readContainedRelativeFile(runDir, "state.json", {
228
+ wantBytes: true,
229
+ requireRegular: true,
230
+ requireExactRequestedPath: true,
231
+ maxBytes: DEFAULT_DASHBOARD_MAX_STATE_BYTES,
232
+ });
233
+ const state = JSON.parse(file.bytes.toString("utf-8"));
234
+ const after = await readSessionLogGeneration(runDir);
235
+ if (after.kind !== "available" || after.sessionLogGeneration !== before.sessionLogGeneration) {
236
+ return undefined;
237
+ }
238
+ return {
239
+ generation: before.sessionLogGeneration,
240
+ fingerprint: fingerprintStateBytes(file.bytes),
241
+ terminal: isTerminalStatus(state.status),
242
+ };
243
+ }
244
+ catch (error) {
245
+ // Class only: messages can carry paths or state values.
246
+ logger.debug({ errClass: error instanceof Error ? error.constructor.name : typeof error, runDir }, "workflow publication identity unavailable");
247
+ return undefined;
248
+ }
249
+ }
250
+ //# sourceMappingURL=workflow-completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-completion.js","sourceRoot":"","sources":["../src/workflow-completion.ts"],"names":[],"mappings":"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"}
@@ -5,11 +5,12 @@
5
5
  * - Standalone STDIO server (ClinkSession child process backend)
6
6
  * - In-process integration (LocalClinkBackend via root ClinkX)
7
7
  *
8
- * The `registerWorkflowTools()` function registers five tools on a Server:
8
+ * The `registerWorkflowTools()` function registers six tools on a Server:
9
9
  * - run_workflow: start a new workflow execution
10
10
  * - resume_workflow: resume a failed/cancelled run
11
11
  * - list_workflows: list available workflow definitions
12
12
  * - workflow_run_status: read workflow run progress/result
13
+ * - workflow_run_wait: observe local execution settlement
13
14
  * - cancel_workflow: cancel a live or orphaned workflow run
14
15
  *
15
16
  * The caller provides a `createBackend` factory that determines how clink
@@ -194,8 +195,8 @@ export declare function handleWorkflowToolCall(server: Server, options: Workflow
194
195
  * Register workflow tools on an MCP Server instance.
195
196
  *
196
197
  * This is the reusable core extracted from createWorkflowServer().
197
- * It registers five tools: run_workflow, resume_workflow, list_workflows,
198
- * workflow_run_status, cancel_workflow.
198
+ * It registers six tools: run_workflow, resume_workflow, list_workflows,
199
+ * workflow_run_status, workflow_run_wait, cancel_workflow.
199
200
  *
200
201
  * The `options.createBackend` factory determines how clink calls are
201
202
  * dispatched — callers provide either a child-process or in-process backend.