@orkestrel/tool 0.0.1 → 0.0.2

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.
@@ -1,4 +1,5 @@
1
1
  import { arrayShape, booleanShape, createContract, integerShape, literalShape, objectShape, optionalShape, schemaToParameters, stringShape, unionShape } from "@orkestrel/contract";
2
+ import { isTerminalError } from "@orkestrel/terminal";
2
3
  import { WorkspaceError, createTool, createWorkspaceManager, isText, rangeOf } from "@orkestrel/agent";
3
4
  import { WorkflowError, createWorkflowContract } from "@orkestrel/workflow";
4
5
  //#region src/core/constants.ts
@@ -236,13 +237,74 @@ var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered t
236
237
  * schema or multi-step protocol to teach.
237
238
  */
238
239
  var DESCRIBE_TOOL_DESCRIPTION = "Return the full description of a registered tool by its name. Required: name - the registered tool name (see another tool listing for available names).";
240
+ /**
241
+ * The name {@link import('./factories.js').createPromptTool} advertises by default — the key a
242
+ * model calls and the `ToolManagerInterface` (`@orkestrel/agent`) registers under.
243
+ */
244
+ var PROMPT_TOOL_NAME = "ask";
245
+ /**
246
+ * The lean {@link import('@orkestrel/agent').ToolInterface.summary} {@link import('./factories.js').createPromptTool}
247
+ * advertises in place of {@link PROMPT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
248
+ * (`@orkestrel/agent`) advertises `summary ?? description`, so this one-sentence text stands in
249
+ * for the full teaching description; the full text stays retrievable via
250
+ * {@link import('./factories.js').createDescribeTool}.
251
+ */
252
+ var PROMPT_TOOL_SUMMARY = "Ask another terminal a question and BLOCK until it answers; the call resolves with the answered value. Call describe('ask') for the required fields.";
253
+ var PROMPT_TOOL_DESCRIPTION = [
254
+ "Ask another terminal a question and block until it answers. This call does not return until the addressed terminal answers, or the prompt fails.",
255
+ "",
256
+ "Required:",
257
+ " to - the terminal name to ask.",
258
+ " form - the prompt kind: one of \"input\", \"password\", \"confirm\", \"select\", \"checkbox\", \"editor\".",
259
+ " message - the question shown to the answering terminal.",
260
+ "Optional:",
261
+ " options - form-specific options (e.g. choices for \"select\"/\"checkbox\").",
262
+ "A cycle (two terminals asking each other) or an expired prompt fails the call with a typed error.",
263
+ "Example:",
264
+ JSON.stringify({
265
+ to: "reviewer",
266
+ form: "confirm",
267
+ message: "Approve the release?"
268
+ })
269
+ ].join("\n");
270
+ /**
271
+ * The name {@link import('./factories.js').createAnswerTool} advertises by default — the key a
272
+ * model calls and the `ToolManagerInterface` (`@orkestrel/agent`) registers under.
273
+ */
274
+ var ANSWER_TOOL_NAME = "answer";
275
+ /**
276
+ * The lean {@link import('@orkestrel/agent').ToolInterface.summary} {@link import('./factories.js').createAnswerTool}
277
+ * advertises in place of {@link ANSWER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
278
+ * (`@orkestrel/agent`) advertises `summary ?? description`, so this one-sentence text stands in
279
+ * for the full teaching description; the full text stays retrievable via
280
+ * {@link import('./factories.js').createDescribeTool}.
281
+ */
282
+ var ANSWER_TOOL_SUMMARY = "List prompts addressed to this terminal, or answer one by id. Call describe('answer') for the required fields.";
283
+ var ANSWER_TOOL_DESCRIPTION = [
284
+ "List the prompts currently addressed to this terminal, or answer one of them by id. Every call is ONE operation, chosen by the \"operation\" field.",
285
+ "",
286
+ "Operations:",
287
+ "- pending { \"operation\": \"pending\" } — list every prompt currently addressed to this terminal (id, form, message, options, time).",
288
+ "- answer { \"operation\": \"answer\", \"id\": \"<prompt id>\", \"value\": <answer value> } — answer the prompt with that id; \"value\" must match the prompt's form (a string for \"input\"/\"password\"/\"editor\", a boolean for \"confirm\", a choice for \"select\", an array of choices for \"checkbox\").",
289
+ "Example — list pending prompts:",
290
+ JSON.stringify({ operation: "pending" }),
291
+ "Example — answer one:",
292
+ JSON.stringify({
293
+ operation: "answer",
294
+ id: "abc123",
295
+ value: true
296
+ })
297
+ ].join("\n");
239
298
  //#endregion
