@bastani/atomic 0.8.31-alpha.3 → 0.8.31-alpha.5

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 (67) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +1 -1
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/mcp/CHANGELOG.md +5 -0
  6. package/dist/builtin/mcp/direct-tools.ts +4 -2
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/mcp/proxy-modes.ts +4 -2
  9. package/dist/builtin/mcp/utils.ts +25 -0
  10. package/dist/builtin/subagents/package.json +1 -1
  11. package/dist/builtin/web-access/package.json +1 -1
  12. package/dist/builtin/workflows/CHANGELOG.md +9 -0
  13. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +89 -0
  14. package/dist/builtin/workflows/builtin/ralph.ts +16 -51
  15. package/dist/builtin/workflows/package.json +1 -1
  16. package/dist/builtin/workflows/src/extension/dispatcher.ts +3 -0
  17. package/dist/builtin/workflows/src/extension/index.ts +15 -0
  18. package/dist/builtin/workflows/src/extension/runtime.ts +7 -0
  19. package/dist/builtin/workflows/src/runs/foreground/executor.ts +103 -7
  20. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +133 -10
  21. package/dist/builtin/workflows/src/shared/persistence-restore.ts +2 -0
  22. package/dist/core/agent-session.d.ts +25 -0
  23. package/dist/core/agent-session.d.ts.map +1 -1
  24. package/dist/core/agent-session.js +124 -8
  25. package/dist/core/agent-session.js.map +1 -1
  26. package/dist/core/auth-guidance.d.ts +12 -0
  27. package/dist/core/auth-guidance.d.ts.map +1 -1
  28. package/dist/core/auth-guidance.js +24 -0
  29. package/dist/core/auth-guidance.js.map +1 -1
  30. package/dist/core/auth-storage.d.ts +42 -0
  31. package/dist/core/auth-storage.d.ts.map +1 -1
  32. package/dist/core/auth-storage.js +71 -10
  33. package/dist/core/auth-storage.js.map +1 -1
  34. package/dist/core/copilot-gemini-payload-sanitizer.d.ts +72 -0
  35. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -0
  36. package/dist/core/copilot-gemini-payload-sanitizer.js +296 -0
  37. package/dist/core/copilot-gemini-payload-sanitizer.js.map +1 -0
  38. package/dist/core/copilot-gemini-reasoning.d.ts +118 -0
  39. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -0
  40. package/dist/core/copilot-gemini-reasoning.js +260 -0
  41. package/dist/core/copilot-gemini-reasoning.js.map +1 -0
  42. package/dist/core/copilot-gemini-tool-arguments.d.ts +42 -0
  43. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -0
  44. package/dist/core/copilot-gemini-tool-arguments.js +179 -0
  45. package/dist/core/copilot-gemini-tool-arguments.js.map +1 -0
  46. package/dist/core/flattened-tool-arguments.d.ts +41 -0
  47. package/dist/core/flattened-tool-arguments.d.ts.map +1 -0
  48. package/dist/core/flattened-tool-arguments.js +136 -0
  49. package/dist/core/flattened-tool-arguments.js.map +1 -0
  50. package/dist/core/http-dispatcher.d.ts.map +1 -1
  51. package/dist/core/http-dispatcher.js +5 -0
  52. package/dist/core/http-dispatcher.js.map +1 -1
  53. package/dist/core/sdk.d.ts.map +1 -1
  54. package/dist/core/sdk.js +38 -8
  55. package/dist/core/sdk.js.map +1 -1
  56. package/dist/core/session-manager.d.ts +1 -1
  57. package/dist/core/session-manager.d.ts.map +1 -1
  58. package/dist/core/session-manager.js.map +1 -1
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -0
  62. package/dist/index.js.map +1 -1
  63. package/docs/providers.md +1 -0
  64. package/docs/sessions.md +4 -0
  65. package/docs/workflows.md +7 -1
  66. package/examples/extensions/gondolin/package-lock.json +183 -183
  67. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattened-tool-arguments.d.ts","sourceRoot":"","sources":["../../src/core/flattened-tool-arguments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAmCrF;AA0CD;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB","sourcesContent":["/**\n * Canonical reconstruction of flattened tool-call arguments.\n *\n * Some upstream providers — notably GitHub Copilot Gemini models proxied through\n * Google's GenAI API — serialize array/object function-call arguments as\n * flattened, indexed keys on the wire. For example a tool called with\n * `{ keywords: [\"a\", \"b\"] }` arrives as `{ \"keywords[0]\": \"a\", \"keywords[1]\": \"b\" }`,\n * and `{ files: [{ path }] }` as `{ \"files[0].path\": \"...\" }`.\n *\n * This module is the single source of truth for turning those flattened keys\n * back into nested arrays/objects. Both the host runtime's per-tool\n * normalization (gated to Copilot Gemini, schema-aware) and the MCP `callTool`\n * boundary (provider-agnostic, bracket self-gating) delegate here so the two\n * paths cannot drift — in particular so the prototype-pollution guard lives in\n * exactly one place.\n *\n * Security: argument keys cross a trust boundary (model/provider wire → tool /\n * MCP server validation). A key path that walks through `__proto__`,\n * `constructor`, or `prototype` could otherwise reach `Object.prototype` and\n * mutate it process-wide. Any key whose path contains such a segment — at any\n * position, including the final segment and a literal plain key — is dropped.\n */\n\n/** Key segments that must never be written or traversed (prototype pollution). */\nconst UNSAFE_KEY_SEGMENTS: ReadonlySet<string> = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction isUnsafeSegment(segment: string | number): boolean {\n return typeof segment === \"string\" && UNSAFE_KEY_SEGMENTS.has(segment);\n}\n\n/**\n * Parse a flattened key such as `a.b[0].c` into path segments\n * `[\"a\", \"b\", 0, \"c\"]`. Returns `undefined` for a plain key with no `.`/`[`, or\n * for a malformed bracket expression (left untouched by the caller).\n */\nexport function parseFlattenedKeyPath(key: string): Array<string | number> | undefined {\n if (!/[.[]/.test(key)) return undefined;\n const segments: Array<string | number> = [];\n let current = \"\";\n let index = 0;\n const flush = () => {\n if (current !== \"\") {\n segments.push(current);\n current = \"\";\n }\n };\n while (index < key.length) {\n const char = key[index];\n if (char === \".\") {\n flush();\n index += 1;\n } else if (char === \"[\") {\n flush();\n const end = key.indexOf(\"]\", index);\n if (end === -1) return undefined; // malformed — leave key untouched\n const inner = key.slice(index + 1, end);\n const numeric = Number(inner);\n if (inner.trim() !== \"\" && Number.isInteger(numeric) && numeric >= 0) {\n segments.push(numeric);\n } else {\n segments.push(inner.replace(/^[\"']|[\"']$/g, \"\"));\n }\n index = end + 1;\n } else {\n current += char;\n index += 1;\n }\n }\n flush();\n return segments.length > 0 ? segments : undefined;\n}\n\n/** Assign `value` at the given path inside `root`, creating arrays/objects as needed. */\nfunction assignFlattenedKeyPath(\n root: Record<string | number, unknown>,\n segments: Array<string | number>,\n value: unknown,\n): void {\n let node: Record<string | number, unknown> = root;\n for (let i = 0; i < segments.length - 1; i += 1) {\n const segment = segments[i];\n const nextIsIndex = typeof segments[i + 1] === \"number\";\n const existing = node[segment];\n if (existing === null || existing === undefined || typeof existing !== \"object\") {\n node[segment] = nextIsIndex ? [] : {};\n }\n node = node[segment] as Record<string | number, unknown>;\n }\n node[segments[segments.length - 1]] = value;\n}\n\n/**\n * Remove empty holes from sparse arrays produced by out-of-order indices.\n *\n * Note: this collapses holes rather than preserving them — `name[0]` + `name[2]`\n * (no index 1) becomes a dense 2-element array `[a, c]`, not `[a, <hole>, c]`.\n * That is the intended healing for Gemini's flattened output (which emits\n * contiguous indices in practice); it would, however, silently misalign two\n * arrays that were meant to be index-paired.\n */\nfunction compactSparseArrays(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.filter((entry) => entry !== undefined).map((entry) => compactSparseArrays(entry));\n }\n if (value !== null && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) out[key] = compactSparseArrays(entry);\n return out;\n }\n return value;\n}\n\n/**\n * Reconstruct (unflatten) flattened keys into nested arrays/objects — for\n * example `\"items[0]\"` -> `{ items: [...] }` and `\"parent.child\"` ->\n * `{ parent: { child: ... } }`. `shouldSplit` decides, per key, whether it is a\n * flattened path (true) or an opaque literal key to be preserved (false);\n * callers apply their own gating/schema logic there.\n *\n * Prototype-pollution safe: a key whose parsed path contains `__proto__`,\n * `constructor`, or `prototype` (at any position) is dropped, as is a literal\n * plain key equal to one of those names.\n */\nexport function reconstructFlattenedKeys(\n args: Record<string, unknown>,\n shouldSplit: (key: string) => boolean,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(args)) {\n const segments = shouldSplit(key) ? parseFlattenedKeyPath(key) : undefined;\n if (!segments) {\n // Plain passthrough — but never assign a literal prototype-polluting key.\n if (!UNSAFE_KEY_SEGMENTS.has(key)) result[key] = value;\n continue;\n }\n if (segments.some(isUnsafeSegment)) continue; // drop a polluting path entirely\n assignFlattenedKeyPath(result, segments, value);\n }\n return compactSparseArrays(result) as Record<string, unknown>;\n}\n"]}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Canonical reconstruction of flattened tool-call arguments.
