@descryy/runtime-controller 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +6 -0
  2. package/dist/attach-fencing.d.ts +109 -0
  3. package/dist/attach-fencing.d.ts.map +1 -0
  4. package/dist/attach-fencing.js +215 -0
  5. package/dist/attach-fencing.js.map +1 -0
  6. package/dist/capability-registry.d.ts +0 -15
  7. package/dist/capability-registry.d.ts.map +1 -1
  8. package/dist/capability-registry.js +9 -19
  9. package/dist/capability-registry.js.map +1 -1
  10. package/dist/collector-version.d.ts +1 -8
  11. package/dist/collector-version.d.ts.map +1 -1
  12. package/dist/collector-version.js +1 -8
  13. package/dist/collector-version.js.map +1 -1
  14. package/dist/container-sandbox.d.ts +66 -150
  15. package/dist/container-sandbox.d.ts.map +1 -1
  16. package/dist/container-sandbox.js +62 -143
  17. package/dist/container-sandbox.js.map +1 -1
  18. package/dist/controller.d.ts +49 -106
  19. package/dist/controller.d.ts.map +1 -1
  20. package/dist/controller.js +65 -123
  21. package/dist/controller.js.map +1 -1
  22. package/dist/dependency-version-check.d.ts +19 -73
  23. package/dist/dependency-version-check.d.ts.map +1 -1
  24. package/dist/dependency-version-check.js +18 -67
  25. package/dist/dependency-version-check.js.map +1 -1
  26. package/dist/env.d.ts +4 -10
  27. package/dist/env.d.ts.map +1 -1
  28. package/dist/env.js +4 -10
  29. package/dist/env.js.map +1 -1
  30. package/dist/environment-metadata.d.ts +9 -17
  31. package/dist/environment-metadata.d.ts.map +1 -1
  32. package/dist/environment-metadata.js +12 -29
  33. package/dist/environment-metadata.js.map +1 -1
  34. package/dist/environment-version-check.d.ts +22 -65
  35. package/dist/environment-version-check.d.ts.map +1 -1
  36. package/dist/environment-version-check.js +24 -66
  37. package/dist/environment-version-check.js.map +1 -1
  38. package/dist/execution-safety.d.ts +54 -112
  39. package/dist/execution-safety.d.ts.map +1 -1
  40. package/dist/execution-safety.js +48 -102
  41. package/dist/execution-safety.js.map +1 -1
  42. package/dist/index.d.ts +2 -10
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/orchestration.d.ts +24 -39
  47. package/dist/orchestration.d.ts.map +1 -1
  48. package/dist/orchestration.js +39 -75
  49. package/dist/orchestration.js.map +1 -1
  50. package/dist/process-collector.d.ts +8 -22
  51. package/dist/process-collector.d.ts.map +1 -1
  52. package/dist/process-collector.js +23 -55
  53. package/dist/process-collector.js.map +1 -1
  54. package/dist/process-manager.d.ts +65 -80
  55. package/dist/process-manager.d.ts.map +1 -1
  56. package/dist/process-manager.js +80 -103
  57. package/dist/process-manager.js.map +1 -1
  58. package/dist/readiness.d.ts +28 -70
  59. package/dist/readiness.d.ts.map +1 -1
  60. package/dist/readiness.js +73 -95
  61. package/dist/readiness.js.map +1 -1
  62. package/dist/sandbox.d.ts +60 -105
  63. package/dist/sandbox.d.ts.map +1 -1
  64. package/dist/sandbox.js +78 -121
  65. package/dist/sandbox.js.map +1 -1
  66. package/package.json +8 -3
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) Descry
2
+
3
+ All rights reserved. This software is proprietary and confidential.
4
+ No license, express or implied, to reproduce, distribute, modify, or
5
+ create derivative works is granted except as explicitly agreed in
6
+ writing.
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Attach fencing (RT-N3): the boundary between "content this run watched happen"
3
+ * and "content that already existed when this run started watching it."
4
+ *
5
+ * Both `orchestrator/src/attach-to-running-process.ts` and this package's own
6
+ * `attachManagedProcess` read a target's ENTIRE log file on every poll —
7
+ * necessary, since a plain file offers no delta API — which means the very
8
+ * first read hands back everything the target had already written before
9
+ * attach, indistinguishable from something that happens during this run
10
+ * unless something records the boundary.
11
+ *
12
+ * Real incident (`documents/plans/descry-uat-phase-4.md`, finding N3): a
13
+ * killed, orphaned FIRST process took its full timeout to exit and kept
14
+ * writing to the SAME log path a SECOND, successful process also used;
15
+ * attaching to the second process's pid read the first process's crash as if
16
+ * it were the second's own. Two independent fences close two independent
17
+ * parts of that gap:
18
+ *
19
+ * - `computeAttachFence` records, at the moment of attach, how much of the
20
+ * log file already existed (`attachOffsetBytes`, `preExistingLineCount`)
21
+ * and the target's own process start time (best-effort, Linux only) — the
22
+ * information a caller needs to mark already-written content instead of
23
+ * treating it as freshly observed.
24
+ * - `detectOtherLogWriters` answers a different question: is some OTHER
25
+ * process, right now, also holding this same log file open for writing?
26
+ * On Linux only, by scanning `/proc/*\/fd`. A genuinely shared log (e.g. a
27
+ * `docker compose` stack's combined stream) is a legitimate answer here,
28
+ * not refused — this function reports who else has the file open, and it
29
+ * is the caller's job to decide what that means for a given target.
30
+ *
31
+ * Neither function makes attach perfectly correct — a log file carries no
32
+ * per-line writer-pid metadata, so "verify every line against the declared
33
+ * pid" is not implementable over a plain text file. What these two give a
34
+ * caller together: the boundary of what this run actually watched happen,
35
+ * and a best-effort, honestly-labelled answer to "is anything else writing
36
+ * here right now."
37
+ */
38
+ export type ProcessStartTimeResult = {
39
+ readonly ok: true;
40
+ readonly startTimeMs: number;
41
+ } | {
42
+ readonly ok: false;
43
+ readonly reason: string;
44
+ };
45
+ /**
46
+ * `pid`'s own start time, as wall-clock epoch milliseconds — Linux only.
47
+ * `/proc/<pid>/stat` field 22 (`starttime`) is clock ticks since boot, not
48
+ * since epoch; converted via `/proc/uptime`'s boot-relative uptime, itself
49
+ * converted to an epoch instant from `Date.now()` at read time (so this is
50
+ * only as precise as the gap between the two reads, sub-millisecond in
51
+ * practice).
52
+ *
53
+ * The `comm` field (`/proc/pid/stat` field 2) can itself contain spaces and
54
+ * parentheses — parsed by finding the LAST `)` on the line, matching every
55
+ * real `/proc/pid/stat` parser: the kernel guarantees the true comm field
56
+ * ends at the last `)`, whatever it contains.
57
+ */
58
+ export declare function getProcessStartTimeMs(pid: number, platform?: NodeJS.Platform): ProcessStartTimeResult;
59
+ export type OtherLogWriterCheck = {
60
+ readonly checked: true;
61
+ readonly otherWriterPids: readonly number[];
62
+ } | {
63
+ readonly checked: false;
64
+ readonly reason: string;
65
+ };
66
+ /**
67
+ * Every OTHER process (not `targetPid`) currently holding an open file
68
+ * descriptor to `logFilePath`, by scanning `/proc/*\/fd` and comparing each
69
+ * descriptor's real target against `logFilePath`'s own real path. Linux only.
70
+ *
71
+ * A non-empty result is not by itself a problem — a deliberately shared log
72
+ * (several processes in one `docker compose` stack writing one combined
73
+ * stream) is legitimate multi-writer use. This function only answers "who
74
+ * else has it open right now"; a caller decides what that means for a given
75
+ * target.
76
+ *
77
+ * `checked: false` (never a thrown exception) only for what stops the scan
78
+ * from running AT ALL: non-Linux, `/proc` itself unreadable, or the log path
79
+ * can't be resolved to a real path. A single pid's `/proc/<pid>/fd` being
80
+ * unreadable (exited mid-scan, or a permission boundary) is NOT that —
81
+ * skipped for that one pid only, the same benign-race tolerance
82
+ * `inferRespawnCommandFromProc` already applies to `/proc` reads elsewhere
83
+ * in this codebase.
84
+ */
85
+ export declare function detectOtherLogWriters(logFilePath: string, targetPid: number, platform?: NodeJS.Platform): OtherLogWriterCheck;
86
+ export interface AttachFence {
87
+ /** The log file's size in bytes at the moment of attach — rotation detection compares this (or a later high-water mark) against the file's current size. */
88
+ readonly attachOffsetBytes: number;
89
+ /**
90
+ * How many complete lines already existed in the log file at the moment of
91
+ * attach. A source that yields lines in file order marks the first
92
+ * this-many `preExisting: true`. See `countCompleteLines` for the one
93
+ * disclosed edge case (a line torn exactly at the attach instant).
94
+ */
95
+ readonly preExistingLineCount: number;
96
+ /** Best-effort, Linux-only; `ok: false` on any other platform or read failure — never blocks the attach itself. */
97
+ readonly processStartTime: ProcessStartTimeResult;
98
+ /** See `detectOtherLogWriters` — computed once, at the moment of attach, against the pid this fence was built for. */
99
+ readonly otherWriters: OtherLogWriterCheck;
100
+ readonly attachedAt: string;
101
+ }
102
+ /**
103
+ * Snapshots the fence at the moment of attach. Never throws: a log file that
104
+ * doesn't exist yet at attach time (a target that hasn't written anything) is
105
+ * an honest zero-backlog fence, not an error — the file-tail machinery
106
+ * downstream already tolerates a not-yet-existing path the same way.
107
+ */
108
+ export declare function computeAttachFence(pid: number, logFilePath: string, platform?: NodeJS.Platform): AttachFence;
109
+ //# sourceMappingURL=attach-fencing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach-fencing.d.ts","sourceRoot":"","sources":["../src/attach-fencing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAsBH,MAAM,MAAM,sBAAsB,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAwB3I;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,sBAAsB,CAiCvH;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,mBAAmB,CA+C/I;AAED,MAAM,WAAW,WAAW;IAC1B,4JAA4J;IAC5J,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,mHAAmH;IACnH,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;IAClD,sHAAsH;IACtH,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,WAAW,CAgB9H"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Attach fencing (RT-N3): the boundary between "content this run watched happen"
3
+ * and "content that already existed when this run started watching it."
4
+ *
5
+ * Both `orchestrator/src/attach-to-running-process.ts` and this package's own
6
+ * `attachManagedProcess` read a target's ENTIRE log file on every poll —
7
+ * necessary, since a plain file offers no delta API — which means the very
8
+ * first read hands back everything the target had already written before
9
+ * attach, indistinguishable from something that happens during this run
10
+ * unless something records the boundary.
11
+ *
12
+ * Real incident (`documents/plans/descry-uat-phase-4.md`, finding N3): a
13
+ * killed, orphaned FIRST process took its full timeout to exit and kept
14
+ * writing to the SAME log path a SECOND, successful process also used;
15
+ * attaching to the second process's pid read the first process's crash as if
16
+ * it were the second's own. Two independent fences close two independent
17
+ * parts of that gap:
18
+ *
19
+ * - `computeAttachFence` records, at the moment of attach, how much of the
20
+ * log file already existed (`attachOffsetBytes`, `preExistingLineCount`)
21
+ * and the target's own process start time (best-effort, Linux only) — the
22
+ * information a caller needs to mark already-written content instead of
23
+ * treating it as freshly observed.
24
+ * - `detectOtherLogWriters` answers a different question: is some OTHER
25
+ * process, right now, also holding this same log file open for writing?
26
+ * On Linux only, by scanning `/proc/*\/fd`. A genuinely shared log (e.g. a
27
+ * `docker compose` stack's combined stream) is a legitimate answer here,
28
+ * not refused — this function reports who else has the file open, and it
29
+ * is the caller's job to decide what that means for a given target.
30
+ *
31
+ * Neither function makes attach perfectly correct — a log file carries no
32
+ * per-line writer-pid metadata, so "verify every line against the declared
33
+ * pid" is not implementable over a plain text file. What these two give a
34
+ * caller together: the boundary of what this run actually watched happen,
35
+ * and a best-effort, honestly-labelled answer to "is anything else writing
36
+ * here right now."
37
+ */
38
+ import { execFileSync } from "node:child_process";
39
+ import { readFileSync, readdirSync, readlinkSync, realpathSync, statSync } from "node:fs";
40
+ /**
41
+ * Complete (newline-terminated) lines in `text` — the same rule `LineSplitter`
42
+ * (`@descryy/runtime-backend-observation`) applies, reimplemented here rather
43
+ * than imported: this package does not depend on that one (the dependency
44
+ * runs the other way), and the rule is three lines. A trailing, unterminated
45
+ * partial line is deliberately NOT counted — see `computeAttachFence`'s doc
46
+ * for the one edge case that leaves disclosed rather than solved.
47
+ */
48
+ function countCompleteLines(text) {
49
+ if (text === "")
50
+ return 0;
51
+ const parts = text.split(/\r\n|\r|\n/);
52
+ // split() always yields at least one element; the LAST one is whatever
53
+ // followed the final terminator (empty, when text ends with one — itself
54
+ // not a line — or a real trailing partial otherwise; neither is counted).
55
+ return parts.length - 1;
56
+ }
57
+ let cachedClockTicksPerSecond = null;
58
+ /**
59
+ * `sysconf(_SC_CLK_TCK)` has no Node binding; `getconf CLK_TCK` is the same
60
+ * syscall via the POSIX utility every Linux ships. Cached process-wide — it
61
+ * cannot change while this process runs. Falls back to 100 (`USER_HZ`, the
62
+ * value on every Linux kernel/architecture this has been measured against)
63
+ * if `getconf` itself is unavailable, rather than failing the whole
64
+ * start-time computation over a missing convenience binary.
65
+ */
66
+ function clockTicksPerSecond() {
67
+ if (cachedClockTicksPerSecond !== null)
68
+ return cachedClockTicksPerSecond;
69
+ try {
70
+ const output = execFileSync("getconf", ["CLK_TCK"], { encoding: "utf8" }).trim();
71
+ const parsed = Number(output);
72
+ cachedClockTicksPerSecond = Number.isFinite(parsed) && parsed > 0 ? parsed : 100;
73
+ }
74
+ catch {
75
+ cachedClockTicksPerSecond = 100;
76
+ }
77
+ return cachedClockTicksPerSecond;
78
+ }
79
+ /**
80
+ * `pid`'s own start time, as wall-clock epoch milliseconds — Linux only.
81
+ * `/proc/<pid>/stat` field 22 (`starttime`) is clock ticks since boot, not
82
+ * since epoch; converted via `/proc/uptime`'s boot-relative uptime, itself
83
+ * converted to an epoch instant from `Date.now()` at read time (so this is
84
+ * only as precise as the gap between the two reads, sub-millisecond in
85
+ * practice).
86
+ *
87
+ * The `comm` field (`/proc/pid/stat` field 2) can itself contain spaces and
88
+ * parentheses — parsed by finding the LAST `)` on the line, matching every
89
+ * real `/proc/pid/stat` parser: the kernel guarantees the true comm field
90
+ * ends at the last `)`, whatever it contains.
91
+ */
92
+ export function getProcessStartTimeMs(pid, platform = process.platform) {
93
+ if (platform !== "linux") {
94
+ return { ok: false, reason: `process start time via /proc is Linux-only; not available on "${platform}"` };
95
+ }
96
+ let statLine;
97
+ try {
98
+ statLine = readFileSync(`/proc/${String(pid)}/stat`, "utf8");
99
+ }
100
+ catch (error) {
101
+ return { ok: false, reason: `could not read /proc/${String(pid)}/stat: ${error instanceof Error ? error.message : String(error)}` };
102
+ }
103
+ const afterComm = statLine.slice(statLine.lastIndexOf(")") + 1).trim();
104
+ const fields = afterComm.split(/\s+/);
105
+ // field 1 of `afterComm` is /proc/pid/stat's field 3 (state); starttime is
106
+ // field 22 overall, i.e. index 19 in this zero-indexed, comm-stripped array.
107
+ const starttimeTicks = Number(fields[19]);
108
+ if (!Number.isFinite(starttimeTicks)) {
109
+ return { ok: false, reason: `/proc/${String(pid)}/stat did not parse as expected — starttime field missing or non-numeric` };
110
+ }
111
+ let uptimeSeconds;
112
+ try {
113
+ const uptimeRaw = readFileSync("/proc/uptime", "utf8").trim().split(/\s+/)[0];
114
+ uptimeSeconds = Number(uptimeRaw);
115
+ if (!Number.isFinite(uptimeSeconds))
116
+ throw new Error("non-numeric");
117
+ }
118
+ catch (error) {
119
+ return { ok: false, reason: `could not read /proc/uptime to convert starttime to wall-clock time: ${error instanceof Error ? error.message : String(error)}` };
120
+ }
121
+ const bootEpochMs = Date.now() - uptimeSeconds * 1000;
122
+ const startTimeMs = bootEpochMs + (starttimeTicks / clockTicksPerSecond()) * 1000;
123
+ return { ok: true, startTimeMs };
124
+ }
125
+ /**
126
+ * Every OTHER process (not `targetPid`) currently holding an open file
127
+ * descriptor to `logFilePath`, by scanning `/proc/*\/fd` and comparing each
128
+ * descriptor's real target against `logFilePath`'s own real path. Linux only.
129
+ *
130
+ * A non-empty result is not by itself a problem — a deliberately shared log
131
+ * (several processes in one `docker compose` stack writing one combined
132
+ * stream) is legitimate multi-writer use. This function only answers "who
133
+ * else has it open right now"; a caller decides what that means for a given
134
+ * target.
135
+ *
136
+ * `checked: false` (never a thrown exception) only for what stops the scan
137
+ * from running AT ALL: non-Linux, `/proc` itself unreadable, or the log path
138
+ * can't be resolved to a real path. A single pid's `/proc/<pid>/fd` being
139
+ * unreadable (exited mid-scan, or a permission boundary) is NOT that —
140
+ * skipped for that one pid only, the same benign-race tolerance
141
+ * `inferRespawnCommandFromProc` already applies to `/proc` reads elsewhere
142
+ * in this codebase.
143
+ */
144
+ export function detectOtherLogWriters(logFilePath, targetPid, platform = process.platform) {
145
+ if (platform !== "linux") {
146
+ return { checked: false, reason: `second-writer detection requires scanning /proc/*/fd, which is Linux-only; not available on "${platform}"` };
147
+ }
148
+ let target;
149
+ try {
150
+ target = realpathSync(logFilePath);
151
+ }
152
+ catch (error) {
153
+ return {
154
+ checked: false,
155
+ reason: `could not resolve a real path for "${logFilePath}" to compare against other processes' open file descriptors: ${error instanceof Error ? error.message : String(error)}`,
156
+ };
157
+ }
158
+ let pidEntries;
159
+ try {
160
+ pidEntries = readdirSync("/proc").filter((entry) => /^\d+$/.test(entry));
161
+ }
162
+ catch (error) {
163
+ return { checked: false, reason: `could not list /proc to scan for other processes holding "${logFilePath}" open: ${error instanceof Error ? error.message : String(error)}` };
164
+ }
165
+ const otherWriterPids = [];
166
+ for (const entry of pidEntries) {
167
+ const pid = Number(entry);
168
+ if (pid === targetPid)
169
+ continue; // the attached target itself is expected to hold it open -- not "other"
170
+ let fds;
171
+ try {
172
+ fds = readdirSync(`/proc/${entry}/fd`);
173
+ }
174
+ catch {
175
+ continue; // exited mid-scan, or no permission -- a benign race, not this scan's failure
176
+ }
177
+ for (const fd of fds) {
178
+ try {
179
+ if (readlinkSync(`/proc/${entry}/fd/${fd}`) === target) {
180
+ otherWriterPids.push(pid);
181
+ break;
182
+ }
183
+ }
184
+ catch {
185
+ // fd closed between readdir and readlink -- benign race, skip
186
+ }
187
+ }
188
+ }
189
+ return { checked: true, otherWriterPids };
190
+ }
191
+ /**
192
+ * Snapshots the fence at the moment of attach. Never throws: a log file that
193
+ * doesn't exist yet at attach time (a target that hasn't written anything) is
194
+ * an honest zero-backlog fence, not an error — the file-tail machinery
195
+ * downstream already tolerates a not-yet-existing path the same way.
196
+ */
197
+ export function computeAttachFence(pid, logFilePath, platform = process.platform) {
198
+ let attachOffsetBytes = 0;
199
+ let preExistingLineCount = 0;
200
+ try {
201
+ attachOffsetBytes = statSync(logFilePath).size;
202
+ preExistingLineCount = countCompleteLines(readFileSync(logFilePath, "utf8"));
203
+ }
204
+ catch {
205
+ // Not yet created -- nothing pre-existing, the honest default.
206
+ }
207
+ return {
208
+ attachOffsetBytes,
209
+ preExistingLineCount,
210
+ processStartTime: getProcessStartTimeMs(pid, platform),
211
+ otherWriters: detectOtherLogWriters(logFilePath, pid, platform),
212
+ attachedAt: new Date().toISOString(),
213
+ };
214
+ }
215
+ //# sourceMappingURL=attach-fencing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach-fencing.js","sourceRoot":"","sources":["../src/attach-fencing.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,3 @@
1
- /**
2
- * Capability registry (plan §16): assembles per-execution capabilities
3
- * from the collectors actually present, so `distributedTrace: unavailable`
4
- * is a first-class answer the AI planner can read rather than a silent
5
- * gap. Does not know how to *produce* a `CollectorCapabilities` value —
6
- * that's each collector's own `capabilities()` (Agents 2-4's concern) —
7
- * only how to assemble and query what's been registered.
8
- */
9
1
  import type { CapabilityStatus, CollectorCapabilities, EnvironmentTier, ExecutionCapabilities, FidelityLevel } from "@descryy/runtime-contracts";
10
2
  export interface RegisteredCollectorCapabilities {
11
3
  readonly collectorId: string;
@@ -13,12 +5,5 @@ export interface RegisteredCollectorCapabilities {
13
5
  }
14
6
  export declare function buildExecutionCapabilities(environmentTier: EnvironmentTier, fidelityLevel: FidelityLevel, collectors: readonly RegisteredCollectorCapabilities[]): ExecutionCapabilities;
15
7
  export type CollectorCapabilityKey = keyof CollectorCapabilities;
16
- /**
17
- * The best (most available) status for one capability across every
18
- * registered collector — "is this observable *anywhere* in this
19
- * execution," not per-collector. An execution with two network collectors
20
- * where one degraded and one is fully available should read as available,
21
- * not degraded.
22
- */
23
8
  export declare function bestCapabilityStatus(capabilities: ExecutionCapabilities, key: CollectorCapabilityKey): CapabilityStatus;
24
9
  //# sourceMappingURL=capability-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"capability-registry.d.ts","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;CAC9C;AAED,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,SAAS,+BAA+B,EAAE,GACrD,qBAAqB,CAEvB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,qBAAqB,CAAC;AAajE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,qBAAqB,EACnC,GAAG,EAAE,sBAAsB,GAC1B,gBAAgB,CAalB"}
1
+ {"version":3,"file":"capability-registry.d.ts","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;CAC9C;AAED,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,SAAS,+BAA+B,EAAE,GACrD,qBAAqB,CAEvB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,qBAAqB,CAAC;AAgBjE,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,qBAAqB,EACnC,GAAG,EAAE,sBAAsB,GAC1B,gBAAgB,CAWlB"}
@@ -1,11 +1,7 @@
1
- /**
2
- * Capability registry (plan §16): assembles per-execution capabilities
3
- * from the collectors actually present, so `distributedTrace: unavailable`
4
- * is a first-class answer the AI planner can read rather than a silent
5
- * gap. Does not know how to *produce* a `CollectorCapabilities` value —
6
- * that's each collector's own `capabilities()` (Agents 2-4's concern) —
7
- * only how to assemble and query what's been registered.
8
- */
1
+ // Capability registry (§16): assembles per-execution capabilities from the collectors
2
+ // actually present, so e.g. `distributedTrace: unavailable` is a first-class answer
3
+ // rather than a silent gap. Assembles and queries only — producing a
4
+ // `CollectorCapabilities` value is each collector's own `capabilities()`.
9
5
  export function buildExecutionCapabilities(environmentTier, fidelityLevel, collectors) {
10
6
  return { environmentTier, fidelityLevel, collectors };
11
7
  }
@@ -18,18 +14,12 @@ const NO_COLLECTOR_STATUS = {
18
14
  availability: "unavailable",
19
15
  reason: "no collector registered for this execution",
20
16
  };
21
- /**
22
- * The best (most available) status for one capability across every
23
- * registered collector — "is this observable *anywhere* in this
24
- * execution," not per-collector. An execution with two network collectors
25
- * where one degraded and one is fully available should read as available,
26
- * not degraded.
27
- */
17
+ // Best (most available) status for one capability across all registered collectors —
18
+ // "observable anywhere in this execution," not per-collector: two network collectors,
19
+ // one degraded one available, should read as available.
28
20
  export function bestCapabilityStatus(capabilities, key) {
29
- // Seeded from the first collector actually seen, not from
30
- // NO_COLLECTOR_STATUS -- seeding from the placeholder would make a
31
- // single collector's genuine "unavailable" tie the placeholder's rank
32
- // and lose its specific reason to the generic one.
21
+ // Seeded from the first collector seen, not NO_COLLECTOR_STATUS — seeding from the
22
+ // placeholder would let a real "unavailable" tie the placeholder and lose its reason.
33
23
  let best = null;
34
24
  for (const collector of capabilities.collectors) {
35
25
  const status = collector.capabilities[key];
@@ -1 +1 @@
1
- {"version":3,"file":"capability-registry.js","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,MAAM,UAAU,0BAA0B,CACxC,eAAgC,EAChC,aAA4B,EAC5B,UAAsD;IAEtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACxD,CAAC;AAID,MAAM,iBAAiB,GAAqD;IAC1E,WAAW,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,mBAAmB,GAAqB;IAC5C,YAAY,EAAE,aAAa;IAC3B,MAAM,EAAE,4CAA4C;CACrD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAmC,EACnC,GAA2B;IAE3B,0DAA0D;IAC1D,mEAAmE;IACnE,sEAAsE;IACtE,mDAAmD;IACnD,IAAI,IAAI,GAA4B,IAAI,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,IAAI,KAAK,IAAI,IAAI,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACnG,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,IAAI,mBAAmB,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"capability-registry.js","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,oFAAoF;AACpF,qEAAqE;AACrE,0EAA0E;AAgB1E,MAAM,UAAU,0BAA0B,CACxC,eAAgC,EAChC,aAA4B,EAC5B,UAAsD;IAEtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACxD,CAAC;AAID,MAAM,iBAAiB,GAAqD;IAC1E,WAAW,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,mBAAmB,GAAqB;IAC5C,YAAY,EAAE,aAAa;IAC3B,MAAM,EAAE,4CAA4C;CACrD,CAAC;AAEF,qFAAqF;AACrF,sFAAsF;AACtF,wDAAwD;AACxD,MAAM,UAAU,oBAAoB,CAClC,YAAmC,EACnC,GAA2B;IAE3B,mFAAmF;IACnF,sFAAsF;IACtF,IAAI,IAAI,GAA4B,IAAI,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,IAAI,KAAK,IAAI,IAAI,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACnG,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,IAAI,mBAAmB,CAAC;AACrC,CAAC"}
@@ -1,10 +1,3 @@
1
- /**
2
- * `Evidence.collectorVersion`'s source for `ProcessCollector`: this
3
- * package's own `package.json` "version", read from the artifact itself
4
- * rather than hand-duplicated into a second string that can drift from
5
- * what actually shipped. Same idea as `nodeVersion` elsewhere in this repo
6
- * (`process.version`, always known, never probed) applied to a package
7
- * instead of the Node runtime.
8
- */
1
+ /** `Evidence.collectorVersion` for `ProcessCollector`: read from package.json itself so it can't drift from what shipped. Same pattern as `nodeVersion` (`process.version`), applied to a package instead of the runtime. */
9
2
  export declare const COLLECTOR_VERSION: string;
10
3
  //# sourceMappingURL=collector-version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,eAAO,MAAM,iBAAiB,EAAE,MAA6E,CAAC"}
1
+ {"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA,6NAA6N;AAM7N,eAAO,MAAM,iBAAiB,EAAE,MAA6E,CAAC"}
@@ -1,11 +1,4 @@
1
- /**
2
- * `Evidence.collectorVersion`'s source for `ProcessCollector`: this
3
- * package's own `package.json` "version", read from the artifact itself
4
- * rather than hand-duplicated into a second string that can drift from
5
- * what actually shipped. Same idea as `nodeVersion` elsewhere in this repo
6
- * (`process.version`, always known, never probed) applied to a package
7
- * instead of the Node runtime.
8
- */
1
+ /** `Evidence.collectorVersion` for `ProcessCollector`: read from package.json itself so it can't drift from what shipped. Same pattern as `nodeVersion` (`process.version`), applied to a package instead of the runtime. */
9
2
  import { createRequire } from "node:module";
10
3
  const require = createRequire(import.meta.url);
11
4
  export const COLLECTOR_VERSION = require("../package.json").version;
@@ -1 +1 @@
1
- {"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAY,OAAO,CAAC,iBAAiB,CAAkC,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA,6NAA6N;AAE7N,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAY,OAAO,CAAC,iBAAiB,CAAkC,CAAC,OAAO,CAAC"}