240
299
  //#region src/core/errors.ts
241
300
  /**
242
301
  * Thrown by {@link import('./factories.js').createAgentTool}'s and
243
302
  * {@link import('./factories.js').createDescribeTool}'s handlers on every failure path — a
244
- * malformed / unresolvable call or an unknown tool name (`TOOL`), or a delegation that would
245
- * exceed the configured depth bound or re-enter an ancestor (`DEPTH`).
303
+ * malformed / unresolvable call or an unknown tool name (`TOOL`), a delegation that would
304
+ * exceed the configured depth bound or re-enter an ancestor (`DEPTH`), a prompt cycle
305
+ * (`DEADLOCK`), a prompt that expired before it was answered (`EXPIRE`), or an answer that
306
+ * failed to apply (`ANSWER`) — the last three thrown by
307
+ * {@link import('./factories.js').createPromptTool} / {@link import('./factories.js').createAnswerTool}.
246
308
  *
247
309
  * @remarks
248
310
  * Carries a machine-readable `code` (see {@link import('./types.js').AgentToolErrorCode}) and
@@ -294,6 +356,106 @@ function isAgentToolError(value) {
294
356
  //#endregion
295
357
  //#region src/core/shapers.ts
296
358
  /**
359
+ * The shape of {@link import('./factories.js').createPromptTool}'s call arguments — `to` (the
360
+ * terminal identity to address), `form` (which of the six {@link import('@orkestrel/terminal').PromptType}
361
+ * forms to ask), `message`, an optional `timeout` override, and every per-form optional field
362
+ * FLATTENED onto one object (mirrors `workspaceToolShape`'s flat-arm style, but a single shared
363
+ * shape rather than a discriminated union — `form` alone does not vary the REQUIRED fields, only
364
+ * which of the optional ones apply, so a flat shape stays faithful without duplicating `to` /
365
+ * `message` / `timeout` across six near-identical arms).
366
+ *
367
+ * @remarks
368
+ * `choices` backs `'select'` / `'checkbox'`; `default` backs `'input'` / `'confirm'` / `'select'`
369
+ * (a string for the first two forms' text default, `'true'`/`'false'` string for confirm — the
370
+ * contract layer cannot vary a field's type by a sibling field's value, so `default` stays a
371
+ * string and the handler coerces per form); `mask` backs `'password'`; `min` / `max` backs
372
+ * `'checkbox'`; `validate` (declarative only) backs the four text-shaped forms
373
+ * (`'input'` / `'password'` / `'confirm'` / `'editor'`).
374
+ */
375
+ var promptToolShape = objectShape({
376
+ to: stringShape({
377
+ min: 1,
378
+ description: "The terminal identity to address the prompt to."
379
+ }),
380
+ form: literalShape([
381
+ "input",
382
+ "password",
383
+ "confirm",
384
+ "select",
385
+ "checkbox",
386
+ "editor"
387
+ ], { description: "Which prompt form to ask." }),
388
+ message: stringShape({
389
+ min: 1,
390
+ description: "The prompt's question."
391
+ }),
392
+ default: optionalShape(stringShape({ description: "The default answer if the responder submits blank — 'input' / 'editor' text, 'confirm' 'true'/'false', or a 'select' choice value." })),
393
+ choices: optionalShape(arrayShape(objectShape({
394
+ name: stringShape({
395
+ min: 1,
396
+ description: "The choice label shown to the answering party."
397
+ }),
398
+ value: stringShape({
399
+ min: 1,
400
+ description: "The value submitted when this choice is picked."
401
+ }),
402
+ description: optionalShape(stringShape({ description: "An optional one-line elaboration." }))
403
+ }), { description: "The selectable choices for 'select' / 'checkbox'." })),
404
+ mask: optionalShape(stringShape({
405
+ min: 1,
406
+ description: "The mask character 'password' renders in place of input."
407
+ })),
408
+ min: optionalShape(integerShape({
409
+ min: 0,
410
+ description: "The minimum number of 'checkbox' selections required."
411
+ })),
412
+ max: optionalShape(integerShape({
413
+ min: 0,
414
+ description: "The maximum number of 'checkbox' selections allowed."
415
+ })),
416
+ validate: optionalShape(objectShape({
417
+ required: optionalShape(booleanShape({ description: "Reject an empty (trimmed) input." })),
418
+ minimum: optionalShape(integerShape({
419
+ min: 0,
420
+ description: "Reject an input shorter than this many characters."
421
+ })),
422
+ maximum: optionalShape(integerShape({
423
+ min: 0,
424
+ description: "Reject an input longer than this many characters."
425
+ })),
426
+ pattern: optionalShape(stringShape({ description: "Reject an input that fails this regular-expression source." })),
427
+ email: optionalShape(booleanShape({ description: "Require a valid email-address shape." })),
428
+ url: optionalShape(booleanShape({ description: "Require a valid URL shape." })),
429
+ numeric: optionalShape(booleanShape({ description: "Require a numeric value." })),
430
+ integer: optionalShape(booleanShape({ description: "Require an integer value." })),
431
+ alphanumeric: optionalShape(booleanShape({ description: "Require letters and digits only." }))
432
+ })),
433
+ timeout: optionalShape(integerShape({
434
+ min: 0,
435
+ description: "Milliseconds to wait before the prompt expires."
436
+ }))
437
+ });
438
+ /**
439
+ * The shape of {@link import('./factories.js').createAnswerTool}'s call arguments — discriminated
440
+ * by `operation`: `'pending'` lists the prompts addressed to this tool's terminal, `'answer'`
441
+ * resolves one by `id` with a `value`.
442
+ *
443
+ * @remarks
444
+ * `value`'s type varies by the ORIGINAL prompt's form (`string` for `'input'` / `'password'` /
445
+ * `'select'` / `'editor'`, `boolean` for `'confirm'`, `readonly string[]` for `'checkbox'`) —
446
+ * `unionShape(stringShape(), booleanShape(), arrayShape(stringShape()))` expresses that
447
+ * union directly, so `value` is typed as the full `string | boolean | readonly string[]` union
448
+ * here (no lossy string-only fallback needed).
449
+ */
450
+ var answerToolShape = unionShape(objectShape({ operation: literalShape(["pending"], { description: "List the prompts currently addressed to this terminal." }) }), objectShape({
451
+ operation: literalShape(["answer"], { description: "Answer one pending prompt by id." }),
452
+ id: stringShape({
453
+ min: 1,
454
+ description: "The id of the pending prompt to answer."
455
+ }),
456
+ value: unionShape(stringShape({ description: "A text / select / editor answer." }), booleanShape({ description: "A confirm answer." }), arrayShape(stringShape(), { description: "A checkbox answer — the checked values." }))
457
+ }));
458
+ /**
297
459
  * The shape of {@link import('./types.js').AgentToolArguments} —
298
460
  * {@link import('./factories.js').createAgentTool}'s advertised `parameters`.
299
461
  *
@@ -655,6 +817,69 @@ function expandSteps(flat) {
655
817
  phases: flat.steps.map((step) => ({ tasks: [{ run: step.name }] }))
656
818
  });
657
819
  }
820
+ /**
821
+ * Normalize an LLM-supplied answer `value` to the type {@link PromptType} `form` expects, so a
822
+ * caller that only ever emits strings can still answer a typed prompt.
823
+ *
824
+ * @remarks
825
+ * `'confirm'` coerces to a `boolean` — a `boolean` passes through, and the strings `'true'` /
826
+ * `'false'` (case-insensitively) map to it; any other string is truthy-coerced via
827
+ * `Boolean(value)`. `'checkbox'` coerces to `readonly string[]` — an array passes through
828
+ * (stringifying each entry), a comma-separated string splits + trims into one, and any other
829
+ * single (non-comma) string becomes a one-item array. Every other form (`'input'` / `'password'`
830
+ * / `'select'` / `'editor'`) coerces to a plain `string` — a string passes through verbatim; a
831
+ * non-string, non-object scalar (`number` / `boolean`) stringifies via `String(value)`; an
832
+ * object or array (no lossless string form) falls back to `''` rather than serializing garbage.
833
+ * Pure and total — never throws.
834
+ *
835
+ * @param form - The {@link PromptType} the answer is being coerced FOR
836
+ * @param value - The raw, LLM-supplied answer value
837
+ * @returns The coerced answer — `boolean` for `'confirm'`, `readonly string[]` for `'checkbox'`,
838
+ * `string` otherwise
839
+ */
840
+ function coerceAnswer(form, value) {
841
+ if (form === "confirm") {
842
+ if (typeof value === "boolean") return value;
843
+ if (typeof value === "string") {
844
+ const lower = value.trim().toLowerCase();
845
+ if (lower === "true") return true;
846
+ if (lower === "false") return false;
847
+ }
848
+ return Boolean(value);
849
+ }
850
+ if (form === "checkbox") {
851
+ if (Array.isArray(value)) return value.map((entry) => String(entry));
852
+ if (typeof value === "string") {
853
+ if (value.includes(",")) return value.split(",").map((entry) => entry.trim());
854
+ return [value];
855
+ }
856
+ return [String(value)];
857
+ }
858
+ if (typeof value === "string") return value;
859
+ if (typeof value === "object" && value !== null) return "";
860
+ return String(value);
861
+ }
862
+ /**
863
+ * Map a caught error to the {@link AgentToolErrorCode} the terminal-tool factory should throw
864
+ * with — the pure classification step of that factory's error handling.
865
+ *
866
+ * @remarks
867
+ * Narrows `error` with {@link isTerminalError} (`@orkestrel/terminal`) first: a non-`TerminalError`
868
+ * value returns `undefined`, telling the caller this mapper does not apply (rethrow / handle
869
+ * otherwise). For a genuine `TerminalError`, `'DEADLOCK'` maps to `'DEADLOCK'`, `'EXPIRE'` maps
870
+ * to `'EXPIRE'`, and every other {@link import('@orkestrel/terminal').TerminalErrorCode}
871
+ * (`'TARGET'`, `'CANCEL'`, `'DRIVER'`) maps to the generic `'TOOL'` code. The mapper only
872
+ * classifies — the factory performs the actual throw.
873
+ *
874
+ * @param error - The value caught from a terminal-manager operation (`ask` / `answer` / …)
875
+ * @returns The mapped {@link AgentToolErrorCode}, or `undefined` if `error` is not a `TerminalError`
876
+ */
877
+ function terminalToolCode(error) {
878
+ if (!isTerminalError(error)) return void 0;
879
+ if (error.code === "DEADLOCK") return "DEADLOCK";
880
+ if (error.code === "EXPIRE") return "EXPIRE";
881
+ return "TOOL";
882
+ }
658
883
  //#endregion