3
+ *
4
+ * Some upstream providers — notably GitHub Copilot Gemini models proxied through
5
+ * Google's GenAI API — serialize array/object function-call arguments as
6
+ * flattened, indexed keys on the wire. For example a tool called with
7
+ * `{ keywords: ["a", "b"] }` arrives as `{ "keywords[0]": "a", "keywords[1]": "b" }`,
8
+ * and `{ files: [{ path }] }` as `{ "files[0].path": "..." }`.
9
+ *
10
+ * This module is the single source of truth for turning those flattened keys
11
+ * back into nested arrays/objects. Both the host runtime's per-tool
12
+ * normalization (gated to Copilot Gemini, schema-aware) and the MCP `callTool`
13
+ * boundary (provider-agnostic, bracket self-gating) delegate here so the two
14
+ * paths cannot drift — in particular so the prototype-pollution guard lives in
15
+ * exactly one place.
16
+ *
17
+ * Security: argument keys cross a trust boundary (model/provider wire → tool /
18
+ * MCP server validation). A key path that walks through `__proto__`,
19
+ * `constructor`, or `prototype` could otherwise reach `Object.prototype` and
20
+ * mutate it process-wide. Any key whose path contains such a segment — at any
21
+ * position, including the final segment and a literal plain key — is dropped.
22
+ */
23
+ /** Key segments that must never be written or traversed (prototype pollution). */
24
+ const UNSAFE_KEY_SEGMENTS = new Set(["__proto__", "constructor", "prototype"]);
25
+ function isUnsafeSegment(segment) {
26
+ return typeof segment === "string" && UNSAFE_KEY_SEGMENTS.has(segment);
27
+ }
28
+ /**
29
+ * Parse a flattened key such as `a.b[0].c` into path segments
30
+ * `["a", "b", 0, "c"]`. Returns `undefined` for a plain key with no `.`/`[`, or
31
+ * for a malformed bracket expression (left untouched by the caller).
32
+ */
33
+ export function parseFlattenedKeyPath(key) {
34
+ if (!/[.[]/.test(key))
35
+ return undefined;
36
+ const segments = [];
37
+ let current = "";
38
+ let index = 0;
39
+ const flush = () => {
40
+ if (current !== "") {
41
+ segments.push(current);
42
+ current = "";
43
+ }
44
+ };
45
+ while (index < key.length) {
46
+ const char = key[index];
47
+ if (char === ".") {
48
+ flush();
49
+ index += 1;
50
+ }
51
+ else if (char === "[") {
52
+ flush();
53
+ const end = key.indexOf("]", index);
54
+ if (end === -1)
55
+ return undefined; // malformed — leave key untouched
56
+ const inner = key.slice(index + 1, end);
57
+ const numeric = Number(inner);
58
+ if (inner.trim() !== "" && Number.isInteger(numeric) && numeric >= 0) {
59
+ segments.push(numeric);
60
+ }
61
+ else {
62
+ segments.push(inner.replace(/^["']|["']$/g, ""));
63
+ }
64
+ index = end + 1;
65
+ }
66
+ else {
67
+ current += char;
68
+ index += 1;
69
+ }
70
+ }
71
+ flush();
72
+ return segments.length > 0 ? segments : undefined;
73
+ }
74
+ /** Assign `value` at the given path inside `root`, creating arrays/objects as needed. */
75
+ function assignFlattenedKeyPath(root, segments, value) {
76
+ let node = root;
77
+ for (let i = 0; i < segments.length - 1; i += 1) {
78
+ const segment = segments[i];
79
+ const nextIsIndex = typeof segments[i + 1] === "number";
80
+ const existing = node[segment];
81
+ if (existing === null || existing === undefined || typeof existing !== "object") {
82
+ node[segment] = nextIsIndex ? [] : {};
83
+ }
84
+ node = node[segment];
85
+ }
86
+ node[segments[segments.length - 1]] = value;
87
+ }
88
+ /**
89
+ * Remove empty holes from sparse arrays produced by out-of-order indices.
90
+ *
91
+ * Note: this collapses holes rather than preserving them — `name[0]` + `name[2]`
92
+ * (no index 1) becomes a dense 2-element array `[a, c]`, not `[a, <hole>, c]`.
93
+ * That is the intended healing for Gemini's flattened output (which emits
94
+ * contiguous indices in practice); it would, however, silently misalign two
95
+ * arrays that were meant to be index-paired.
96
+ */
97
+ function compactSparseArrays(value) {
98
+ if (Array.isArray(value)) {
99
+ return value.filter((entry) => entry !== undefined).map((entry) => compactSparseArrays(entry));
100
+ }
101
+ if (value !== null && typeof value === "object") {
102
+ const out = {};
103
+ for (const [key, entry] of Object.entries(value))
104
+ out[key] = compactSparseArrays(entry);
105
+ return out;
106
+ }
107
+ return value;
108
+ }
109
+ /**
110
+ * Reconstruct (unflatten) flattened keys into nested arrays/objects — for
111
+ * example `"items[0]"` -> `{ items: [...] }` and `"parent.child"` ->
112
+ * `{ parent: { child: ... } }`. `shouldSplit` decides, per key, whether it is a
113
+ * flattened path (true) or an opaque literal key to be preserved (false);
114
+ * callers apply their own gating/schema logic there.
115
+ *
116
+ * Prototype-pollution safe: a key whose parsed path contains `__proto__`,
117
+ * `constructor`, or `prototype` (at any position) is dropped, as is a literal
118
+ * plain key equal to one of those names.
119
+ */
120
+ export function reconstructFlattenedKeys(args, shouldSplit) {
121
+ const result = {};
122
+ for (const [key, value] of Object.entries(args)) {
123
+ const segments = shouldSplit(key) ? parseFlattenedKeyPath(key) : undefined;
124
+ if (!segments) {
125
+ // Plain passthrough — but never assign a literal prototype-polluting key.
126
+ if (!UNSAFE_KEY_SEGMENTS.has(key))
127
+ result[key] = value;
128
+ continue;
129
+ }
130
+ if (segments.some(isUnsafeSegment))
131
+ continue; // drop a polluting path entirely
132
+ assignFlattenedKeyPath(result, segments, value);
133
+ }
134
+ return compactSparseArrays(result);
135
+ }
136
+ //# sourceMappingURL=flattened-tool-arguments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattened-tool-arguments.js","sourceRoot":"","sources":["../../src/core/flattened-tool-arguments.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Canonical reconstruction of flattened tool-call arguments.\n *\n * Some upstream providers — notably GitHub Copilot Gemini models proxied through\n * Google's GenAI API — serialize array/object function-call arguments as\n * flattened, indexed keys on the wire. For example a tool called with\n * `{ keywords: [\"a\", \"b\"] }` arrives as `{ \"keywords[0]\": \"a\", \"keywords[1]\": \"b\" }`,\n * and `{ files: [{ path }] }` as `{ \"files[0].path\": \"...\" }`.\n *\n * This module is the single source of truth for turning those flattened keys\n * back into nested arrays/objects. Both the host runtime's per-tool\n * normalization (gated to Copilot Gemini, schema-aware) and the MCP `callTool`\n * boundary (provider-agnostic, bracket self-gating) delegate here so the two\n * paths cannot drift — in particular so the prototype-pollution guard lives in\n * exactly one place.\n *\n * Security: argument keys cross a trust boundary (model/provider wire → tool /\n * MCP server validation). A key path that walks through `__proto__`,\n * `constructor`, or `prototype` could otherwise reach `Object.prototype` and\n * mutate it process-wide. Any key whose path contains such a segment — at any\n * position, including the final segment and a literal plain key — is dropped.\n */\n\n/** Key segments that must never be written or traversed (prototype pollution). */\nconst UNSAFE_KEY_SEGMENTS: ReadonlySet<string> = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction isUnsafeSegment(segment: string | number): boolean {\n return typeof segment === \"string\" && UNSAFE_KEY_SEGMENTS.has(segment);\n}\n\n/**\n * Parse a flattened key such as `a.b[0].c` into path segments\n * `[\"a\", \"b\", 0, \"c\"]`. Returns `undefined` for a plain key with no `.`/`[`, or\n * for a malformed bracket expression (left untouched by the caller).\n */\nexport function parseFlattenedKeyPath(key: string): Array<string | number> | undefined {\n if (!/[.[]/.test(key)) return undefined;\n const segments: Array<string | number> = [];\n let current = \"\";\n let index = 0;\n const flush = () => {\n if (current !== \"\") {\n segments.push(current);\n current = \"\";\n }\n };\n while (index < key.length) {\n const char = key[index];\n if (char === \".\") {\n flush();\n index += 1;\n } else if (char === \"[\") {\n flush();\n const end = key.indexOf(\"]\", index);\n if (end === -1) return undefined; // malformed — leave key untouched\n const inner = key.slice(index + 1, end);\n const numeric = Number(inner);\n if (inner.trim() !== \"\" && Number.isInteger(numeric) && numeric >= 0) {\n segments.push(numeric);\n } else {\n segments.push(inner.replace(/^[\"']|[\"']$/g, \"\"));\n }\n index = end + 1;\n } else {\n current += char;\n index += 1;\n }\n }\n flush();\n return segments.length > 0 ? segments : undefined;\n}\n\n/** Assign `value` at the given path inside `root`, creating arrays/objects as needed. */\nfunction assignFlattenedKeyPath(\n root: Record<string | number, unknown>,\n segments: Array<string | number>,\n value: unknown,\n): void {\n let node: Record<string | number, unknown> = root;\n for (let i = 0; i < segments.length - 1; i += 1) {\n const segment = segments[i];\n const nextIsIndex = typeof segments[i + 1] === \"number\";\n const existing = node[segment];\n if (existing === null || existing === undefined || typeof existing !== \"object\") {\n node[segment] = nextIsIndex ? [] : {};\n }\n node = node[segment] as Record<string | number, unknown>;\n }\n node[segments[segments.length - 1]] = value;\n}\n\n/**\n * Remove empty holes from sparse arrays produced by out-of-order indices.\n *\n * Note: this collapses holes rather than preserving them — `name[0]` + `name[2]`\n * (no index 1) becomes a dense 2-element array `[a, c]`, not `[a, <hole>, c]`.\n * That is the intended healing for Gemini's flattened output (which emits\n * contiguous indices in practice); it would, however, silently misalign two\n * arrays that were meant to be index-paired.\n */\nfunction compactSparseArrays(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.filter((entry) => entry !== undefined).map((entry) => compactSparseArrays(entry));\n }\n if (value !== null && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) out[key] = compactSparseArrays(entry);\n return out;\n }\n return value;\n}\n\n/**\n * Reconstruct (unflatten) flattened keys into nested arrays/objects — for\n * example `\"items[0]\"` -> `{ items: [...] }` and `\"parent.child\"` ->\n * `{ parent: { child: ... } }`. `shouldSplit` decides, per key, whether it is a\n * flattened path (true) or an opaque literal key to be preserved (false);\n * callers apply their own gating/schema logic there.\n *\n * Prototype-pollution safe: a key whose parsed path contains `__proto__`,\n * `constructor`, or `prototype` (at any position) is dropped, as is a literal\n * plain key equal to one of those names.\n */\nexport function reconstructFlattenedKeys(\n args: Record<string, unknown>,\n shouldSplit: (key: string) => boolean,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(args)) {\n const segments = shouldSplit(key) ? parseFlattenedKeyPath(key) : undefined;\n if (!segments) {\n // Plain passthrough — but never assign a literal prototype-polluting key.\n if (!UNSAFE_KEY_SEGMENTS.has(key)) result[key] = value;\n continue;\n }\n if (segments.some(isUnsafeSegment)) continue; // drop a polluting path entirely\n assignFlattenedKeyPath(result, segments, value);\n }\n return compactSparseArrays(result) as Record<string, unknown>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"http-dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/http-dispatcher.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,4BAA4B,SAAU,CAAC;AAEpD,eAAO,MAAM,yBAAyB;aACnC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,KAAM;;aAClC,KAAK,EAAE,OAAO;aAAE,SAAS,EAAE,KAAM;;aACjC,KAAK,EAAE,OAAO;aAAE,SAAS,EAAE,MAAO;;aAClC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,MAAO;;aACnC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,OAAS;;aACrC,KAAK,EAAE,UAAU;aAAE,SAAS,EAAE,CAAC;EACxB,CAAC;AAEX,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,GAAE,MAAqC,GAAG,IAAI,CAa9F","sourcesContent":["import { EnvHttpProxyAgent, setGlobalDispatcher } from \"undici\";\n\nexport const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n\nexport const HTTP_IDLE_TIMEOUT_CHOICES = [\n\t{ label: \"30 sec\", timeoutMs: 30_000 },\n\t{ label: \"1 min\", timeoutMs: 60_000 },\n\t{ label: \"5 min\", timeoutMs: 300_000 },\n\t{ label: \"10 min\", timeoutMs: 600_000 },\n\t{ label: \"30 min\", timeoutMs: 1_800_000 },\n\t{ label: \"Disabled\", timeoutMs: 0 },\n] as const;\n\nexport function parseHttpIdleTimeoutMs(value: unknown): number | undefined {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n\t\treturn undefined;\n\t}\n\treturn Math.floor(value);\n}\n\nexport function formatHttpIdleTimeoutMs(timeoutMs: number): string {\n\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);\n\tif (choice) {\n\t\treturn choice.label;\n\t}\n\treturn `${timeoutMs / 1000} sec`;\n}\n\n/**\n * Configure the global undici dispatcher used by fetch and SDK HTTP clients.\n *\n * Keep HTTP/2 disabled for now because some Node/undici combinations have\n * produced stream-reset crashes, and use a configurable idle timeout so stale\n * connections are eventually reclaimed while long-running requests remain\n * supported.\n */\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);\n\tif (normalizedTimeoutMs === undefined) {\n\t\tthrow new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);\n\t}\n\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: normalizedTimeoutMs,\n\t\t\theadersTimeout: normalizedTimeoutMs,\n\t\t}),\n\t);\n}\n"]}
1
+ {"version":3,"file":"http-dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/http-dispatcher.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,4BAA4B,SAAU,CAAC;AAEpD,eAAO,MAAM,yBAAyB;aACnC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,KAAM;;aAClC,KAAK,EAAE,OAAO;aAAE,SAAS,EAAE,KAAM;;aACjC,KAAK,EAAE,OAAO;aAAE,SAAS,EAAE,MAAO;;aAClC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,MAAO;;aACnC,KAAK,EAAE,QAAQ;aAAE,SAAS,EAAE,OAAS;;aACrC,KAAK,EAAE,UAAU;aAAE,SAAS,EAAE,CAAC;EACxB,CAAC;AAEX,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,GAAE,MAAqC,GAAG,IAAI,CAkB9F","sourcesContent":["import { EnvHttpProxyAgent, setGlobalDispatcher } from \"undici\";\nimport { installCopilotGeminiReasoningInterceptor } from \"./copilot-gemini-reasoning.ts\";\n\nexport const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n\nexport const HTTP_IDLE_TIMEOUT_CHOICES = [\n\t{ label: \"30 sec\", timeoutMs: 30_000 },\n\t{ label: \"1 min\", timeoutMs: 60_000 },\n\t{ label: \"5 min\", timeoutMs: 300_000 },\n\t{ label: \"10 min\", timeoutMs: 600_000 },\n\t{ label: \"30 min\", timeoutMs: 1_800_000 },\n\t{ label: \"Disabled\", timeoutMs: 0 },\n] as const;\n\nexport function parseHttpIdleTimeoutMs(value: unknown): number | undefined {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n\t\treturn undefined;\n\t}\n\treturn Math.floor(value);\n}\n\nexport function formatHttpIdleTimeoutMs(timeoutMs: number): string {\n\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);\n\tif (choice) {\n\t\treturn choice.label;\n\t}\n\treturn `${timeoutMs / 1000} sec`;\n}\n\n/**\n * Configure the global undici dispatcher used by fetch and SDK HTTP clients.\n *\n * Keep HTTP/2 disabled for now because some Node/undici combinations have\n * produced stream-reset crashes, and use a configurable idle timeout so stale\n * connections are eventually reclaimed while long-running requests remain\n * supported.\n */\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);\n\tif (normalizedTimeoutMs === undefined) {\n\t\tthrow new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);\n\t}\n\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: normalizedTimeoutMs,\n\t\t\theadersTimeout: normalizedTimeoutMs,\n\t\t}),\n\t);\n\n\t// Bridge CAPI Gemini thought signatures (`reasoning_opaque`) on the inbound\n\t// SSE stream so multi-turn Copilot Gemini tool use does not stall on empty\n\t// completions. Idempotent and scoped to `*.githubcopilot.com` event streams.\n\tinstallCopilotGeminiReasoningInterceptor();\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
2
+ import { installCopilotGeminiReasoningInterceptor } from "./copilot-gemini-reasoning.js";
2
3
  export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
3
4
  export const HTTP_IDLE_TIMEOUT_CHOICES = [
4
5
  { label: "30 sec", timeoutMs: 30_000 },
@@ -39,5 +40,9 @@ export function configureHttpDispatcher(timeoutMs = DEFAULT_HTTP_IDLE_TIMEOUT_MS
39
40
  bodyTimeout: normalizedTimeoutMs,
40
41
  headersTimeout: normalizedTimeoutMs,
41
42
  }));
43
+ // Bridge CAPI Gemini thought signatures (`reasoning_opaque`) on the inbound
44
+ // SSE stream so multi-turn Copilot Gemini tool use does not stall on empty
45
+ // completions. Idempotent and scoped to `*.githubcopilot.com` event streams.
46
+ installCopilotGeminiReasoningInterceptor();
42
47
  }
43
48
  //# sourceMappingURL=http-dispatcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-dispatcher.js","sourceRoot":"","sources":["../../src/core/http-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAEhE,MAAM,CAAC,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAEpD,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE;IACtC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACrC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;IACtC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE;IACvC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE;IACzC,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE;CAC1B,CAAC;AAEX,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACxD,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IACtF,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,SAAS,GAAG,IAAI,MAAM,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAS,GAAW,4BAA4B;IACvF,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAC9D,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,mBAAmB,CAClB,IAAI,iBAAiB,CAAC;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,mBAAmB;QAChC,cAAc,EAAE,mBAAmB;KACnC,CAAC,CACF,CAAC;AACH,CAAC","sourcesContent":["import { EnvHttpProxyAgent, setGlobalDispatcher } from \"undici\";\n\nexport const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n\nexport const HTTP_IDLE_TIMEOUT_CHOICES = [\n\t{ label: \"30 sec\", timeoutMs: 30_000 },\n\t{ label: \"1 min\", timeoutMs: 60_000 },\n\t{ label: \"5 min\", timeoutMs: 300_000 },\n\t{ label: \"10 min\", timeoutMs: 600_000 },\n\t{ label: \"30 min\", timeoutMs: 1_800_000 },\n\t{ label: \"Disabled\", timeoutMs: 0 },\n] as const;\n\nexport function parseHttpIdleTimeoutMs(value: unknown): number | undefined {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n\t\treturn undefined;\n\t}\n\treturn Math.floor(value);\n}\n\nexport function formatHttpIdleTimeoutMs(timeoutMs: number): string {\n\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);\n\tif (choice) {\n\t\treturn choice.label;\n\t}\n\treturn `${timeoutMs / 1000} sec`;\n}\n\n/**\n * Configure the global undici dispatcher used by fetch and SDK HTTP clients.\n *\n * Keep HTTP/2 disabled for now because some Node/undici combinations have\n * produced stream-reset crashes, and use a configurable idle timeout so stale\n * connections are eventually reclaimed while long-running requests remain\n * supported.\n */\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);\n\tif (normalizedTimeoutMs === undefined) {\n\t\tthrow new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);\n\t}\n\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: normalizedTimeoutMs,\n\t\t\theadersTimeout: normalizedTimeoutMs,\n\t\t}),\n\t);\n}\n"]}
1
+ {"version":3,"file":"http-dispatcher.js","sourceRoot":"","sources":["../../src/core/http-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,wCAAwC,EAAE,MAAM,+BAA+B,CAAC;AAEzF,MAAM,CAAC,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAEpD,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE;IACtC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;IACrC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;IACtC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE;IACvC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE;IACzC,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE;CAC1B,CAAC;AAEX,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACxD,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IACtF,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,SAAS,GAAG,IAAI,MAAM,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAS,GAAW,4BAA4B;IACvF,MAAM,mBAAmB,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAC9D,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,mBAAmB,CAClB,IAAI,iBAAiB,CAAC;QACrB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,mBAAmB;QAChC,cAAc,EAAE,mBAAmB;KACnC,CAAC,CACF,CAAC;IAEF,4EAA4E;IAC5E,2EAA2E;IAC3E,6EAA6E;IAC7E,wCAAwC,EAAE,CAAC;AAC5C,CAAC","sourcesContent":["import { EnvHttpProxyAgent, setGlobalDispatcher } from \"undici\";\nimport { installCopilotGeminiReasoningInterceptor } from \"./copilot-gemini-reasoning.ts\";\n\nexport const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n\nexport const HTTP_IDLE_TIMEOUT_CHOICES = [\n\t{ label: \"30 sec\", timeoutMs: 30_000 },\n\t{ label: \"1 min\", timeoutMs: 60_000 },\n\t{ label: \"5 min\", timeoutMs: 300_000 },\n\t{ label: \"10 min\", timeoutMs: 600_000 },\n\t{ label: \"30 min\", timeoutMs: 1_800_000 },\n\t{ label: \"Disabled\", timeoutMs: 0 },\n] as const;\n\nexport function parseHttpIdleTimeoutMs(value: unknown): number | undefined {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n\t\treturn undefined;\n\t}\n\treturn Math.floor(value);\n}\n\nexport function formatHttpIdleTimeoutMs(timeoutMs: number): string {\n\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.timeoutMs === timeoutMs);\n\tif (choice) {\n\t\treturn choice.label;\n\t}\n\treturn `${timeoutMs / 1000} sec`;\n}\n\n/**\n * Configure the global undici dispatcher used by fetch and SDK HTTP clients.\n *\n * Keep HTTP/2 disabled for now because some Node/undici combinations have\n * produced stream-reset crashes, and use a configurable idle timeout so stale\n * connections are eventually reclaimed while long-running requests remain\n * supported.\n */\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);\n\tif (normalizedTimeoutMs === undefined) {\n\t\tthrow new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);\n\t}\n\n\tsetGlobalDispatcher(\n\t\tnew EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\tbodyTimeout: normalizedTimeoutMs,\n\t\t\theadersTimeout: normalizedTimeoutMs,\n\t\t}),\n\t);\n\n\t// Bridge CAPI Gemini thought signatures (`reasoning_opaque`) on the inbound\n\t// SSE stream so multi-turn Copilot Gemini tool use does not stall on empty\n\t// completions. Idempotent and scoped to `*.githubcopilot.com` event streams.\n\tinstallCopilotGeminiReasoningInterceptor();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,GAAG,EAER,KAAK,KAAK,EAEX,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUhD,OAAO,KAAK,EAEV,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,2BAA2B,EAC3B,6BAA6B,EAC7B,0BAA0B,EAC1B,eAAe,EAEf,qBAAqB,EACrB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oFAAoF;IACpF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,uFAAuF;IACvF,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B,iEAAiE;IACjE,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oHAAoH;IACpH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gEAAgE;IAChE,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAAC,aAAa,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAE3E;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gEAAgE;IAChE,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,gGAAgG;IAChG,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAE/B,oEAAoE;IACpE,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,2DAA2D;IAC3D,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,uEAAuE;IACvE,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,qCAAqC;AACrC,MAAM,WAAW,wBAAwB;IACvC,0BAA0B;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,mEAAmE;IACnE,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,wEAAwE;IACxE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4FAA4F;IAC5F,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,cAAc,4BAA4B,CAAC;AAC3C,YAAY,EACV,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,UAAU,EACV,aAAa,EACb,SAAS,EACT,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,IAAI,GACL,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,2BAA2B,EAE3B,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACZ,6BAA6B,EAC7B,0BAA0B,GAC3B,CAAC;AAuBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA0VnC","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { restoreAnthropicReplayThinkingBlocks } from \"./anthropic-thinking-guard.ts\";\nimport { getModelDefaultContextWindow, getSupportedContextWindows, selectContextWindow } from \"./context-window.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveSavedModelReference } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n STRUCTURED_OUTPUT_TOOL_NAME,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Context window token count. Default: model scalar contextWindow, or settings/session override when supported. */\n contextWindow?: number;\n /** Treat unsupported contextWindow as an error instead of a warning/fallback. */\n contextWindowStrict?: boolean;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n /** Warning if a saved/default context window could not be applied to the selected model. */\n contextWindowWarning?: string;\n /** Error if an explicit strict context-window selection is unsupported. */\n contextWindowError?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n JsonObject,\n JsonPrimitive,\n JsonValue,\n StructuredOutputCapture,\n StructuredOutputFileCapture,\n StructuredOutputToolOptions,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n STRUCTURED_OUTPUT_TOOL_NAME,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\ntype ContextWindowRequestSource = \"explicit\" | \"incoming-model\" | \"session\" | \"model-settings\" | \"global-settings\";\n\nconst COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS = { allowCopilotLongContextFallback: true } as const;\n\nfunction getAlreadyAppliedContextWindow(model: Model<Api>): number | undefined {\n const defaultContextWindow = getModelDefaultContextWindow(model);\n if (model.contextWindow === defaultContextWindow) {\n return undefined;\n }\n\n return getSupportedContextWindows(model).includes(model.contextWindow)\n ? model.contextWindow\n : undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = await resolveSavedModelReference(\n existingSession.model.provider,\n existingSession.model.modelId,\n modelRegistry,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n let selectedContextWindow: number | undefined;\n let contextWindowWarning: string | undefined;\n let contextWindowError: string | undefined;\n const explicitContextWindowSelection = options.contextWindow !== undefined;\n const incomingModelContextWindow =\n model && options.model ? getAlreadyAppliedContextWindow(model) : undefined;\n const sessionContextWindow = hasExistingSession ? existingSession.contextWindow : undefined;\n const modelSettingsContextWindow = model ? settingsManager.getDefaultContextWindowForModel(model.provider, model.id) : undefined;\n const globalSettingsContextWindow = settingsManager.getDefaultContextWindow();\n const contextWindowRequest:\n | { contextWindow: number; source: ContextWindowRequestSource }\n | undefined =\n options.contextWindow !== undefined\n ? { contextWindow: options.contextWindow, source: \"explicit\" }\n : incomingModelContextWindow !== undefined\n ? { contextWindow: incomingModelContextWindow, source: \"incoming-model\" }\n : sessionContextWindow !== undefined\n ? { contextWindow: sessionContextWindow, source: \"session\" }\n : modelSettingsContextWindow !== undefined\n ? { contextWindow: modelSettingsContextWindow, source: \"model-settings\" }\n : globalSettingsContextWindow !== undefined\n ? { contextWindow: globalSettingsContextWindow, source: \"global-settings\" }\n : undefined;\n if (model && contextWindowRequest !== undefined) {\n const selected = selectContextWindow(\n model,\n contextWindowRequest.contextWindow,\n COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS,\n );\n if (\"error\" in selected) {\n if (options.contextWindowStrict) {\n contextWindowError = selected.error;\n } else if (contextWindowRequest.source !== \"global-settings\") {\n contextWindowWarning = selected.error;\n }\n } else {\n model = selected.model;\n selectedContextWindow = selected.contextWindow;\n }\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n let lastConvertedLlmMessages: Message[] | undefined;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n lastConvertedLlmMessages = converted;\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n const filtered = converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n lastConvertedLlmMessages = filtered;\n return filtered;\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const sourceMessages = lastConvertedLlmMessages;\n const replayGuardedPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model)\n : guardedPayload;\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return replayGuardedPayload;\n }\n const extensionPayload = await runner.emitBeforeProviderRequest(\n replayGuardedPayload,\n );\n return sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)\n : extensionPayload;\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n const transcriptContextWindow = model\n ? (existingSession.contextWindow ?? getModelDefaultContextWindow(model))\n : undefined;\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== transcriptContextWindow)\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== getModelDefaultContextWindow(model))\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n contextWindowWarning,\n contextWindowError,\n };\n}\n"]}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { restoreAnthropicReplayThinkingBlocks } from \"./anthropic-thinking-guard.ts\";\nimport { sanitizeCopilotGeminiPayload } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { restoreCopilotGeminiReasoningOpaque } from \"./copilot-gemini-reasoning.ts\";\nimport { normalizeCopilotGeminiReplayToolArguments } from \"./copilot-gemini-tool-arguments.ts\";\nimport { getModelDefaultContextWindow, getSupportedContextWindows, selectContextWindow } from \"./context-window.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveSavedModelReference } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n STRUCTURED_OUTPUT_TOOL_NAME,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Context window token count. Default: model scalar contextWindow, or settings/session override when supported. */\n contextWindow?: number;\n /** Treat unsupported contextWindow as an error instead of a warning/fallback. */\n contextWindowStrict?: boolean;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n /** Warning if a saved/default context window could not be applied to the selected model. */\n contextWindowWarning?: string;\n /** Error if an explicit strict context-window selection is unsupported. */\n contextWindowError?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n JsonObject,\n JsonPrimitive,\n JsonValue,\n StructuredOutputCapture,\n StructuredOutputFileCapture,\n StructuredOutputToolOptions,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n STRUCTURED_OUTPUT_TOOL_NAME,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\ntype ContextWindowRequestSource = \"explicit\" | \"incoming-model\" | \"session\" | \"model-settings\" | \"global-settings\";\n\nconst COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS = { allowCopilotLongContextFallback: true } as const;\n\nfunction getAlreadyAppliedContextWindow(model: Model<Api>): number | undefined {\n const defaultContextWindow = getModelDefaultContextWindow(model);\n if (model.contextWindow === defaultContextWindow) {\n return undefined;\n }\n\n return getSupportedContextWindows(model).includes(model.contextWindow)\n ? model.contextWindow\n : undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry. When a modelRegistry\n // is supplied (e.g. a workflow stage reusing one registry across model\n // fallback candidates), do NOT also build a fresh AuthStorage: its\n // constructor eagerly calls reload(), which acquires the auth.json file lock\n // and, under contention, can fail and leave an empty credential set. Reusing\n // the supplied registry's already-loaded auth avoids that race (issue #1431).\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const modelRegistry =\n options.modelRegistry ??\n ModelRegistry.create(options.authStorage ?? AuthStorage.create(authPath), modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = await resolveSavedModelReference(\n existingSession.model.provider,\n existingSession.model.modelId,\n modelRegistry,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n let selectedContextWindow: number | undefined;\n let contextWindowWarning: string | undefined;\n let contextWindowError: string | undefined;\n const explicitContextWindowSelection = options.contextWindow !== undefined;\n const incomingModelContextWindow =\n model && options.model ? getAlreadyAppliedContextWindow(model) : undefined;\n const sessionContextWindow = hasExistingSession ? existingSession.contextWindow : undefined;\n const modelSettingsContextWindow = model ? settingsManager.getDefaultContextWindowForModel(model.provider, model.id) : undefined;\n const globalSettingsContextWindow = settingsManager.getDefaultContextWindow();\n const contextWindowRequest:\n | { contextWindow: number; source: ContextWindowRequestSource }\n | undefined =\n options.contextWindow !== undefined\n ? { contextWindow: options.contextWindow, source: \"explicit\" }\n : incomingModelContextWindow !== undefined\n ? { contextWindow: incomingModelContextWindow, source: \"incoming-model\" }\n : sessionContextWindow !== undefined\n ? { contextWindow: sessionContextWindow, source: \"session\" }\n : modelSettingsContextWindow !== undefined\n ? { contextWindow: modelSettingsContextWindow, source: \"model-settings\" }\n : globalSettingsContextWindow !== undefined\n ? { contextWindow: globalSettingsContextWindow, source: \"global-settings\" }\n : undefined;\n if (model && contextWindowRequest !== undefined) {\n const selected = selectContextWindow(\n model,\n contextWindowRequest.contextWindow,\n COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS,\n );\n if (\"error\" in selected) {\n if (options.contextWindowStrict) {\n contextWindowError = selected.error;\n } else if (contextWindowRequest.source !== \"global-settings\") {\n contextWindowWarning = selected.error;\n }\n } else {\n model = selected.model;\n selectedContextWindow = selected.contextWindow;\n }\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n let lastConvertedLlmMessages: Message[] | undefined;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n lastConvertedLlmMessages = converted;\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n const filtered = converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n lastConvertedLlmMessages = filtered;\n return filtered;\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const sourceMessages = lastConvertedLlmMessages;\n const replayGuardedPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model)\n : guardedPayload;\n const runner = extensionRunnerRef.current;\n let finalPayload: unknown;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n finalPayload = replayGuardedPayload;\n } else {\n const extensionPayload = await runner.emitBeforeProviderRequest(\n replayGuardedPayload,\n );\n finalPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)\n : extensionPayload;\n }\n // GitHub Copilot Gemini models are served through CAPI, which translates\n // the OpenAI request into Google GenAI and rejects tool schemas whose\n // `anyOf`/`oneOf` wraps a complex object (HTTP 400 invalid request body).\n // Sanitize tool JSON Schemas into Gemini's supported subset. No-op for\n // every other provider/model, and runs last so it also covers tools\n // injected by `before_provider_request` extensions.\n const schemaSanitized = sanitizeCopilotGeminiPayload(finalPayload, model);\n // Reconstruct flattened tool-call arguments on replayed assistant\n // messages (for example `edits[0].newText` -> `edits: [{ newText }]`).\n // CAPI parses replayed arguments straight into Gemini's FunctionCall,\n // and a flattened/malformed prior call ends the next turn with\n // `finish_reason: \"error\"`. No-op for well-formed args / other models.\n const replayArgsNormalized = normalizeCopilotGeminiReplayToolArguments(schemaSanitized, model);\n // CAPI carries Gemini thought signatures in a `reasoning_opaque` field it\n // reads back off the assistant message on replay. Convert the\n // `reasoning_details` the client re-emits (captured inbound by the SSE\n // interceptor) into that field so multi-turn tool use keeps its thought\n // signature instead of dying on an empty completion. No-op otherwise.\n return restoreCopilotGeminiReasoningOpaque(replayArgsNormalized, model);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n const transcriptContextWindow = model\n ? (existingSession.contextWindow ?? getModelDefaultContextWindow(model))\n : undefined;\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== transcriptContextWindow)\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== getModelDefaultContextWindow(model))\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n contextWindowWarning,\n contextWindowError,\n };\n}\n"]}
package/dist/core/sdk.js CHANGED
@@ -8,6 +8,9 @@ import { formatNoModelsAvailableMessage } from "./auth-guidance.js";
8
8
  import { AuthStorage } from "./auth-storage.js";
9
9
  import { shouldApplyCodexFastMode, streamWithCodexFastMode, withCodexFastModePayload, withCodexFastModeStreamOptions, } from "./codex-fast-mode.js";
10
10
  import { restoreAnthropicReplayThinkingBlocks } from "./anthropic-thinking-guard.js";
11
+ import { sanitizeCopilotGeminiPayload } from "./copilot-gemini-payload-sanitizer.js";
12
+ import { restoreCopilotGeminiReasoningOpaque } from "./copilot-gemini-reasoning.js";
13
+ import { normalizeCopilotGeminiReplayToolArguments } from "./copilot-gemini-tool-arguments.js";
11
14
  import { getModelDefaultContextWindow, getSupportedContextWindows, selectContextWindow } from "./context-window.js";
12
15
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
13
16
  import { convertToLlm } from "./messages.js";
@@ -77,13 +80,18 @@ export async function createAgentSession(options = {}) {
77
80
  const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());
78
81
  const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();
79
82
  let resourceLoader = options.resourceLoader;
80
- // Use provided or create AuthStorage and ModelRegistry
83
+ // Use provided or create AuthStorage and ModelRegistry. When a modelRegistry
84
+ // is supplied (e.g. a workflow stage reusing one registry across model
85
+ // fallback candidates), do NOT also build a fresh AuthStorage: its
86
+ // constructor eagerly calls reload(), which acquires the auth.json file lock
87
+ // and, under contention, can fail and leave an empty credential set. Reusing
88
+ // the supplied registry's already-loaded auth avoids that race (issue #1431).
81
89
  const authPath = options.agentDir ? join(agentDir, "auth.json") : undefined;
82
90
  const modelsPath = options.agentDir
83
91
  ? join(agentDir, "models.json")
84
92
  : undefined;
85
- const authStorage = options.authStorage ?? AuthStorage.create(authPath);
86
- const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);
93
+ const modelRegistry = options.modelRegistry ??
94
+ ModelRegistry.create(options.authStorage ?? AuthStorage.create(authPath), modelsPath);
87
95
  const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
88
96
  const sessionManager = options.sessionManager ??
89
97
  SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));
