pi-subagents 0.80.0 → 0.81.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 (87) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bundle/agents.js +18 -0
  3. package/bundle/background-work.js +16 -0
  4. package/bundle/capability-ceiling.js +22 -0
  5. package/bundle/child-tool-plan.js +11 -0
  6. package/bundle/chunk-26C3WLYQ.js +8388 -0
  7. package/bundle/chunk-2XYWN7QJ.js +128 -0
  8. package/bundle/chunk-2ZSNH26H.js +4164 -0
  9. package/bundle/chunk-3DC5KBVP.js +235 -0
  10. package/bundle/chunk-3IF2PGW7.js +380 -0
  11. package/bundle/chunk-4QFZZV3O.js +57 -0
  12. package/bundle/chunk-563BHAZ5.js +1365 -0
  13. package/bundle/chunk-5LFLVSTM.js +260 -0
  14. package/bundle/chunk-62OCY2S4.js +14 -0
  15. package/bundle/chunk-74RT5YQE.js +326 -0
  16. package/bundle/chunk-7F6RKT6W.js +132 -0
  17. package/bundle/chunk-7J4ZXMY7.js +18 -0
  18. package/bundle/chunk-ARRKBB2B.js +179 -0
  19. package/bundle/chunk-BJN5LUQY.js +458 -0
  20. package/bundle/chunk-BPS2AGBZ.js +5421 -0
  21. package/bundle/chunk-CEKS3TFO.js +1337 -0
  22. package/bundle/chunk-CTPAE36Y.js +94 -0
  23. package/bundle/chunk-D4EHT5SJ.js +14 -0
  24. package/bundle/chunk-EYNJ6VL6.js +526 -0
  25. package/bundle/chunk-G5DKFLLI.js +1185 -0
  26. package/bundle/chunk-KSWQVGDW.js +1027 -0
  27. package/bundle/chunk-L5ZHE4L4.js +946 -0
  28. package/bundle/chunk-LSRUTHT7.js +57 -0
  29. package/bundle/chunk-N2BN7BF5.js +159 -0
  30. package/bundle/chunk-NF7IEKBH.js +490 -0
  31. package/bundle/chunk-NIXWDZZV.js +9 -0
  32. package/bundle/chunk-NNLASTQE.js +4082 -0
  33. package/bundle/chunk-TUWLXTAM.js +935 -0
  34. package/bundle/chunk-UKG4V45O.js +228 -0
  35. package/bundle/chunk-V2JWDB5U.js +753 -0
  36. package/bundle/chunk-V3BIUPLU.js +9163 -0
  37. package/bundle/chunk-V6MR5TOQ.js +270 -0
  38. package/bundle/chunk-VJHOELQP.js +8245 -0
  39. package/bundle/chunk-WSBNENFS.js +732 -0
  40. package/bundle/chunk-XMFLCKMQ.js +16664 -0
  41. package/bundle/chunk-YFCK7H2S.js +129 -0
  42. package/bundle/chunk-YYREO77V.js +137 -0
  43. package/bundle/chunk-Z7KVHQHL.js +173 -0
  44. package/bundle/chunk-ZBC44JJ3.js +8606 -0
  45. package/bundle/chunk-ZP3HODFH.js +175 -0
  46. package/bundle/control-channel.js +11 -0
  47. package/bundle/delegation.js +14 -0
  48. package/bundle/external-job-provider.js +20 -0
  49. package/bundle/external-runs.js +21 -0
  50. package/bundle/index.js +30 -0
  51. package/bundle/inspectors.js +8 -0
  52. package/bundle/intercom-bridge.js +11 -0
  53. package/bundle/preflight.js +425 -0
  54. package/bundle/project-panes.js +28 -0
  55. package/bundle/required-child-extensions.js +6 -0
  56. package/bundle/shared-types.js +8 -0
  57. package/bundle/workflow-resources.js +7 -0
  58. package/docs/extension-api.md +27 -0
  59. package/docs/observability.md +6 -2
  60. package/package.json +18 -18
  61. package/src/agents/frontmatter.d.ts +2 -0
  62. package/src/agents/frontmatter.js +9 -2
  63. package/src/api/delegation.d.ts +20 -0
  64. package/src/extension/index.js +27 -7
  65. package/src/runs/background/run-child-session.js +18 -0
  66. package/src/runs/background/stale-run-reconciler.js +2 -0
  67. package/src/runs/foreground/execution.js +39 -2
  68. package/src/runs/shared/structured-output.d.ts +1 -0
  69. package/src/runs/shared/structured-output.js +1 -0
  70. package/src/shared/accessible-dir.js +4 -1
  71. package/src/shared/provider-diagnostics.d.ts +9 -0
  72. package/src/shared/provider-diagnostics.js +75 -0
  73. package/src/shared/types.d.ts +3 -0
  74. package/src/slash/delegation-adapters.d.ts +3 -1
  75. package/src/slash/delegation-adapters.js +7 -0
  76. package/src/slash/prompt-template-bridge.js +3 -1
  77. package/src/slash/subagents-admin.d.ts +1 -0
  78. package/src/slash/subagents-admin.js +1 -1
  79. package/src/tui/fleet-layout.d.ts +20 -0
  80. package/src/tui/fleet-layout.js +17 -0
  81. package/src/tui/fleet-viewport.d.ts +12 -0
  82. package/src/tui/fleet-viewport.js +35 -0
  83. package/src/tui/fleet.d.ts +5 -6
  84. package/src/tui/fleet.js +69 -51
  85. package/src/workflows/structured-workflow-scripts.d.ts +1 -0
  86. package/src/workflows/structured-workflow-scripts.js +5 -4
  87. package/src/workflows/workflow-resources.js +2 -2