659
884
  //#region src/core/factories.ts
660
885
  /**
@@ -1193,7 +1418,175 @@ function createDescribeTool(tools) {
1193
1418
  }
1194
1419
  });
1195
1420
  }
1421
+ /**
1422
+ * Build an LLM-callable prompt tool — the ASK side of the terminal seam. Asks
1423
+ * {@link import('./types.js').PromptToolOptions.to} a question and BLOCKS until it answers,
1424
+ * returning the resolved answer value.
1425
+ *
1426
+ * @remarks
1427
+ * The universal tool-handler contract (AGENTS §14): validates the call args against
1428
+ * {@link import('./shapers.js').promptToolShape}, dispatches to the matching
1429
+ * `TerminalManagerInterface.ask` overload (`@orkestrel/terminal`) for the call's `form`, and
1430
+ * RETURNS the resolved answer on success. `from` is FIXED at construction
1431
+ * ({@link import('./types.js').PromptToolOptions.from}) — never read from the model-supplied
1432
+ * args — so a model cannot spoof which terminal is asking. A prompt CYCLE rejects with
1433
+ * `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
1434
+ * {@link import('./errors.js').AgentToolError}; an expired prompt re-surfaces as `EXPIRE`; an
1435
+ * unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
1436
+ * plus the known ones (`manager.terminals()`).
1437
+ *
1438
+ * @param options - The live manager, the fixed `from` identity, and advertised overrides (see
1439
+ * {@link import('./types.js').PromptToolOptions})
1440
+ * @returns A `ToolInterface` (named {@link import('./constants.js').PROMPT_TOOL_NAME} by default)
1441
+ *
1442
+ * @example
1443
+ * ```ts
1444
+ * import { createPromptTool } from '@src/core'
1445
+ * import { createTerminalManager, createToolManager } from '@orkestrel/terminal'
1446
+ *
1447
+ * const manager = createTerminalManager()
1448
+ * manager.add('agent')
1449
+ * manager.add('reviewer')
1450
+ * const tool = createPromptTool({ manager, from: 'agent' })
1451
+ * const tools = createToolManager()
1452
+ * tools.add(tool) // the agent can now ask 'reviewer' and block for the answer
1453
+ * ```
1454
+ */
1455
+ function createPromptTool(options) {
1456
+ const contract = createContract(promptToolShape);
1457
+ const parameters = schemaToParameters(contract.schema);
1458
+ return createTool({
1459
+ name: options.name ?? "ask",
1460
+ description: options.description ?? PROMPT_TOOL_DESCRIPTION,
1461
+ summary: PROMPT_TOOL_SUMMARY,
1462
+ parameters,
1463
+ execute: async (args) => {
1464
+ const call = contract.parse(args);
1465
+ if (call === void 0) throw new AgentToolError("TOOL", "malformed ask call", { args });
1466
+ if ((call.form === "select" || call.form === "checkbox") && (call.choices ?? []).length === 0) throw new AgentToolError("TOOL", "select/checkbox requires at least one choice", {
1467
+ to: call.to,
1468
+ form: call.form
1469
+ });
1470
+ try {
1471
+ switch (call.form) {
1472
+ case "input": return await options.manager.ask(options.from, call.to, call.form, {
1473
+ message: call.message,
1474
+ ...call.default === void 0 ? {} : { default: call.default },
1475
+ ...call.validate === void 0 ? {} : { validate: call.validate }
1476
+ });
1477
+ case "editor": return await options.manager.ask(options.from, call.to, call.form, {
1478
+ message: call.message,
1479
+ ...call.default === void 0 ? {} : { default: call.default },
1480
+ ...call.validate === void 0 ? {} : { validate: call.validate }
1481
+ });
1482
+ case "password": return await options.manager.ask(options.from, call.to, call.form, {
1483
+ message: call.message,
1484
+ ...call.mask === void 0 ? {} : { mask: call.mask },
1485
+ ...call.validate === void 0 ? {} : { validate: call.validate }
1486
+ });
1487
+ case "confirm": return await options.manager.ask(options.from, call.to, call.form, {
1488
+ message: call.message,
1489
+ ...call.default === void 0 ? {} : { default: call.default === "true" }
1490
+ });
1491
+ case "select": return await options.manager.ask(options.from, call.to, call.form, {
1492
+ message: call.message,
1493
+ choices: call.choices ?? [],
1494
+ ...call.default === void 0 ? {} : { default: call.default }
1495
+ });
1496
+ case "checkbox": return await options.manager.ask(options.from, call.to, call.form, {
1497
+ message: call.message,
1498
+ choices: call.choices ?? [],
1499
+ ...call.min === void 0 ? {} : { min: call.min },
1500
+ ...call.max === void 0 ? {} : { max: call.max }
1501
+ });
1502
+ }
1503
+ } catch (error) {
1504
+ const code = terminalToolCode(error);
1505
+ if (code === void 0) throw error;
1506
+ if (code === "DEADLOCK") throw new AgentToolError("DEADLOCK", `asking '${call.to}' would form a prompt cycle`, isTerminalError(error) ? error.context : {
1507
+ from: options.from,
1508
+ to: call.to
1509
+ });
1510
+ if (code === "EXPIRE") throw new AgentToolError("EXPIRE", `prompt to '${call.to}' expired before it was answered`, { to: call.to });
1511
+ if (isTerminalError(error) && error.code === "TARGET") throw new AgentToolError("TOOL", `unknown terminal '${call.to}'`, {
1512
+ to: call.to,
1513
+ known: options.manager.terminals()
1514
+ });
1515
+ throw new AgentToolError("TOOL", `asking '${call.to}' failed`, { to: call.to });
1516
+ }
1517
+ }
1518
+ });
1519
+ }
1520
+ /**
1521
+ * Build an LLM-callable answer tool — the ANSWER side of the terminal seam. Lists the prompts
1522
+ * currently addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of
1523
+ * them by id.
1524
+ *
1525
+ * @remarks
1526
+ * The universal tool-handler contract (AGENTS §14): validates the call args against
1527
+ * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`). `'pending'`
1528
+ * returns a compact list (`{ id, from, form, message }`) of every prompt currently addressed to
1529
+ * `to` (`TerminalManagerInterface.pending`, `@orkestrel/terminal`). `'answer'` looks the prompt
1530
+ * up by `id` (an unknown id throws a typed `ANSWER` {@link import('./errors.js').AgentToolError}),
1531
+ * normalizes the model-supplied `value` to the prompt's own form
1532
+ * ({@link import('./helpers.js').coerceAnswer}), and applies it via
1533
+ * `TerminalManagerInterface.answer` — a rejected / unknown / unresolvable outcome
1534
+ * (`TerminalAnswerResult.error`) re-surfaces as a typed `ANSWER` `AgentToolError`; success returns
1535
+ * `{ answered: id }`. `to` is FIXED at construction
1536
+ * ({@link import('./types.js').AnswerToolOptions.to}) — never read from the model-supplied args —
1537
+ * so a model cannot spoof which terminal it is answering for. Concurrent answerers racing on one
1538
+ * endpoint are FIRST-WRITE-WINS — a late answer to an already-settled prompt returns a typed
1539
+ * `ANSWER` `AgentToolError` (surfaced as a 422 over HTTP).
1540
+ *
1541
+ * @param options - The live manager, the fixed `to` identity, and advertised overrides (see
1542
+ * {@link import('./types.js').AnswerToolOptions})
1543
+ * @returns A `ToolInterface` (named {@link import('./constants.js').ANSWER_TOOL_NAME} by default)
1544
+ *
1545
+ * @example
1546
+ * ```ts
1547
+ * import { createAnswerTool } from '@src/core'
1548
+ * import { createTerminalManager, createToolManager } from '@orkestrel/terminal'
1549
+ *
1550
+ * const manager = createTerminalManager()
1551
+ * manager.add('reviewer')
1552
+ * const tool = createAnswerTool({ manager, to: 'reviewer' })
1553
+ * const tools = createToolManager()
1554
+ * tools.add(tool) // the reviewer terminal can now list/answer prompts addressed to it
1555
+ * ```
1556
+ */
1557
+ function createAnswerTool(options) {
1558
+ const contract = createContract(answerToolShape);
1559
+ const parameters = schemaToParameters(contract.schema);
1560
+ return createTool({
1561
+ name: options.name ?? "answer",
1562
+ description: options.description ?? ANSWER_TOOL_DESCRIPTION,
1563
+ summary: ANSWER_TOOL_SUMMARY,
1564
+ parameters,
1565
+ execute: async (args) => {
1566
+ const call = contract.parse(args);
1567
+ if (call === void 0) throw new AgentToolError("TOOL", "malformed answer call", { args });
1568
+ if (call.operation === "pending") return options.manager.pending(options.to).map((prompt) => ({
1569
+ id: prompt.id,
1570
+ from: prompt.from,
1571
+ form: prompt.form,
1572
+ message: prompt.message
1573
+ }));
1574
+ const prompt = options.manager.pending(options.to).find((entry) => entry.id === call.id);
1575
+ if (prompt === void 0) throw new AgentToolError("ANSWER", `unknown prompt '${call.id}'`, {
1576
+ id: call.id,
1577
+ reason: "unknown"
1578
+ });
1579
+ const coerced = coerceAnswer(prompt.form, call.value);
1580
+ const result = options.manager.answer(options.to, call.id, coerced);
1581
+ if (!result.success) throw new AgentToolError("ANSWER", `failed to answer prompt '${call.id}': ${result.error}`, {
1582
+ id: call.id,
1583
+ reason: result.error
1584
+ });
1585
+ return { answered: call.id };
1586
+ }
1587
+ });
1588
+ }
1196
1589
  //#endregion
1197
- export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, AgentToolError, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, completeDraft, completePhaseDraft, completeTaskDraft, createAgentFunction, createAgentTool, createDescribeTool, createToolFunction, createWorkflowDraftContract, createWorkflowTool, createWorkspaceTool, describeToolShape, expandSteps, isAgentToolError, phaseDraftShape, stepShape, taskDraftShape, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
1590
+ export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, AgentToolError, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, coerceAnswer, completeDraft, completePhaseDraft, completeTaskDraft, createAgentFunction, createAgentTool, createAnswerTool, createDescribeTool, createPromptTool, createToolFunction, createWorkflowDraftContract, createWorkflowTool, createWorkspaceTool, describeToolShape, expandSteps, isAgentToolError, phaseDraftShape, promptToolShape, stepShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
1198
1591
 
1199
1592
  //# sourceMappingURL=index.js.map