@@ -278,13 +286,35 @@ export async function createAgentSession(options = {}) {
278
286
  ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model)
279
287
  : guardedPayload;
280
288
  const runner = extensionRunnerRef.current;
289
+ let finalPayload;
281
290
  if (!runner?.hasHandlers("before_provider_request")) {
282
- return replayGuardedPayload;
291
+ finalPayload = replayGuardedPayload;
283
292
  }
284
- const extensionPayload = await runner.emitBeforeProviderRequest(replayGuardedPayload);
285
- return sourceMessages
286
- ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)
287
- : extensionPayload;
293
+ else {
294
+ const extensionPayload = await runner.emitBeforeProviderRequest(replayGuardedPayload);
295
+ finalPayload = sourceMessages
296
+ ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)
297
+ : extensionPayload;
298
+ }
299
+ // GitHub Copilot Gemini models are served through CAPI, which translates
300
+ // the OpenAI request into Google GenAI and rejects tool schemas whose
301
+ // `anyOf`/`oneOf` wraps a complex object (HTTP 400 invalid request body).
302
+ // Sanitize tool JSON Schemas into Gemini's supported subset. No-op for
303
+ // every other provider/model, and runs last so it also covers tools
304
+ // injected by `before_provider_request` extensions.
305
+ const schemaSanitized = sanitizeCopilotGeminiPayload(finalPayload, model);
306
+ // Reconstruct flattened tool-call arguments on replayed assistant
307
+ // messages (for example `edits[0].newText` -> `edits: [{ newText }]`).
308
+ // CAPI parses replayed arguments straight into Gemini's FunctionCall,
309
+ // and a flattened/malformed prior call ends the next turn with
310
+ // `finish_reason: "error"`. No-op for well-formed args / other models.
311
+ const replayArgsNormalized = normalizeCopilotGeminiReplayToolArguments(schemaSanitized, model);
312
+ // CAPI carries Gemini thought signatures in a `reasoning_opaque` field it
313
+ // reads back off the assistant message on replay. Convert the
314
+ // `reasoning_details` the client re-emits (captured inbound by the SSE
315
+ // interceptor) into that field so multi-turn tool use keeps its thought
316
+ // signature instead of dying on an empty completion. No-op otherwise.
317
+ return restoreCopilotGeminiReasoningOpaque(replayArgsNormalized, model);
288
318
  },
