@astralyn/pi 0.87.2 → 0.87.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bundle/chunks/{chunk-NBIUJDV6.js → chunk-MJ45I4SM.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-RFLBIA5Z.js → chunk-TYOQD6VQ.js} +33 -20
  4. package/dist/bundle/chunks/{virtual-modules-GOHLE5JL.js → virtual-modules-NDGCHNG7.js} +1 -1
  5. package/dist/bundle/cli.js +1 -1
  6. package/dist/bundle/index.js +1 -1
  7. package/dist/bundle/rpc-entry.js +1 -1
  8. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  9. package/dist/core/tools/tool-definition-wrapper.js +114 -1
  10. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  11. package/dist/extensions/background/actions.d.ts.map +1 -1
  12. package/dist/extensions/background/actions.js +107 -55
  13. package/dist/extensions/background/actions.js.map +1 -1
  14. package/dist/extensions/background/index.js +3 -3
  15. package/dist/extensions/background/index.js.map +1 -1
  16. package/dist/extensions/question/index.d.ts.map +1 -1
  17. package/dist/extensions/question/index.js +3 -1
  18. package/dist/extensions/question/index.js.map +1 -1
  19. package/dist/extensions/question/schema.d.ts +18 -0
  20. package/dist/extensions/question/schema.d.ts.map +1 -1
  21. package/dist/extensions/question/schema.js +52 -0
  22. package/dist/extensions/question/schema.js.map +1 -1
  23. package/dist/extensions/subagent/index.d.ts.map +1 -1
  24. package/dist/extensions/subagent/index.js +3 -1
  25. package/dist/extensions/subagent/index.js.map +1 -1
  26. package/dist/extensions/subagent/resolve.d.ts +14 -1
  27. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  28. package/dist/extensions/subagent/resolve.js +32 -18
  29. package/dist/extensions/subagent/resolve.js.map +1 -1
  30. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  31. package/dist/extensions/subagent/runner.js +4 -1
  32. package/dist/extensions/subagent/runner.js.map +1 -1
  33. package/dist/extensions/subagent/schema.d.ts +9 -0
  34. package/dist/extensions/subagent/schema.d.ts.map +1 -1
  35. package/dist/extensions/subagent/schema.js +30 -1
  36. package/dist/extensions/subagent/schema.js.map +1 -1
  37. package/dist/extensions/subagent/types.d.ts +2 -0
  38. package/dist/extensions/subagent/types.d.ts.map +1 -1
  39. package/dist/extensions/subagent/types.js.map +1 -1
  40. package/dist/extensions/todo/constants.d.ts +1 -1
  41. package/dist/extensions/todo/constants.d.ts.map +1 -1
  42. package/dist/extensions/todo/constants.js +8 -8
  43. package/dist/extensions/todo/constants.js.map +1 -1
  44. package/dist/extensions/todo/index.d.ts.map +1 -1
  45. package/dist/extensions/todo/index.js +1 -0
  46. package/dist/extensions/todo/index.js.map +1 -1
  47. package/dist/extensions/todo/schema.d.ts +30 -25
  48. package/dist/extensions/todo/schema.d.ts.map +1 -1
  49. package/dist/extensions/todo/schema.js +26 -20
  50. package/dist/extensions/todo/schema.js.map +1 -1
  51. package/dist/extensions/todo/state.d.ts +12 -8
  52. package/dist/extensions/todo/state.d.ts.map +1 -1
  53. package/dist/extensions/todo/state.js +255 -171
  54. package/dist/extensions/todo/state.js.map +1 -1
  55. package/dist/extensions/todo/view.d.ts +6 -2
  56. package/dist/extensions/todo/view.d.ts.map +1 -1
  57. package/dist/extensions/todo/view.js +234 -144
  58. package/dist/extensions/todo/view.js.map +1 -1
  59. package/docs/bundled/extensions/background.md +1 -1
  60. package/docs/bundled/extensions/question.md +1 -0
  61. package/docs/bundled/extensions/subagent.md +3 -1
  62. package/docs/bundled/extensions/todo.md +35 -23
  63. package/docs/extensions.md +2 -0
  64. package/npm-shrinkwrap.json +2 -2
  65. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/question/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACN,oBAAoB,EACpB,cAAc,EACd,0BAA0B,EAC1B,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhE,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAgB;IAChD,EAAE,CAAC,YAAY,CAA6C;QAC3D,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,oBAAoB;QACjC,aAAa,EAAE,uBAAuB;QACtC,gBAAgB,EAAE,0BAA0B;QAC5C,UAAU,EAAE,cAAc;QAC1B,aAAa,EAAE,YAAY;QAC3B,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO;YAC9B,OAAO,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1D,CAAC;QACD,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO;YAC3C,OAAO,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;YACxD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAuB,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO,WAAW,CAAC,OAAO,EAAE,2CAA2C,CAAC,CAAC;YAE/G,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,UAAU,CAAC,EAAE;gBAAE,OAAO,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;YAE7E,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAe,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;YAC1F,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW;gBAAE,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACxE,IAAI,MAAM,CAAC,OAAO,KAAK,qBAAqB;gBAAE,OAAO,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACzF,OAAO,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACpC,IAAI,KAAK,CAAC,QAAQ,KAAK,kBAAkB;YAAE,OAAO;QAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAA0C,CAAC;QACjE,IAAI,OAAO,EAAE,OAAO,KAAK,OAAO;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * question — ask the user structured questions during execution.\n *\n * This is a Pi-native AskUserQuestion-style tool. It keeps the UI lightweight\n * while returning a structured result envelope the model can reliably consume.\n */\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport {\n\tQUESTION_DESCRIPTION,\n\tQUESTION_LABEL,\n\tQUESTION_PROMPT_GUIDELINES,\n\tQUESTION_PROMPT_SNIPPET,\n\tQUESTION_TOOL_NAME,\n} from \"./constants.ts\";\nimport { createQuestionDialog } from \"./dialog.ts\";\nimport { renderQuestionCall, renderQuestionResult } from \"./render.ts\";\nimport { cancelResult, clarificationResult, errorResult, successResult } from \"./results.ts\";\nimport { QuestionParams, validateQuestions } from \"./schema.ts\";\nimport type { DialogResult, Question, QuestionToolDetails } from \"./types.ts\";\n\nexport default function question(pi: ExtensionAPI): void {\n\tpi.registerTool<typeof QuestionParams, QuestionToolDetails>({\n\t\tname: QUESTION_TOOL_NAME,\n\t\tlabel: QUESTION_LABEL,\n\t\tdescription: QUESTION_DESCRIPTION,\n\t\tpromptSnippet: QUESTION_PROMPT_SNIPPET,\n\t\tpromptGuidelines: QUESTION_PROMPT_GUIDELINES,\n\t\tparameters: QuestionParams,\n\t\texecutionMode: \"sequential\",\n\t\trenderCall(args, theme, context) {\n\t\t\treturn renderQuestionCall(args, theme, context.expanded);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\treturn renderQuestionResult(result, options, theme, context.args);\n\t\t},\n\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst questions = params.questions as Question[];\n\t\t\tif (!ctx.hasUI || ctx.mode !== \"tui\") return errorResult(\"no_ui\", \"question tool requires an interactive TUI\");\n\n\t\t\tconst validation = validateQuestions(questions);\n\t\t\tif (!validation.ok) return errorResult(validation.error, validation.message);\n\n\t\t\tconst result = await ctx.ui.custom<DialogResult>(createQuestionDialog(questions, signal));\n\t\t\tif (result.outcome === \"cancelled\") return cancelResult(result.answers);\n\t\t\tif (result.outcome === \"needs_clarification\") return clarificationResult(result.answers);\n\t\t\treturn successResult(result.answers);\n\t\t},\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== QUESTION_TOOL_NAME) return;\n\t\tconst details = event.details as QuestionToolDetails | undefined;\n\t\tif (details?.outcome === \"error\") return { isError: true };\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/question/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACN,oBAAoB,EACpB,cAAc,EACd,0BAA0B,EAC1B,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG5F,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAgB;IAChD,EAAE,CAAC,YAAY,CAA6C;QAC3D,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,oBAAoB;QACjC,aAAa,EAAE,uBAAuB;QACtC,gBAAgB,EAAE,0BAA0B;QAC5C,UAAU,EAAE,cAAc;QAC1B,mBAAmB,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9D,gBAAgB,EAAE,0BAA0B;QAC5C,aAAa,EAAE,YAAY;QAC3B,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO;YAC9B,OAAO,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1D,CAAC;QACD,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO;YAC3C,OAAO,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;YACxD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAuB,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO,WAAW,CAAC,OAAO,EAAE,2CAA2C,CAAC,CAAC;YAE/G,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,UAAU,CAAC,EAAE;gBAAE,OAAO,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;YAE7E,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAe,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;YAC1F,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW;gBAAE,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACxE,IAAI,MAAM,CAAC,OAAO,KAAK,qBAAqB;gBAAE,OAAO,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACzF,OAAO,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;KACD,CAAC,CAAC;IAEH,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACpC,IAAI,KAAK,CAAC,QAAQ,KAAK,kBAAkB;YAAE,OAAO;QAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAA0C,CAAC;QACjE,IAAI,OAAO,EAAE,OAAO,KAAK,OAAO;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * question — ask the user structured questions during execution.\n *\n * This is a Pi-native AskUserQuestion-style tool. It keeps the UI lightweight\n * while returning a structured result envelope the model can reliably consume.\n */\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport {\n\tQUESTION_DESCRIPTION,\n\tQUESTION_LABEL,\n\tQUESTION_PROMPT_GUIDELINES,\n\tQUESTION_PROMPT_SNIPPET,\n\tQUESTION_TOOL_NAME,\n} from \"./constants.ts\";\nimport { createQuestionDialog } from \"./dialog.ts\";\nimport { renderQuestionCall, renderQuestionResult } from \"./render.ts\";\nimport { cancelResult, clarificationResult, errorResult, successResult } from \"./results.ts\";\nimport { normalizeQuestionArguments, QuestionParams, validateQuestions } from \"./schema.ts\";\nimport type { DialogResult, Question, QuestionToolDetails } from \"./types.ts\";\n\nexport default function question(pi: ExtensionAPI): void {\n\tpi.registerTool<typeof QuestionParams, QuestionToolDetails>({\n\t\tname: QUESTION_TOOL_NAME,\n\t\tlabel: QUESTION_LABEL,\n\t\tdescription: QUESTION_DESCRIPTION,\n\t\tpromptSnippet: QUESTION_PROMPT_SNIPPET,\n\t\tpromptGuidelines: QUESTION_PROMPT_GUIDELINES,\n\t\tparameters: QuestionParams,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tprepareArguments: normalizeQuestionArguments,\n\t\texecutionMode: \"sequential\",\n\t\trenderCall(args, theme, context) {\n\t\t\treturn renderQuestionCall(args, theme, context.expanded);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\treturn renderQuestionResult(result, options, theme, context.args);\n\t\t},\n\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst questions = params.questions as Question[];\n\t\t\tif (!ctx.hasUI || ctx.mode !== \"tui\") return errorResult(\"no_ui\", \"question tool requires an interactive TUI\");\n\n\t\t\tconst validation = validateQuestions(questions);\n\t\t\tif (!validation.ok) return errorResult(validation.error, validation.message);\n\n\t\t\tconst result = await ctx.ui.custom<DialogResult>(createQuestionDialog(questions, signal));\n\t\t\tif (result.outcome === \"cancelled\") return cancelResult(result.answers);\n\t\t\tif (result.outcome === \"needs_clarification\") return clarificationResult(result.answers);\n\t\t\treturn successResult(result.answers);\n\t\t},\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== QUESTION_TOOL_NAME) return;\n\t\tconst details = event.details as QuestionToolDetails | undefined;\n\t\tif (details?.outcome === \"error\") return { isError: true };\n\t});\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  import { Type } from "typebox";
