@narumitw/pi-subagents 3.0.1 → 3.1.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/README.md +80 -136
- package/dist/child-communication-bridge.ts +9 -12
- package/dist/child-communication-bridge.ts.map +2 -2
- package/dist/child-readiness-probe.ts +68 -0
- package/dist/child-readiness-probe.ts.map +7 -0
- package/dist/chunks/chunk-3FCHXUKZ.ts +98 -0
- package/dist/chunks/chunk-3FCHXUKZ.ts.map +7 -0
- package/dist/chunks/{chunk-UKTWUQRM.js → chunk-YMVSJ5GE.ts} +89 -152
- package/dist/chunks/chunk-YMVSJ5GE.ts.map +7 -0
- package/dist/chunks/chunk-ZOAFWLDJ.ts +130 -0
- package/dist/chunks/chunk-ZOAFWLDJ.ts.map +7 -0
- package/dist/index.ts +1201 -120
- package/dist/index.ts.map +4 -4
- package/docs/attachments.md +37 -0
- package/docs/design-principles.md +8 -2
- package/docs/messaging.md +23 -0
- package/docs/tools.md +22 -56
- package/package.json +56 -54
- package/src/attachment-utils.ts +104 -0
- package/src/broker-credentials.ts +132 -54
- package/src/child-communication-bridge.ts +117 -125
- package/src/child-communication-tools.ts +89 -117
- package/src/child-readiness-probe.ts +72 -0
- package/src/completion-renderer.ts +46 -48
- package/src/message-broker.ts +531 -561
- package/src/model-output.ts +25 -28
- package/src/process.ts +942 -619
- package/src/resource-attachments.ts +724 -0
- package/src/runtime.ts +471 -478
- package/src/skill-attachments.ts +302 -0
- package/src/subagents.ts +20 -23
- package/src/timeout.ts +24 -0
- package/src/tools.ts +389 -325
- package/src/types.ts +45 -58
- package/src/widget.ts +85 -91
- package/dist/chunks/chunk-UKTWUQRM.js.map +0 -7
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/attachment-utils.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport { realpathSync } from \"node:fs\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type ignore from \"ignore\";\nimport { sanitizeTerminalText } from \"./message-broker.js\";\n\nconst ATTACHMENT_IGNORE_FILE_NAMES = [\".gitignore\", \".ignore\", \".fdignore\"];\n\nexport type IgnoreMatcher = ReturnType<typeof ignore>;\n\nfunction throwInvalidAttachmentIgnoreFile(): never {\n throw new Error(\"Subagent attachment ignore files must be regular files.\");\n}\n\nexport function throwIfAttachmentAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) return;\n const error = new Error(\"Subagent attachment validation was cancelled.\");\n error.name = \"AbortError\";\n throw error;\n}\n\nexport function isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nfunction prefixIgnorePattern(line: string, prefix: string): string | null {\n const trimmed = line.trim();\n if (!trimmed || (trimmed.startsWith(\"#\") && !trimmed.startsWith(\"\\\\#\"))) return null;\n let pattern = line;\n let negated = false;\n if (pattern.startsWith(\"!\")) {\n negated = true;\n pattern = pattern.slice(1);\n } else if (pattern.startsWith(\"\\\\!\")) {\n pattern = pattern.slice(1);\n }\n if (pattern.startsWith(\"/\")) pattern = pattern.slice(1);\n const prefixed = prefix ? `${prefix}${pattern}` : pattern;\n return negated ? `!${prefixed}` : prefixed;\n}\n\nexport async function addAttachmentIgnoreRules(\n ignoreMatcher: IgnoreMatcher,\n directory: string,\n rootDirectory: string,\n signal: AbortSignal | undefined,\n accountForFile: (ignorePath: string, bytes: number) => void,\n): Promise<void> {\n const relativeDirectory = path.relative(rootDirectory, directory);\n const prefix = relativeDirectory ? `${toPosixPath(relativeDirectory)}/` : \"\";\n for (const filename of ATTACHMENT_IGNORE_FILE_NAMES) {\n throwIfAttachmentAborted(signal);\n const ignorePath = path.join(directory, filename);\n let ignoreStats: Awaited<ReturnType<typeof stat>>;\n try {\n ignoreStats = await stat(ignorePath);\n } catch (error) {\n if (isAbortError(error)) throw error;\n throwIfAttachmentAborted(signal);\n continue;\n }\n if (!ignoreStats.isFile()) throwInvalidAttachmentIgnoreFile();\n accountForFile(ignorePath, ignoreStats.size);\n try {\n const content = await readFile(ignorePath, { encoding: \"utf8\", signal });\n throwIfAttachmentAborted(signal);\n const patterns = content\n .split(/\\r?\\n/u)\n .map((line) => prefixIgnorePattern(line, prefix))\n .filter((line): line is string => line !== null);\n if (patterns.length > 0) ignoreMatcher.add(patterns);\n } catch (error) {\n if (isAbortError(error)) throw error;\n throwIfAttachmentAborted(signal);\n // Pi ignores unreadable ignore files.\n }\n }\n}\n\nexport function toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nexport function toolSourceId(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function realpath(value: string, label: string): string {\n try {\n return realpathSync(value);\n } catch {\n throw new Error(`${label} path does not exist: ${sanitizeTerminalText(value).slice(0, 512)}`);\n }\n}\n\nexport function isWithin(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return relative === \"\" || (!relative.startsWith(`..${path.sep}`) && relative !== \"..\" && !path.isAbsolute(relative));\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["relative"]
|
|
7
|
+
}
|
|
@@ -129,9 +129,7 @@ var MessageBroker = class {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
hasPendingMainRequest() {
|
|
132
|
-
return [...this.requests.values()].some(
|
|
133
|
-
(request) => request.origin === "child" && request.response === void 0
|
|
134
|
-
);
|
|
132
|
+
return [...this.requests.values()].some((request) => request.origin === "child" && request.response === void 0);
|
|
135
133
|
}
|
|
136
134
|
takePendingInboundResponse() {
|
|
137
135
|
const pending = this.pendingInboundResponse;
|
|
@@ -198,9 +196,7 @@ var MessageBroker = class {
|
|
|
198
196
|
(request2) => request2.jobId === job.jobId && request2.consumedAt === void 0
|
|
199
197
|
).length;
|
|
200
198
|
if (outstanding >= MAX_OUTSTANDING_REQUESTS) {
|
|
201
|
-
throw new Error(
|
|
202
|
-
`Subagent job may have at most ${MAX_OUTSTANDING_REQUESTS} outstanding requests.`
|
|
203
|
-
);
|
|
199
|
+
throw new Error(`Subagent job may have at most ${MAX_OUTSTANDING_REQUESTS} outstanding requests.`);
|
|
204
200
|
}
|
|
205
201
|
const request = {
|
|
206
202
|
requestId: `req_${randomUUID()}`,
|
|
@@ -374,11 +370,7 @@ var MessageBroker = class {
|
|
|
374
370
|
return;
|
|
375
371
|
}
|
|
376
372
|
if (record.response !== void 0) {
|
|
377
|
-
this.respond(
|
|
378
|
-
socket,
|
|
379
|
-
{ ok: true, response: record.response },
|
|
380
|
-
() => this.markConsumed(record)
|
|
381
|
-
);
|
|
373
|
+
this.respond(socket, { ok: true, response: record.response }, () => this.markConsumed(record));
|
|
382
374
|
return;
|
|
383
375
|
}
|
|
384
376
|
if (this.hasQueuedRequestForChild(job.jobId)) {
|
|
@@ -402,13 +394,8 @@ var MessageBroker = class {
|
|
|
402
394
|
markConsumed(request) {
|
|
403
395
|
if (this.requests.get(request.requestId) !== request || request.response === void 0) return;
|
|
404
396
|
request.consumedAt ??= this.now();
|
|
405
|
-
const consumed = [...this.requests.values()].filter(
|
|
406
|
-
|
|
407
|
-
).sort((left, right) => (left.consumedAt ?? 0) - (right.consumedAt ?? 0));
|
|
408
|
-
for (const expired of consumed.slice(
|
|
409
|
-
0,
|
|
410
|
-
Math.max(0, consumed.length - MAX_RETAINED_CONSUMED_REQUESTS)
|
|
411
|
-
)) {
|
|
397
|
+
const consumed = [...this.requests.values()].filter((candidate) => candidate.jobId === request.jobId && candidate.consumedAt !== void 0).sort((left, right) => (left.consumedAt ?? 0) - (right.consumedAt ?? 0));
|
|
398
|
+
for (const expired of consumed.slice(0, Math.max(0, consumed.length - MAX_RETAINED_CONSUMED_REQUESTS))) {
|
|
412
399
|
this.requests.delete(expired.requestId);
|
|
413
400
|
}
|
|
414
401
|
}
|
|
@@ -489,41 +476,83 @@ function isLoopbackAddress(address) {
|
|
|
489
476
|
import * as fs from "node:fs";
|
|
490
477
|
var BROKER_CREDENTIAL_FD = 3;
|
|
491
478
|
var BROKER_CREDENTIAL_FD_ENV = "PI_SUBAGENT_BROKER_FD";
|
|
492
|
-
var
|
|
493
|
-
|
|
494
|
-
|
|
479
|
+
var CHILD_READINESS_FD = 4;
|
|
480
|
+
var CHILD_READINESS_FD_ENV = "PI_SUBAGENT_READINESS_FD";
|
|
481
|
+
var MAX_READINESS_FRAME_BYTES = 16 * 1024;
|
|
482
|
+
var MAX_BOOTSTRAP_BYTES = 16 * 1024;
|
|
483
|
+
var MAX_EXPECTED_TOOLS = 66;
|
|
484
|
+
var MAX_TOOL_NAME_LENGTH = 128;
|
|
485
|
+
var READINESS_STATE_KEY = /* @__PURE__ */ Symbol.for("@narumitw/pi-subagents/child-readiness");
|
|
486
|
+
function childBootstrapEnvironment(expectReadiness) {
|
|
487
|
+
return {
|
|
488
|
+
[BROKER_CREDENTIAL_FD_ENV]: String(BROKER_CREDENTIAL_FD),
|
|
489
|
+
...expectReadiness ? { [CHILD_READINESS_FD_ENV]: String(CHILD_READINESS_FD) } : {}
|
|
490
|
+
};
|
|
495
491
|
}
|
|
496
|
-
function
|
|
497
|
-
return JSON.stringify(
|
|
492
|
+
function serializeChildBootstrap(bootstrap) {
|
|
493
|
+
return JSON.stringify(bootstrap);
|
|
498
494
|
}
|
|
499
|
-
function
|
|
500
|
-
const
|
|
495
|
+
function assertChildBootstrapCapacity(expectedTools) {
|
|
496
|
+
const serialized = serializeChildBootstrap({
|
|
497
|
+
communication: { host: "127.0.0.1", port: 65535, token: "f".repeat(64) },
|
|
498
|
+
expectedTools
|
|
499
|
+
});
|
|
500
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_BOOTSTRAP_BYTES) {
|
|
501
|
+
throw new Error(`Subagent selected tool names exceed the ${MAX_BOOTSTRAP_BYTES}-byte child bootstrap size limit.`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function captureChildBootstrap(readBootstrap = readBootstrapPipe) {
|
|
505
|
+
const credentialDescriptor = process.env[BROKER_CREDENTIAL_FD_ENV];
|
|
506
|
+
const readinessDescriptor = process.env[CHILD_READINESS_FD_ENV];
|
|
501
507
|
delete process.env[BROKER_CREDENTIAL_FD_ENV];
|
|
502
|
-
|
|
503
|
-
|
|
508
|
+
delete process.env[CHILD_READINESS_FD_ENV];
|
|
509
|
+
setCapturedReadiness(void 0);
|
|
510
|
+
if (credentialDescriptor === void 0) {
|
|
511
|
+
if (readinessDescriptor !== void 0) throw new Error("Unexpected pi-subagents readiness descriptor.");
|
|
512
|
+
return void 0;
|
|
513
|
+
}
|
|
514
|
+
if (credentialDescriptor !== String(BROKER_CREDENTIAL_FD)) {
|
|
504
515
|
throw new Error("Invalid pi-subagents broker credential descriptor.");
|
|
505
516
|
}
|
|
506
517
|
let serialized;
|
|
507
518
|
try {
|
|
508
|
-
serialized =
|
|
519
|
+
serialized = readBootstrap();
|
|
509
520
|
} catch {
|
|
510
|
-
throw new Error("Unable to read pi-subagents
|
|
521
|
+
throw new Error("Unable to read pi-subagents child bootstrap.");
|
|
511
522
|
}
|
|
512
|
-
if (Buffer.byteLength(serialized, "utf8") >
|
|
513
|
-
throw new Error("Invalid pi-subagents
|
|
523
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_BOOTSTRAP_BYTES) {
|
|
524
|
+
throw new Error("Invalid pi-subagents child bootstrap.");
|
|
514
525
|
}
|
|
515
526
|
let value;
|
|
516
527
|
try {
|
|
517
528
|
value = JSON.parse(serialized);
|
|
518
529
|
} catch {
|
|
519
|
-
throw new Error("Invalid pi-subagents
|
|
530
|
+
throw new Error("Invalid pi-subagents child bootstrap.");
|
|
520
531
|
}
|
|
521
|
-
if (!
|
|
522
|
-
|
|
532
|
+
if (!isChildBootstrap(value)) throw new Error("Invalid pi-subagents child bootstrap.");
|
|
533
|
+
if (value.expectedTools.length > 0) {
|
|
534
|
+
if (readinessDescriptor !== String(CHILD_READINESS_FD)) {
|
|
535
|
+
throw new Error("Invalid pi-subagents readiness descriptor.");
|
|
536
|
+
}
|
|
537
|
+
setCapturedReadiness({ fd: CHILD_READINESS_FD, expectedTools: [...value.expectedTools] });
|
|
538
|
+
return { ...value, readinessFd: CHILD_READINESS_FD };
|
|
523
539
|
}
|
|
540
|
+
if (readinessDescriptor !== void 0) throw new Error("Unexpected pi-subagents readiness descriptor.");
|
|
541
|
+
setCapturedReadiness(void 0);
|
|
524
542
|
return value;
|
|
525
543
|
}
|
|
526
|
-
function
|
|
544
|
+
function takeCapturedReadiness() {
|
|
545
|
+
const store = globalThis;
|
|
546
|
+
const readiness = store[READINESS_STATE_KEY];
|
|
547
|
+
delete store[READINESS_STATE_KEY];
|
|
548
|
+
return readiness ? { fd: readiness.fd, expectedTools: [...readiness.expectedTools] } : void 0;
|
|
549
|
+
}
|
|
550
|
+
function setCapturedReadiness(readiness) {
|
|
551
|
+
const store = globalThis;
|
|
552
|
+
if (readiness) store[READINESS_STATE_KEY] = readiness;
|
|
553
|
+
else delete store[READINESS_STATE_KEY];
|
|
554
|
+
}
|
|
555
|
+
function readBootstrapPipe() {
|
|
527
556
|
try {
|
|
528
557
|
return fs.readFileSync(BROKER_CREDENTIAL_FD, "utf8");
|
|
529
558
|
} finally {
|
|
@@ -533,121 +562,26 @@ function readCredentialPipe() {
|
|
|
533
562
|
}
|
|
534
563
|
}
|
|
535
564
|
}
|
|
536
|
-
function
|
|
537
|
-
if (!value ||
|
|
538
|
-
|
|
539
|
-
|
|
565
|
+
function isChildBootstrap(value) {
|
|
566
|
+
if (!isRecord2(value) || Object.keys(value).length !== 2) return false;
|
|
567
|
+
if (!isBrokerCredentials(value.communication)) return false;
|
|
568
|
+
if (!Array.isArray(value.expectedTools) || value.expectedTools.length > MAX_EXPECTED_TOOLS) return false;
|
|
569
|
+
return value.expectedTools.every(
|
|
570
|
+
(tool) => typeof tool === "string" && tool.length > 0 && tool.length <= MAX_TOOL_NAME_LENGTH && !tool.includes(",") && !hasControlCharacter(tool)
|
|
571
|
+
);
|
|
540
572
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
var CHILD_COMMUNICATION_TOOL_NAMES = ["subagent_send", "subagent_wait"];
|
|
545
|
-
var MAX_TIMEOUT_MS2 = 2147483647;
|
|
546
|
-
var MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS2 / 1e3;
|
|
547
|
-
var SendParameters = Type.Object(
|
|
548
|
-
{
|
|
549
|
-
requestId: Type.Optional(
|
|
550
|
-
Type.String({
|
|
551
|
-
description: "Pending main-agent request to answer. Omit when starting a new request.",
|
|
552
|
-
minLength: 1,
|
|
553
|
-
maxLength: MAX_IDENTIFIER_LENGTH
|
|
554
|
-
})
|
|
555
|
-
),
|
|
556
|
-
message: Type.String({
|
|
557
|
-
description: "Plain-text request or response. Maximum 48 KiB of UTF-8 text and 1,992 lines.",
|
|
558
|
-
minLength: 1,
|
|
559
|
-
maxLength: MAX_MESSAGE_BYTES
|
|
560
|
-
})
|
|
561
|
-
},
|
|
562
|
-
{ additionalProperties: false }
|
|
563
|
-
);
|
|
564
|
-
var WaitParameters = Type.Object(
|
|
565
|
-
{
|
|
566
|
-
requestId: Type.String({
|
|
567
|
-
description: "Request ID returned by subagent_send.",
|
|
568
|
-
minLength: 1,
|
|
569
|
-
maxLength: MAX_IDENTIFIER_LENGTH
|
|
570
|
-
}),
|
|
571
|
-
timeout: Type.Optional(
|
|
572
|
-
Type.Number({ description: "Timeout in seconds (optional, no default timeout)" })
|
|
573
|
-
)
|
|
574
|
-
},
|
|
575
|
-
{ additionalProperties: false }
|
|
576
|
-
);
|
|
577
|
-
function createChildCommunicationExtension(client) {
|
|
578
|
-
return (pi) => {
|
|
579
|
-
pi.registerTool({
|
|
580
|
-
name: "subagent_send",
|
|
581
|
-
label: "Subagent \xB7 Send to Main",
|
|
582
|
-
description: "Use subagent_send to send one request to the main agent or answer one pending main-agent request. Omit requestId to start a request. Provide requestId to answer that request.",
|
|
583
|
-
promptSnippet: "Use subagent_send to send or answer one main-agent message",
|
|
584
|
-
parameters: SendParameters,
|
|
585
|
-
async execute(_toolCallId, params, signal) {
|
|
586
|
-
validateMessage(params.message, "Subagent message");
|
|
587
|
-
const requestId = optionalIdentifier(params.requestId, "requestId");
|
|
588
|
-
const acknowledgement = await client.send(
|
|
589
|
-
requestId === void 0 ? { recipient: "main", message: params.message } : { requestId, message: params.message },
|
|
590
|
-
signal
|
|
591
|
-
);
|
|
592
|
-
return {
|
|
593
|
-
content: [{ type: "text", text: JSON.stringify(acknowledgement) }],
|
|
594
|
-
details: acknowledgement
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
pi.registerTool({
|
|
599
|
-
name: "subagent_wait",
|
|
600
|
-
label: "Subagent \xB7 Wait for Main Agent",
|
|
601
|
-
description: "Use subagent_wait with a request ID from subagent_send to wait for the main agent's plain-text response. A timeout, caller cancellation, or incoming main-agent request stops only this wait and does not cancel the original request. Retry the wait when its response is still needed.",
|
|
602
|
-
promptSnippet: "Use subagent_wait to receive a requested main-agent response",
|
|
603
|
-
parameters: WaitParameters,
|
|
604
|
-
prepareArguments: prepareWaitArguments,
|
|
605
|
-
async execute(_toolCallId, params, signal) {
|
|
606
|
-
const requestId = requiredIdentifier(params.requestId, "requestId");
|
|
607
|
-
const response = await client.wait(requestId, resolveTimeoutMs(params.timeout), signal);
|
|
608
|
-
return {
|
|
609
|
-
content: [{ type: "text", text: sanitizeTerminalText(response) }],
|
|
610
|
-
details: { requestId }
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
});
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
function resolveTimeoutMs(timeout) {
|
|
617
|
-
if (timeout === void 0) return void 0;
|
|
618
|
-
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
619
|
-
throw new Error("Invalid timeout: must be a finite number of seconds");
|
|
620
|
-
}
|
|
621
|
-
const timeoutMs = timeout * 1e3;
|
|
622
|
-
if (timeoutMs > MAX_TIMEOUT_MS2) {
|
|
623
|
-
throw new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);
|
|
624
|
-
}
|
|
625
|
-
return timeoutMs;
|
|
626
|
-
}
|
|
627
|
-
function prepareWaitArguments(args) {
|
|
628
|
-
if (!args || typeof args !== "object") return args;
|
|
629
|
-
if (!Object.hasOwn(args, "timeoutMs")) return args;
|
|
630
|
-
const record = args;
|
|
631
|
-
if (typeof record.timeoutMs !== "number") return record;
|
|
632
|
-
const { timeoutMs, ...prepared } = record;
|
|
633
|
-
if (prepared.timeout === void 0) {
|
|
634
|
-
return { ...prepared, timeout: timeoutMs / 1e3 };
|
|
635
|
-
}
|
|
636
|
-
return prepared;
|
|
637
|
-
}
|
|
638
|
-
function optionalIdentifier(value, field) {
|
|
639
|
-
return value === void 0 ? void 0 : requiredIdentifier(value, field);
|
|
573
|
+
function isBrokerCredentials(value) {
|
|
574
|
+
if (!isRecord2(value) || Object.keys(value).length !== 3) return false;
|
|
575
|
+
return value.host === "127.0.0.1" && Number.isSafeInteger(value.port) && value.port >= 1 && value.port <= 65535 && typeof value.token === "string" && /^[a-f0-9]{64}$/u.test(value.token);
|
|
640
576
|
}
|
|
641
|
-
function
|
|
642
|
-
|
|
643
|
-
const identifier = value.trim();
|
|
644
|
-
if (identifier.length > MAX_IDENTIFIER_LENGTH || [...identifier].some((character) => {
|
|
577
|
+
function hasControlCharacter(value) {
|
|
578
|
+
return [...value].some((character) => {
|
|
645
579
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
646
580
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
return
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
function isRecord2(value) {
|
|
584
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
651
585
|
}
|
|
652
586
|
|
|
653
587
|
export {
|
|
@@ -658,10 +592,13 @@ export {
|
|
|
658
592
|
validateMessage,
|
|
659
593
|
sanitizeTerminalText,
|
|
660
594
|
BROKER_CREDENTIAL_FD,
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
595
|
+
CHILD_READINESS_FD,
|
|
596
|
+
CHILD_READINESS_FD_ENV,
|
|
597
|
+
MAX_READINESS_FRAME_BYTES,
|
|
598
|
+
childBootstrapEnvironment,
|
|
599
|
+
serializeChildBootstrap,
|
|
600
|
+
assertChildBootstrapCapacity,
|
|
601
|
+
captureChildBootstrap,
|
|
602
|
+
takeCapturedReadiness
|
|
666
603
|
};
|
|
667
|
-
//# sourceMappingURL=chunk-
|
|
604
|
+
//# sourceMappingURL=chunk-YMVSJ5GE.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/message-broker.ts", "../../src/broker-credentials.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomBytes, randomUUID } from \"node:crypto\";\nimport net, { type AddressInfo, type Server, type Socket } from \"node:net\";\nimport type { BrokerCredentials } from \"./types.js\";\n\nexport const BROKER_HOST = \"127.0.0.1\" as const;\nexport const MAX_MESSAGE_BYTES = 48 * 1024;\nexport const MAX_MESSAGE_LINES = 1_992;\nexport const MAX_FRAME_BYTES = 384 * 1024;\nexport const MAX_ERROR_BYTES = 8 * 1024;\nexport const MAX_OUTSTANDING_REQUESTS = 4;\nexport const MAX_IDENTIFIER_LENGTH = 128;\n\nconst MAX_CONNECTIONS = 32;\nconst REQUEST_FRAME_TIMEOUT_MS = 2_000;\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_RETAINED_CONSUMED_REQUESTS = 4;\nconst CHILD_WAIT_INTERRUPTED_ERROR =\n \"Subagent wait was interrupted by an incoming main-agent request. The original request remains active.\";\n\nexport type BrokerRequestOrigin = \"main\" | \"child\";\n\nexport interface BrokerInboundMessage {\n kind: \"request\" | \"response\";\n requestId: string;\n jobId: string;\n message: string;\n}\n\nexport interface BrokerSendAcknowledgement {\n requestId: string;\n accepted: boolean;\n duplicate: boolean;\n}\n\nexport interface MessageBrokerOptions {\n onMessage(message: BrokerInboundMessage): void;\n createServer?: (listener: (socket: Socket) => void) => Server;\n now?: () => number;\n requestFrameTimeoutMs?: number;\n}\n\ninterface JobRecord {\n jobId: string;\n generation: number;\n token: string;\n}\n\ninterface RequestWaiter {\n socket: Socket;\n timer?: NodeJS.Timeout;\n}\n\ninterface RequestRecord {\n requestId: string;\n jobId: string;\n generation: number;\n origin: BrokerRequestOrigin;\n expectedResponder: BrokerRequestOrigin;\n message: string;\n createdAt: number;\n response?: string;\n consumedAt?: number;\n deliveryQueued?: boolean;\n waiter?: RequestWaiter;\n}\n\ntype BrokerRequest =\n | {\n type: \"send\";\n token: string;\n recipient?: string;\n requestId?: string;\n message: string;\n }\n | { type: \"wait\"; token: string; requestId: string; timeoutMs?: number };\n\n/** Session-scoped authenticated loopback transport for bidirectional requests and responses. */\nexport class MessageBroker {\n private server?: Server;\n private starting?: Promise<void>;\n private address?: { host: typeof BROKER_HOST; port: number };\n private failure?: string;\n private generation = 0;\n private readonly sockets = new Set<Socket>();\n private readonly jobsByToken = new Map<string, JobRecord>();\n private readonly tokenByJob = new Map<string, string>();\n private readonly requests = new Map<string, RequestRecord>();\n private readonly inboundMessageListeners = new Set<() => void>();\n private pendingInboundResponse = false;\n private readonly createServer: (listener: (socket: Socket) => void) => Server;\n private readonly now: () => number;\n private readonly requestFrameTimeoutMs: number;\n\n constructor(private readonly options: MessageBrokerOptions) {\n this.createServer = options.createServer ?? ((listener) => net.createServer(listener));\n this.now = options.now ?? Date.now;\n this.requestFrameTimeoutMs = options.requestFrameTimeoutMs ?? REQUEST_FRAME_TIMEOUT_MS;\n if (!Number.isFinite(this.requestFrameTimeoutMs) || this.requestFrameTimeoutMs <= 0) {\n throw new Error(\"Subagent broker frame timeout must be positive.\");\n }\n }\n\n async start(): Promise<void> {\n if (this.server && this.address && !this.failure) return;\n if (this.starting) return this.starting;\n const ownerGeneration = ++this.generation;\n this.failure = undefined;\n this.starting = new Promise<void>((resolve, reject) => {\n const server = this.createServer((socket) => this.accept(socket));\n server.maxConnections = MAX_CONNECTIONS;\n const onStartError = (error: Error) => {\n server.close(() => undefined);\n reject(error);\n };\n server.once(\"error\", onStartError);\n server.listen({ host: BROKER_HOST, port: 0 }, () => {\n server.off(\"error\", onStartError);\n if (ownerGeneration !== this.generation) {\n server.close(() => undefined);\n reject(new Error(\"Subagent message broker start was superseded.\"));\n return;\n }\n const address = server.address();\n if (!isLoopbackAddress(address)) {\n server.close(() => undefined);\n reject(new Error(\"Subagent message broker did not bind to loopback.\"));\n return;\n }\n this.server = server;\n this.address = { host: BROKER_HOST, port: address.port };\n server.on(\"error\", (error) => this.fail(error));\n server.unref();\n resolve();\n });\n })\n .catch((error) => {\n const message = truncateError(error instanceof Error ? error.message : String(error));\n this.failure = message;\n throw new Error(`Subagent message broker failed to start: ${message}`);\n })\n .finally(() => {\n this.starting = undefined;\n });\n return this.starting;\n }\n\n assertReady(): void {\n if (this.server && this.address && !this.failure) return;\n throw new Error(\n this.failure\n ? `Subagent messaging is unavailable: ${this.failure}`\n : \"Subagent messaging is unavailable because the session broker is not ready.\",\n );\n }\n\n issueCredentials(input: { jobId: string; generation: number }): BrokerCredentials {\n this.assertReady();\n if (this.tokenByJob.has(input.jobId)) {\n throw new Error(`Subagent messaging credentials already exist for ${input.jobId}.`);\n }\n const token = randomBytes(32).toString(\"hex\");\n const record: JobRecord = { ...input, token };\n this.jobsByToken.set(token, record);\n this.tokenByJob.set(input.jobId, token);\n return { host: BROKER_HOST, port: this.address?.port ?? 0, token };\n }\n\n createMainRequest(jobId: string, message: string): BrokerSendAcknowledgement {\n this.assertReady();\n const job = this.requireJob(jobId);\n return this.createRequest(job, \"main\", message);\n }\n\n replyFromMain(requestId: string, message: string): BrokerSendAcknowledgement {\n this.assertReady();\n return this.acceptResponse(\"main\", undefined, requestId, message);\n }\n\n rollbackMainRequest(requestId: string): void {\n const request = this.requests.get(requestId);\n if (request?.origin === \"main\" && request.response === undefined) {\n this.clearWaiter(request);\n this.requests.delete(requestId);\n }\n }\n\n revokeJob(jobId: string, reason = \"Subagent job is no longer active.\"): void {\n const token = this.tokenByJob.get(jobId);\n if (token) {\n this.tokenByJob.delete(jobId);\n this.jobsByToken.delete(token);\n }\n for (const request of [...this.requests.values()]) {\n if (request.jobId !== jobId) continue;\n if (request.waiter) this.respondError(request.waiter.socket, reason);\n this.clearWaiter(request);\n this.requests.delete(request.requestId);\n }\n }\n\n hasPendingMainRequest(): boolean {\n return [...this.requests.values()].some((request) => request.origin === \"child\" && request.response === undefined);\n }\n\n takePendingInboundResponse(): boolean {\n const pending = this.pendingInboundResponse;\n this.pendingInboundResponse = false;\n return pending;\n }\n\n markMainRequestQueued(requestId: string): boolean {\n const request = this.requests.get(requestId);\n if (request?.origin !== \"main\") {\n throw new Error(\"Unknown or expired main-agent subagent request.\");\n }\n request.deliveryQueued = true;\n return request.response === undefined;\n }\n\n interruptChildWaits(jobId: string): number {\n let interrupted = 0;\n for (const request of this.requests.values()) {\n if (request.jobId !== jobId || request.origin !== \"child\" || !request.waiter) continue;\n const socket = request.waiter.socket;\n this.clearWaiter(request);\n this.respondError(socket, CHILD_WAIT_INTERRUPTED_ERROR);\n interrupted++;\n }\n return interrupted;\n }\n\n subscribeInboundMessage(listener: () => void): () => void {\n this.inboundMessageListeners.add(listener);\n if (this.hasPendingMainRequest()) listener();\n return () => this.inboundMessageListeners.delete(listener);\n }\n\n async shutdown(): Promise<void> {\n ++this.generation;\n const starting = this.starting;\n if (starting) await starting.catch(() => undefined);\n for (const jobId of [...this.tokenByJob.keys()]) {\n this.revokeJob(jobId, \"Subagent session shut down.\");\n }\n this.requests.clear();\n this.inboundMessageListeners.clear();\n this.pendingInboundResponse = false;\n for (const socket of this.sockets) socket.destroy();\n this.sockets.clear();\n const server = this.server;\n this.server = undefined;\n this.address = undefined;\n this.failure = undefined;\n if (!server?.listening) return;\n await new Promise<void>((resolve) => server.close(() => resolve()));\n }\n\n private requireJob(jobId: string): JobRecord {\n const token = this.tokenByJob.get(jobId);\n const job = token ? this.jobsByToken.get(token) : undefined;\n if (!job) throw new Error(\"Unknown or inactive subagent job.\");\n return job;\n }\n\n private hasQueuedRequestForChild(jobId: string): boolean {\n return [...this.requests.values()].some(\n (request) =>\n request.jobId === jobId &&\n request.origin === \"main\" &&\n request.deliveryQueued === true &&\n request.response === undefined,\n );\n }\n\n private createRequest(job: JobRecord, origin: BrokerRequestOrigin, message: string): BrokerSendAcknowledgement {\n validateMessage(message, \"Subagent request\");\n const outstanding = [...this.requests.values()].filter(\n (request) => request.jobId === job.jobId && request.consumedAt === undefined,\n ).length;\n if (outstanding >= MAX_OUTSTANDING_REQUESTS) {\n throw new Error(`Subagent job may have at most ${MAX_OUTSTANDING_REQUESTS} outstanding requests.`);\n }\n const request: RequestRecord = {\n requestId: `req_${randomUUID()}`,\n jobId: job.jobId,\n generation: job.generation,\n origin,\n expectedResponder: origin === \"main\" ? \"child\" : \"main\",\n message,\n createdAt: this.now(),\n };\n this.requests.set(request.requestId, request);\n if (origin === \"child\") {\n try {\n this.deliverInbound({\n kind: \"request\",\n requestId: request.requestId,\n jobId: request.jobId,\n message: request.message,\n });\n } catch (error) {\n this.requests.delete(request.requestId);\n throw error;\n }\n }\n return { requestId: request.requestId, accepted: true, duplicate: false };\n }\n\n private acceptResponse(\n responder: BrokerRequestOrigin,\n job: JobRecord | undefined,\n requestId: string,\n message: string,\n ): BrokerSendAcknowledgement {\n validateRequestId(requestId);\n validateMessage(message, \"Subagent response\");\n const request = this.requests.get(requestId);\n if (\n !request ||\n request.expectedResponder !== responder ||\n (job && (request.jobId !== job.jobId || request.generation !== job.generation))\n ) {\n throw new Error(\"Unknown, expired, or unauthorized subagent request.\");\n }\n if (request.response !== undefined) {\n return { requestId, accepted: false, duplicate: true };\n }\n request.response = message;\n if (responder === \"child\") {\n try {\n this.deliverInbound({\n kind: \"response\",\n requestId,\n jobId: request.jobId,\n message,\n });\n this.markConsumed(request);\n } catch (error) {\n request.response = undefined;\n throw error;\n }\n } else if (request.waiter) {\n const socket = request.waiter.socket;\n this.clearWaiter(request);\n this.respond(socket, { ok: true, response: message }, () => this.markConsumed(request));\n }\n return { requestId, accepted: true, duplicate: false };\n }\n\n private deliverInbound(message: BrokerInboundMessage): void {\n this.options.onMessage(message);\n if (message.kind === \"response\" && this.inboundMessageListeners.size === 0) {\n this.pendingInboundResponse = true;\n }\n for (const listener of [...this.inboundMessageListeners]) {\n try {\n listener();\n } catch {\n // Runtime wait observers cannot interrupt broker delivery.\n }\n }\n }\n\n private accept(socket: Socket): void {\n if (!this.server || this.failure || this.sockets.size >= MAX_CONNECTIONS) {\n socket.destroy();\n return;\n }\n this.sockets.add(socket);\n socket.setNoDelay(true);\n let frame = Buffer.alloc(0);\n let handled = false;\n const frameTimer = setTimeout(() => {\n if (handled) return;\n handled = true;\n this.respondError(socket, \"Subagent broker request frame timed out.\");\n }, this.requestFrameTimeoutMs);\n frameTimer.unref();\n const cleanup = () => {\n clearTimeout(frameTimer);\n this.sockets.delete(socket);\n for (const request of this.requests.values()) {\n if (request.waiter?.socket === socket) this.clearWaiter(request);\n }\n };\n socket.on(\"data\", (chunk: Buffer) => {\n if (handled) return;\n frame = Buffer.concat([frame, chunk]);\n if (frame.byteLength > MAX_FRAME_BYTES) {\n handled = true;\n clearTimeout(frameTimer);\n this.respondError(socket, \"Subagent broker request exceeded its size limit.\");\n return;\n }\n const newline = frame.indexOf(0x0a);\n if (newline < 0) return;\n handled = true;\n clearTimeout(frameTimer);\n socket.removeAllListeners(\"data\");\n const trailing = frame\n .subarray(newline + 1)\n .toString(\"utf8\")\n .trim();\n if (trailing) {\n this.respondError(socket, \"Subagent broker accepts one request per connection.\");\n return;\n }\n this.handleFrame(socket, frame.subarray(0, newline).toString(\"utf8\"));\n });\n socket.once(\"close\", cleanup);\n socket.once(\"error\", cleanup);\n }\n\n private handleFrame(socket: Socket, frame: string): void {\n let request: BrokerRequest;\n try {\n const parsed = JSON.parse(frame) as unknown;\n if (!isRecord(parsed)) throw new Error();\n request = parsed as BrokerRequest;\n } catch {\n this.respondError(socket, \"Malformed subagent broker request.\");\n return;\n }\n const job = typeof request.token === \"string\" ? this.jobsByToken.get(request.token) : undefined;\n if (!job) {\n this.respondError(socket, \"Unauthenticated subagent broker request.\");\n return;\n }\n try {\n if (request.type === \"send\") {\n this.handleSend(socket, job, request);\n return;\n }\n if (request.type === \"wait\") {\n this.handleWait(socket, job, request);\n return;\n }\n throw new Error(\"Unsupported subagent broker request.\");\n } catch (error) {\n this.respondError(socket, error instanceof Error ? error.message : String(error));\n }\n }\n\n private handleSend(socket: Socket, job: JobRecord, request: Extract<BrokerRequest, { type: \"send\" }>): void {\n if (typeof request.message !== \"string\") {\n throw new Error(\"subagent_send requires a message string.\");\n }\n const hasRecipient = typeof request.recipient === \"string\";\n const hasRequestId = typeof request.requestId === \"string\";\n if (hasRecipient === hasRequestId) {\n throw new Error(\"subagent_send requires exactly one of recipient or requestId.\");\n }\n const acknowledgement = hasRecipient\n ? request.recipient === \"main\"\n ? this.createRequest(job, \"child\", request.message)\n : (() => {\n throw new Error('A subagent may send new requests only to recipient \"main\".');\n })()\n : this.acceptResponse(\"child\", job, request.requestId ?? \"\", request.message);\n this.respond(socket, { ok: true, ...acknowledgement });\n }\n\n private handleWait(socket: Socket, job: JobRecord, request: Extract<BrokerRequest, { type: \"wait\" }>): void {\n if (typeof request.requestId !== \"string\") {\n throw new Error(\"Subagent wait requires a request ID.\");\n }\n validateRequestId(request.requestId);\n if (request.timeoutMs !== undefined) validateTimeout(request.timeoutMs);\n const record = this.requests.get(request.requestId);\n if (record?.origin !== \"child\" || record.jobId !== job.jobId || record.generation !== job.generation) {\n throw new Error(\"Unknown or expired subagent request.\");\n }\n if (record.consumedAt !== undefined && record.response !== undefined) {\n this.respond(socket, { ok: true, response: record.response });\n return;\n }\n if (record.response !== undefined) {\n this.respond(socket, { ok: true, response: record.response }, () => this.markConsumed(record));\n return;\n }\n if (this.hasQueuedRequestForChild(job.jobId)) {\n this.respondError(socket, CHILD_WAIT_INTERRUPTED_ERROR);\n return;\n }\n if (record.waiter) {\n throw new Error(`A wait is already active for subagent request ${request.requestId}.`);\n }\n const waiter: RequestWaiter = { socket };\n record.waiter = waiter;\n if (request.timeoutMs !== undefined) {\n waiter.timer = setTimeout(() => {\n if (record.waiter !== waiter) return;\n this.clearWaiter(record);\n this.respondError(socket, \"Subagent response wait timed out.\");\n }, request.timeoutMs);\n waiter.timer.unref();\n }\n }\n\n private markConsumed(request: RequestRecord): void {\n if (this.requests.get(request.requestId) !== request || request.response === undefined) return;\n request.consumedAt ??= this.now();\n const consumed = [...this.requests.values()]\n .filter((candidate) => candidate.jobId === request.jobId && candidate.consumedAt !== undefined)\n .sort((left, right) => (left.consumedAt ?? 0) - (right.consumedAt ?? 0));\n for (const expired of consumed.slice(0, Math.max(0, consumed.length - MAX_RETAINED_CONSUMED_REQUESTS))) {\n this.requests.delete(expired.requestId);\n }\n }\n\n private clearWaiter(request: RequestRecord): void {\n if (request.waiter?.timer) clearTimeout(request.waiter.timer);\n request.waiter = undefined;\n }\n\n private respondError(socket: Socket, error: string): void {\n this.respond(socket, { ok: false, error: truncateError(error) });\n }\n\n private respond(socket: Socket, value: Record<string, unknown>, onFlushed?: () => void): void {\n if (socket.destroyed) return;\n let content = `${JSON.stringify(value)}\\n`;\n if (Buffer.byteLength(content, \"utf8\") > MAX_FRAME_BYTES) {\n content = `${JSON.stringify({ ok: false, error: \"Subagent broker response exceeded its size limit.\" })}\\n`;\n }\n socket.end(content, onFlushed);\n }\n\n private fail(error: Error): void {\n this.failure = truncateError(error.message);\n for (const jobId of [...this.tokenByJob.keys()]) {\n this.revokeJob(jobId, \"Subagent message broker failed.\");\n }\n for (const socket of this.sockets) socket.destroy();\n this.sockets.clear();\n this.address = undefined;\n }\n}\n\nexport function validateMessage(message: string, label: string): void {\n if (!message.trim()) throw new Error(`${label} is required.`);\n if (message.includes(\"\\0\")) throw new Error(`${label} must not contain NUL bytes.`);\n if (Buffer.byteLength(message, \"utf8\") > MAX_MESSAGE_BYTES) {\n throw new Error(`${label} must be at most ${MAX_MESSAGE_BYTES} UTF-8 bytes.`);\n }\n if (lineCount(message) > MAX_MESSAGE_LINES) {\n throw new Error(`${label} must contain at most ${MAX_MESSAGE_LINES} lines.`);\n }\n}\n\nexport function sanitizeTerminalText(value: string): string {\n return [...value]\n .filter((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n if (character === \"\\n\" || character === \"\\t\") return true;\n if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) return false;\n return !((codePoint >= 0x202a && codePoint <= 0x202e) || (codePoint >= 0x2066 && codePoint <= 0x2069));\n })\n .join(\"\");\n}\n\nfunction validateRequestId(requestId: string): void {\n if (\n requestId.length > MAX_IDENTIFIER_LENGTH ||\n !/^req_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(requestId)\n ) {\n throw new Error(\"Invalid subagent request ID.\");\n }\n}\n\nfunction validateTimeout(timeoutMs: number): void {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {\n throw new Error(\"Subagent wait timeout is outside the supported range.\");\n }\n}\n\nfunction lineCount(value: string): number {\n let count = 1;\n for (const character of value) if (character === \"\\n\") count++;\n return count;\n}\n\nfunction truncateError(error: string): string {\n const bytes = Buffer.from(error, \"utf8\");\n if (bytes.length <= MAX_ERROR_BYTES) return error;\n return `${bytes\n .subarray(0, Math.max(0, MAX_ERROR_BYTES - 18))\n .toString(\"utf8\")\n .replace(/\uFFFD+$/gu, \"\")}\\n\u2026 [truncated]`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isLoopbackAddress(address: AddressInfo | string | null): address is AddressInfo {\n return Boolean(address && typeof address !== \"string\" && address.address === BROKER_HOST);\n}\n", "import * as fs from \"node:fs\";\nimport type { BrokerCredentials } from \"./types.js\";\n\nexport const BROKER_CREDENTIAL_FD = 3;\nexport const BROKER_CREDENTIAL_FD_ENV = \"PI_SUBAGENT_BROKER_FD\";\nexport const CHILD_READINESS_FD = 4;\nexport const CHILD_READINESS_FD_ENV = \"PI_SUBAGENT_READINESS_FD\";\nexport const MAX_READINESS_FRAME_BYTES = 16 * 1024;\n\nconst MAX_BOOTSTRAP_BYTES = 16 * 1024;\nconst MAX_EXPECTED_TOOLS = 66;\nconst MAX_TOOL_NAME_LENGTH = 128;\nconst READINESS_STATE_KEY = Symbol.for(\"@narumitw/pi-subagents/child-readiness\");\n\nexport interface ChildBootstrap {\n communication: BrokerCredentials;\n expectedTools: string[];\n}\n\nexport interface CapturedChildBootstrap extends ChildBootstrap {\n readinessFd?: number;\n}\n\nexport function childBootstrapEnvironment(expectReadiness: boolean): NodeJS.ProcessEnv {\n return {\n [BROKER_CREDENTIAL_FD_ENV]: String(BROKER_CREDENTIAL_FD),\n ...(expectReadiness ? { [CHILD_READINESS_FD_ENV]: String(CHILD_READINESS_FD) } : {}),\n };\n}\n\nexport function serializeChildBootstrap(bootstrap: ChildBootstrap): string {\n return JSON.stringify(bootstrap);\n}\n\nexport function assertChildBootstrapCapacity(expectedTools: string[]): void {\n const serialized = serializeChildBootstrap({\n communication: { host: \"127.0.0.1\", port: 65_535, token: \"f\".repeat(64) },\n expectedTools,\n });\n if (Buffer.byteLength(serialized, \"utf8\") > MAX_BOOTSTRAP_BYTES) {\n throw new Error(`Subagent selected tool names exceed the ${MAX_BOOTSTRAP_BYTES}-byte child bootstrap size limit.`);\n }\n}\n\nexport function captureChildBootstrap(\n readBootstrap: () => string = readBootstrapPipe,\n): CapturedChildBootstrap | undefined {\n const credentialDescriptor = process.env[BROKER_CREDENTIAL_FD_ENV];\n const readinessDescriptor = process.env[CHILD_READINESS_FD_ENV];\n delete process.env[BROKER_CREDENTIAL_FD_ENV];\n delete process.env[CHILD_READINESS_FD_ENV];\n setCapturedReadiness(undefined);\n if (credentialDescriptor === undefined) {\n if (readinessDescriptor !== undefined) throw new Error(\"Unexpected pi-subagents readiness descriptor.\");\n return undefined;\n }\n if (credentialDescriptor !== String(BROKER_CREDENTIAL_FD)) {\n throw new Error(\"Invalid pi-subagents broker credential descriptor.\");\n }\n let serialized: string;\n try {\n serialized = readBootstrap();\n } catch {\n throw new Error(\"Unable to read pi-subagents child bootstrap.\");\n }\n if (Buffer.byteLength(serialized, \"utf8\") > MAX_BOOTSTRAP_BYTES) {\n throw new Error(\"Invalid pi-subagents child bootstrap.\");\n }\n let value: unknown;\n try {\n value = JSON.parse(serialized);\n } catch {\n throw new Error(\"Invalid pi-subagents child bootstrap.\");\n }\n if (!isChildBootstrap(value)) throw new Error(\"Invalid pi-subagents child bootstrap.\");\n\n if (value.expectedTools.length > 0) {\n if (readinessDescriptor !== String(CHILD_READINESS_FD)) {\n throw new Error(\"Invalid pi-subagents readiness descriptor.\");\n }\n setCapturedReadiness({ fd: CHILD_READINESS_FD, expectedTools: [...value.expectedTools] });\n return { ...value, readinessFd: CHILD_READINESS_FD };\n }\n if (readinessDescriptor !== undefined) throw new Error(\"Unexpected pi-subagents readiness descriptor.\");\n setCapturedReadiness(undefined);\n return value;\n}\n\nexport function takeCapturedReadiness(): { fd: number; expectedTools: string[] } | undefined {\n const store = globalThis as unknown as Record<symbol, { fd: number; expectedTools: string[] } | undefined>;\n const readiness = store[READINESS_STATE_KEY];\n delete store[READINESS_STATE_KEY];\n return readiness ? { fd: readiness.fd, expectedTools: [...readiness.expectedTools] } : undefined;\n}\n\nfunction setCapturedReadiness(readiness: { fd: number; expectedTools: string[] } | undefined): void {\n const store = globalThis as unknown as Record<symbol, { fd: number; expectedTools: string[] } | undefined>;\n if (readiness) store[READINESS_STATE_KEY] = readiness;\n else delete store[READINESS_STATE_KEY];\n}\n\nfunction readBootstrapPipe(): string {\n try {\n return fs.readFileSync(BROKER_CREDENTIAL_FD, \"utf8\");\n } finally {\n try {\n fs.closeSync(BROKER_CREDENTIAL_FD);\n } catch {\n // The descriptor may already be closed after a failed read.\n }\n }\n}\n\nfunction isChildBootstrap(value: unknown): value is ChildBootstrap {\n if (!isRecord(value) || Object.keys(value).length !== 2) return false;\n if (!isBrokerCredentials(value.communication)) return false;\n if (!Array.isArray(value.expectedTools) || value.expectedTools.length > MAX_EXPECTED_TOOLS) return false;\n return value.expectedTools.every(\n (tool) =>\n typeof tool === \"string\" &&\n tool.length > 0 &&\n tool.length <= MAX_TOOL_NAME_LENGTH &&\n !tool.includes(\",\") &&\n !hasControlCharacter(tool),\n );\n}\n\nfunction isBrokerCredentials(value: unknown): value is BrokerCredentials {\n if (!isRecord(value) || Object.keys(value).length !== 3) return false;\n return (\n value.host === \"127.0.0.1\" &&\n Number.isSafeInteger(value.port) &&\n (value.port as number) >= 1 &&\n (value.port as number) <= 65_535 &&\n typeof value.token === \"string\" &&\n /^[a-f0-9]{64}$/u.test(value.token)\n );\n}\n\nfunction hasControlCharacter(value: string): boolean {\n return [...value].some((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAAA,SAAS,aAAa,kBAAkB;AACxC,OAAO,SAAyD;AAGzD,IAAM,cAAc;AACpB,IAAM,oBAAoB,KAAK;AAC/B,IAAM,oBAAoB;AAC1B,IAAM,kBAAkB,MAAM;AAC9B,IAAM,kBAAkB,IAAI;AAC5B,IAAM,2BAA2B;AACjC,IAAM,wBAAwB;AAErC,IAAM,kBAAkB;AACxB,IAAM,2BAA2B;AACjC,IAAM,iBAAiB;AACvB,IAAM,iCAAiC;AACvC,IAAM,+BACJ;AA4DK,IAAM,gBAAN,MAAoB;AAAA,EAgBzB,YAA6B,SAA+B;AAA/B;AAC3B,SAAK,eAAe,QAAQ,iBAAiB,CAAC,aAAa,IAAI,aAAa,QAAQ;AACpF,SAAK,MAAM,QAAQ,OAAO,KAAK;AAC/B,SAAK,wBAAwB,QAAQ,yBAAyB;AAC9D,QAAI,CAAC,OAAO,SAAS,KAAK,qBAAqB,KAAK,KAAK,yBAAyB,GAAG;AACnF,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAAA,EACF;AAAA,EAP6B;AAAA,EAfrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACJ,UAAU,oBAAI,IAAY;AAAA,EAC1B,cAAc,oBAAI,IAAuB;AAAA,EACzC,aAAa,oBAAI,IAAoB;AAAA,EACrC,WAAW,oBAAI,IAA2B;AAAA,EAC1C,0BAA0B,oBAAI,IAAgB;AAAA,EACvD,yBAAyB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EAWjB,MAAM,QAAuB;AAC3B,QAAI,KAAK,UAAU,KAAK,WAAW,CAAC,KAAK,QAAS;AAClD,QAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,UAAM,kBAAkB,EAAE,KAAK;AAC/B,SAAK,UAAU;AACf,SAAK,WAAW,IAAI,QAAc,CAAC,SAAS,WAAW;AACrD,YAAM,SAAS,KAAK,aAAa,CAAC,WAAW,KAAK,OAAO,MAAM,CAAC;AAChE,aAAO,iBAAiB;AACxB,YAAM,eAAe,CAAC,UAAiB;AACrC,eAAO,MAAM,MAAM,MAAS;AAC5B,eAAO,KAAK;AAAA,MACd;AACA,aAAO,KAAK,SAAS,YAAY;AACjC,aAAO,OAAO,EAAE,MAAM,aAAa,MAAM,EAAE,GAAG,MAAM;AAClD,eAAO,IAAI,SAAS,YAAY;AAChC,YAAI,oBAAoB,KAAK,YAAY;AACvC,iBAAO,MAAM,MAAM,MAAS;AAC5B,iBAAO,IAAI,MAAM,+CAA+C,CAAC;AACjE;AAAA,QACF;AACA,cAAM,UAAU,OAAO,QAAQ;AAC/B,YAAI,CAAC,kBAAkB,OAAO,GAAG;AAC/B,iBAAO,MAAM,MAAM,MAAS;AAC5B,iBAAO,IAAI,MAAM,mDAAmD,CAAC;AACrE;AAAA,QACF;AACA,aAAK,SAAS;AACd,aAAK,UAAU,EAAE,MAAM,aAAa,MAAM,QAAQ,KAAK;AACvD,eAAO,GAAG,SAAS,CAAC,UAAU,KAAK,KAAK,KAAK,CAAC;AAC9C,eAAO,MAAM;AACb,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC,EACE,MAAM,CAAC,UAAU;AAChB,YAAM,UAAU,cAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpF,WAAK,UAAU;AACf,YAAM,IAAI,MAAM,4CAA4C,OAAO,EAAE;AAAA,IACvE,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,WAAW;AAAA,IAClB,CAAC;AACH,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAoB;AAClB,QAAI,KAAK,UAAU,KAAK,WAAW,CAAC,KAAK,QAAS;AAClD,UAAM,IAAI;AAAA,MACR,KAAK,UACD,sCAAsC,KAAK,OAAO,KAClD;AAAA,IACN;AAAA,EACF;AAAA,EAEA,iBAAiB,OAAiE;AAChF,SAAK,YAAY;AACjB,QAAI,KAAK,WAAW,IAAI,MAAM,KAAK,GAAG;AACpC,YAAM,IAAI,MAAM,oDAAoD,MAAM,KAAK,GAAG;AAAA,IACpF;AACA,UAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,UAAM,SAAoB,EAAE,GAAG,OAAO,MAAM;AAC5C,SAAK,YAAY,IAAI,OAAO,MAAM;AAClC,SAAK,WAAW,IAAI,MAAM,OAAO,KAAK;AACtC,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK,SAAS,QAAQ,GAAG,MAAM;AAAA,EACnE;AAAA,EAEA,kBAAkB,OAAe,SAA4C;AAC3E,SAAK,YAAY;AACjB,UAAM,MAAM,KAAK,WAAW,KAAK;AACjC,WAAO,KAAK,cAAc,KAAK,QAAQ,OAAO;AAAA,EAChD;AAAA,EAEA,cAAc,WAAmB,SAA4C;AAC3E,SAAK,YAAY;AACjB,WAAO,KAAK,eAAe,QAAQ,QAAW,WAAW,OAAO;AAAA,EAClE;AAAA,EAEA,oBAAoB,WAAyB;AAC3C,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,SAAS,WAAW,UAAU,QAAQ,aAAa,QAAW;AAChE,WAAK,YAAY,OAAO;AACxB,WAAK,SAAS,OAAO,SAAS;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,UAAU,OAAe,SAAS,qCAA2C;AAC3E,UAAM,QAAQ,KAAK,WAAW,IAAI,KAAK;AACvC,QAAI,OAAO;AACT,WAAK,WAAW,OAAO,KAAK;AAC5B,WAAK,YAAY,OAAO,KAAK;AAAA,IAC/B;AACA,eAAW,WAAW,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,GAAG;AACjD,UAAI,QAAQ,UAAU,MAAO;AAC7B,UAAI,QAAQ,OAAQ,MAAK,aAAa,QAAQ,OAAO,QAAQ,MAAM;AACnE,WAAK,YAAY,OAAO;AACxB,WAAK,SAAS,OAAO,QAAQ,SAAS;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,wBAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,QAAQ,WAAW,WAAW,QAAQ,aAAa,MAAS;AAAA,EACnH;AAAA,EAEA,6BAAsC;AACpC,UAAM,UAAU,KAAK;AACrB,SAAK,yBAAyB;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,sBAAsB,WAA4B;AAChD,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,SAAS,WAAW,QAAQ;AAC9B,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AACA,YAAQ,iBAAiB;AACzB,WAAO,QAAQ,aAAa;AAAA,EAC9B;AAAA,EAEA,oBAAoB,OAAuB;AACzC,QAAI,cAAc;AAClB,eAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC5C,UAAI,QAAQ,UAAU,SAAS,QAAQ,WAAW,WAAW,CAAC,QAAQ,OAAQ;AAC9E,YAAM,SAAS,QAAQ,OAAO;AAC9B,WAAK,YAAY,OAAO;AACxB,WAAK,aAAa,QAAQ,4BAA4B;AACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,wBAAwB,UAAkC;AACxD,SAAK,wBAAwB,IAAI,QAAQ;AACzC,QAAI,KAAK,sBAAsB,EAAG,UAAS;AAC3C,WAAO,MAAM,KAAK,wBAAwB,OAAO,QAAQ;AAAA,EAC3D;AAAA,EAEA,MAAM,WAA0B;AAC9B,MAAE,KAAK;AACP,UAAM,WAAW,KAAK;AACtB,QAAI,SAAU,OAAM,SAAS,MAAM,MAAM,MAAS;AAClD,eAAW,SAAS,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC,GAAG;AAC/C,WAAK,UAAU,OAAO,6BAA6B;AAAA,IACrD;AACA,SAAK,SAAS,MAAM;AACpB,SAAK,wBAAwB,MAAM;AACnC,SAAK,yBAAyB;AAC9B,eAAW,UAAU,KAAK,QAAS,QAAO,QAAQ;AAClD,SAAK,QAAQ,MAAM;AACnB,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,UAAU;AACf,QAAI,CAAC,QAAQ,UAAW;AACxB,UAAM,IAAI,QAAc,CAAC,YAAY,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,EACpE;AAAA,EAEQ,WAAW,OAA0B;AAC3C,UAAM,QAAQ,KAAK,WAAW,IAAI,KAAK;AACvC,UAAM,MAAM,QAAQ,KAAK,YAAY,IAAI,KAAK,IAAI;AAClD,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mCAAmC;AAC7D,WAAO;AAAA,EACT;AAAA,EAEQ,yBAAyB,OAAwB;AACvD,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MACjC,CAAC,YACC,QAAQ,UAAU,SAClB,QAAQ,WAAW,UACnB,QAAQ,mBAAmB,QAC3B,QAAQ,aAAa;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,cAAc,KAAgB,QAA6B,SAA4C;AAC7G,oBAAgB,SAAS,kBAAkB;AAC3C,UAAM,cAAc,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MAC9C,CAACA,aAAYA,SAAQ,UAAU,IAAI,SAASA,SAAQ,eAAe;AAAA,IACrE,EAAE;AACF,QAAI,eAAe,0BAA0B;AAC3C,YAAM,IAAI,MAAM,iCAAiC,wBAAwB,wBAAwB;AAAA,IACnG;AACA,UAAM,UAAyB;AAAA,MAC7B,WAAW,OAAO,WAAW,CAAC;AAAA,MAC9B,OAAO,IAAI;AAAA,MACX,YAAY,IAAI;AAAA,MAChB;AAAA,MACA,mBAAmB,WAAW,SAAS,UAAU;AAAA,MACjD;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AACA,SAAK,SAAS,IAAI,QAAQ,WAAW,OAAO;AAC5C,QAAI,WAAW,SAAS;AACtB,UAAI;AACF,aAAK,eAAe;AAAA,UAClB,MAAM;AAAA,UACN,WAAW,QAAQ;AAAA,UACnB,OAAO,QAAQ;AAAA,UACf,SAAS,QAAQ;AAAA,QACnB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,aAAK,SAAS,OAAO,QAAQ,SAAS;AACtC,cAAM;AAAA,MACR;AAAA,IACF;AACA,WAAO,EAAE,WAAW,QAAQ,WAAW,UAAU,MAAM,WAAW,MAAM;AAAA,EAC1E;AAAA,EAEQ,eACN,WACA,KACA,WACA,SAC2B;AAC3B,sBAAkB,SAAS;AAC3B,oBAAgB,SAAS,mBAAmB;AAC5C,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QACE,CAAC,WACD,QAAQ,sBAAsB,aAC7B,QAAQ,QAAQ,UAAU,IAAI,SAAS,QAAQ,eAAe,IAAI,aACnE;AACA,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AACA,QAAI,QAAQ,aAAa,QAAW;AAClC,aAAO,EAAE,WAAW,UAAU,OAAO,WAAW,KAAK;AAAA,IACvD;AACA,YAAQ,WAAW;AACnB,QAAI,cAAc,SAAS;AACzB,UAAI;AACF,aAAK,eAAe;AAAA,UAClB,MAAM;AAAA,UACN;AAAA,UACA,OAAO,QAAQ;AAAA,UACf;AAAA,QACF,CAAC;AACD,aAAK,aAAa,OAAO;AAAA,MAC3B,SAAS,OAAO;AACd,gBAAQ,WAAW;AACnB,cAAM;AAAA,MACR;AAAA,IACF,WAAW,QAAQ,QAAQ;AACzB,YAAM,SAAS,QAAQ,OAAO;AAC9B,WAAK,YAAY,OAAO;AACxB,WAAK,QAAQ,QAAQ,EAAE,IAAI,MAAM,UAAU,QAAQ,GAAG,MAAM,KAAK,aAAa,OAAO,CAAC;AAAA,IACxF;AACA,WAAO,EAAE,WAAW,UAAU,MAAM,WAAW,MAAM;AAAA,EACvD;AAAA,EAEQ,eAAe,SAAqC;AAC1D,SAAK,QAAQ,UAAU,OAAO;AAC9B,QAAI,QAAQ,SAAS,cAAc,KAAK,wBAAwB,SAAS,GAAG;AAC1E,WAAK,yBAAyB;AAAA,IAChC;AACA,eAAW,YAAY,CAAC,GAAG,KAAK,uBAAuB,GAAG;AACxD,UAAI;AACF,iBAAS;AAAA,MACX,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,OAAO,QAAsB;AACnC,QAAI,CAAC,KAAK,UAAU,KAAK,WAAW,KAAK,QAAQ,QAAQ,iBAAiB;AACxE,aAAO,QAAQ;AACf;AAAA,IACF;AACA,SAAK,QAAQ,IAAI,MAAM;AACvB,WAAO,WAAW,IAAI;AACtB,QAAI,QAAQ,OAAO,MAAM,CAAC;AAC1B,QAAI,UAAU;AACd,UAAM,aAAa,WAAW,MAAM;AAClC,UAAI,QAAS;AACb,gBAAU;AACV,WAAK,aAAa,QAAQ,0CAA0C;AAAA,IACtE,GAAG,KAAK,qBAAqB;AAC7B,eAAW,MAAM;AACjB,UAAM,UAAU,MAAM;AACpB,mBAAa,UAAU;AACvB,WAAK,QAAQ,OAAO,MAAM;AAC1B,iBAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC5C,YAAI,QAAQ,QAAQ,WAAW,OAAQ,MAAK,YAAY,OAAO;AAAA,MACjE;AAAA,IACF;AACA,WAAO,GAAG,QAAQ,CAAC,UAAkB;AACnC,UAAI,QAAS;AACb,cAAQ,OAAO,OAAO,CAAC,OAAO,KAAK,CAAC;AACpC,UAAI,MAAM,aAAa,iBAAiB;AACtC,kBAAU;AACV,qBAAa,UAAU;AACvB,aAAK,aAAa,QAAQ,kDAAkD;AAC5E;AAAA,MACF;AACA,YAAM,UAAU,MAAM,QAAQ,EAAI;AAClC,UAAI,UAAU,EAAG;AACjB,gBAAU;AACV,mBAAa,UAAU;AACvB,aAAO,mBAAmB,MAAM;AAChC,YAAM,WAAW,MACd,SAAS,UAAU,CAAC,EACpB,SAAS,MAAM,EACf,KAAK;AACR,UAAI,UAAU;AACZ,aAAK,aAAa,QAAQ,qDAAqD;AAC/E;AAAA,MACF;AACA,WAAK,YAAY,QAAQ,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,MAAM,CAAC;AAAA,IACtE,CAAC;AACD,WAAO,KAAK,SAAS,OAAO;AAC5B,WAAO,KAAK,SAAS,OAAO;AAAA,EAC9B;AAAA,EAEQ,YAAY,QAAgB,OAAqB;AACvD,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,UAAI,CAAC,SAAS,MAAM,EAAG,OAAM,IAAI,MAAM;AACvC,gBAAU;AAAA,IACZ,QAAQ;AACN,WAAK,aAAa,QAAQ,oCAAoC;AAC9D;AAAA,IACF;AACA,UAAM,MAAM,OAAO,QAAQ,UAAU,WAAW,KAAK,YAAY,IAAI,QAAQ,KAAK,IAAI;AACtF,QAAI,CAAC,KAAK;AACR,WAAK,aAAa,QAAQ,0CAA0C;AACpE;AAAA,IACF;AACA,QAAI;AACF,UAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAK,WAAW,QAAQ,KAAK,OAAO;AACpC;AAAA,MACF;AACA,UAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAK,WAAW,QAAQ,KAAK,OAAO;AACpC;AAAA,MACF;AACA,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD,SAAS,OAAO;AACd,WAAK,aAAa,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IAClF;AAAA,EACF;AAAA,EAEQ,WAAW,QAAgB,KAAgB,SAAyD;AAC1G,QAAI,OAAO,QAAQ,YAAY,UAAU;AACvC,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,UAAM,eAAe,OAAO,QAAQ,cAAc;AAClD,UAAM,eAAe,OAAO,QAAQ,cAAc;AAClD,QAAI,iBAAiB,cAAc;AACjC,YAAM,IAAI,MAAM,+DAA+D;AAAA,IACjF;AACA,UAAM,kBAAkB,eACpB,QAAQ,cAAc,SACpB,KAAK,cAAc,KAAK,SAAS,QAAQ,OAAO,KAC/C,MAAM;AACL,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E,GAAG,IACL,KAAK,eAAe,SAAS,KAAK,QAAQ,aAAa,IAAI,QAAQ,OAAO;AAC9E,SAAK,QAAQ,QAAQ,EAAE,IAAI,MAAM,GAAG,gBAAgB,CAAC;AAAA,EACvD;AAAA,EAEQ,WAAW,QAAgB,KAAgB,SAAyD;AAC1G,QAAI,OAAO,QAAQ,cAAc,UAAU;AACzC,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,sBAAkB,QAAQ,SAAS;AACnC,QAAI,QAAQ,cAAc,OAAW,iBAAgB,QAAQ,SAAS;AACtE,UAAM,SAAS,KAAK,SAAS,IAAI,QAAQ,SAAS;AAClD,QAAI,QAAQ,WAAW,WAAW,OAAO,UAAU,IAAI,SAAS,OAAO,eAAe,IAAI,YAAY;AACpG,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,QAAI,OAAO,eAAe,UAAa,OAAO,aAAa,QAAW;AACpE,WAAK,QAAQ,QAAQ,EAAE,IAAI,MAAM,UAAU,OAAO,SAAS,CAAC;AAC5D;AAAA,IACF;AACA,QAAI,OAAO,aAAa,QAAW;AACjC,WAAK,QAAQ,QAAQ,EAAE,IAAI,MAAM,UAAU,OAAO,SAAS,GAAG,MAAM,KAAK,aAAa,MAAM,CAAC;AAC7F;AAAA,IACF;AACA,QAAI,KAAK,yBAAyB,IAAI,KAAK,GAAG;AAC5C,WAAK,aAAa,QAAQ,4BAA4B;AACtD;AAAA,IACF;AACA,QAAI,OAAO,QAAQ;AACjB,YAAM,IAAI,MAAM,iDAAiD,QAAQ,SAAS,GAAG;AAAA,IACvF;AACA,UAAM,SAAwB,EAAE,OAAO;AACvC,WAAO,SAAS;AAChB,QAAI,QAAQ,cAAc,QAAW;AACnC,aAAO,QAAQ,WAAW,MAAM;AAC9B,YAAI,OAAO,WAAW,OAAQ;AAC9B,aAAK,YAAY,MAAM;AACvB,aAAK,aAAa,QAAQ,mCAAmC;AAAA,MAC/D,GAAG,QAAQ,SAAS;AACpB,aAAO,MAAM,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEQ,aAAa,SAA8B;AACjD,QAAI,KAAK,SAAS,IAAI,QAAQ,SAAS,MAAM,WAAW,QAAQ,aAAa,OAAW;AACxF,YAAQ,eAAe,KAAK,IAAI;AAChC,UAAM,WAAW,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EACxC,OAAO,CAAC,cAAc,UAAU,UAAU,QAAQ,SAAS,UAAU,eAAe,MAAS,EAC7F,KAAK,CAAC,MAAM,WAAW,KAAK,cAAc,MAAM,MAAM,cAAc,EAAE;AACzE,eAAW,WAAW,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,SAAS,SAAS,8BAA8B,CAAC,GAAG;AACtG,WAAK,SAAS,OAAO,QAAQ,SAAS;AAAA,IACxC;AAAA,EACF;AAAA,EAEQ,YAAY,SAA8B;AAChD,QAAI,QAAQ,QAAQ,MAAO,cAAa,QAAQ,OAAO,KAAK;AAC5D,YAAQ,SAAS;AAAA,EACnB;AAAA,EAEQ,aAAa,QAAgB,OAAqB;AACxD,SAAK,QAAQ,QAAQ,EAAE,IAAI,OAAO,OAAO,cAAc,KAAK,EAAE,CAAC;AAAA,EACjE;AAAA,EAEQ,QAAQ,QAAgB,OAAgC,WAA8B;AAC5F,QAAI,OAAO,UAAW;AACtB,QAAI,UAAU,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA;AACtC,QAAI,OAAO,WAAW,SAAS,MAAM,IAAI,iBAAiB;AACxD,gBAAU,GAAG,KAAK,UAAU,EAAE,IAAI,OAAO,OAAO,oDAAoD,CAAC,CAAC;AAAA;AAAA,IACxG;AACA,WAAO,IAAI,SAAS,SAAS;AAAA,EAC/B;AAAA,EAEQ,KAAK,OAAoB;AAC/B,SAAK,UAAU,cAAc,MAAM,OAAO;AAC1C,eAAW,SAAS,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC,GAAG;AAC/C,WAAK,UAAU,OAAO,iCAAiC;AAAA,IACzD;AACA,eAAW,UAAU,KAAK,QAAS,QAAO,QAAQ;AAClD,SAAK,QAAQ,MAAM;AACnB,SAAK,UAAU;AAAA,EACjB;AACF;AAEO,SAAS,gBAAgB,SAAiB,OAAqB;AACpE,MAAI,CAAC,QAAQ,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,eAAe;AAC5D,MAAI,QAAQ,SAAS,IAAI,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,8BAA8B;AAClF,MAAI,OAAO,WAAW,SAAS,MAAM,IAAI,mBAAmB;AAC1D,UAAM,IAAI,MAAM,GAAG,KAAK,oBAAoB,iBAAiB,eAAe;AAAA,EAC9E;AACA,MAAI,UAAU,OAAO,IAAI,mBAAmB;AAC1C,UAAM,IAAI,MAAM,GAAG,KAAK,yBAAyB,iBAAiB,SAAS;AAAA,EAC7E;AACF;AAEO,SAAS,qBAAqB,OAAuB;AAC1D,SAAO,CAAC,GAAG,KAAK,EACb,OAAO,CAAC,cAAc;AACrB,UAAM,YAAY,UAAU,YAAY,CAAC,KAAK;AAC9C,QAAI,cAAc,QAAQ,cAAc,IAAM,QAAO;AACrD,QAAI,aAAa,MAAS,aAAa,OAAQ,aAAa,IAAO,QAAO;AAC1E,WAAO,EAAG,aAAa,QAAU,aAAa,QAAY,aAAa,QAAU,aAAa;AAAA,EAChG,CAAC,EACA,KAAK,EAAE;AACZ;AAEA,SAAS,kBAAkB,WAAyB;AAClD,MACE,UAAU,SAAS,yBACnB,CAAC,6EAA6E,KAAK,SAAS,GAC5F;AACA,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACF;AAEA,SAAS,gBAAgB,WAAyB;AAChD,MAAI,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,KAAK,YAAY,gBAAgB;AAC/E,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACF;AAEA,SAAS,UAAU,OAAuB;AACxC,MAAI,QAAQ;AACZ,aAAW,aAAa,MAAO,KAAI,cAAc,KAAM;AACvD,SAAO;AACT;AAEA,SAAS,cAAc,OAAuB;AAC5C,QAAM,QAAQ,OAAO,KAAK,OAAO,MAAM;AACvC,MAAI,MAAM,UAAU,gBAAiB,QAAO;AAC5C,SAAO,GAAG,MACP,SAAS,GAAG,KAAK,IAAI,GAAG,kBAAkB,EAAE,CAAC,EAC7C,SAAS,MAAM,EACf,QAAQ,SAAS,EAAE,CAAC;AAAA;AACzB;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,kBAAkB,SAA8D;AACvF,SAAO,QAAQ,WAAW,OAAO,YAAY,YAAY,QAAQ,YAAY,WAAW;AAC1F;;;ACrlBA,YAAY,QAAQ;AAGb,IAAM,uBAAuB;AAC7B,IAAM,2BAA2B;AACjC,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B,KAAK;AAE9C,IAAM,sBAAsB,KAAK;AACjC,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB,uBAAO,IAAI,wCAAwC;AAWxE,SAAS,0BAA0B,iBAA6C;AACrF,SAAO;AAAA,IACL,CAAC,wBAAwB,GAAG,OAAO,oBAAoB;AAAA,IACvD,GAAI,kBAAkB,EAAE,CAAC,sBAAsB,GAAG,OAAO,kBAAkB,EAAE,IAAI,CAAC;AAAA,EACpF;AACF;AAEO,SAAS,wBAAwB,WAAmC;AACzE,SAAO,KAAK,UAAU,SAAS;AACjC;AAEO,SAAS,6BAA6B,eAA+B;AAC1E,QAAM,aAAa,wBAAwB;AAAA,IACzC,eAAe,EAAE,MAAM,aAAa,MAAM,OAAQ,OAAO,IAAI,OAAO,EAAE,EAAE;AAAA,IACxE;AAAA,EACF,CAAC;AACD,MAAI,OAAO,WAAW,YAAY,MAAM,IAAI,qBAAqB;AAC/D,UAAM,IAAI,MAAM,2CAA2C,mBAAmB,mCAAmC;AAAA,EACnH;AACF;AAEO,SAAS,sBACd,gBAA8B,mBACM;AACpC,QAAM,uBAAuB,QAAQ,IAAI,wBAAwB;AACjE,QAAM,sBAAsB,QAAQ,IAAI,sBAAsB;AAC9D,SAAO,QAAQ,IAAI,wBAAwB;AAC3C,SAAO,QAAQ,IAAI,sBAAsB;AACzC,uBAAqB,MAAS;AAC9B,MAAI,yBAAyB,QAAW;AACtC,QAAI,wBAAwB,OAAW,OAAM,IAAI,MAAM,+CAA+C;AACtG,WAAO;AAAA,EACT;AACA,MAAI,yBAAyB,OAAO,oBAAoB,GAAG;AACzD,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,MAAI;AACJ,MAAI;AACF,iBAAa,cAAc;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,MAAI,OAAO,WAAW,YAAY,MAAM,IAAI,qBAAqB;AAC/D,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACA,MAAI;AACJ,MAAI;AACF,YAAQ,KAAK,MAAM,UAAU;AAAA,EAC/B,QAAQ;AACN,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACA,MAAI,CAAC,iBAAiB,KAAK,EAAG,OAAM,IAAI,MAAM,uCAAuC;AAErF,MAAI,MAAM,cAAc,SAAS,GAAG;AAClC,QAAI,wBAAwB,OAAO,kBAAkB,GAAG;AACtD,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,yBAAqB,EAAE,IAAI,oBAAoB,eAAe,CAAC,GAAG,MAAM,aAAa,EAAE,CAAC;AACxF,WAAO,EAAE,GAAG,OAAO,aAAa,mBAAmB;AAAA,EACrD;AACA,MAAI,wBAAwB,OAAW,OAAM,IAAI,MAAM,+CAA+C;AACtG,uBAAqB,MAAS;AAC9B,SAAO;AACT;AAEO,SAAS,wBAA6E;AAC3F,QAAM,QAAQ;AACd,QAAM,YAAY,MAAM,mBAAmB;AAC3C,SAAO,MAAM,mBAAmB;AAChC,SAAO,YAAY,EAAE,IAAI,UAAU,IAAI,eAAe,CAAC,GAAG,UAAU,aAAa,EAAE,IAAI;AACzF;AAEA,SAAS,qBAAqB,WAAsE;AAClG,QAAM,QAAQ;AACd,MAAI,UAAW,OAAM,mBAAmB,IAAI;AAAA,MACvC,QAAO,MAAM,mBAAmB;AACvC;AAEA,SAAS,oBAA4B;AACnC,MAAI;AACF,WAAU,gBAAa,sBAAsB,MAAM;AAAA,EACrD,UAAE;AACA,QAAI;AACF,MAAG,aAAU,oBAAoB;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,OAAyC;AACjE,MAAI,CAACC,UAAS,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAChE,MAAI,CAAC,oBAAoB,MAAM,aAAa,EAAG,QAAO;AACtD,MAAI,CAAC,MAAM,QAAQ,MAAM,aAAa,KAAK,MAAM,cAAc,SAAS,mBAAoB,QAAO;AACnG,SAAO,MAAM,cAAc;AAAA,IACzB,CAAC,SACC,OAAO,SAAS,YAChB,KAAK,SAAS,KACd,KAAK,UAAU,wBACf,CAAC,KAAK,SAAS,GAAG,KAClB,CAAC,oBAAoB,IAAI;AAAA,EAC7B;AACF;AAEA,SAAS,oBAAoB,OAA4C;AACvE,MAAI,CAACA,UAAS,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAChE,SACE,MAAM,SAAS,eACf,OAAO,cAAc,MAAM,IAAI,KAC9B,MAAM,QAAmB,KACzB,MAAM,QAAmB,SAC1B,OAAO,MAAM,UAAU,YACvB,kBAAkB,KAAK,MAAM,KAAK;AAEtC;AAEA,SAAS,oBAAoB,OAAwB;AACnD,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,cAAc;AACpC,UAAM,YAAY,UAAU,YAAY,CAAC,KAAK;AAC9C,WAAO,aAAa,MAAS,aAAa,OAAQ,aAAa;AAAA,EACjE,CAAC;AACH;AAEA,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;",
|
|
6
|
+
"names": ["request", "isRecord"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
+
// @ts-nocheck -- generated entries use .ts extensions for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
MAX_IDENTIFIER_LENGTH,
|
|
5
|
+
MAX_MESSAGE_BYTES,
|
|
6
|
+
sanitizeTerminalText,
|
|
7
|
+
validateMessage
|
|
8
|
+
} from "./chunk-YMVSJ5GE.ts";
|
|
9
|
+
|
|
10
|
+
// src/child-communication-tools.ts
|
|
11
|
+
import { Type } from "typebox";
|
|
12
|
+
|
|
13
|
+
// src/timeout.ts
|
|
14
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
15
|
+
var MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1e3;
|
|
16
|
+
function resolveTimeoutMs(timeout) {
|
|
17
|
+
if (timeout === void 0) return void 0;
|
|
18
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
19
|
+
throw new Error("Invalid timeout: must be a finite number of seconds");
|
|
20
|
+
}
|
|
21
|
+
const timeoutMs = timeout * 1e3;
|
|
22
|
+
if (timeoutMs > MAX_TIMEOUT_MS) {
|
|
23
|
+
throw new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);
|
|
24
|
+
}
|
|
25
|
+
return timeoutMs;
|
|
26
|
+
}
|
|
27
|
+
function prepareTimeoutArguments(args) {
|
|
28
|
+
if (!args || typeof args !== "object") return args;
|
|
29
|
+
if (!Object.hasOwn(args, "timeoutMs")) return args;
|
|
30
|
+
const record = args;
|
|
31
|
+
if (typeof record.timeoutMs !== "number") return record;
|
|
32
|
+
const { timeoutMs, ...prepared } = record;
|
|
33
|
+
if (prepared.timeout === void 0) return { ...prepared, timeout: timeoutMs / 1e3 };
|
|
34
|
+
return prepared;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/child-communication-tools.ts
|
|
38
|
+
var CHILD_COMMUNICATION_TOOL_NAMES = ["subagent_send", "subagent_wait"];
|
|
39
|
+
var SendParameters = Type.Object(
|
|
40
|
+
{
|
|
41
|
+
requestId: Type.Optional(
|
|
42
|
+
Type.String({
|
|
43
|
+
description: "Pending main-agent request to answer. Omit when starting a new request.",
|
|
44
|
+
minLength: 1,
|
|
45
|
+
maxLength: MAX_IDENTIFIER_LENGTH
|
|
46
|
+
})
|
|
47
|
+
),
|
|
48
|
+
message: Type.String({
|
|
49
|
+
description: "Plain-text request or response. Maximum 48 KiB of UTF-8 text and 1,992 lines.",
|
|
50
|
+
minLength: 1,
|
|
51
|
+
maxLength: MAX_MESSAGE_BYTES
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
{ additionalProperties: false }
|
|
55
|
+
);
|
|
56
|
+
var WaitParameters = Type.Object(
|
|
57
|
+
{
|
|
58
|
+
requestId: Type.String({
|
|
59
|
+
description: "Request ID returned by subagent_send.",
|
|
60
|
+
minLength: 1,
|
|
61
|
+
maxLength: MAX_IDENTIFIER_LENGTH
|
|
62
|
+
}),
|
|
63
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (optional, no default timeout)" }))
|
|
64
|
+
},
|
|
65
|
+
{ additionalProperties: false }
|
|
66
|
+
);
|
|
67
|
+
function createChildCommunicationExtension(client) {
|
|
68
|
+
return (pi) => {
|
|
69
|
+
pi.registerTool({
|
|
70
|
+
name: "subagent_send",
|
|
71
|
+
label: "Subagent \xB7 Send to Main",
|
|
72
|
+
description: "Use subagent_send to send one request to the main agent or answer one pending main-agent request. Omit requestId to start a request. Provide requestId to answer that request.",
|
|
73
|
+
promptSnippet: "Use subagent_send to send or answer one main-agent message",
|
|
74
|
+
parameters: SendParameters,
|
|
75
|
+
async execute(_toolCallId, params, signal) {
|
|
76
|
+
validateMessage(params.message, "Subagent message");
|
|
77
|
+
const requestId = optionalIdentifier(params.requestId, "requestId");
|
|
78
|
+
const acknowledgement = await client.send(
|
|
79
|
+
requestId === void 0 ? { recipient: "main", message: params.message } : { requestId, message: params.message },
|
|
80
|
+
signal
|
|
81
|
+
);
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: "text", text: JSON.stringify(acknowledgement) }],
|
|
84
|
+
details: acknowledgement
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
pi.registerTool({
|
|
89
|
+
name: "subagent_wait",
|
|
90
|
+
label: "Subagent \xB7 Wait for Main Agent",
|
|
91
|
+
description: "Use subagent_wait with a request ID from subagent_send to wait for the main agent's plain-text response. A timeout, caller cancellation, or incoming main-agent request stops only this wait and does not cancel the original request. Retry the wait when its response is still needed.",
|
|
92
|
+
promptSnippet: "Use subagent_wait to receive a requested main-agent response",
|
|
93
|
+
parameters: WaitParameters,
|
|
94
|
+
prepareArguments: prepareWaitArguments,
|
|
95
|
+
async execute(_toolCallId, params, signal) {
|
|
96
|
+
const requestId = requiredIdentifier(params.requestId, "requestId");
|
|
97
|
+
const response = await client.wait(requestId, resolveTimeoutMs(params.timeout), signal);
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: sanitizeTerminalText(response) }],
|
|
100
|
+
details: { requestId }
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function prepareWaitArguments(args) {
|
|
107
|
+
return prepareTimeoutArguments(args);
|
|
108
|
+
}
|
|
109
|
+
function optionalIdentifier(value, field) {
|
|
110
|
+
return value === void 0 ? void 0 : requiredIdentifier(value, field);
|
|
111
|
+
}
|
|
112
|
+
function requiredIdentifier(value, field) {
|
|
113
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`Subagent ${field} is required.`);
|
|
114
|
+
const identifier = value.trim();
|
|
115
|
+
if (identifier.length > MAX_IDENTIFIER_LENGTH || [...identifier].some((character) => {
|
|
116
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
117
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
118
|
+
})) {
|
|
119
|
+
throw new Error(`Subagent ${field} is invalid.`);
|
|
120
|
+
}
|
|
121
|
+
return identifier;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
resolveTimeoutMs,
|
|
126
|
+
prepareTimeoutArguments,
|
|
127
|
+
CHILD_COMMUNICATION_TOOL_NAMES,
|
|
128
|
+
createChildCommunicationExtension
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=chunk-ZOAFWLDJ.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/child-communication-tools.ts", "../../src/timeout.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ExtensionFactory } from \"@earendil-works/pi-coding-agent\";\nimport { type Static, Type } from \"typebox\";\nimport {\n type BrokerSendAcknowledgement,\n MAX_IDENTIFIER_LENGTH,\n MAX_MESSAGE_BYTES,\n sanitizeTerminalText,\n validateMessage,\n} from \"./message-broker.js\";\nimport { prepareTimeoutArguments, resolveTimeoutMs } from \"./timeout.js\";\n\nexport const CHILD_COMMUNICATION_TOOL_NAMES = [\"subagent_send\", \"subagent_wait\"] as const;\n\nconst SendParameters = Type.Object(\n {\n requestId: Type.Optional(\n Type.String({\n description: \"Pending main-agent request to answer. Omit when starting a new request.\",\n minLength: 1,\n maxLength: MAX_IDENTIFIER_LENGTH,\n }),\n ),\n message: Type.String({\n description: \"Plain-text request or response. Maximum 48 KiB of UTF-8 text and 1,992 lines.\",\n minLength: 1,\n maxLength: MAX_MESSAGE_BYTES,\n }),\n },\n { additionalProperties: false },\n);\n\nconst WaitParameters = Type.Object(\n {\n requestId: Type.String({\n description: \"Request ID returned by subagent_send.\",\n minLength: 1,\n maxLength: MAX_IDENTIFIER_LENGTH,\n }),\n timeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n },\n { additionalProperties: false },\n);\n\ntype ChildBrokerSendArguments = { recipient: \"main\"; message: string } | { requestId: string; message: string };\n\ntype WaitArguments = Static<typeof WaitParameters>;\n\nexport interface ChildCommunicationClient {\n send(params: ChildBrokerSendArguments, signal?: AbortSignal): Promise<BrokerSendAcknowledgement>;\n wait(requestId: string, timeoutMs: number | undefined, signal?: AbortSignal): Promise<string>;\n}\n\nexport function createChildCommunicationExtension(client: ChildCommunicationClient): ExtensionFactory {\n return (pi) => {\n pi.registerTool({\n name: \"subagent_send\",\n label: \"Subagent \u00B7 Send to Main\",\n description:\n \"Use subagent_send to send one request to the main agent or answer one pending main-agent request. Omit requestId to start a request. Provide requestId to answer that request.\",\n promptSnippet: \"Use subagent_send to send or answer one main-agent message\",\n parameters: SendParameters,\n async execute(_toolCallId, params, signal) {\n validateMessage(params.message, \"Subagent message\");\n const requestId = optionalIdentifier(params.requestId, \"requestId\");\n const acknowledgement = await client.send(\n requestId === undefined\n ? { recipient: \"main\", message: params.message }\n : { requestId, message: params.message },\n signal,\n );\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(acknowledgement) }],\n details: acknowledgement,\n };\n },\n });\n\n pi.registerTool({\n name: \"subagent_wait\",\n label: \"Subagent \u00B7 Wait for Main Agent\",\n description:\n \"Use subagent_wait with a request ID from subagent_send to wait for the main agent's plain-text response. A timeout, caller cancellation, or incoming main-agent request stops only this wait and does not cancel the original request. Retry the wait when its response is still needed.\",\n promptSnippet: \"Use subagent_wait to receive a requested main-agent response\",\n parameters: WaitParameters,\n prepareArguments: prepareWaitArguments,\n async execute(_toolCallId, params, signal) {\n const requestId = requiredIdentifier(params.requestId, \"requestId\");\n const response = await client.wait(requestId, resolveTimeoutMs(params.timeout), signal);\n return {\n content: [{ type: \"text\" as const, text: sanitizeTerminalText(response) }],\n details: { requestId },\n };\n },\n });\n };\n}\n\nfunction prepareWaitArguments(args: unknown): WaitArguments {\n return prepareTimeoutArguments(args) as WaitArguments;\n}\n\nfunction optionalIdentifier(value: unknown, field: string): string | undefined {\n return value === undefined ? undefined : requiredIdentifier(value, field);\n}\n\nfunction requiredIdentifier(value: unknown, field: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`Subagent ${field} is required.`);\n const identifier = value.trim();\n if (\n identifier.length > MAX_IDENTIFIER_LENGTH ||\n [...identifier].some((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);\n })\n ) {\n throw new Error(`Subagent ${field} is invalid.`);\n }\n return identifier;\n}\n", "const MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nexport function resolveTimeoutMs(timeout: number | undefined): number | undefined {\n if (timeout === undefined) return undefined;\n if (!Number.isFinite(timeout) || timeout <= 0) {\n throw new Error(\"Invalid timeout: must be a finite number of seconds\");\n }\n const timeoutMs = timeout * 1000;\n if (timeoutMs > MAX_TIMEOUT_MS) {\n throw new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n }\n return timeoutMs;\n}\n\nexport function prepareTimeoutArguments(args: unknown): Record<string, unknown> {\n if (!args || typeof args !== \"object\") return args as Record<string, unknown>;\n if (!Object.hasOwn(args, \"timeoutMs\")) return args as Record<string, unknown>;\n const record = args as Record<string, unknown>;\n if (typeof record.timeoutMs !== \"number\") return record;\n const { timeoutMs, ...prepared } = record;\n if (prepared.timeout === undefined) return { ...prepared, timeout: timeoutMs / 1000 };\n return prepared;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|