@@ -0,0 +1,129 @@
1
+ // src/shared/display-text.ts
2
+ function codePointWidth(codePoint) {
3
+ return codePoint > 65535 ? 2 : 1;
4
+ }
5
+ function isWhitespaceOrControl(codePoint) {
6
+ if (codePoint <= 32 || codePoint >= 127 && codePoint <= 159) return true;
7
+ if (codePoint >= 55296 && codePoint <= 57343) return true;
8
+ return String.fromCodePoint(codePoint).trim().length === 0;
9
+ }
10
+ function consumeControlString(value, index, osc) {
11
+ while (index < value.length) {
12
+ const codePoint = value.codePointAt(index);
13
+ const width = codePointWidth(codePoint);
14
+ if (osc && codePoint === 7) return index + width;
15
+ if (codePoint === 156) return index + width;
16
+ if (codePoint === 27 && value.charCodeAt(index + 1) === 92) return index + 2;
17
+ index += width;
18
+ }
19
+ return value.length;
20
+ }
21
+ function consumeCsi(value, index) {
22
+ while (index < value.length) {
23
+ const codePoint = value.codePointAt(index);
24
+ const width = codePointWidth(codePoint);
25
+ if (codePoint >= 64 && codePoint <= 126) return index + width;
26
+ index += width;
27
+ }
28
+ return value.length;
29
+ }
30
+ function sanitizeDisplayText(value) {
31
+ const output = [];
32
+ let pendingSpace = false;
33
+ const appendSpace = () => {
34
+ if (output.length > 0) pendingSpace = true;
35
+ };
36
+ const appendText = (text) => {
37
+ if (pendingSpace) output.push(" ");
38
+ output.push(text);
39
+ pendingSpace = false;
40
+ };
41
+ for (let index = 0; index < value.length; ) {
42
+ const codePoint = value.codePointAt(index);
43
+ const width = codePointWidth(codePoint);
44
+ if (codePoint === 27) {
45
+ const next = value.charCodeAt(index + 1);
46
+ appendSpace();
47
+ if (next === 91) {
48
+ index = consumeCsi(value, index + 2);
49
+ continue;
50
+ }
51
+ if (next === 93 || next === 80 || next === 88 || next === 94 || next === 95) {
52
+ index = consumeControlString(value, index + 2, next === 93);
53
+ continue;
54
+ }
55
+ index += next ? 2 : 1;
56
+ continue;
57
+ }
58
+ if (codePoint === 155) {
59
+ appendSpace();
60
+ index = consumeCsi(value, index + width);
61
+ continue;
62
+ }
63
+ if (codePoint === 144 || codePoint === 152 || codePoint === 157 || codePoint === 158 || codePoint === 159) {
64
+ appendSpace();
65
+ index = consumeControlString(value, index + width, codePoint === 157);
66
+ continue;
67
+ }
68
+ if (isWhitespaceOrControl(codePoint)) appendSpace();
69
+ else appendText(String.fromCodePoint(codePoint));
70
+ index += width;
71
+ }
72
+ return output.join("");
73
+ }
74
+ function truncateDisplayText(value, maxLength) {
75
+ if (maxLength <= 0) return "";
76
+ if (value.length <= maxLength) return value;
77
+ let output = "";
78
+ for (const char of value) {
79
+ if (output.length + char.length > maxLength) break;
80
+ output += char;
81
+ }
82
+ return output;
83
+ }
84
+ function previewDisplayText(value, maxLength) {
85
+ const normalized = sanitizeDisplayText(value);
86
+ if (normalized.length <= maxLength) return normalized;
87
+ if (maxLength <= 3) return truncateDisplayText(normalized, maxLength);
88
+ return `${truncateDisplayText(normalized, maxLength - 3)}...`;
89
+ }
90
+ var BINARY_CONTENT_PLACEHOLDER = "[binary content omitted for safe display]";
91
+ function isUnsafeTerminalCodePoint(codePoint) {
92
+ const terminalControl = codePoint <= 31 && codePoint !== 9 && codePoint !== 10 || codePoint >= 127 && codePoint <= 159;
93
+ const bidiControl = codePoint === 1564 || codePoint === 8206 || codePoint === 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297;
94
+ const privateUse = codePoint >= 57344 && codePoint <= 63743 || codePoint >= 983040 && codePoint <= 1048573 || codePoint >= 1048576 && codePoint <= 1114109;
95
+ const invalidScalar = codePoint >= 55296 && codePoint <= 57343;
96
+ const nonCharacter = codePoint >= 64976 && codePoint <= 65007 || (codePoint & 65535) === 65534 || (codePoint & 65535) === 65535;
97
+ return terminalControl || bidiControl || privateUse || invalidScalar || nonCharacter;
98
+ }
99
+ function looksLikeBinaryContent(text) {
100
+ if (text.includes("\0")) return true;
101
+ let suspiciousControls = 0;
102
+ let replacementCharacters = 0;
103
+ let codePoints = 0;
104
+ for (const character of text) {
105
+ codePoints++;
106
+ const codePoint = character.codePointAt(0) ?? 0;
107
+ if (codePoint <= 8 || codePoint >= 14 && codePoint <= 31) suspiciousControls++;
108
+ if (codePoint === 65533) replacementCharacters++;
109
+ }
110
+ if (codePoints === 0) return false;
111
+ return suspiciousControls >= 4 && suspiciousControls / codePoints >= 0.1 || replacementCharacters >= 3 && replacementCharacters / codePoints >= 0.1;
112
+ }
113
+ function safeTerminalText(value) {
114
+ const normalized = value.replace(/\r\n/g, "\n");
115
+ if (looksLikeBinaryContent(normalized)) return BINARY_CONTENT_PLACEHOLDER;
116
+ let safe = "";
117
+ for (const character of normalized) {
118
+ const codePoint = character.codePointAt(0) ?? 0;
119
+ safe += isUnsafeTerminalCodePoint(codePoint) ? `[U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}]` : character;
120
+ }
121
+ return safe;
122
+ }
123
+
124
+ export {
125
+ sanitizeDisplayText,
126
+ truncateDisplayText,
127
+ previewDisplayText,
128
+ safeTerminalText
129
+ };
@@ -0,0 +1,137 @@
1
+ // src/api/external-job-provider.ts
2
+ var EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION = 1;
3
+ var EXTERNAL_JOB_PROVIDER_REGISTRY_KEY = "pi-subagents.external-job-providers.v1";
4
+ var MAX_PROVIDER_NAME_LENGTH = 128;
5
+ var MAX_PROVIDERS = 100;
6
+ var MAX_JOB_ID_LENGTH = 256;
7
+ var MAX_FAILURE_CODE_LENGTH = 128;
8
+ var MAX_FAILURE_MESSAGE_LENGTH = 4096;
9
+ var MAX_URL_LENGTH = 4096;
10
+ var ExternalJobProviderError = class extends Error {
11
+ code;
12
+ blockingJobId;
13
+ constructor(message, options) {
14
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
15
+ this.name = "ExternalJobProviderError";
16
+ this.code = options.code;
17
+ this.blockingJobId = options.blockingJobId;
18
+ }
19
+ };
20
+ function registry() {
21
+ const key = Symbol.for(EXTERNAL_JOB_PROVIDER_REGISTRY_KEY);
22
+ const globalObject = globalThis;
23
+ const existing = globalObject[key];
24
+ if (existing === void 0) {
25
+ const created = {
26
+ version: EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION,
27
+ providers: /* @__PURE__ */ new Map()
28
+ };
29
+ globalObject[key] = created;
30
+ return created;
31
+ }
32
+ if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
33
+ throw new Error(`Malformed external-job provider registry at Symbol.for("${EXTERNAL_JOB_PROVIDER_REGISTRY_KEY}").`);
34
+ }
35
+ const candidate = existing;
36
+ if (candidate.version !== EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION || !(candidate.providers instanceof Map)) {
37
+ throw new Error(`Unsupported external-job provider registry at Symbol.for("${EXTERNAL_JOB_PROVIDER_REGISTRY_KEY}").`);
38
+ }
39
+ return candidate;
40
+ }
41
+ function validateString(value, field, maxLength) {
42
+ if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
43
+ throw new Error(`${field} must be a non-empty string without leading or trailing whitespace.`);
44
+ }
45
+ if (value.length > maxLength) throw new Error(`${field} must be at most ${maxLength} characters.`);
46
+ if (value.includes("\0")) throw new Error(`${field} must not contain NUL characters.`);
47
+ return value;
48
+ }
49
+ function validateOptionalString(value, field, maxLength) {
50
+ if (value === void 0) return void 0;
51
+ return validateString(value, field, maxLength);
52
+ }
53
+ function validateState(value, field) {
54
+ if (value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "stopped" || value === "blocked") return value;
55
+ throw new Error(`${field} must be queued, running, completed, failed, stopped, or blocked.`);
56
+ }
57
+ function validateHandle(provider, value, field, extraFields = []) {
58
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${field} from external-job provider '${provider}' must be an object.`);
59
+ const handle = value;
60
+ const supported = /* @__PURE__ */ new Set(["providerJobId", "state", "handleUrl", "conversationUrl", "failureCode", "failureMessage", "blockingJobId", ...extraFields]);
61
+ const unknown = Object.keys(handle).filter((key) => !supported.has(key));
62
+ if (unknown.length > 0) throw new Error(`${field} from external-job provider '${provider}' has unknown fields: ${unknown.join(", ")}.`);
63
+ const handleUrl = validateOptionalString(handle.handleUrl, `${field}.handleUrl`, MAX_URL_LENGTH);
64
+ const conversationUrl = validateOptionalString(handle.conversationUrl, `${field}.conversationUrl`, MAX_URL_LENGTH);
65
+ const failureCode = validateOptionalString(handle.failureCode, `${field}.failureCode`, MAX_FAILURE_CODE_LENGTH);
66
+ const failureMessage = validateOptionalString(handle.failureMessage, `${field}.failureMessage`, MAX_FAILURE_MESSAGE_LENGTH);
67
+ const blockingJobId = validateOptionalString(handle.blockingJobId, `${field}.blockingJobId`, MAX_JOB_ID_LENGTH);
68
+ return {
69
+ providerJobId: validateString(handle.providerJobId, `${field}.providerJobId`, MAX_JOB_ID_LENGTH),
70
+ state: validateState(handle.state, `${field}.state`),
71
+ ...handleUrl ? { handleUrl } : {},
72
+ ...conversationUrl ? { conversationUrl } : {},
73
+ ...failureCode ? { failureCode } : {},
74
+ ...failureMessage ? { failureMessage } : {},
75
+ ...blockingJobId ? { blockingJobId } : {}
76
+ };
77
+ }
78
+ function validateExternalJobHandle(provider, value, field = "External-job handle") {
79
+ return validateHandle(provider, value, field);
80
+ }
81
+ function validateExternalJobResult(provider, value, field = "External-job result") {
82
+ const result = validateHandle(provider, value, field, ["output", "artifactPath"]);
83
+ const record = value;
84
+ const output = validateOptionalString(record.output, `${field}.output`, 1024 * 1024);
85
+ const artifactPath = validateOptionalString(record.artifactPath, `${field}.artifactPath`, MAX_URL_LENGTH);
86
+ return {
87
+ ...result,
88
+ ...output !== void 0 ? { output } : {},
89
+ ...artifactPath !== void 0 ? { artifactPath } : {}
90
+ };
91
+ }
92
+ function validateProvider(value) {
93
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("External-job provider must be an object.");
94
+ const provider = value;
95
+ const name = validateString(provider.name, "External-job provider name", MAX_PROVIDER_NAME_LENGTH);
96
+ for (const op of ["start", "status", "result", "reattach"]) {
97
+ if (typeof provider[op] !== "function") throw new Error(`External-job provider '${name}' must expose ${op}().`);
98
+ }
99
+ return value;
100
+ }
101
+ function registerExternalJobProvider(provider) {
102
+ const validated = validateProvider(provider);
103
+ const current = registry();
104
+ if (!current.providers.has(validated.name) && current.providers.size >= MAX_PROVIDERS) {
105
+ throw new Error(`External-job provider registry supports at most ${MAX_PROVIDERS} providers.`);
106
+ }
107
+ current.providers.set(validated.name, validated);
108
+ return () => {
109
+ if (current.providers.get(validated.name) === validated) current.providers.delete(validated.name);
110
+ };
111
+ }
112
+ function listExternalJobProviders() {
113
+ const current = registry();
114
+ if (current.providers.size > MAX_PROVIDERS) throw new Error(`External-job provider registry contains more than ${MAX_PROVIDERS} providers.`);
115
+ const providers = [];
116
+ for (const [key, value] of current.providers) {
117
+ const provider = validateProvider(value);
118
+ if (key !== provider.name) throw new Error(`External-job provider registry key '${key}' does not match provider name '${provider.name}'.`);
119
+ providers.push(provider);
120
+ }
121
+ return providers;
122
+ }
123
+ function getExternalJobProvider(name) {
124
+ const safeName = validateString(name, "External-job provider name", MAX_PROVIDER_NAME_LENGTH);
125
+ return listExternalJobProviders().find((provider) => provider.name === safeName);
126
+ }
127
+
128
+ export {
129
+ EXTERNAL_JOB_PROVIDER_PROTOCOL_VERSION,
130
+ EXTERNAL_JOB_PROVIDER_REGISTRY_KEY,
131
+ ExternalJobProviderError,
132
+ validateExternalJobHandle,
133
+ validateExternalJobResult,
134
+ registerExternalJobProvider,
135
+ listExternalJobProviders,
136
+ getExternalJobProvider
137
+ };
@@ -0,0 +1,173 @@
1
+ // src/runs/shared/capability-ceiling.ts
2
+ import { Buffer } from "node:buffer";
3
+ var SUBAGENT_CAPABILITY_CEILING_VERSION = 1;
4
+ var SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = "pi-subagents.capability-ceiling.v1";
5
+ function registry() {
6
+ const key = Symbol.for(SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY);
7
+ const store = globalThis;
8
+ const existing = store[key];
9
+ if (existing instanceof Map) return existing;
10
+ const created = /* @__PURE__ */ new Map();
11
+ store[key] = created;
12
+ return created;
13
+ }
14
+ function validateText(value, field) {
15
+ if (typeof value !== "string" || !value.trim() || /[\u0000-\u001f\u007f]/u.test(value) || Buffer.byteLength(value.trim(), "utf8") > 256) {
16
+ throw new Error(`Invalid capability ceiling ${field}; expected a non-empty string without control characters (max 256 UTF-8 bytes).`);
17
+ }
18
+ return value.trim();
19
+ }
20
+ function normalizeCapabilityCeilingAllowedAgents(values) {
21
+ return normalizeCeiling({ allowedAgents: values }).allowedAgents;
22
+ }
23
+ function normalizeCeiling(ceiling) {
24
+ if (!ceiling || typeof ceiling !== "object" || Array.isArray(ceiling)) throw new Error("Invalid capability ceiling; expected an object.");
25
+ const hasAllowedTools = Object.hasOwn(ceiling, "allowedTools");
26
+ const hasAllowedAgents = Object.hasOwn(ceiling, "allowedAgents");
27
+ const hasDenyExtensions = Object.hasOwn(ceiling, "denyExtensions");
28
+ if (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.");
29
+ if (hasDenyExtensions && typeof ceiling.denyExtensions !== "boolean") throw new Error("Invalid capability ceiling denyExtensions; expected a boolean.");
30
+ const normalizeList = (field, pattern) => {
31
+ if (!Object.hasOwn(ceiling, field)) return void 0;
32
+ const values = ceiling[field];
33
+ if (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);
34
+ if (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);
35
+ return [...new Set(values.map((entry) => {
36
+ const name = validateText(entry, `${field} entry`);
37
+ if (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);
38
+ if (Buffer.byteLength(name, "utf8") > 128) throw new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);
39
+ return name;
40
+ }))].sort();
41
+ };
42
+ const allowedTools = normalizeList("allowedTools", /^[A-Za-z0-9_.:-]+$/u);
43
+ const allowedAgents = normalizeList("allowedAgents", /^[A-Za-z0-9_.:-]+$/u);
44
+ return {
45
+ version: SUBAGENT_CAPABILITY_CEILING_VERSION,
46
+ ...allowedTools !== void 0 ? { allowedTools } : {},
47
+ ...allowedAgents !== void 0 ? { allowedAgents } : {},
48
+ denyExtensions: ceiling.denyExtensions === true,
49
+ sources: []
50
+ };
51
+ }
52
+ function parseSubagentCapabilityCeiling(value, field = "capability ceiling") {
53
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid ${field}; expected an object.`);
54
+ const record = value;
55
+ if (record.version !== SUBAGENT_CAPABILITY_CEILING_VERSION) throw new Error(`Invalid ${field} version.`);
56
+ const normalized = normalizeCeiling(record);
57
+ const sources = record.sources;
58
+ if (!Array.isArray(sources) || sources.some((source) => typeof source !== "string")) throw new Error(`Invalid ${field} sources; expected an array of strings.`);
59
+ normalized.sources = [...new Set(sources.map((source) => validateText(source, `${field} source`)))].sort();
60
+ return normalized;
61
+ }
62
+ function registerSubagentCapabilityCeiling(options) {
63
+ const sessionId = validateText(options.sessionId, "sessionId");
64
+ const source = validateText(options.source, "source");
65
+ let normalized = normalizeCeiling(options.ceiling);
66
+ const token = Symbol(source);
67
+ const store = registry();
68
+ let session = store.get(sessionId);
69
+ if (!session) {
70
+ session = /* @__PURE__ */ new Map();
71
+ store.set(sessionId, session);
72
+ }
73
+ const setRegistration = () => {
74
+ normalized = normalizeCeiling(normalized);
75
+ normalized.sources = [source];
76
+ session.set(token, { source, ceiling: normalized });
77
+ };
78
+ setRegistration();
79
+ let disposed = false;
80
+ return {
81
+ update(ceiling) {
82
+ if (disposed) throw new Error("Cannot update a disposed capability ceiling handle.");
83
+ normalized = normalizeCeiling(ceiling);
84
+ normalized.sources = [source];
85
+ session.set(token, { source, ceiling: normalized });
86
+ },
87
+ dispose() {
88
+ if (disposed) return;
89
+ disposed = true;
90
+ session.delete(token);
91
+ if (session.size === 0) store.delete(sessionId);
92
+ }
93
+ };
94
+ }
95
+ function intersectSubagentCapabilityCeilings(...ceilings) {
96
+ const active = ceilings.filter((ceiling) => ceiling !== void 0);
97
+ if (active.length === 0) return void 0;
98
+ const intersectLists = (field) => {
99
+ const definedLists = active.filter((ceiling) => ceiling[field] !== void 0).map((ceiling) => new Set(ceiling[field]));
100
+ if (definedLists.length === 0) return void 0;
101
+ return [...definedLists[0]].filter((entry) => definedLists.every((list) => list.has(entry))).sort();
102
+ };
103
+ const allowedTools = intersectLists("allowedTools");
104
+ const allowedAgents = intersectLists("allowedAgents");
105
+ return {
106
+ version: SUBAGENT_CAPABILITY_CEILING_VERSION,
107
+ ...allowedTools !== void 0 ? { allowedTools } : {},
108
+ ...allowedAgents !== void 0 ? { allowedAgents } : {},
109
+ denyExtensions: active.some((ceiling) => ceiling.denyExtensions),
110
+ sources: [...new Set(active.flatMap((ceiling) => ceiling.sources))].sort()
111
+ };
112
+ }
113
+ function resolveSubagentCapabilityCeiling(sessionId, inherited) {
114
+ const active = [];
115
+ if (sessionId) {
116
+ const registrations = registry().get(sessionId);
117
+ if (registrations) active.push(...Array.from(registrations.values(), ({ ceiling }) => ceiling));
118
+ }
119
+ return intersectSubagentCapabilityCeilings(inherited, ...active);
120
+ }
121
+ function resolveCurrentSubagentCapabilityCeiling(sessionId) {
122
+ return resolveSubagentCapabilityCeiling(sessionId, void 0);
123
+ }
124
+ function isAgentAllowedByCapabilityCeiling(agentName, ceiling) {
125
+ return ceiling?.allowedAgents === void 0 || ceiling.allowedAgents.includes(agentName);
126
+ }
127
+ function capabilityCeilingAgentRestrictionMessage(agentName, ceiling) {
128
+ if (isAgentAllowedByCapabilityCeiling(agentName, ceiling)) return void 0;
129
+ const sources = ceiling?.sources.length ? ceiling.sources.join(", ") : "unknown source";
130
+ const allowed = ceiling?.allowedAgents?.length ? ceiling.allowedAgents.join(", ") : "(none)";
131
+ return `Capability ceiling from ${sources} does not allow agent '${agentName}'. Allowed agents: ${allowed}.`;
132
+ }
133
+ function assertAgentAllowedByCapabilityCeiling(agentName, ceiling) {
134
+ const message = capabilityCeilingAgentRestrictionMessage(agentName, ceiling);
135
+ if (message) throw new Error(message);
136
+ }
137
+ function capabilityCeilingAgentRestrictionSources(ceiling) {
138
+ return ceiling?.allowedAgents === void 0 ? void 0 : [...ceiling.sources];
139
+ }
140
+ function encodeSubagentCapabilityCeiling(ceiling) {
141
+ if (!ceiling) return void 0;
142
+ return Buffer.from(JSON.stringify(ceiling), "utf8").toString("base64url");
143
+ }
144
+ function decodeSubagentCapabilityCeiling(value) {
145
+ if (value === void 0 || value === "") return void 0;
146
+ let parsed;
147
+ try {
148
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
149
+ } catch (error) {
150
+ throw new Error(`Invalid inherited capability ceiling: ${error instanceof Error ? error.message : String(error)}`);
151
+ }
152
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== SUBAGENT_CAPABILITY_CEILING_VERSION) {
153
+ throw new Error("Invalid inherited capability ceiling version.");
154
+ }
155
+ return parseSubagentCapabilityCeiling(parsed, "inherited capability ceiling");
156
+ }
157
+
158
+ export {
159
+ SUBAGENT_CAPABILITY_CEILING_VERSION,
160
+ SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY,
161
+ normalizeCapabilityCeilingAllowedAgents,
162
+ parseSubagentCapabilityCeiling,
163
+ registerSubagentCapabilityCeiling,
164
+ intersectSubagentCapabilityCeilings,
165
+ resolveSubagentCapabilityCeiling,
166
+ resolveCurrentSubagentCapabilityCeiling,
167
+ isAgentAllowedByCapabilityCeiling,
168
+ capabilityCeilingAgentRestrictionMessage,
169
+ assertAgentAllowedByCapabilityCeiling,
170
+ capabilityCeilingAgentRestrictionSources,
171
+ encodeSubagentCapabilityCeiling,
172
+ decodeSubagentCapabilityCeiling
173
+ };