2
2
  import type { Question, QuestionToolError } from "./types.ts";
3
+ type QuestionArgs = {
4
+ questions: Question[];
5
+ };
3
6
  export declare const QuestionParams: Type.TObject<{
4
7
  questions: Type.TArray<Type.TObject<{
5
8
  question: Type.TString;
@@ -12,6 +15,20 @@ export declare const QuestionParams: Type.TObject<{
12
15
  multiSelect: Type.TOptional<Type.TBoolean>;
13
16
  }>>;
14
17
  }>;
18
+ /**
19
+ * Unambiguous argument normalizations applied before schema validation; the
20
+ * schema itself stays a single object shape because strict constrained
21
+ * sampling does not support object unions. Everything else is left for
22
+ * validation to report.
23
+ *
24
+ * - a lone question written flat at the root ({question, header?, options,
25
+ * multiSelect?} with no questions) wraps into questions[0];
26
+ * - a root-level question string fills a questions[0] object that lost its
27
+ * own question field to the root;
28
+ * - a stringified questions array parses back into an array (a parse failure
29
+ * stays for validation to reject).
30
+ */
31
+ export declare function normalizeQuestionArguments(raw: unknown): QuestionArgs;
15
32
  /**
16
33
  * Checks visible non-whitespace text and cross-field rules the schema cannot
17
34
  * express. Length and count limits remain enforced by `QuestionParams` first.
@@ -23,4 +40,5 @@ export declare function validateQuestions(questions: Question[]): {
23
40
  error: QuestionToolError;
24
41
  message: string;
25
42
  };
43
+ export {};
26
44
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAyD9E,eAAO,MAAM,cAAc;;;;;;;;;;;EAMzB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,QAAQ,EAAE,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiEzE"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE9E,KAAK,YAAY,GAAG;IAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAyD9C,eAAO,MAAM,cAAc;;;;;;;;;;;EAMzB,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAuCrE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,QAAQ,EAAE,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiEzE"}
@@ -50,6 +50,58 @@ export const QuestionParams = Type.Object({
50
50
  description: "Related decisions to ask now (1-4); ask only what is needed to unblock progress.",
51
51
  }),
52
52
  });
53
+ /**
54
+ * Unambiguous argument normalizations applied before schema validation; the
55
+ * schema itself stays a single object shape because strict constrained
56
+ * sampling does not support object unions. Everything else is left for
57
+ * validation to report.
58
+ *
59
+ * - a lone question written flat at the root ({question, header?, options,
60
+ * multiSelect?} with no questions) wraps into questions[0];
61
+ * - a root-level question string fills a questions[0] object that lost its
62
+ * own question field to the root;
63
+ * - a stringified questions array parses back into an array (a parse failure
64
+ * stays for validation to reject).
65
+ */
66
+ export function normalizeQuestionArguments(raw) {
67
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
68
+ return raw;
69
+ const args = { ...raw };
70
+ if (typeof args.questions === "string") {
71
+ try {
72
+ const parsed = JSON.parse(args.questions);
73
+ if (Array.isArray(parsed))
74
+ args.questions = parsed;
75
+ }
76
+ catch {
77
+ // Leave the string for schema validation to reject.
78
+ }
79
+ }
80
+ const rootQuestion = typeof args.question === "string" ? args.question : undefined;
81
+ const onlyQuestion = Array.isArray(args.questions) && args.questions.length === 1 ? args.questions[0] : undefined;
82
+ if (rootQuestion !== undefined &&
83
+ onlyQuestion !== null &&
84
+ typeof onlyQuestion === "object" &&
85
+ !Array.isArray(onlyQuestion) &&
86
+ onlyQuestion.question === undefined) {
87
+ args.questions = [{ ...onlyQuestion, question: rootQuestion }];
88
+ delete args.question;
89
+ return args;
90
+ }
91
+ if (args.questions === undefined && rootQuestion !== undefined && args.options !== undefined) {
92
+ const question = { question: rootQuestion, options: args.options };
93
+ if (args.header !== undefined)
94
+ question.header = args.header;
95
+ if (args.multiSelect !== undefined)
96
+ question.multiSelect = args.multiSelect;
97
+ delete args.question;
98
+ delete args.options;
99
+ delete args.header;
100
+ delete args.multiSelect;
101
+ args.questions = [question];
102
+ }
103
+ return args;
104
+ }
53
105
  /**
54
106
  * Checks visible non-whitespace text and cross-field rules the schema cannot
55
107
  * express. Length and count limits remain enforced by `QuestionParams` first.
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,CAAU,CAAC;AAEnG,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACnE,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC;QAClB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,eAAe,CAAC,gBAAgB;QAC3C,WAAW,EACV,oMAAoM;KACrM,CAAC;IACF,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;QACxB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,eAAe,CAAC,sBAAsB;QACjD,WAAW,EAAE,iFAAiF;KAC9F,CAAC;IACF,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,MAAM,CAAC;QACX,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,eAAe,CAAC,YAAY;QACvC,WAAW,EACV,0IAA0I;KAC3I,CAAC,CACF;CACD,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IAClC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;QACrB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,eAAe,CAAC,aAAa;QACxC,WAAW,EACV,oHAAoH;KACrH,CAAC;IACF,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;QACnB,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,eAAe,CAAC,WAAW;QACtC,WAAW,EAAE,2FAA2F;KACxG,CAAC;IACF,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE;QACjC,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,WAAW;QACrB,WAAW,EAAE,4FAA4F;KACzG,CAAC;IACF,WAAW,EAAE,IAAI,CAAC,QAAQ,CACzB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EACV,yHAAyH;KAC1H,CAAC,CACF;CACD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QACrC,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,aAAa;QACvB,WAAW,EAAE,kFAAkF;KAC/F,CAAC;CACF,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAChC,SAAqB;IAErB,MAAM,eAAe,GAAG,CACvB,KAAc,EACd,IAAY,EAC2D,EAAE;QACzE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,IAAI,4BAA4B,EAAE,CAAC;IACzF,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,KAAK,IAAI,aAAa,GAAG,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,CAAC;QAC/E,MAAM,CAAC,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;QACnC,MAAM,aAAa,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,aAAa,aAAa,YAAY,CAAC,CAAC;QAC1F,IAAI,aAAa;YAAE,OAAO,aAAa,CAAC;QACxC,MAAM,WAAW,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,EAAE,aAAa,aAAa,UAAU,CAAC,CAAC;QACpF,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QAEpC,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC;QAC5F,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAE/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,IAAI,WAAW,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC;YACzE,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAmB,CAAC;YACxD,MAAM,UAAU,GAAG,aAAa,aAAa,aAAa,WAAW,GAAG,CAAC;YACzE,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,UAAU,QAAQ,CAAC,CAAC;YACxE,IAAI,UAAU;gBAAE,OAAO,UAAU,CAAC;YAClC,MAAM,gBAAgB,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,UAAU,cAAc,CAAC,CAAC;YAC1F,IAAI,gBAAgB;gBAAE,OAAO,gBAAgB,CAAC;YAC9C,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAClC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,UAAU,UAAU,CAAC,CAAC;gBAC9E,IAAI,YAAY;oBAAE,OAAO,YAAY,CAAC;YACvC,CAAC;YAED,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACvC,OAAO;oBACN,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,gBAAgB;oBACvB,OAAO,EAAE,6BAA6B,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;iBACnE,CAAC;YACH,CAAC;YACD,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,OAAO;oBACN,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,wBAAwB;oBAC/B,OAAO,EAAE,gDAAgD;iBACzD,CAAC;YACH,CAAC;YACD,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACjE,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EACN,kHAAkH;aACnH,CAAC;QACH,CAAC;IACF,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACrB,CAAC","sourcesContent":["import { Type } from \"typebox\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport type { Question, QuestionOption, QuestionToolError } from \"./types.ts\";\n\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 4;\nconst MAX_QUESTIONS = 4;\nconst RESERVED_LABELS = [\"Other\", \"Type something\", \"Type something.\", \"Chat about this\"] as const;\n\nconst normalizeLabel = (text: string) => text.trim().toLowerCase();\nconst RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));\n\nconst OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.optionLabelChars,\n\t\tdescription:\n\t\t\t\"Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something', 'Chat about this') are rejected — the UI adds the custom-answer path itself.\",\n\t}),\n\tdescription: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.optionDescriptionChars,\n\t\tdescription: \"One concise sentence explaining the option's meaning, consequence, or tradeoff.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: QUESTION_LIMITS.previewChars,\n\t\t\tdescription:\n\t\t\t\t\"Optional markdown preview shown only for focused single-select options; use for concrete snippets, layouts, copy, or config comparisons.\",\n\t\t}),\n\t),\n});\n\nconst QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.questionChars,\n\t\tdescription:\n\t\t\t\"One clear, specific decision or preference question ending in a question mark. Ask only what is needed to proceed.\",\n\t}),\n\theader: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.headerChars,\n\t\tdescription: \"Very short decision label shown as a chip; aim for ~12 chars, max 32. E.g. 'Auth method'.\",\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription: \"Mutually distinct options for this decision; do not include reserved custom-answer labels.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Allow selecting multiple options only when choices can be combined. Defaults to false for mutually exclusive decisions.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionParams = Type.Object({\n\tquestions: Type.Array(QuestionSchema, {\n\t\tminItems: 1,\n\t\tmaxItems: MAX_QUESTIONS,\n\t\tdescription: \"Related decisions to ask now (1-4); ask only what is needed to unblock progress.\",\n\t}),\n});\n\n/**\n * Checks visible non-whitespace text and cross-field rules the schema cannot\n * express. Length and count limits remain enforced by `QuestionParams` first.\n */\nexport function validateQuestions(\n\tquestions: Question[],\n): { ok: true } | { ok: false; error: QuestionToolError; message: string } {\n\tconst requireNonBlank = (\n\t\tvalue: unknown,\n\t\tpath: string,\n\t): { ok: false; error: QuestionToolError; message: string } | undefined => {\n\t\tif (typeof value === \"string\" && value.trim().length > 0) return undefined;\n\t\treturn { ok: false, error: \"blank_text\", message: `${path} must contain visible text` };\n\t};\n\n\tconst seenQuestions = new Set<string>();\n\tfor (let questionIndex = 0; questionIndex < questions.length; questionIndex++) {\n\t\tconst q = questions[questionIndex];\n\t\tconst questionError = requireNonBlank(q.question, `questions[${questionIndex}].question`);\n\t\tif (questionError) return questionError;\n\t\tconst headerError = requireNonBlank(q.header, `questions[${questionIndex}].header`);\n\t\tif (headerError) return headerError;\n\n\t\tconst questionKey = normalizeLabel(q.question);\n\t\tif (seenQuestions.has(questionKey)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: \"Question text must be unique\" };\n\t\t}\n\t\tseenQuestions.add(questionKey);\n\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (let optionIndex = 0; optionIndex < q.options.length; optionIndex++) {\n\t\t\tconst option = q.options[optionIndex] as QuestionOption;\n\t\t\tconst optionPath = `questions[${questionIndex}].options[${optionIndex}]`;\n\t\t\tconst labelError = requireNonBlank(option.label, `${optionPath}.label`);\n\t\t\tif (labelError) return labelError;\n\t\t\tconst descriptionError = requireNonBlank(option.description, `${optionPath}.description`);\n\t\t\tif (descriptionError) return descriptionError;\n\t\t\tif (option.preview !== undefined) {\n\t\t\t\tconst previewError = requireNonBlank(option.preview, `${optionPath}.preview`);\n\t\t\t\tif (previewError) return previewError;\n\t\t\t}\n\n\t\t\tconst labelKey = normalizeLabel(option.label);\n\t\t\tif (RESERVED_NORMALIZED.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"reserved_label\",\n\t\t\t\t\tmessage: `Option label is reserved (${RESERVED_LABELS.join(\", \")})`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seenLabels.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: \"Option labels must be unique within a question\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(labelKey);\n\t\t}\n\n\t\tif (q.multiSelect && q.options.some((option) => option.preview)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"preview_multiselect\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Option previews are not supported on multiSelect questions; drop the previews or make the question single-select\",\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/question/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { Type } from \"typebox\";\nimport { QUESTION_LIMITS } from \"./limits.ts\";\nimport type { Question, QuestionOption, QuestionToolError } from \"./types.ts\";\n\ntype QuestionArgs = { questions: Question[] };\n\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 4;\nconst MAX_QUESTIONS = 4;\nconst RESERVED_LABELS = [\"Other\", \"Type something\", \"Type something.\", \"Chat about this\"] as const;\n\nconst normalizeLabel = (text: string) => text.trim().toLowerCase();\nconst RESERVED_NORMALIZED = new Set(RESERVED_LABELS.map(normalizeLabel));\n\nconst OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.optionLabelChars,\n\t\tdescription:\n\t\t\t\"Short user-facing option label (1-5 words), distinct within the question. Reserved labels ('Other', 'Type something', 'Chat about this') are rejected — the UI adds the custom-answer path itself.\",\n\t}),\n\tdescription: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.optionDescriptionChars,\n\t\tdescription: \"One concise sentence explaining the option's meaning, consequence, or tradeoff.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: QUESTION_LIMITS.previewChars,\n\t\t\tdescription:\n\t\t\t\t\"Optional markdown preview shown only for focused single-select options; use for concrete snippets, layouts, copy, or config comparisons.\",\n\t\t}),\n\t),\n});\n\nconst QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.questionChars,\n\t\tdescription:\n\t\t\t\"One clear, specific decision or preference question ending in a question mark. Ask only what is needed to proceed.\",\n\t}),\n\theader: Type.String({\n\t\tminLength: 1,\n\t\tmaxLength: QUESTION_LIMITS.headerChars,\n\t\tdescription: \"Very short decision label shown as a chip; aim for ~12 chars, max 32. E.g. 'Auth method'.\",\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription: \"Mutually distinct options for this decision; do not include reserved custom-answer labels.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Allow selecting multiple options only when choices can be combined. Defaults to false for mutually exclusive decisions.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionParams = Type.Object({\n\tquestions: Type.Array(QuestionSchema, {\n\t\tminItems: 1,\n\t\tmaxItems: MAX_QUESTIONS,\n\t\tdescription: \"Related decisions to ask now (1-4); ask only what is needed to unblock progress.\",\n\t}),\n});\n\n/**\n * Unambiguous argument normalizations applied before schema validation; the\n * schema itself stays a single object shape because strict constrained\n * sampling does not support object unions. Everything else is left for\n * validation to report.\n *\n * - a lone question written flat at the root ({question, header?, options,\n * multiSelect?} with no questions) wraps into questions[0];\n * - a root-level question string fills a questions[0] object that lost its\n * own question field to the root;\n * - a stringified questions array parses back into an array (a parse failure\n * stays for validation to reject).\n */\nexport function normalizeQuestionArguments(raw: unknown): QuestionArgs {\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) return raw as QuestionArgs;\n\tconst args: Record<string, unknown> = { ...(raw as Record<string, unknown>) };\n\n\tif (typeof args.questions === \"string\") {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(args.questions);\n\t\t\tif (Array.isArray(parsed)) args.questions = parsed;\n\t\t} catch {\n\t\t\t// Leave the string for schema validation to reject.\n\t\t}\n\t}\n\n\tconst rootQuestion = typeof args.question === \"string\" ? args.question : undefined;\n\tconst onlyQuestion =\n\t\tArray.isArray(args.questions) && args.questions.length === 1 ? (args.questions[0] as unknown) : undefined;\n\tif (\n\t\trootQuestion !== undefined &&\n\t\tonlyQuestion !== null &&\n\t\ttypeof onlyQuestion === \"object\" &&\n\t\t!Array.isArray(onlyQuestion) &&\n\t\t(onlyQuestion as Record<string, unknown>).question === undefined\n\t) {\n\t\targs.questions = [{ ...(onlyQuestion as Record<string, unknown>), question: rootQuestion }];\n\t\tdelete args.question;\n\t\treturn args as QuestionArgs;\n\t}\n\n\tif (args.questions === undefined && rootQuestion !== undefined && args.options !== undefined) {\n\t\tconst question: Record<string, unknown> = { question: rootQuestion, options: args.options };\n\t\tif (args.header !== undefined) question.header = args.header;\n\t\tif (args.multiSelect !== undefined) question.multiSelect = args.multiSelect;\n\t\tdelete args.question;\n\t\tdelete args.options;\n\t\tdelete args.header;\n\t\tdelete args.multiSelect;\n\t\targs.questions = [question];\n\t}\n\treturn args as QuestionArgs;\n}\n\n/**\n * Checks visible non-whitespace text and cross-field rules the schema cannot\n * express. Length and count limits remain enforced by `QuestionParams` first.\n */\nexport function validateQuestions(\n\tquestions: Question[],\n): { ok: true } | { ok: false; error: QuestionToolError; message: string } {\n\tconst requireNonBlank = (\n\t\tvalue: unknown,\n\t\tpath: string,\n\t): { ok: false; error: QuestionToolError; message: string } | undefined => {\n\t\tif (typeof value === \"string\" && value.trim().length > 0) return undefined;\n\t\treturn { ok: false, error: \"blank_text\", message: `${path} must contain visible text` };\n\t};\n\n\tconst seenQuestions = new Set<string>();\n\tfor (let questionIndex = 0; questionIndex < questions.length; questionIndex++) {\n\t\tconst q = questions[questionIndex];\n\t\tconst questionError = requireNonBlank(q.question, `questions[${questionIndex}].question`);\n\t\tif (questionError) return questionError;\n\t\tconst headerError = requireNonBlank(q.header, `questions[${questionIndex}].header`);\n\t\tif (headerError) return headerError;\n\n\t\tconst questionKey = normalizeLabel(q.question);\n\t\tif (seenQuestions.has(questionKey)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: \"Question text must be unique\" };\n\t\t}\n\t\tseenQuestions.add(questionKey);\n\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (let optionIndex = 0; optionIndex < q.options.length; optionIndex++) {\n\t\t\tconst option = q.options[optionIndex] as QuestionOption;\n\t\t\tconst optionPath = `questions[${questionIndex}].options[${optionIndex}]`;\n\t\t\tconst labelError = requireNonBlank(option.label, `${optionPath}.label`);\n\t\t\tif (labelError) return labelError;\n\t\t\tconst descriptionError = requireNonBlank(option.description, `${optionPath}.description`);\n\t\t\tif (descriptionError) return descriptionError;\n\t\t\tif (option.preview !== undefined) {\n\t\t\t\tconst previewError = requireNonBlank(option.preview, `${optionPath}.preview`);\n\t\t\t\tif (previewError) return previewError;\n\t\t\t}\n\n\t\t\tconst labelKey = normalizeLabel(option.label);\n\t\t\tif (RESERVED_NORMALIZED.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"reserved_label\",\n\t\t\t\t\tmessage: `Option label is reserved (${RESERVED_LABELS.join(\", \")})`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (seenLabels.has(labelKey)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: \"Option labels must be unique within a question\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(labelKey);\n\t\t}\n\n\t\tif (q.multiSelect && q.options.some((option) => option.preview)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"preview_multiselect\",\n\t\t\t\tmessage:\n\t\t\t\t\t\"Option previews are not supported on multiSelect questions; drop the previews or make the question single-select\",\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYpF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAwIvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYpF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA0IvD"}
@@ -6,7 +6,7 @@ import { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME } from "
6
6
  import { renderSubagentCall, renderSubagentResult, scheduleLiveRefresh } from "./render.js";
7
7
  import { subagentGroupTitle } from "./resolve.js";
8
8
  import { ConcurrencyGate, isSubagentError, runSubagentInvocation } from "./runner.js";
9
- import { SubagentParamsSchema } from "./schema.js";
9
+ import { normalizeSubagentArguments, SubagentParamsSchema } from "./schema.js";
10
10
  import { statusSummary } from "./state.js";
11
11
  export default function subagent(pi) {
12
12
  const gate = new ConcurrencyGate();
@@ -22,6 +22,8 @@ export default function subagent(pi) {
22
22
  "Give every task a short `description` label; it is shown in the /bg list, live rows, and report headings.",
23
23
  ],
24
24
  parameters: SubagentParamsSchema,
25
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
26
+ prepareArguments: normalizeSubagentArguments,
25
27
  async execute(toolCallId, params, signal, onUpdate, ctx) {
26
28
  const host = ctx.background;
27
29
  if (host?.closed)
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAgentDir } from \"../../config.ts\";\nimport type { BackgroundControl, BackgroundProjection, BackgroundTerminalStatus } from \"../../core/background/types.ts\";\nimport type { AgentToolResult, ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { subagentToolDescription } from \"./agents.ts\";\nimport { showAgentsCommand } from \"./agents-command.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME } from \"./constants.ts\";\nimport { renderSubagentCall, renderSubagentResult, type SubagentRenderState, scheduleLiveRefresh } from \"./render.ts\";\nimport { type ParentModelContext, subagentGroupTitle } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { statusSummary } from \"./state.ts\";\nimport type { SubagentDetails } from \"./types.ts\";\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tlet workerOrdinal = 0;\n\tconst activeAborters = new Set<() => Promise<void>>();\n\n\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails, SubagentRenderState>({\n\t\tname: SUBAGENT_TOOL_NAME,\n\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\tdescription: subagentToolDescription(),\n\t\tpromptSnippet: \"Delegate bounded work to isolated explorer or general workers\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `subagent` for bounded work that benefits from isolated context or concurrent investigation; do not delegate a task you can finish with one or two direct tool calls.\",\n\t\t\t\"Give every task a short `description` label; it is shown in the /bg list, live rows, and report headings.\",\n\t\t],\n\t\tparameters: SubagentParamsSchema,\n\t\tasync execute(toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\tconst host = ctx.background;\n\t\t\tif (host?.closed) throw new Error(\"Background service is closed\");\n\t\t\tconst managed = host?.enabled || params.background === true;\n\t\t\tif (managed && !host) throw new Error(\"Background execution requires an enabled Background host.\");\n\t\t\tlet latest: SubagentDetails = {\n\t\t\t\tstatus: \"running\",\n\t\t\t\truns: [],\n\t\t\t\tstartedAt: Date.now(),\n\t\t\t\tusage: emptyUsage(),\n\t\t\t};\n\t\t\tconst ordinals = new Map<string, number>();\n\t\t\tconst projection = (view: BackgroundProjection): BackgroundProjection => ({\n\t\t\t\t...view,\n\t\t\t\tworkers: view.workers?.map((worker) => {\n\t\t\t\t\tlet ordinal = ordinals.get(worker.id);\n\t\t\t\t\tif (ordinal === undefined) {\n\t\t\t\t\t\tordinal = ++workerOrdinal;\n\t\t\t\t\t\tordinals.set(worker.id, ordinal);\n\t\t\t\t\t}\n\t\t\t\t\treturn { ...worker, label: `#${ordinal} ${worker.profile}` };\n\t\t\t\t}),\n\t\t\t});\n\t\t\tconst run = async (\n\t\t\t\tcontrol?: BackgroundControl<SubagentDetails>,\n\t\t\t): Promise<AgentToolResult<SubagentDetails>> => {\n\t\t\t\tconst update = (details: SubagentDetails, view?: BackgroundProjection): void => {\n\t\t\t\t\tlatest = details;\n\t\t\t\t\tconst result: AgentToolResult<SubagentDetails> = {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: statusSummary(details) }],\n\t\t\t\t\t\tdetails,\n\t\t\t\t\t};\n\t\t\t\t\tif (control) control.publish(result, view ? projection(view) : undefined);\n\t\t\t\t\telse onUpdate?.(result);\n\t\t\t\t};\n\t\t\t\tupdate(latest);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: ctx.modelRuntime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal: control?.signal ?? signal,\n\t\t\t\t\tgate,\n\t\t\t\t\tbatchId: control?.id ?? toolCallId,\n\t\t\t\t\tonUpdate: update,\n\t\t\t\t\tonAccepted: control ? () => control.accept() : undefined,\n\t\t\t\t\tonConfigWarning: (message) => ctx.ui.notify(message, \"warning\"),\n\t\t\t\t\tregisterAbort: control\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (abort) => {\n\t\t\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tconst result: AgentToolResult<SubagentDetails> = {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t\tcontrol?.publish(result, projection(execution.projection));\n\t\t\t\treturn result;\n\t\t\t};\n\t\t\tif (!managed) return run();\n\t\t\tconst outcome = await host!.execute<SubagentDetails>({\n\t\t\t\tkind: \"subagent\",\n\t\t\t\ttitle: subagentGroupTitle(params.tasks),\n\t\t\t\ttoolCallId,\n\t\t\t\tbackground: params.background === true,\n\t\t\t\tsignal,\n\t\t\t\tonUpdate,\n\t\t\t\trun: async (control) => {\n\t\t\t\t\tconst result = await run(control);\n\t\t\t\t\tconst status = result.details!.status;\n\t\t\t\t\tconst terminal: BackgroundTerminalStatus =\n\t\t\t\t\t\tstatus === \"aborted\" ? \"cancelled\" : status === \"running\" ? \"failed\" : status;\n\t\t\t\t\treturn { result, status: terminal };\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (outcome.kind === \"result\") return outcome.result;\n\t\t\tconst submittedAt = Date.now();\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Subagent group handed to background: ${outcome.task.id}. Completion arrives automatically as a notification — do not poll or immediately wait on it; continue with other work or hand back to the user. Use bg read to inspect progress, bg wait only when your next step is blocked on the result (prefer foreground next time in that case), or bg kill to stop the whole group.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { ...latest, endedAt: submittedAt, background: { id: outcome.task.id, submittedAt } },\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\treturn renderSubagentCall(args, theme);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tscheduleLiveRefresh(context, options.isPartial && !result.details?.background);\n\t\t\treturn renderSubagentResult(result, options, theme, context.args, context.isError);\n\t\t},\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && !details.background && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAgentDir } from \"../../config.ts\";\nimport type { BackgroundControl, BackgroundProjection, BackgroundTerminalStatus } from \"../../core/background/types.ts\";\nimport type { AgentToolResult, ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { subagentToolDescription } from \"./agents.ts\";\nimport { showAgentsCommand } from \"./agents-command.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME } from \"./constants.ts\";\nimport { renderSubagentCall, renderSubagentResult, type SubagentRenderState, scheduleLiveRefresh } from \"./render.ts\";\nimport { type ParentModelContext, subagentGroupTitle } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation } from \"./runner.ts\";\nimport { normalizeSubagentArguments, SubagentParamsSchema } from \"./schema.ts\";\nimport { statusSummary } from \"./state.ts\";\nimport type { SubagentDetails } from \"./types.ts\";\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tlet workerOrdinal = 0;\n\tconst activeAborters = new Set<() => Promise<void>>();\n\n\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails, SubagentRenderState>({\n\t\tname: SUBAGENT_TOOL_NAME,\n\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\tdescription: subagentToolDescription(),\n\t\tpromptSnippet: \"Delegate bounded work to isolated explorer or general workers\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `subagent` for bounded work that benefits from isolated context or concurrent investigation; do not delegate a task you can finish with one or two direct tool calls.\",\n\t\t\t\"Give every task a short `description` label; it is shown in the /bg list, live rows, and report headings.\",\n\t\t],\n\t\tparameters: SubagentParamsSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tprepareArguments: normalizeSubagentArguments,\n\t\tasync execute(toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\tconst host = ctx.background;\n\t\t\tif (host?.closed) throw new Error(\"Background service is closed\");\n\t\t\tconst managed = host?.enabled || params.background === true;\n\t\t\tif (managed && !host) throw new Error(\"Background execution requires an enabled Background host.\");\n\t\t\tlet latest: SubagentDetails = {\n\t\t\t\tstatus: \"running\",\n\t\t\t\truns: [],\n\t\t\t\tstartedAt: Date.now(),\n\t\t\t\tusage: emptyUsage(),\n\t\t\t};\n\t\t\tconst ordinals = new Map<string, number>();\n\t\t\tconst projection = (view: BackgroundProjection): BackgroundProjection => ({\n\t\t\t\t...view,\n\t\t\t\tworkers: view.workers?.map((worker) => {\n\t\t\t\t\tlet ordinal = ordinals.get(worker.id);\n\t\t\t\t\tif (ordinal === undefined) {\n\t\t\t\t\t\tordinal = ++workerOrdinal;\n\t\t\t\t\t\tordinals.set(worker.id, ordinal);\n\t\t\t\t\t}\n\t\t\t\t\treturn { ...worker, label: `#${ordinal} ${worker.profile}` };\n\t\t\t\t}),\n\t\t\t});\n\t\t\tconst run = async (\n\t\t\t\tcontrol?: BackgroundControl<SubagentDetails>,\n\t\t\t): Promise<AgentToolResult<SubagentDetails>> => {\n\t\t\t\tconst update = (details: SubagentDetails, view?: BackgroundProjection): void => {\n\t\t\t\t\tlatest = details;\n\t\t\t\t\tconst result: AgentToolResult<SubagentDetails> = {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: statusSummary(details) }],\n\t\t\t\t\t\tdetails,\n\t\t\t\t\t};\n\t\t\t\t\tif (control) control.publish(result, view ? projection(view) : undefined);\n\t\t\t\t\telse onUpdate?.(result);\n\t\t\t\t};\n\t\t\t\tupdate(latest);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: ctx.modelRuntime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal: control?.signal ?? signal,\n\t\t\t\t\tgate,\n\t\t\t\t\tbatchId: control?.id ?? toolCallId,\n\t\t\t\t\tonUpdate: update,\n\t\t\t\t\tonAccepted: control ? () => control.accept() : undefined,\n\t\t\t\t\tonConfigWarning: (message) => ctx.ui.notify(message, \"warning\"),\n\t\t\t\t\tregisterAbort: control\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (abort) => {\n\t\t\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tconst result: AgentToolResult<SubagentDetails> = {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t\tcontrol?.publish(result, projection(execution.projection));\n\t\t\t\treturn result;\n\t\t\t};\n\t\t\tif (!managed) return run();\n\t\t\tconst outcome = await host!.execute<SubagentDetails>({\n\t\t\t\tkind: \"subagent\",\n\t\t\t\ttitle: subagentGroupTitle(params.tasks),\n\t\t\t\ttoolCallId,\n\t\t\t\tbackground: params.background === true,\n\t\t\t\tsignal,\n\t\t\t\tonUpdate,\n\t\t\t\trun: async (control) => {\n\t\t\t\t\tconst result = await run(control);\n\t\t\t\t\tconst status = result.details!.status;\n\t\t\t\t\tconst terminal: BackgroundTerminalStatus =\n\t\t\t\t\t\tstatus === \"aborted\" ? \"cancelled\" : status === \"running\" ? \"failed\" : status;\n\t\t\t\t\treturn { result, status: terminal };\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (outcome.kind === \"result\") return outcome.result;\n\t\t\tconst submittedAt = Date.now();\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Subagent group handed to background: ${outcome.task.id}. Completion arrives automatically as a notification — do not poll or immediately wait on it; continue with other work or hand back to the user. Use bg read to inspect progress, bg wait only when your next step is blocked on the result (prefer foreground next time in that case), or bg kill to stop the whole group.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { ...latest, endedAt: submittedAt, background: { id: outcome.task.id, submittedAt } },\n\t\t\t};\n\t\t},\n\t\trenderCall(args, theme) {\n\t\t\treturn renderSubagentCall(args, theme);\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tscheduleLiveRefresh(context, options.isPartial && !result.details?.background);\n\t\t\treturn renderSubagentResult(result, options, theme, context.args, context.isError);\n\t\t},\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && !details.background && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
@@ -8,7 +8,20 @@ export interface ParentModelContext {
8
8
  thinking: ThinkingLevel;
9
9
  modelRegistry: Pick<ModelRegistry, "find" | "getAvailable" | "hasConfiguredAuth">;
10
10
  }
11
- export declare function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): string;
11
+ export interface ResolvedTaskCwd {
12
+ cwd: string;
13
+ /** False for a directory outside the parent: the task runs untrusted there. */
14
+ insideParent: boolean;
15
+ }
16
+ /**
17
+ * Resolve the requested cwd against the parent working directory. A directory
18
+ * outside the parent is allowed but marked untrusted (the runner starts it
19
+ * without the parent project's trust, so its .pi settings are never loaded);
20
+ * only a missing or non-directory cwd fails. The inside/outside decision is
21
+ * always made on canonical paths, so a symlink that points outside the parent
22
+ * is treated as outside rather than rejected.
23
+ */
24
+ export declare function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): ResolvedTaskCwd;
12
25
  export declare function taskLabel(task: Pick<SubagentTask, "prompt"> & {
13
26
  description?: string | null;
14
27
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,GAAG,EAAsB,KAAK,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAExF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAA2B,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,GAAG,mBAAmB,CAAC,CAAC;CAClF;AAkBD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAqB1F;AAyBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,CAItG;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAIzE;AAED,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,EAC1B,cAAc,SAAgB,EAC9B,eAAe,CAAC,EAAE,kBAAkB,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAkB/B"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,GAAG,EAAsB,KAAK,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAExF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAA2B,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,GAAG,mBAAmB,CAAC,CAAC;CAClF;AA8BD,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,YAAY,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,CAYnG;AAyBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,MAAM,CAItG;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAIzE;AAED,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,EAC1B,cAAc,SAAgB,EAC9B,eAAe,CAAC,EAAE,kBAAkB,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAoB/B"}
@@ -1,5 +1,5 @@
1
1
  import { existsSync, realpathSync, statSync } from "node:fs";
2
- import { isAbsolute, relative, resolve } from "node:path";
2
+ import { dirname, isAbsolute, relative, resolve } from "node:path";
3
3
  import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
4
4
  import { getAgentDir } from "../../config.js";
5
5
  import { AGENT_PROFILES } from "./agents.js";
@@ -21,28 +21,40 @@ function isWithin(parent, child) {
21
21
  return rest === "" || (rest !== ".." && !rest.startsWith(`..${PATH_SEP}`) && !isAbsolute(rest));
22
22
  }
23
23
  const PATH_SEP = process.platform === "win32" ? "\\" : "/";
24
+ /** Closest existing ancestor directory, for "does not exist" diagnostics. */
25
+ function nearestExistingParent(candidate) {
26
+ let current = candidate;
27
+ for (let depth = 0; depth < 64; depth++) {
28
+ const parent = dirname(current);
29
+ if (parent === current)
30
+ return current;
31
+ if (existsSync(parent))
32
+ return parent;
33
+ current = parent;
34
+ }
35
+ return current;
36
+ }
37
+ /**
38
+ * Resolve the requested cwd against the parent working directory. A directory
39
+ * outside the parent is allowed but marked untrusted (the runner starts it
40
+ * without the parent project's trust, so its .pi settings are never loaded);
41
+ * only a missing or non-directory cwd fails. The inside/outside decision is
42
+ * always made on canonical paths, so a symlink that points outside the parent
43
+ * is treated as outside rather than rejected.
44
+ */
24
45
  export function resolveTaskCwd(parentCwd, requestedCwd) {
25
46
  const value = normalizeCwdInput(requestedCwd);
26
47
  // Models routinely echo the parent working directory back as an absolute
27
- // path; accept an absolute cwd that stays inside the parent instead of
28
- // failing the whole task over path style.
29
- const absolute = value !== undefined && isAbsolute(value);
30
- const candidate = absolute ? resolve(value) : resolve(parentCwd, value ?? ".");
31
- // Relative inputs get a lexical traversal check before touching the file
32
- // system. Absolute inputs may use the real spelling of a symlinked parent,
33
- // so their containment is decided by the canonical check below.
34
- if (!absolute && !isWithin(resolve(parentCwd), candidate))
35
- throw new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);
36
- if (!existsSync(candidate) || !statSync(candidate).isDirectory())
48
+ // path; accept an absolute cwd instead of failing the task over path style.
49
+ const candidate = value !== undefined && isAbsolute(value) ? resolve(value) : resolve(parentCwd, value ?? ".");
50
+ if (!existsSync(candidate)) {
51
+ throw new Error(`${candidate} does not exist; nearest existing parent: ${nearestExistingParent(candidate)}`);
52
+ }
53
+ if (!statSync(candidate).isDirectory())
37
54
  throw new Error(`Subagent cwd is not a directory: ${candidate}`);
38
55
  const realParent = realpathSync(parentCwd);
39
56
  const realCandidate = realpathSync(candidate);
40
- if (!isWithin(realParent, realCandidate)) {
41
- if (absolute)
42
- throw new Error("cwd must stay inside the parent working directory.");
43
- throw new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);
44
- }
45
- return realCandidate;
57
+ return { cwd: realCandidate, insideParent: isWithin(realParent, realCandidate) };
46
58
  }
47
59
  // Exactly two layers: a /agents override wins, otherwise the subagent
48
60
  // inherits the parent session. Callers and agent files cannot pick models.
@@ -85,11 +97,13 @@ export async function resolveSubagentTask(task, parentCwd, parent, configAgentDi
85
97
  const config = preloadedConfig ?? (await loadSubagentConfig(configAgentDir));
86
98
  const override = config.profiles[agent.name];
87
99
  const model = resolveModel(override, parent);
100
+ const { cwd, insideParent } = resolveTaskCwd(parentCwd, task.cwd ?? undefined);
88
101
  return {
89
102
  agent,
90
103
  description: taskLabel(task),
91
104
  prompt: task.prompt,
92
- cwd: resolveTaskCwd(parentCwd, task.cwd ?? undefined),
105
+ cwd,
106
+ insideParent,
93
107
  model,
94
108
  thinking: resolveThinking(override, parent, model),
95
109
  };
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, realpathSync, statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { type Api, clampThinkingLevel, type Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { AGENT_PROFILES } from \"./agents.ts\";\nimport { findAvailableModel } from \"./model-selection.ts\";\nimport type { SubagentTask } from \"./schema.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport { firstPlainLine, truncate } from \"./text.ts\";\nimport type { ResolvedSubagentTask, SubagentConfigFile, SubagentProfileOverride } from \"./types.ts\";\n\nexport interface ParentModelContext {\n\tmodel: Model<Api> | undefined;\n\tthinking: ThinkingLevel;\n\tmodelRegistry: Pick<ModelRegistry, \"find\" | \"getAvailable\" | \"hasConfiguredAuth\">;\n}\n\nfunction normalizeCwdInput(value: string | undefined): string | undefined {\n\tif (value === undefined) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) throw new Error(\"cwd must not be empty.\");\n\treturn trimmed.startsWith(\"@\") ? trimmed.slice(1) : trimmed;\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n\tconst normalizedParent = process.platform === \"win32\" ? parent.toLowerCase() : parent;\n\tconst normalizedChild = process.platform === \"win32\" ? child.toLowerCase() : child;\n\tconst rest = relative(normalizedParent, normalizedChild);\n\treturn rest === \"\" || (rest !== \"..\" && !rest.startsWith(`..${PATH_SEP}`) && !isAbsolute(rest));\n}\n\nconst PATH_SEP = process.platform === \"win32\" ? \"\\\\\" : \"/\";\n\nexport function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): string {\n\tconst value = normalizeCwdInput(requestedCwd);\n\t// Models routinely echo the parent working directory back as an absolute\n\t// path; accept an absolute cwd that stays inside the parent instead of\n\t// failing the whole task over path style.\n\tconst absolute = value !== undefined && isAbsolute(value);\n\tconst candidate = absolute ? resolve(value) : resolve(parentCwd, value ?? \".\");\n\t// Relative inputs get a lexical traversal check before touching the file\n\t// system. Absolute inputs may use the real spelling of a symlinked parent,\n\t// so their containment is decided by the canonical check below.\n\tif (!absolute && !isWithin(resolve(parentCwd), candidate))\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\tif (!existsSync(candidate) || !statSync(candidate).isDirectory())\n\t\tthrow new Error(`Subagent cwd is not a directory: ${candidate}`);\n\tconst realParent = realpathSync(parentCwd);\n\tconst realCandidate = realpathSync(candidate);\n\tif (!isWithin(realParent, realCandidate)) {\n\t\tif (absolute) throw new Error(\"cwd must stay inside the parent working directory.\");\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\t}\n\treturn realCandidate;\n}\n\n// Exactly two layers: a /agents override wins, otherwise the subagent\n// inherits the parent session. Callers and agent files cannot pick models.\nfunction resolveModel(override: SubagentProfileOverride | undefined, parent: ParentModelContext): Model<Api> {\n\tif (override?.model) return findAvailableModel(override.model, parent.modelRegistry);\n\tif (!parent.model) throw new Error(\"The parent session has no active model.\");\n\treturn parent.model;\n}\n\nfunction resolveThinking(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n\tmodel: Model<Api>,\n): ThinkingLevel {\n\tconst requested = override?.thinking ?? parent.thinking;\n\treturn clampThinkingLevel(model, requested) as ThinkingLevel;\n}\n\n// The schema restricts task.agent to explorer|general; an omitted or null\n// agent resolves to the read-only explorer profile.\nconst DEFAULT_AGENT_NAME = \"explorer\";\n\n// An explicit short label wins; otherwise derive a bounded plain-text label from\n// the briefing's first meaningful line so UI rows and report headings stay readable.\nexport function taskLabel(task: Pick<SubagentTask, \"prompt\"> & { description?: string | null }): string {\n\tconst explicit = task.description?.trim();\n\tif (explicit) return explicit;\n\treturn truncate(firstPlainLine(task.prompt), 80);\n}\n\n/** Group title shown in the /bg list, status line, and completion notification. */\nexport function subagentGroupTitle(tasks: readonly SubagentTask[]): string {\n\tconst labels = tasks.map((task) => taskLabel(task));\n\tif (labels.length === 1) return truncate(`Subagent · ${labels[0]}`, 100);\n\treturn truncate(`Subagent · ${tasks.length} tasks: ${labels.join(\", \")}`, 100);\n}\n\nexport async function resolveSubagentTask(\n\ttask: SubagentTask,\n\tparentCwd: string,\n\tparent: ParentModelContext,\n\tconfigAgentDir = getAgentDir(),\n\tpreloadedConfig?: SubagentConfigFile,\n): Promise<ResolvedSubagentTask> {\n\tconst agentName = task.agent ?? DEFAULT_AGENT_NAME;\n\tconst agent = AGENT_PROFILES.find((candidate) => candidate.name === agentName);\n\tif (!agent) {\n\t\tconst available = AGENT_PROFILES.map((candidate) => candidate.name).join(\", \") || \"none\";\n\t\tthrow new Error(`Unknown agent \"${agentName}\". Available agents: ${available}.`);\n\t}\n\tconst config = preloadedConfig ?? (await loadSubagentConfig(configAgentDir));\n\tconst override = config.profiles[agent.name];\n\tconst model = resolveModel(override, parent);\n\treturn {\n\t\tagent,\n\t\tdescription: taskLabel(task),\n\t\tprompt: task.prompt,\n\t\tcwd: resolveTaskCwd(parentCwd, task.cwd ?? undefined),\n\t\tmodel,\n\t\tthinking: resolveThinking(override, parent, model),\n\t};\n}\n"]}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, realpathSync, statSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { type Api, clampThinkingLevel, type Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { AGENT_PROFILES } from \"./agents.ts\";\nimport { findAvailableModel } from \"./model-selection.ts\";\nimport type { SubagentTask } from \"./schema.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport { firstPlainLine, truncate } from \"./text.ts\";\nimport type { ResolvedSubagentTask, SubagentConfigFile, SubagentProfileOverride } from \"./types.ts\";\n\nexport interface ParentModelContext {\n\tmodel: Model<Api> | undefined;\n\tthinking: ThinkingLevel;\n\tmodelRegistry: Pick<ModelRegistry, \"find\" | \"getAvailable\" | \"hasConfiguredAuth\">;\n}\n\nfunction normalizeCwdInput(value: string | undefined): string | undefined {\n\tif (value === undefined) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) throw new Error(\"cwd must not be empty.\");\n\treturn trimmed.startsWith(\"@\") ? trimmed.slice(1) : trimmed;\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n\tconst normalizedParent = process.platform === \"win32\" ? parent.toLowerCase() : parent;\n\tconst normalizedChild = process.platform === \"win32\" ? child.toLowerCase() : child;\n\tconst rest = relative(normalizedParent, normalizedChild);\n\treturn rest === \"\" || (rest !== \"..\" && !rest.startsWith(`..${PATH_SEP}`) && !isAbsolute(rest));\n}\n\nconst PATH_SEP = process.platform === \"win32\" ? \"\\\\\" : \"/\";\n\n/** Closest existing ancestor directory, for \"does not exist\" diagnostics. */\nfunction nearestExistingParent(candidate: string): string {\n\tlet current = candidate;\n\tfor (let depth = 0; depth < 64; depth++) {\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return current;\n\t\tif (existsSync(parent)) return parent;\n\t\tcurrent = parent;\n\t}\n\treturn current;\n}\n\nexport interface ResolvedTaskCwd {\n\tcwd: string;\n\t/** False for a directory outside the parent: the task runs untrusted there. */\n\tinsideParent: boolean;\n}\n\n/**\n * Resolve the requested cwd against the parent working directory. A directory\n * outside the parent is allowed but marked untrusted (the runner starts it\n * without the parent project's trust, so its .pi settings are never loaded);\n * only a missing or non-directory cwd fails. The inside/outside decision is\n * always made on canonical paths, so a symlink that points outside the parent\n * is treated as outside rather than rejected.\n */\nexport function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): ResolvedTaskCwd {\n\tconst value = normalizeCwdInput(requestedCwd);\n\t// Models routinely echo the parent working directory back as an absolute\n\t// path; accept an absolute cwd instead of failing the task over path style.\n\tconst candidate = value !== undefined && isAbsolute(value) ? resolve(value) : resolve(parentCwd, value ?? \".\");\n\tif (!existsSync(candidate)) {\n\t\tthrow new Error(`${candidate} does not exist; nearest existing parent: ${nearestExistingParent(candidate)}`);\n\t}\n\tif (!statSync(candidate).isDirectory()) throw new Error(`Subagent cwd is not a directory: ${candidate}`);\n\tconst realParent = realpathSync(parentCwd);\n\tconst realCandidate = realpathSync(candidate);\n\treturn { cwd: realCandidate, insideParent: isWithin(realParent, realCandidate) };\n}\n\n// Exactly two layers: a /agents override wins, otherwise the subagent\n// inherits the parent session. Callers and agent files cannot pick models.\nfunction resolveModel(override: SubagentProfileOverride | undefined, parent: ParentModelContext): Model<Api> {\n\tif (override?.model) return findAvailableModel(override.model, parent.modelRegistry);\n\tif (!parent.model) throw new Error(\"The parent session has no active model.\");\n\treturn parent.model;\n}\n\nfunction resolveThinking(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n\tmodel: Model<Api>,\n): ThinkingLevel {\n\tconst requested = override?.thinking ?? parent.thinking;\n\treturn clampThinkingLevel(model, requested) as ThinkingLevel;\n}\n\n// The schema restricts task.agent to explorer|general; an omitted or null\n// agent resolves to the read-only explorer profile.\nconst DEFAULT_AGENT_NAME = \"explorer\";\n\n// An explicit short label wins; otherwise derive a bounded plain-text label from\n// the briefing's first meaningful line so UI rows and report headings stay readable.\nexport function taskLabel(task: Pick<SubagentTask, \"prompt\"> & { description?: string | null }): string {\n\tconst explicit = task.description?.trim();\n\tif (explicit) return explicit;\n\treturn truncate(firstPlainLine(task.prompt), 80);\n}\n\n/** Group title shown in the /bg list, status line, and completion notification. */\nexport function subagentGroupTitle(tasks: readonly SubagentTask[]): string {\n\tconst labels = tasks.map((task) => taskLabel(task));\n\tif (labels.length === 1) return truncate(`Subagent · ${labels[0]}`, 100);\n\treturn truncate(`Subagent · ${tasks.length} tasks: ${labels.join(\", \")}`, 100);\n}\n\nexport async function resolveSubagentTask(\n\ttask: SubagentTask,\n\tparentCwd: string,\n\tparent: ParentModelContext,\n\tconfigAgentDir = getAgentDir(),\n\tpreloadedConfig?: SubagentConfigFile,\n): Promise<ResolvedSubagentTask> {\n\tconst agentName = task.agent ?? DEFAULT_AGENT_NAME;\n\tconst agent = AGENT_PROFILES.find((candidate) => candidate.name === agentName);\n\tif (!agent) {\n\t\tconst available = AGENT_PROFILES.map((candidate) => candidate.name).join(\", \") || \"none\";\n\t\tthrow new Error(`Unknown agent \"${agentName}\". Available agents: ${available}.`);\n\t}\n\tconst config = preloadedConfig ?? (await loadSubagentConfig(configAgentDir));\n\tconst override = config.profiles[agent.name];\n\tconst model = resolveModel(override, parent);\n\tconst { cwd, insideParent } = resolveTaskCwd(parentCwd, task.cwd ?? undefined);\n\treturn {\n\t\tagent,\n\t\tdescription: taskLabel(task),\n\t\tprompt: task.prompt,\n\t\tcwd,\n\t\tinsideParent,\n\t\tmodel,\n\t\tthinking: resolveThinking(override, parent, model),\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,EAAE,KAAK,kBAAkB,EAAuB,MAAM,cAAc,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,aAAa,CAAC;AAahE,OAAO,KAAK,EAAwB,eAAe,EAAE,uBAAuB,EAAiB,MAAM,YAAY,CAAC;AAUhH,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,SAAkB;IAInC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAsBlD,OAAO,CAAC,OAAO;CAYf;AAED,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChF,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC;IAC3D,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAmKD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA0DhH;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,EAAE,KAAK,kBAAkB,EAAuB,MAAM,cAAc,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,aAAa,CAAC;AAahE,OAAO,KAAK,EAAwB,eAAe,EAAE,uBAAuB,EAAiB,MAAM,YAAY,CAAC;AAUhH,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,SAAkB;IAInC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAsBlD,OAAO,CAAC,OAAO;CAYf;AAED,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChF,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC;IAC3D,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAsKD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA0DhH;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -145,7 +145,10 @@ async function runWithGate(task, scope, options, dispatch, getState, onProgress)
145
145
  dispatch,
146
146
  modelRuntime: options.modelRuntime,
147
147
  agentDir: options.agentDir,
148
- projectTrusted: options.projectTrusted,
148
+ // Trust follows the directory, not the parent session: a task that
149
+ // works outside the parent must never load that directory's project
150
+ // settings under the parent's trust conclusion.
151
+ projectTrusted: task.insideParent ? options.projectTrusted : false,
149
152
  onProgress,
150
153
  });