289
319
  onResponse: async (response, _model) => {
290
320
  const runner = extensionRunnerRef.current;
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { restoreAnthropicReplayThinkingBlocks } from \"./anthropic-thinking-guard.ts\";\nimport { getModelDefaultContextWindow, getSupportedContextWindows, selectContextWindow } from \"./context-window.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveSavedModelReference } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n STRUCTURED_OUTPUT_TOOL_NAME,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Context window token count. Default: model scalar contextWindow, or settings/session override when supported. */\n contextWindow?: number;\n /** Treat unsupported contextWindow as an error instead of a warning/fallback. */\n contextWindowStrict?: boolean;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n /** Warning if a saved/default context window could not be applied to the selected model. */\n contextWindowWarning?: string;\n /** Error if an explicit strict context-window selection is unsupported. */\n contextWindowError?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n JsonObject,\n JsonPrimitive,\n JsonValue,\n StructuredOutputCapture,\n StructuredOutputFileCapture,\n StructuredOutputToolOptions,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n STRUCTURED_OUTPUT_TOOL_NAME,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\ntype ContextWindowRequestSource = \"explicit\" | \"incoming-model\" | \"session\" | \"model-settings\" | \"global-settings\";\n\nconst COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS = { allowCopilotLongContextFallback: true } as const;\n\nfunction getAlreadyAppliedContextWindow(model: Model<Api>): number | undefined {\n const defaultContextWindow = getModelDefaultContextWindow(model);\n if (model.contextWindow === defaultContextWindow) {\n return undefined;\n }\n\n return getSupportedContextWindows(model).includes(model.contextWindow)\n ? model.contextWindow\n : undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const authStorage = options.authStorage ?? AuthStorage.create(authPath);\n const modelRegistry =\n options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = await resolveSavedModelReference(\n existingSession.model.provider,\n existingSession.model.modelId,\n modelRegistry,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n let selectedContextWindow: number | undefined;\n let contextWindowWarning: string | undefined;\n let contextWindowError: string | undefined;\n const explicitContextWindowSelection = options.contextWindow !== undefined;\n const incomingModelContextWindow =\n model && options.model ? getAlreadyAppliedContextWindow(model) : undefined;\n const sessionContextWindow = hasExistingSession ? existingSession.contextWindow : undefined;\n const modelSettingsContextWindow = model ? settingsManager.getDefaultContextWindowForModel(model.provider, model.id) : undefined;\n const globalSettingsContextWindow = settingsManager.getDefaultContextWindow();\n const contextWindowRequest:\n | { contextWindow: number; source: ContextWindowRequestSource }\n | undefined =\n options.contextWindow !== undefined\n ? { contextWindow: options.contextWindow, source: \"explicit\" }\n : incomingModelContextWindow !== undefined\n ? { contextWindow: incomingModelContextWindow, source: \"incoming-model\" }\n : sessionContextWindow !== undefined\n ? { contextWindow: sessionContextWindow, source: \"session\" }\n : modelSettingsContextWindow !== undefined\n ? { contextWindow: modelSettingsContextWindow, source: \"model-settings\" }\n : globalSettingsContextWindow !== undefined\n ? { contextWindow: globalSettingsContextWindow, source: \"global-settings\" }\n : undefined;\n if (model && contextWindowRequest !== undefined) {\n const selected = selectContextWindow(\n model,\n contextWindowRequest.contextWindow,\n COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS,\n );\n if (\"error\" in selected) {\n if (options.contextWindowStrict) {\n contextWindowError = selected.error;\n } else if (contextWindowRequest.source !== \"global-settings\") {\n contextWindowWarning = selected.error;\n }\n } else {\n model = selected.model;\n selectedContextWindow = selected.contextWindow;\n }\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n let lastConvertedLlmMessages: Message[] | undefined;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n lastConvertedLlmMessages = converted;\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n const filtered = converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n lastConvertedLlmMessages = filtered;\n return filtered;\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const sourceMessages = lastConvertedLlmMessages;\n const replayGuardedPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model)\n : guardedPayload;\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n return replayGuardedPayload;\n }\n const extensionPayload = await runner.emitBeforeProviderRequest(\n replayGuardedPayload,\n );\n return sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)\n : extensionPayload;\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n const transcriptContextWindow = model\n ? (existingSession.contextWindow ?? getModelDefaultContextWindow(model))\n : undefined;\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== transcriptContextWindow)\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== getModelDefaultContextWindow(model))\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n contextWindowWarning,\n contextWindowError,\n };\n}\n"]}
1
+ {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,KAAK,GAGN,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAIlB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,oCAAoC,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,mCAAmC,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAE,yCAAyC,EAAE,MAAM,oCAAoC,CAAC;AAC/F,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAQvD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,2BAA2B,EAC3B,6BAA6B,EAC7B,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,kBAAkB,CAAC;AAkF1B,aAAa;AAEb,cAAc,4BAA4B,CAAC;AA+B3C,OAAO,EACL,qBAAqB,EACrB,2BAA2B;AAC3B,kCAAkC;AAClC,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,EACZ,6BAA6B,EAC7B,0BAA0B,GAC3B,CAAC;AAEF,mBAAmB;AAEnB,SAAS,kBAAkB;IACzB,OAAO,WAAW,EAAE,CAAC;AACvB,CAAC;AAID,MAAM,wCAAwC,GAAG,EAAE,+BAA+B,EAAE,IAAI,EAAW,CAAC;AAEpG,SAAS,8BAA8B,CAAC,KAAiB;IACvD,MAAM,oBAAoB,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,aAAa,KAAK,oBAAoB,EAAE,CAAC;QACjD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,0BAA0B,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC;QACpE,CAAC,CAAC,KAAK,CAAC,aAAa;QACrB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAO,GAA8B,EAAE;IAEvC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1F,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC;IACzF,IAAI,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAE5C,6EAA6E;IAC7E,uEAAuE;IACvE,mEAAmE;IACnE,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ;QACjC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC;QAC/B,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,aAAa,GACjB,OAAO,CAAC,aAAa;QACrB,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;IAExF,MAAM,eAAe,GACnB,OAAO,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnE,MAAM,cAAc,GAClB,OAAO,CAAC,cAAc;QACtB,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,oBAAoB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAElE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,IAAI,qBAAqB,CAAC;YACzC,GAAG;YACH,QAAQ;YACR,eAAe;SAChB,CAAC,CAAC;QACH,MAAM,cAAc,CAAC,MAAM,EAAE,CAAC;QAC9B,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAChC,CAAC;IAED,gDAAgD;IAChD,MAAM,eAAe,GAAG,cAAc,CAAC,mBAAmB,EAAE,CAAC;IAC7D,MAAM,kBAAkB,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,cAAc;SACpC,SAAS,EAAE;SACX,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,uBAAuB,CAAC,CAAC;IAE3D,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,IAAI,oBAAwC,CAAC;IAE7C,oDAAoD;IACpD,IAAI,CAAC,KAAK,IAAI,kBAAkB,IAAI,eAAe,CAAC,KAAK,EAAE,CAAC;QAC1D,MAAM,aAAa,GAAG,MAAM,0BAA0B,CACpD,eAAe,CAAC,KAAK,CAAC,QAAQ,EAC9B,eAAe,CAAC,KAAK,CAAC,OAAO,EAC7B,aAAa,CACd,CAAC;QACF,IAAI,aAAa,IAAI,aAAa,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC;YACpE,KAAK,GAAG,aAAa,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,oBAAoB,GAAG,2BAA2B,eAAe,CAAC,KAAK,CAAC,QAAQ,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACtH,CAAC;IACH,CAAC;IAED,4FAA4F;IAC5F,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;YACpC,YAAY,EAAE,EAAE;YAChB,YAAY,EAAE,kBAAkB;YAChC,eAAe,EAAE,eAAe,CAAC,kBAAkB,EAAE;YACrD,cAAc,EAAE,eAAe,CAAC,eAAe,EAAE;YACjD,oBAAoB,EAAE,eAAe,CAAC,uBAAuB,EAAE;YAC/D,aAAa;SACd,CAAC,CAAC;QACH,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QACrB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,oBAAoB,GAAG,8BAA8B,EAAE,CAAC;QAC1D,CAAC;aAAM,IAAI,oBAAoB,EAAE,CAAC;YAChC,oBAAoB,IAAI,WAAW,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;QAClE,CAAC;IACH,CAAC;IAED,IAAI,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAE1C,sDAAsD;IACtD,IAAI,aAAa,KAAK,SAAS,IAAI,kBAAkB,EAAE,CAAC;QACtD,aAAa,GAAG,gBAAgB;YAC9B,CAAC,CAAE,eAAe,CAAC,aAA+B;YAClD,CAAC,CAAC,CAAC,eAAe,CAAC,uBAAuB,EAAE,IAAI,sBAAsB,CAAC,CAAC;IAC5E,CAAC;IAED,gCAAgC;IAChC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,aAAa;YACX,eAAe,CAAC,uBAAuB,EAAE,IAAI,sBAAsB,CAAC;IACxE,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,aAAa,GAAG,KAAK,CAAC;IACxB,CAAC;SAAM,CAAC;QACN,aAAa,GAAG,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAkB,CAAC;IAC5E,CAAC;IAED,IAAI,qBAAyC,CAAC;IAC9C,IAAI,oBAAwC,CAAC;IAC7C,IAAI,kBAAsC,CAAC;IAC3C,MAAM,8BAA8B,GAAG,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC;IAC3E,MAAM,0BAA0B,GAC9B,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,8BAA8B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,CAAC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,MAAM,0BAA0B,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,+BAA+B,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjI,MAAM,2BAA2B,GAAG,eAAe,CAAC,uBAAuB,EAAE,CAAC;IAC9E,MAAM,oBAAoB,GAGxB,OAAO,CAAC,aAAa,KAAK,SAAS;QACjC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE;QAC9D,CAAC,CAAC,0BAA0B,KAAK,SAAS;YACxC,CAAC,CAAC,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,EAAE,gBAAgB,EAAE;YACzE,CAAC,CAAC,oBAAoB,KAAK,SAAS;gBAClC,CAAC,CAAC,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE;gBAC5D,CAAC,CAAC,0BAA0B,KAAK,SAAS;oBACxC,CAAC,CAAC,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,EAAE,gBAAgB,EAAE;oBACzE,CAAC,CAAC,2BAA2B,KAAK,SAAS;wBACzC,CAAC,CAAC,EAAE,aAAa,EAAE,2BAA2B,EAAE,MAAM,EAAE,iBAAiB,EAAE;wBAC3E,CAAC,CAAC,SAAS,CAAC;IACxB,IAAI,KAAK,IAAI,oBAAoB,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,mBAAmB,CAClC,KAAK,EACL,oBAAoB,CAAC,aAAa,EAClC,wCAAwC,CACzC,CAAC;QACF,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;YACxB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;gBAChC,kBAAkB,GAAG,QAAQ,CAAC,KAAK,CAAC;YACtC,CAAC;iBAAM,IAAI,oBAAoB,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;gBAC7D,oBAAoB,GAAG,QAAQ,CAAC,KAAK,CAAC;YACxC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;YACvB,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC;QACjD,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GACpB,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAChE,MAAM,sBAAsB,GAAa,OAAO,CAAC,KAAK;QACpD,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACpB,CAAC,CAAC,OAAO,CAAC,OAAO;YACf,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC;IAE5B,IAAI,KAAY,CAAC;IAEjB,IAAI,wBAA+C,CAAC;IAEpD,+FAA+F;IAC/F,MAAM,2BAA2B,GAAG,CAAC,QAAwB,EAAa,EAAE;QAC1E,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACzC,+DAA+D;QAC/D,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,EAAE,CAAC;YACtC,wBAAwB,GAAG,SAAS,CAAC;YACrC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACrC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACrD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;gBAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;oBAC1D,IAAI,SAAS,EAAE,CAAC;wBACd,MAAM,eAAe,GAAG,OAAO;6BAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,CAAC,IAAI,KAAK,OAAO;4BAChB,CAAC,CAAC;gCACE,IAAI,EAAE,MAAe;gCACrB,IAAI,EAAE,4BAA4B;6BACnC;4BACH,CAAC,CAAC,CAAC,CACN;6BACA,MAAM,CACL,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE;wBACZ,wDAAwD;wBACxD,CAAC,CACC,CAAC,CAAC,IAAI,KAAK,MAAM;4BACjB,CAAC,CAAC,IAAI,KAAK,4BAA4B;4BACvC,CAAC,GAAG,CAAC;4BACL,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM;4BACzB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAoC,CAAC,IAAI;gCACjD,4BAA4B,CAC/B,CACJ,CAAC;wBACJ,OAAO,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;oBAC9C,CAAC;gBACH,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;QACH,wBAAwB,GAAG,QAAQ,CAAC;QACpC,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAkC,EAAE,CAAC;IAC7D,MAAM,sBAAsB,GAAG,CAAC,YAAwB,EAAW,EAAE,CACnE,wBAAwB,CACtB,YAAY,EACZ,eAAe,CAAC,wBAAwB,EAAE,EAC1C,OAAO,CAAC,oBAAoB,CAC7B,CAAC;IAEJ,KAAK,GAAG,IAAI,KAAK,CAAC;QAChB,YAAY,EAAE;YACZ,YAAY,EAAE,EAAE;YAChB,KAAK;YACL,aAAa;YACb,KAAK,EAAE,EAAE;SACV;QACD,YAAY,EAAE,2BAA2B;QACzC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE;YAChD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAC5D,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;YACD,MAAM,qBAAqB,GAAG,eAAe,CAAC,wBAAwB,EAAE,CAAC;YACzE,MAAM,iBAAiB,GAAG,eAAe,CAAC,oBAAoB,EAAE,CAAC;YACjE,qEAAqE;YACrE,oDAAoD;YACpD,MAAM,kBAAkB,GAAG,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC;YACpF,MAAM,SAAS,GAAG,aAAa,EAAE,SAAS,IAAI,qBAAqB,CAAC,SAAS,IAAI,kBAAkB,CAAC;YACpG,MAAM,yBAAyB,GAC7B,aAAa,EAAE,yBAAyB,IAAI,eAAe,CAAC,4BAA4B,EAAE,CAAC;YAC7F,MAAM,kBAAkB,GAAG,+BAA+B,CACxD,KAAK,EACL,eAAe,EACf,aAAa,EAAE,SAAS,EACxB,IAAI,CAAC,OAAO,EACZ,aAAa,EAAE,OAAO,CACvB,CAAC;YACF,MAAM,eAAe,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,0BAA0B,GAAG,8BAA8B,CAC/D;gBACE,GAAG,aAAa;gBAChB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,SAAS;gBACT,yBAAyB;gBACzB,UAAU,EAAE,aAAa,EAAE,UAAU,IAAI,qBAAqB,CAAC,UAAU;gBACzE,eAAe,EACb,aAAa,EAAE,eAAe,IAAI,qBAAqB,CAAC,eAAe;gBACzE,OAAO,EAAE,kBAAkB;aAC5B,EACD,eAAe,CAChB,CAAC;YACF,IAAI,aAAa,CAAC,+BAA+B,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7D,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,0BAA0B,CAAC,CAAC;YAClE,CAAC;YACD,OAAO,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,0BAA0B,CAAC,CAAC;QAC7E,CAAC;QACD,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAClC,MAAM,eAAe,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,cAAc,GAAG,wBAAwB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;YAC1E,MAAM,cAAc,GAAG,wBAAwB,CAAC;YAChD,MAAM,oBAAoB,GAAG,cAAc;gBACzC,CAAC,CAAC,oCAAoC,CAAC,cAAc,EAAE,cAAc,EAAE,KAAK,CAAC;gBAC7E,CAAC,CAAC,cAAc,CAAC;YACnB,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC;YAC1C,IAAI,YAAqB,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,yBAAyB,CAAC,EAAE,CAAC;gBACpD,YAAY,GAAG,oBAAoB,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACN,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAC7D,oBAAoB,CACrB,CAAC;gBACF,YAAY,GAAG,cAAc;oBAC3B,CAAC,CAAC,oCAAoC,CAAC,gBAAgB,EAAE,cAAc,EAAE,KAAK,CAAC;oBAC/E,CAAC,CAAC,gBAAgB,CAAC;YACvB,CAAC;YACD,yEAAyE;YACzE,sEAAsE;YACtE,0EAA0E;YAC1E,uEAAuE;YACvE,oEAAoE;YACpE,oDAAoD;YACpD,MAAM,eAAe,GAAG,4BAA4B,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YAC1E,kEAAkE;YAClE,uEAAuE;YACvE,sEAAsE;YACtE,+DAA+D;YAC/D,uEAAuE;YACvE,MAAM,oBAAoB,GAAG,yCAAyC,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;YAC/F,0EAA0E;YAC1E,8DAA8D;YAC9D,uEAAuE;YACvE,wEAAwE;YACxE,sEAAsE;YACtE,OAAO,mCAAmC,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;QACD,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC;YAC1C,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,yBAAyB,CAAC,EAAE,CAAC;gBACpD,OAAO;YACT,CAAC;YACD,MAAM,MAAM,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,yBAAyB;gBAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;aAC1B,CAAC,CAAC;QACL,CAAC;QACD,SAAS,EAAE,cAAc,CAAC,YAAY,EAAE;QACxC,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;YACnC,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC;YAC1C,IAAI,CAAC,MAAM;gBAAE,OAAO,QAAQ,CAAC;YAC7B,OAAO,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;QACD,YAAY,EAAE,eAAe,CAAC,eAAe,EAAE;QAC/C,YAAY,EAAE,eAAe,CAAC,eAAe,EAAE;QAC/C,SAAS,EAAE,eAAe,CAAC,YAAY,EAAE;QACzC,eAAe,EAAE,eAAe,CAAC,kBAAkB,EAAE;QACrD,eAAe,EAAE,eAAe,CAAC,wBAAwB,EAAE,CAAC,eAAe;KAC5E,CAAC,CAAC;IAEH,gDAAgD;IAChD,IAAI,kBAAkB,EAAE,CAAC;QACvB,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC;QAChD,MAAM,uBAAuB,GAAG,KAAK;YACnC,CAAC,CAAC,CAAC,eAAe,CAAC,aAAa,IAAI,4BAA4B,CAAC,KAAK,CAAC,CAAC;YACxE,CAAC,CAAC,SAAS,CAAC;QACd,IACE,qBAAqB,KAAK,SAAS;YACnC,CAAC,8BAA8B,IAAI,qBAAqB,KAAK,uBAAuB,CAAC,EACrF,CAAC;YACD,cAAc,CAAC,yBAAyB,CAAC,qBAAqB,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,cAAc,CAAC,yBAAyB,CAAC,aAAa,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;SAAM,CAAC;QACN,2FAA2F;QAC3F,IAAI,KAAK,EAAE,CAAC;YACV,cAAc,CAAC,iBAAiB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAC3D,IACE,qBAAqB,KAAK,SAAS;gBACnC,CAAC,8BAA8B,IAAI,qBAAqB,KAAK,4BAA4B,CAAC,KAAK,CAAC,CAAC,EACjG,CAAC;gBACD,cAAc,CAAC,yBAAyB,CAAC,qBAAqB,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QACD,cAAc,CAAC,yBAAyB,CAAC,aAAa,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC;QAC/B,KAAK;QACL,cAAc;QACd,eAAe;QACf,GAAG;QACH,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,cAAc;QACd,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,aAAa;QACb,sBAAsB;QACtB,gBAAgB;QAChB,iBAAiB,EAAE,OAAO,CAAC,aAAa;QACxC,kBAAkB;QAClB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;KACnD,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,cAAc,CAAC,aAAa,EAAE,CAAC;IAExD,OAAO;QACL,OAAO;QACP,gBAAgB;QAChB,oBAAoB;QACpB,oBAAoB;QACpB,kBAAkB;KACnB,CAAC;AACJ,CAAC","sourcesContent":["import { join } from \"node:path\";\nimport {\n Agent,\n type AgentMessage,\n type ThinkingLevel,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n clampThinkingLevel,\n type Api,\n type Message,\n type Model,\n streamSimple,\n} from \"@earendil-works/pi-ai\";\nimport { getAgentDir } from \"../config.ts\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport { formatNoModelsAvailableMessage } from \"./auth-guidance.ts\";\nimport { AuthStorage } from \"./auth-storage.ts\";\nimport {\n shouldApplyCodexFastMode,\n streamWithCodexFastMode,\n withCodexFastModePayload,\n withCodexFastModeStreamOptions,\n} from \"./codex-fast-mode.ts\";\nimport { restoreAnthropicReplayThinkingBlocks } from \"./anthropic-thinking-guard.ts\";\nimport { sanitizeCopilotGeminiPayload } from \"./copilot-gemini-payload-sanitizer.ts\";\nimport { restoreCopilotGeminiReasoningOpaque } from \"./copilot-gemini-reasoning.ts\";\nimport { normalizeCopilotGeminiReplayToolArguments } from \"./copilot-gemini-tool-arguments.ts\";\nimport { getModelDefaultContextWindow, getSupportedContextWindows, selectContextWindow } from \"./context-window.ts\";\nimport { DEFAULT_THINKING_LEVEL } from \"./defaults.ts\";\nimport type {\n ExtensionRunner,\n LoadExtensionsResult,\n OrchestrationContext,\n SessionStartEvent,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nimport { convertToLlm } from \"./messages.ts\";\nimport { ModelRegistry } from \"./model-registry.ts\";\nimport { findInitialModel, resolveSavedModelReference } from \"./model-resolver.ts\";\nimport type { ResourceLoader } from \"./resource-loader.ts\";\nimport { DefaultResourceLoader } from \"./resource-loader.ts\";\nimport { getDefaultSessionDir, SessionManager } from \"./session-manager.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\nimport { mergeProviderAttributionHeaders } from \"./provider-attribution.ts\";\nimport { time } from \"./timings.ts\";\nimport {\n createBashTool,\n createCodingTools,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadOnlyTools,\n createReadTool,\n STRUCTURED_OUTPUT_TOOL_NAME,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n createWriteTool,\n defaultToolNames,\n withFileMutationQueue,\n type BashCommandPolicy,\n} from \"./tools/index.ts\";\n\nexport interface CreateAgentSessionOptions {\n /** Working directory for project-local discovery. Default: process.cwd() */\n cwd?: string;\n /** Global config directory. Default: ~/.atomic/agent */\n agentDir?: string;\n\n /** Auth storage for credentials. Default: AuthStorage.create(agentDir/auth.json) */\n authStorage?: AuthStorage;\n /** Model registry. Default: ModelRegistry.create(authStorage, agentDir/models.json) */\n modelRegistry?: ModelRegistry;\n\n /** Model to use. Default: from settings, else first available */\n model?: Model<Api>;\n /** Thinking level. Default: from settings, else 'medium' (clamped to model capabilities) */\n thinkingLevel?: ThinkingLevel;\n /** Context window token count. Default: model scalar contextWindow, or settings/session override when supported. */\n contextWindow?: number;\n /** Treat unsupported contextWindow as an error instead of a warning/fallback. */\n contextWindowStrict?: boolean;\n /** Models available for cycling (Ctrl+P in interactive mode) */\n scopedModels?: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }>;\n\n /**\n * Optional default tool suppression mode when no explicit allowlist is provided.\n *\n * - \"all\": start with no tools enabled\n * - \"builtin\": disable the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) but keep extension/custom tools enabled\n */\n noTools?: \"all\" | \"builtin\";\n /**\n * Optional allowlist of tool names.\n *\n * When omitted, pi enables the default built-in tools (read, bash, edit, write,\n * ask_user_question, todo) and leaves extension/custom tools enabled unless\n * `noTools` changes that default.\n * When provided, only the listed tool names are enabled, minus any names in\n * `excludedTools`.\n */\n tools?: string[];\n /**\n * Optional blocklist of tool names.\n *\n * Matching built-in, extension, and SDK custom tools are omitted from the\n * final session tool registry and active tool set. Unknown names are ignored.\n */\n excludedTools?: string[];\n /** Custom tools to register (in addition to built-in tools). */\n customTools?: ToolDefinition[];\n /** Optional command-level policy for the built-in bash tool. Does not expose bash by itself. */\n bashPolicy?: BashCommandPolicy;\n\n /** Resource loader. When omitted, DefaultResourceLoader is used. */\n resourceLoader?: ResourceLoader;\n\n /** Session manager. Default: SessionManager.create(cwd) */\n sessionManager?: SessionManager;\n\n /** Settings manager. Default: SettingsManager.create(cwd, agentDir) */\n settingsManager?: SettingsManager;\n /** Session start event metadata for extension runtime startup. */\n sessionStartEvent?: SessionStartEvent;\n /** Session-scoped orchestration policy exposed to extension/tool handlers. */\n orchestrationContext?: OrchestrationContext;\n}\n\n/** Result from createAgentSession */\nexport interface CreateAgentSessionResult {\n /** The created session */\n session: AgentSession;\n /** Extensions result (for UI context setup in interactive mode) */\n extensionsResult: LoadExtensionsResult;\n /** Warning if session was restored with a different model than saved */\n modelFallbackMessage?: string;\n /** Warning if a saved/default context window could not be applied to the selected model. */\n contextWindowWarning?: string;\n /** Error if an explicit strict context-window selection is unsupported. */\n contextWindowError?: string;\n}\n\n// Re-exports\n\nexport * from \"./agent-session-runtime.ts\";\nexport type {\n ExtensionAPI,\n ExtensionCommandContext,\n ExtensionContext,\n ExtensionFactory,\n SlashCommandInfo,\n SlashCommandSource,\n ToolDefinition,\n} from \"./extensions/index.ts\";\nexport type { PromptTemplate } from \"./prompt-templates.ts\";\nexport type { Skill } from \"./skills.ts\";\nexport type {\n BashCommandParseError,\n BashCommandParseResult,\n BashCommandPolicy,\n BashCommandPolicyDecision,\n BashCommandPolicyMatchMode,\n BashCommandPolicyRejection,\n BashCommandRule,\n BashCommandSegment,\n BashCommandSegmentSource,\n JsonObject,\n JsonPrimitive,\n JsonValue,\n StructuredOutputCapture,\n StructuredOutputFileCapture,\n StructuredOutputToolOptions,\n Tool,\n} from \"./tools/index.ts\";\n\nexport {\n withFileMutationQueue,\n STRUCTURED_OUTPUT_TOOL_NAME,\n // Tool factories (for custom cwd)\n createCodingTools,\n createReadOnlyTools,\n createReadTool,\n createBashTool,\n createEditTool,\n createWriteTool,\n createGrepTool,\n createFindTool,\n createLsTool,\n createStructuredOutputCapture,\n createStructuredOutputTool,\n};\n\n// Helper Functions\n\nfunction getDefaultAgentDir(): string {\n return getAgentDir();\n}\n\ntype ContextWindowRequestSource = \"explicit\" | \"incoming-model\" | \"session\" | \"model-settings\" | \"global-settings\";\n\nconst COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS = { allowCopilotLongContextFallback: true } as const;\n\nfunction getAlreadyAppliedContextWindow(model: Model<Api>): number | undefined {\n const defaultContextWindow = getModelDefaultContextWindow(model);\n if (model.contextWindow === defaultContextWindow) {\n return undefined;\n }\n\n return getSupportedContextWindows(model).includes(model.contextWindow)\n ? model.contextWindow\n : undefined;\n}\n\n/**\n * Create an AgentSession with the specified options.\n *\n * @example\n * ```typescript\n * // Minimal - uses defaults\n * const { session } = await createAgentSession();\n *\n * // With explicit model\n * import { getModel } from '@earendil-works/pi-ai';\n * const { session } = await createAgentSession({\n * model: getModel('anthropic', 'claude-opus-4-5'),\n * thinkingLevel: 'high',\n * });\n *\n * // Continue previous session\n * const { session, modelFallbackMessage } = await createAgentSession({\n * continueSession: true,\n * });\n *\n * // Full control\n * const loader = new DefaultResourceLoader({\n * cwd: process.cwd(),\n * agentDir: getAgentDir(),\n * settingsManager: SettingsManager.create(),\n * });\n * await loader.reload();\n * const { session } = await createAgentSession({\n * model: myModel,\n * tools: [\"read\", \"bash\"],\n * resourceLoader: loader,\n * sessionManager: SessionManager.inMemory(),\n * });\n * ```\n */\nexport async function createAgentSession(\n options: CreateAgentSessionOptions = {},\n): Promise<CreateAgentSessionResult> {\n const cwd = resolvePath(options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd());\n const agentDir = options.agentDir ? resolvePath(options.agentDir) : getDefaultAgentDir();\n let resourceLoader = options.resourceLoader;\n\n // Use provided or create AuthStorage and ModelRegistry. When a modelRegistry\n // is supplied (e.g. a workflow stage reusing one registry across model\n // fallback candidates), do NOT also build a fresh AuthStorage: its\n // constructor eagerly calls reload(), which acquires the auth.json file lock\n // and, under contention, can fail and leave an empty credential set. Reusing\n // the supplied registry's already-loaded auth avoids that race (issue #1431).\n const authPath = options.agentDir ? join(agentDir, \"auth.json\") : undefined;\n const modelsPath = options.agentDir\n ? join(agentDir, \"models.json\")\n : undefined;\n const modelRegistry =\n options.modelRegistry ??\n ModelRegistry.create(options.authStorage ?? AuthStorage.create(authPath), modelsPath);\n\n const settingsManager =\n options.settingsManager ?? SettingsManager.create(cwd, agentDir);\n const sessionManager =\n options.sessionManager ??\n SessionManager.create(cwd, getDefaultSessionDir(cwd, agentDir));\n\n if (!resourceLoader) {\n resourceLoader = new DefaultResourceLoader({\n cwd,\n agentDir,\n settingsManager,\n });\n await resourceLoader.reload();\n time(\"resourceLoader.reload\");\n }\n\n // Check if session has existing data to restore\n const existingSession = sessionManager.buildSessionContext();\n const hasExistingSession = existingSession.messages.length > 0;\n const hasThinkingEntry = sessionManager\n .getBranch()\n .some((entry) => entry.type === \"thinking_level_change\");\n\n let model = options.model;\n let modelFallbackMessage: string | undefined;\n\n // If session has data, try to restore model from it\n if (!model && hasExistingSession && existingSession.model) {\n const restoredModel = await resolveSavedModelReference(\n existingSession.model.provider,\n existingSession.model.modelId,\n modelRegistry,\n );\n if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n model = restoredModel;\n }\n if (!model) {\n modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n }\n }\n\n // If still no model, use findInitialModel (checks settings default, then provider defaults)\n if (!model) {\n const result = await findInitialModel({\n scopedModels: [],\n isContinuing: hasExistingSession,\n defaultProvider: settingsManager.getDefaultProvider(),\n defaultModelId: settingsManager.getDefaultModel(),\n defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n modelRegistry,\n });\n model = result.model;\n if (!model) {\n modelFallbackMessage = formatNoModelsAvailableMessage();\n } else if (modelFallbackMessage) {\n modelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n }\n }\n\n let thinkingLevel = options.thinkingLevel;\n\n // If session has data, restore thinking level from it\n if (thinkingLevel === undefined && hasExistingSession) {\n thinkingLevel = hasThinkingEntry\n ? (existingSession.thinkingLevel as ThinkingLevel)\n : (settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);\n }\n\n // Fall back to settings default\n if (thinkingLevel === undefined) {\n thinkingLevel =\n settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;\n }\n\n // Clamp to model capabilities\n if (!model) {\n thinkingLevel = \"off\";\n } else {\n thinkingLevel = clampThinkingLevel(model, thinkingLevel) as ThinkingLevel;\n }\n\n let selectedContextWindow: number | undefined;\n let contextWindowWarning: string | undefined;\n let contextWindowError: string | undefined;\n const explicitContextWindowSelection = options.contextWindow !== undefined;\n const incomingModelContextWindow =\n model && options.model ? getAlreadyAppliedContextWindow(model) : undefined;\n const sessionContextWindow = hasExistingSession ? existingSession.contextWindow : undefined;\n const modelSettingsContextWindow = model ? settingsManager.getDefaultContextWindowForModel(model.provider, model.id) : undefined;\n const globalSettingsContextWindow = settingsManager.getDefaultContextWindow();\n const contextWindowRequest:\n | { contextWindow: number; source: ContextWindowRequestSource }\n | undefined =\n options.contextWindow !== undefined\n ? { contextWindow: options.contextWindow, source: \"explicit\" }\n : incomingModelContextWindow !== undefined\n ? { contextWindow: incomingModelContextWindow, source: \"incoming-model\" }\n : sessionContextWindow !== undefined\n ? { contextWindow: sessionContextWindow, source: \"session\" }\n : modelSettingsContextWindow !== undefined\n ? { contextWindow: modelSettingsContextWindow, source: \"model-settings\" }\n : globalSettingsContextWindow !== undefined\n ? { contextWindow: globalSettingsContextWindow, source: \"global-settings\" }\n : undefined;\n if (model && contextWindowRequest !== undefined) {\n const selected = selectContextWindow(\n model,\n contextWindowRequest.contextWindow,\n COPILOT_CONTEXT_WINDOW_SELECTION_OPTIONS,\n );\n if (\"error\" in selected) {\n if (options.contextWindowStrict) {\n contextWindowError = selected.error;\n } else if (contextWindowRequest.source !== \"global-settings\") {\n contextWindowWarning = selected.error;\n }\n } else {\n model = selected.model;\n selectedContextWindow = selected.contextWindow;\n }\n }\n\n const allowedToolNames =\n options.tools ?? (options.noTools === \"all\" ? [] : undefined);\n const initialActiveToolNames: string[] = options.tools\n ? [...options.tools]\n : options.noTools\n ? []\n : [...defaultToolNames];\n\n let agent: Agent;\n\n let lastConvertedLlmMessages: Message[] | undefined;\n\n // Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)\n const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {\n const converted = convertToLlm(messages);\n // Check setting dynamically so mid-session changes take effect\n if (!settingsManager.getBlockImages()) {\n lastConvertedLlmMessages = converted;\n return converted;\n }\n // Filter out ImageContent from all messages, replacing with text placeholder\n const filtered = converted.map((msg) => {\n if (msg.role === \"user\" || msg.role === \"toolResult\") {\n const content = msg.content;\n if (Array.isArray(content)) {\n const hasImages = content.some((c) => c.type === \"image\");\n if (hasImages) {\n const filteredContent = content\n .map((c) =>\n c.type === \"image\"\n ? {\n type: \"text\" as const,\n text: \"Image reading is disabled.\",\n }\n : c,\n )\n .filter(\n (c, i, arr) =>\n // Dedupe consecutive \"Image reading is disabled.\" texts\n !(\n c.type === \"text\" &&\n c.text === \"Image reading is disabled.\" &&\n i > 0 &&\n arr[i - 1].type === \"text\" &&\n (arr[i - 1] as { type: \"text\"; text: string }).text ===\n \"Image reading is disabled.\"\n ),\n );\n return { ...msg, content: filteredContent };\n }\n }\n }\n return msg;\n });\n lastConvertedLlmMessages = filtered;\n return filtered;\n };\n\n const extensionRunnerRef: { current?: ExtensionRunner } = {};\n const isCodexFastModeEnabled = (requestModel: Model<Api>): boolean =>\n shouldApplyCodexFastMode(\n requestModel,\n settingsManager.getCodexFastModeSettings(),\n options.orchestrationContext,\n );\n\n agent = new Agent({\n initialState: {\n systemPrompt: \"\",\n model,\n thinkingLevel,\n tools: [],\n },\n convertToLlm: convertToLlmWithBlockImages,\n streamFn: async (model, context, streamOptions) => {\n const auth = await modelRegistry.getApiKeyAndHeaders(model);\n if (!auth.ok) {\n throw new Error(auth.error);\n }\n const providerRetrySettings = settingsManager.getProviderRetrySettings();\n const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();\n // SDKs treat timeout=0 as 0ms (immediate timeout), not \"no timeout\".\n // Use max int32 to effectively disable the timeout.\n const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;\n const timeoutMs = streamOptions?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;\n const websocketConnectTimeoutMs =\n streamOptions?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();\n const attributionHeaders = mergeProviderAttributionHeaders(\n model,\n settingsManager,\n streamOptions?.sessionId,\n auth.headers,\n streamOptions?.headers,\n );\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const codexFastModeStreamOptions = withCodexFastModeStreamOptions(\n {\n ...streamOptions,\n apiKey: auth.apiKey,\n timeoutMs,\n websocketConnectTimeoutMs,\n maxRetries: streamOptions?.maxRetries ?? providerRetrySettings.maxRetries,\n maxRetryDelayMs:\n streamOptions?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,\n headers: attributionHeaders,\n },\n fastModeEnabled,\n );\n if (modelRegistry.hasRegisteredStreamSimpleForApi(model.api)) {\n return streamSimple(model, context, codexFastModeStreamOptions);\n }\n return streamWithCodexFastMode(model, context, codexFastModeStreamOptions);\n },\n onPayload: async (payload, model) => {\n const fastModeEnabled = isCodexFastModeEnabled(model);\n const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);\n const sourceMessages = lastConvertedLlmMessages;\n const replayGuardedPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model)\n : guardedPayload;\n const runner = extensionRunnerRef.current;\n let finalPayload: unknown;\n if (!runner?.hasHandlers(\"before_provider_request\")) {\n finalPayload = replayGuardedPayload;\n } else {\n const extensionPayload = await runner.emitBeforeProviderRequest(\n replayGuardedPayload,\n );\n finalPayload = sourceMessages\n ? restoreAnthropicReplayThinkingBlocks(extensionPayload, sourceMessages, model)\n : extensionPayload;\n }\n // GitHub Copilot Gemini models are served through CAPI, which translates\n // the OpenAI request into Google GenAI and rejects tool schemas whose\n // `anyOf`/`oneOf` wraps a complex object (HTTP 400 invalid request body).\n // Sanitize tool JSON Schemas into Gemini's supported subset. No-op for\n // every other provider/model, and runs last so it also covers tools\n // injected by `before_provider_request` extensions.\n const schemaSanitized = sanitizeCopilotGeminiPayload(finalPayload, model);\n // Reconstruct flattened tool-call arguments on replayed assistant\n // messages (for example `edits[0].newText` -> `edits: [{ newText }]`).\n // CAPI parses replayed arguments straight into Gemini's FunctionCall,\n // and a flattened/malformed prior call ends the next turn with\n // `finish_reason: \"error\"`. No-op for well-formed args / other models.\n const replayArgsNormalized = normalizeCopilotGeminiReplayToolArguments(schemaSanitized, model);\n // CAPI carries Gemini thought signatures in a `reasoning_opaque` field it\n // reads back off the assistant message on replay. Convert the\n // `reasoning_details` the client re-emits (captured inbound by the SSE\n // interceptor) into that field so multi-turn tool use keeps its thought\n // signature instead of dying on an empty completion. No-op otherwise.\n return restoreCopilotGeminiReasoningOpaque(replayArgsNormalized, model);\n },\n onResponse: async (response, _model) => {\n const runner = extensionRunnerRef.current;\n if (!runner?.hasHandlers(\"after_provider_response\")) {\n return;\n }\n await runner.emit({\n type: \"after_provider_response\",\n status: response.status,\n headers: response.headers,\n });\n },\n sessionId: sessionManager.getSessionId(),\n transformContext: async (messages) => {\n const runner = extensionRunnerRef.current;\n if (!runner) return messages;\n return runner.emitContext(messages);\n },\n steeringMode: settingsManager.getSteeringMode(),\n followUpMode: settingsManager.getFollowUpMode(),\n transport: settingsManager.getTransport(),\n thinkingBudgets: settingsManager.getThinkingBudgets(),\n maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs,\n });\n\n // Restore messages if session has existing data\n if (hasExistingSession) {\n agent.state.messages = existingSession.messages;\n const transcriptContextWindow = model\n ? (existingSession.contextWindow ?? getModelDefaultContextWindow(model))\n : undefined;\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== transcriptContextWindow)\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n if (!hasThinkingEntry) {\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n } else {\n // Save initial model and thinking level for new sessions so they can be restored on resume\n if (model) {\n sessionManager.appendModelChange(model.provider, model.id);\n if (\n selectedContextWindow !== undefined &&\n (explicitContextWindowSelection || selectedContextWindow !== getModelDefaultContextWindow(model))\n ) {\n sessionManager.appendContextWindowChange(selectedContextWindow);\n }\n }\n sessionManager.appendThinkingLevelChange(thinkingLevel);\n }\n\n const session = new AgentSession({\n agent,\n sessionManager,\n settingsManager,\n cwd,\n scopedModels: options.scopedModels,\n resourceLoader,\n customTools: options.customTools,\n bashPolicy: options.bashPolicy,\n modelRegistry,\n initialActiveToolNames,\n allowedToolNames,\n excludedToolNames: options.excludedTools,\n extensionRunnerRef,\n sessionStartEvent: options.sessionStartEvent,\n orchestrationContext: options.orchestrationContext,\n });\n const extensionsResult = resourceLoader.getExtensions();\n\n return {\n session,\n extensionsResult,\n modelFallbackMessage,\n contextWindowWarning,\n contextWindowError,\n };\n}\n"]}