151
154
  const state = getState();
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/extensions/subagent/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRetryableAssistantError } from \"@earendil-works/pi-ai/compat\";\nimport type { BackgroundProjection } from \"../../core/background/types.ts\";\nimport type { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { emptyUsage, mergeUsage, toNestedUsage } from \"./activity.ts\";\nimport { boundSubagentDetails, resultContent } from \"./budget.ts\";\nimport { abortableSleep, createRunCancellation, type RunCancellation } from \"./cancellation.ts\";\nimport { MAX_CONCURRENCY, MAX_TASKS, TASK_RETRY_BASE_DELAY_MS, TASK_RETRY_LIMIT } from \"./constants.ts\";\nimport { type ParentModelContext, resolveSubagentTask } from \"./resolve.ts\";\nimport type { SubagentParams, SubagentTask } from \"./schema.ts\";\nimport { runSdkTask } from \"./sdk-runner.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport {\n\tcreateRunState,\n\treduceRun,\n\ttype SubagentRunEvent,\n\ttype SubagentRunState,\n\tstatusOf,\n\tstatusSummary,\n\ttoRunDetails,\n\tversionSum,\n} from \"./state.ts\";\nimport type { ResolvedSubagentTask, SubagentDetails, SubagentExecutionResult, SubagentUsage } from \"./types.ts\";\n\ninterface Waiter {\n\tresolve: (release: () => void) => void;\n\treject: (error: Error) => void;\n\tsignal?: AbortSignal;\n\tabortListener?: () => void;\n\tsettled: boolean;\n}\n\nexport class ConcurrencyGate {\n\tprivate active = 0;\n\tprivate readonly waiters: Waiter[] = [];\n\tprivate readonly limit: number;\n\n\tconstructor(limit = MAX_CONCURRENCY) {\n\t\tthis.limit = limit;\n\t}\n\n\tacquire(signal?: AbortSignal): Promise<() => void> {\n\t\tif (signal?.aborted) return Promise.reject(new Error(\"Subagent was aborted while queued.\"));\n\t\tif (this.active < this.limit) {\n\t\t\tthis.active++;\n\t\t\treturn Promise.resolve(() => this.release());\n\t\t}\n\t\treturn new Promise<() => void>((resolve, reject) => {\n\t\t\tconst waiter: Waiter = { resolve, reject, signal, settled: false };\n\t\t\tif (signal) {\n\t\t\t\twaiter.abortListener = () => {\n\t\t\t\t\tif (waiter.settled) return;\n\t\t\t\t\twaiter.settled = true;\n\t\t\t\t\tconst index = this.waiters.indexOf(waiter);\n\t\t\t\t\tif (index >= 0) this.waiters.splice(index, 1);\n\t\t\t\t\treject(new Error(\"Subagent was aborted while queued.\"));\n\t\t\t\t};\n\t\t\t\tsignal.addEventListener(\"abort\", waiter.abortListener, { once: true });\n\t\t\t}\n\t\t\tthis.waiters.push(waiter);\n\t\t});\n\t}\n\n\tprivate release(): void {\n\t\tthis.active = Math.max(0, this.active - 1);\n\t\twhile (this.waiters.length > 0) {\n\t\t\tconst waiter = this.waiters.shift()!;\n\t\t\tif (waiter.settled || waiter.signal?.aborted) continue;\n\t\t\twaiter.settled = true;\n\t\t\twaiter.signal?.removeEventListener(\"abort\", waiter.abortListener!);\n\t\t\tthis.active++;\n\t\t\twaiter.resolve(() => this.release());\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nexport interface SubagentInvocationOptions {\n\tparams: SubagentParams;\n\tparentCwd: string;\n\tparent: ParentModelContext;\n\tmodelRuntime: ModelRuntime;\n\tagentDir: string;\n\tprojectTrusted: boolean;\n\tsignal?: AbortSignal;\n\tgate: ConcurrencyGate;\n\tonUpdate?: (details: SubagentDetails, projection: BackgroundProjection) => void;\n\t/** Whole-batch preflight and initial publication finished; no worker has started. */\n\tonAccepted?: () => void;\n\tonConfigWarning?: (message: string) => void;\n\tregisterAbort?: (abort: () => Promise<void>) => () => void;\n\t/** Test hook: overrides the task-retry backoff base delay. */\n\ttaskRetryBaseDelayMs?: number;\n\t/** Tool-call id of the invoking call; prefixes per-run ids for readability. */\n\tbatchId?: string;\n}\n\nfunction aggregateUsage(runs: readonly SubagentRunState[]): SubagentUsage {\n\tconst usage = emptyUsage();\n\tfor (const run of runs) mergeUsage(usage, run.usage);\n\treturn usage;\n}\n\n/** Capture report provenance before the tool-details budget can discard it. */\nfunction backgroundProjection(\n\truns: readonly SubagentRunState[],\n\ttasks: readonly ResolvedSubagentTask[],\n\tdetails: SubagentDetails,\n): BackgroundProjection {\n\treturn {\n\t\ttext: statusSummary(details),\n\t\tworkers: runs.map((run, index) => ({\n\t\t\tid: run.id,\n\t\t\tlabel: run.agent,\n\t\t\tprofile: run.agent,\n\t\t\tdescription: run.description,\n\t\t\tstatus: run.status,\n\t\t\tprompt: tasks[index]?.prompt ?? run.description,\n\t\t\tactivity:\n\t\t\t\trun.currentActivity ??\n\t\t\t\trun.activities\n\t\t\t\t\t.slice(-3)\n\t\t\t\t\t.map((item) => item.summary)\n\t\t\t\t\t.join(\"\\n\"),\n\t\t\treport: { text: run.report, truncated: run.reportTruncated },\n\t\t\terror: run.error,\n\t\t\tmodel: `${run.model} · ${run.thinking}`,\n\t\t\tusage: `${run.usage.totalTokens} tokens · $${run.usage.cost.toFixed(4)} · ${run.usage.toolUses} tool calls`,\n\t\t})),\n\t};\n}\n\nfunction emitDetails(\n\truns: SubagentRunState[],\n\ttasks: readonly ResolvedSubagentTask[],\n\tstartedAt: number,\n\tonUpdate: SubagentInvocationOptions[\"onUpdate\"],\n): SubagentDetails {\n\tconst details: SubagentDetails = {\n\t\tstatus: statusOf(runs),\n\t\truns: runs.map(toRunDetails),\n\t\tstartedAt,\n\t\tusage: aggregateUsage(runs),\n\t};\n\tonUpdate?.(boundSubagentDetails(details), backgroundProjection(runs, tasks, details));\n\treturn details;\n}\n\nfunction validateTaskCount(tasks: readonly SubagentTask[]): void {\n\tif (tasks.length === 0) throw new Error(\"Subagent task list must not be empty.\");\n\tif (tasks.length > MAX_TASKS) throw new Error(`Subagent task list is limited to ${MAX_TASKS} tasks.`);\n}\n\nasync function resolveTasks(\n\ttasks: readonly SubagentTask[],\n\toptions: SubagentInvocationOptions,\n): Promise<ResolvedSubagentTask[]> {\n\tvalidateTaskCount(tasks);\n\t// One config load and one resolution pass for the whole batch: every\n\t// task/profile/cwd/model is settled before any run is created or any\n\t// worker starts, so a failing task cannot strand half-initialized runs.\n\tconst config = await loadSubagentConfig(options.agentDir, options.onConfigWarning);\n\treturn Promise.all(\n\t\ttasks.map((task, index) =>\n\t\t\tresolveSubagentTask(task, options.parentCwd, options.parent, options.agentDir, config).catch((error) => {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(`Subagent task tasks[${index}] failed to resolve: ${message}`);\n\t\t\t}),\n\t\t),\n\t);\n}\n\n// Reuses pi-ai's provider-error classification by wrapping the stored error\n// text in the assistant-message shape it inspects.\nfunction isRetryableRunError(error: string | undefined): boolean {\n\tif (!error) return false;\n\tconst probe = { role: \"assistant\", content: [], stopReason: \"error\", errorMessage: error };\n\treturn isRetryableAssistantError(probe as unknown as Parameters<typeof isRetryableAssistantError>[0]);\n}\n\n// Session-level auto-retry already covers errors that happen inside the agent\n// loop; this catches the paths that bypass it (preflight throws such as auth\n// checks). Only a run that produced nothing is retried, so partial work is\n// never discarded.\nfunction shouldRetryTask(run: SubagentRunState): boolean {\n\treturn (\n\t\trun.status === \"failed\" && run.usage.turns === 0 && run.usage.toolUses === 0 && isRetryableRunError(run.error)\n\t);\n}\n\n// The gate slot is held only while a worker session actually runs. Backoff\n// waits outside the slot (release, sleep, reacquire) so a retrying task\n// cannot starve queued tasks, and aborting during the wait settles the run\n// without occupying the gate.\nasync function runWithGate(\n\ttask: ResolvedSubagentTask,\n\tscope: RunCancellation,\n\toptions: SubagentInvocationOptions,\n\tdispatch: (event: SubagentRunEvent) => void,\n\tgetState: () => SubagentRunState,\n\tonProgress: () => void,\n): Promise<void> {\n\tconst baseDelayMs = options.taskRetryBaseDelayMs ?? TASK_RETRY_BASE_DELAY_MS;\n\tfor (let attempt = 0; ; attempt++) {\n\t\tdispatch({ type: \"retry_started\" });\n\t\tlet release: (() => void) | undefined;\n\t\tlet delayMs = 0;\n\t\ttry {\n\t\t\trelease = await options.gate.acquire(scope.signal);\n\t\t\tif (scope.signal.aborted) {\n\t\t\t\tdispatch({ type: \"abort_while_queued\", endedAt: Date.now() });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdispatch({ type: \"slot_acquired\", startedAt: Date.now() });\n\t\t\tawait runSdkTask({\n\t\t\t\ttask,\n\t\t\t\tscope,\n\t\t\t\tdispatch,\n\t\t\t\tmodelRuntime: options.modelRuntime,\n\t\t\t\tagentDir: options.agentDir,\n\t\t\t\tprojectTrusted: options.projectTrusted,\n\t\t\t\tonProgress,\n\t\t\t});\n\t\t\tconst state = getState();\n\t\t\tif (attempt >= TASK_RETRY_LIMIT || scope.signal.aborted || !shouldRetryTask(state)) return;\n\t\t\tdelayMs = baseDelayMs * 2 ** attempt;\n\t\t\tdispatch({\n\t\t\t\ttype: \"retry_scheduled\",\n\t\t\t\tattempt: attempt + 1,\n\t\t\t\tmaxAttempts: TASK_RETRY_LIMIT,\n\t\t\t\tdeadline: Date.now() + delayMs,\n\t\t\t\terror: state.error,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (scope.signal.aborted) {\n\t\t\t\tdispatch({ type: \"abort_while_queued\", endedAt: Date.now() });\n\t\t\t} else {\n\t\t\t\tdispatch({\n\t\t\t\t\ttype: \"settle\",\n\t\t\t\t\tverdict: \"failed\",\n\t\t\t\t\treport: \"\",\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tendedAt: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t\ttry {\n\t\t\tawait abortableSleep(delayMs, scope);\n\t\t} catch {\n\t\t\tdispatch({ type: \"abort_while_retrying\", endedAt: Date.now() });\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nexport async function runSubagentInvocation(options: SubagentInvocationOptions): Promise<SubagentExecutionResult> {\n\t// Register one invocation-level scope before the first await so shutdown can\n\t// never snapshot the active set between preflight and worker creation.\n\tconst invocationScope = createRunCancellation(options.signal);\n\tconst unregisterInvocation = options.registerAbort?.(invocationScope.abort);\n\ttry {\n\t\t// Defensive count check: the schema already enforces 1..MAX_TASKS, but\n\t\t// strict providers can bypass minItems, so validate before any work.\n\t\tconst tasks = options.params.tasks ?? [];\n\t\tconst resolved = await resolveTasks(tasks, options);\n\t\tconst runs = resolved.map((task, index) => createRunState(task, index, options.batchId, options.parentCwd));\n\t\tconst startedAt = Date.now();\n\t\tconst scopes = runs.map(() => createRunCancellation(invocationScope.signal));\n\t\tconst unregisterChildren = invocationScope.onAbort(async () => {\n\t\t\tawait Promise.allSettled(scopes.map((scope) => scope.abort()));\n\t\t});\n\t\tlet lastVersion = versionSum(runs);\n\t\tconst progress = (): void => {\n\t\t\tconst version = versionSum(runs);\n\t\t\tif (version === lastVersion) return;\n\t\t\tlastVersion = version;\n\t\t\temitDetails(runs, resolved, startedAt, options.onUpdate);\n\t\t};\n\t\tconst dispatch = (index: number, event: SubagentRunEvent): void => {\n\t\t\truns[index] = reduceRun(runs[index]!, event);\n\t\t\tprogress();\n\t\t};\n\t\tlet latestDetails = emitDetails(runs, resolved, startedAt, options.onUpdate);\n\n\t\t// Every task runs concurrently through the shared gate; Promise.all keeps\n\t\t// result order identical to input order. There is no single-task branch:\n\t\t// one task is just a batch of one.\n\t\ttry {\n\t\t\tif (!invocationScope.aborted) options.onAccepted?.();\n\t\t\tawait Promise.all(\n\t\t\t\tresolved.map((task, index) => {\n\t\t\t\t\tconst dispatchToRun = (event: SubagentRunEvent): void => dispatch(index, event);\n\t\t\t\t\treturn runWithGate(task, scopes[index]!, options, dispatchToRun, () => runs[index]!, progress);\n\t\t\t\t}),\n\t\t\t);\n\t\t} finally {\n\t\t\tunregisterChildren();\n\t\t\tfor (const scope of scopes) scope.dispose();\n\t\t}\n\t\tlatestDetails = emitDetails(runs, resolved, startedAt, undefined);\n\t\tlatestDetails.endedAt = Date.now();\n\t\t// Error classification lives in the tool_result handler (index.ts), the\n\t\t// only channel that reaches the session transcript and export.\n\t\treturn {\n\t\t\tcontent: resultContent(latestDetails),\n\t\t\tdetails: boundSubagentDetails(latestDetails),\n\t\t\tprojection: backgroundProjection(runs, resolved, latestDetails),\n\t\t\tusage: toNestedUsage(latestDetails.usage),\n\t\t};\n\t} finally {\n\t\tunregisterInvocation?.();\n\t\tinvocationScope.dispose();\n\t}\n}\n\nexport { isSubagentError } from \"./state.ts\";\n"]}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/extensions/subagent/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRetryableAssistantError } from \"@earendil-works/pi-ai/compat\";\nimport type { BackgroundProjection } from \"../../core/background/types.ts\";\nimport type { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { emptyUsage, mergeUsage, toNestedUsage } from \"./activity.ts\";\nimport { boundSubagentDetails, resultContent } from \"./budget.ts\";\nimport { abortableSleep, createRunCancellation, type RunCancellation } from \"./cancellation.ts\";\nimport { MAX_CONCURRENCY, MAX_TASKS, TASK_RETRY_BASE_DELAY_MS, TASK_RETRY_LIMIT } from \"./constants.ts\";\nimport { type ParentModelContext, resolveSubagentTask } from \"./resolve.ts\";\nimport type { SubagentParams, SubagentTask } from \"./schema.ts\";\nimport { runSdkTask } from \"./sdk-runner.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport {\n\tcreateRunState,\n\treduceRun,\n\ttype SubagentRunEvent,\n\ttype SubagentRunState,\n\tstatusOf,\n\tstatusSummary,\n\ttoRunDetails,\n\tversionSum,\n} from \"./state.ts\";\nimport type { ResolvedSubagentTask, SubagentDetails, SubagentExecutionResult, SubagentUsage } from \"./types.ts\";\n\ninterface Waiter {\n\tresolve: (release: () => void) => void;\n\treject: (error: Error) => void;\n\tsignal?: AbortSignal;\n\tabortListener?: () => void;\n\tsettled: boolean;\n}\n\nexport class ConcurrencyGate {\n\tprivate active = 0;\n\tprivate readonly waiters: Waiter[] = [];\n\tprivate readonly limit: number;\n\n\tconstructor(limit = MAX_CONCURRENCY) {\n\t\tthis.limit = limit;\n\t}\n\n\tacquire(signal?: AbortSignal): Promise<() => void> {\n\t\tif (signal?.aborted) return Promise.reject(new Error(\"Subagent was aborted while queued.\"));\n\t\tif (this.active < this.limit) {\n\t\t\tthis.active++;\n\t\t\treturn Promise.resolve(() => this.release());\n\t\t}\n\t\treturn new Promise<() => void>((resolve, reject) => {\n\t\t\tconst waiter: Waiter = { resolve, reject, signal, settled: false };\n\t\t\tif (signal) {\n\t\t\t\twaiter.abortListener = () => {\n\t\t\t\t\tif (waiter.settled) return;\n\t\t\t\t\twaiter.settled = true;\n\t\t\t\t\tconst index = this.waiters.indexOf(waiter);\n\t\t\t\t\tif (index >= 0) this.waiters.splice(index, 1);\n\t\t\t\t\treject(new Error(\"Subagent was aborted while queued.\"));\n\t\t\t\t};\n\t\t\t\tsignal.addEventListener(\"abort\", waiter.abortListener, { once: true });\n\t\t\t}\n\t\t\tthis.waiters.push(waiter);\n\t\t});\n\t}\n\n\tprivate release(): void {\n\t\tthis.active = Math.max(0, this.active - 1);\n\t\twhile (this.waiters.length > 0) {\n\t\t\tconst waiter = this.waiters.shift()!;\n\t\t\tif (waiter.settled || waiter.signal?.aborted) continue;\n\t\t\twaiter.settled = true;\n\t\t\twaiter.signal?.removeEventListener(\"abort\", waiter.abortListener!);\n\t\t\tthis.active++;\n\t\t\twaiter.resolve(() => this.release());\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nexport interface SubagentInvocationOptions {\n\tparams: SubagentParams;\n\tparentCwd: string;\n\tparent: ParentModelContext;\n\tmodelRuntime: ModelRuntime;\n\tagentDir: string;\n\tprojectTrusted: boolean;\n\tsignal?: AbortSignal;\n\tgate: ConcurrencyGate;\n\tonUpdate?: (details: SubagentDetails, projection: BackgroundProjection) => void;\n\t/** Whole-batch preflight and initial publication finished; no worker has started. */\n\tonAccepted?: () => void;\n\tonConfigWarning?: (message: string) => void;\n\tregisterAbort?: (abort: () => Promise<void>) => () => void;\n\t/** Test hook: overrides the task-retry backoff base delay. */\n\ttaskRetryBaseDelayMs?: number;\n\t/** Tool-call id of the invoking call; prefixes per-run ids for readability. */\n\tbatchId?: string;\n}\n\nfunction aggregateUsage(runs: readonly SubagentRunState[]): SubagentUsage {\n\tconst usage = emptyUsage();\n\tfor (const run of runs) mergeUsage(usage, run.usage);\n\treturn usage;\n}\n\n/** Capture report provenance before the tool-details budget can discard it. */\nfunction backgroundProjection(\n\truns: readonly SubagentRunState[],\n\ttasks: readonly ResolvedSubagentTask[],\n\tdetails: SubagentDetails,\n): BackgroundProjection {\n\treturn {\n\t\ttext: statusSummary(details),\n\t\tworkers: runs.map((run, index) => ({\n\t\t\tid: run.id,\n\t\t\tlabel: run.agent,\n\t\t\tprofile: run.agent,\n\t\t\tdescription: run.description,\n\t\t\tstatus: run.status,\n\t\t\tprompt: tasks[index]?.prompt ?? run.description,\n\t\t\tactivity:\n\t\t\t\trun.currentActivity ??\n\t\t\t\trun.activities\n\t\t\t\t\t.slice(-3)\n\t\t\t\t\t.map((item) => item.summary)\n\t\t\t\t\t.join(\"\\n\"),\n\t\t\treport: { text: run.report, truncated: run.reportTruncated },\n\t\t\terror: run.error,\n\t\t\tmodel: `${run.model} · ${run.thinking}`,\n\t\t\tusage: `${run.usage.totalTokens} tokens · $${run.usage.cost.toFixed(4)} · ${run.usage.toolUses} tool calls`,\n\t\t})),\n\t};\n}\n\nfunction emitDetails(\n\truns: SubagentRunState[],\n\ttasks: readonly ResolvedSubagentTask[],\n\tstartedAt: number,\n\tonUpdate: SubagentInvocationOptions[\"onUpdate\"],\n): SubagentDetails {\n\tconst details: SubagentDetails = {\n\t\tstatus: statusOf(runs),\n\t\truns: runs.map(toRunDetails),\n\t\tstartedAt,\n\t\tusage: aggregateUsage(runs),\n\t};\n\tonUpdate?.(boundSubagentDetails(details), backgroundProjection(runs, tasks, details));\n\treturn details;\n}\n\nfunction validateTaskCount(tasks: readonly SubagentTask[]): void {\n\tif (tasks.length === 0) throw new Error(\"Subagent task list must not be empty.\");\n\tif (tasks.length > MAX_TASKS) throw new Error(`Subagent task list is limited to ${MAX_TASKS} tasks.`);\n}\n\nasync function resolveTasks(\n\ttasks: readonly SubagentTask[],\n\toptions: SubagentInvocationOptions,\n): Promise<ResolvedSubagentTask[]> {\n\tvalidateTaskCount(tasks);\n\t// One config load and one resolution pass for the whole batch: every\n\t// task/profile/cwd/model is settled before any run is created or any\n\t// worker starts, so a failing task cannot strand half-initialized runs.\n\tconst config = await loadSubagentConfig(options.agentDir, options.onConfigWarning);\n\treturn Promise.all(\n\t\ttasks.map((task, index) =>\n\t\t\tresolveSubagentTask(task, options.parentCwd, options.parent, options.agentDir, config).catch((error) => {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(`Subagent task tasks[${index}] failed to resolve: ${message}`);\n\t\t\t}),\n\t\t),\n\t);\n}\n\n// Reuses pi-ai's provider-error classification by wrapping the stored error\n// text in the assistant-message shape it inspects.\nfunction isRetryableRunError(error: string | undefined): boolean {\n\tif (!error) return false;\n\tconst probe = { role: \"assistant\", content: [], stopReason: \"error\", errorMessage: error };\n\treturn isRetryableAssistantError(probe as unknown as Parameters<typeof isRetryableAssistantError>[0]);\n}\n\n// Session-level auto-retry already covers errors that happen inside the agent\n// loop; this catches the paths that bypass it (preflight throws such as auth\n// checks). Only a run that produced nothing is retried, so partial work is\n// never discarded.\nfunction shouldRetryTask(run: SubagentRunState): boolean {\n\treturn (\n\t\trun.status === \"failed\" && run.usage.turns === 0 && run.usage.toolUses === 0 && isRetryableRunError(run.error)\n\t);\n}\n\n// The gate slot is held only while a worker session actually runs. Backoff\n// waits outside the slot (release, sleep, reacquire) so a retrying task\n// cannot starve queued tasks, and aborting during the wait settles the run\n// without occupying the gate.\nasync function runWithGate(\n\ttask: ResolvedSubagentTask,\n\tscope: RunCancellation,\n\toptions: SubagentInvocationOptions,\n\tdispatch: (event: SubagentRunEvent) => void,\n\tgetState: () => SubagentRunState,\n\tonProgress: () => void,\n): Promise<void> {\n\tconst baseDelayMs = options.taskRetryBaseDelayMs ?? TASK_RETRY_BASE_DELAY_MS;\n\tfor (let attempt = 0; ; attempt++) {\n\t\tdispatch({ type: \"retry_started\" });\n\t\tlet release: (() => void) | undefined;\n\t\tlet delayMs = 0;\n\t\ttry {\n\t\t\trelease = await options.gate.acquire(scope.signal);\n\t\t\tif (scope.signal.aborted) {\n\t\t\t\tdispatch({ type: \"abort_while_queued\", endedAt: Date.now() });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdispatch({ type: \"slot_acquired\", startedAt: Date.now() });\n\t\t\tawait runSdkTask({\n\t\t\t\ttask,\n\t\t\t\tscope,\n\t\t\t\tdispatch,\n\t\t\t\tmodelRuntime: options.modelRuntime,\n\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t// Trust follows the directory, not the parent session: a task that\n\t\t\t\t// works outside the parent must never load that directory's project\n\t\t\t\t// settings under the parent's trust conclusion.\n\t\t\t\tprojectTrusted: task.insideParent ? options.projectTrusted : false,\n\t\t\t\tonProgress,\n\t\t\t});\n\t\t\tconst state = getState();\n\t\t\tif (attempt >= TASK_RETRY_LIMIT || scope.signal.aborted || !shouldRetryTask(state)) return;\n\t\t\tdelayMs = baseDelayMs * 2 ** attempt;\n\t\t\tdispatch({\n\t\t\t\ttype: \"retry_scheduled\",\n\t\t\t\tattempt: attempt + 1,\n\t\t\t\tmaxAttempts: TASK_RETRY_LIMIT,\n\t\t\t\tdeadline: Date.now() + delayMs,\n\t\t\t\terror: state.error,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (scope.signal.aborted) {\n\t\t\t\tdispatch({ type: \"abort_while_queued\", endedAt: Date.now() });\n\t\t\t} else {\n\t\t\t\tdispatch({\n\t\t\t\t\ttype: \"settle\",\n\t\t\t\t\tverdict: \"failed\",\n\t\t\t\t\treport: \"\",\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tendedAt: Date.now(),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t\ttry {\n\t\t\tawait abortableSleep(delayMs, scope);\n\t\t} catch {\n\t\t\tdispatch({ type: \"abort_while_retrying\", endedAt: Date.now() });\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nexport async function runSubagentInvocation(options: SubagentInvocationOptions): Promise<SubagentExecutionResult> {\n\t// Register one invocation-level scope before the first await so shutdown can\n\t// never snapshot the active set between preflight and worker creation.\n\tconst invocationScope = createRunCancellation(options.signal);\n\tconst unregisterInvocation = options.registerAbort?.(invocationScope.abort);\n\ttry {\n\t\t// Defensive count check: the schema already enforces 1..MAX_TASKS, but\n\t\t// strict providers can bypass minItems, so validate before any work.\n\t\tconst tasks = options.params.tasks ?? [];\n\t\tconst resolved = await resolveTasks(tasks, options);\n\t\tconst runs = resolved.map((task, index) => createRunState(task, index, options.batchId, options.parentCwd));\n\t\tconst startedAt = Date.now();\n\t\tconst scopes = runs.map(() => createRunCancellation(invocationScope.signal));\n\t\tconst unregisterChildren = invocationScope.onAbort(async () => {\n\t\t\tawait Promise.allSettled(scopes.map((scope) => scope.abort()));\n\t\t});\n\t\tlet lastVersion = versionSum(runs);\n\t\tconst progress = (): void => {\n\t\t\tconst version = versionSum(runs);\n\t\t\tif (version === lastVersion) return;\n\t\t\tlastVersion = version;\n\t\t\temitDetails(runs, resolved, startedAt, options.onUpdate);\n\t\t};\n\t\tconst dispatch = (index: number, event: SubagentRunEvent): void => {\n\t\t\truns[index] = reduceRun(runs[index]!, event);\n\t\t\tprogress();\n\t\t};\n\t\tlet latestDetails = emitDetails(runs, resolved, startedAt, options.onUpdate);\n\n\t\t// Every task runs concurrently through the shared gate; Promise.all keeps\n\t\t// result order identical to input order. There is no single-task branch:\n\t\t// one task is just a batch of one.\n\t\ttry {\n\t\t\tif (!invocationScope.aborted) options.onAccepted?.();\n\t\t\tawait Promise.all(\n\t\t\t\tresolved.map((task, index) => {\n\t\t\t\t\tconst dispatchToRun = (event: SubagentRunEvent): void => dispatch(index, event);\n\t\t\t\t\treturn runWithGate(task, scopes[index]!, options, dispatchToRun, () => runs[index]!, progress);\n\t\t\t\t}),\n\t\t\t);\n\t\t} finally {\n\t\t\tunregisterChildren();\n\t\t\tfor (const scope of scopes) scope.dispose();\n\t\t}\n\t\tlatestDetails = emitDetails(runs, resolved, startedAt, undefined);\n\t\tlatestDetails.endedAt = Date.now();\n\t\t// Error classification lives in the tool_result handler (index.ts), the\n\t\t// only channel that reaches the session transcript and export.\n\t\treturn {\n\t\t\tcontent: resultContent(latestDetails),\n\t\t\tdetails: boundSubagentDetails(latestDetails),\n\t\t\tprojection: backgroundProjection(runs, resolved, latestDetails),\n\t\t\tusage: toNestedUsage(latestDetails.usage),\n\t\t};\n\t} finally {\n\t\tunregisterInvocation?.();\n\t\tinvocationScope.dispose();\n\t}\n}\n\nexport { isSubagentError } from \"./state.ts\";\n"]}
@@ -16,4 +16,13 @@ export declare const SubagentParamsSchema: Type.TObject<{
16
16
  }>;
17
17
  export type SubagentParams = Static<typeof SubagentParamsSchema>;
18
18
  export type SubagentTask = Static<typeof TaskSchema>;
19
+ /**
20
+ * Unambiguous argument normalizations applied before schema validation:
21
+ * a single task written at the root ({agent, prompt, ...} with no tasks)
22
+ * wraps into tasks[0] with background left at the root, and root-level null
23
+ * fields the schema does not declare are dropped as strict-mode leftovers.
24
+ * Non-null unknown fields are kept on purpose: validation names them instead
25
+ * of silently discarding what may be briefing content.
26
+ */
27
+ export declare function normalizeSubagentArguments(raw: unknown): SubagentParams;
19
28
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAgB,IAAI,EAAE,MAAM,SAAS,CAAC;AAW1D,eAAO,MAAM,UAAU;;;;;EAqBtB,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;EAahC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAgB,IAAI,EAAE,MAAM,SAAS,CAAC;AAW1D,eAAO,MAAM,UAAU;;;;;EAqBtB,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;EAahC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAKrD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAevE"}