@code-yeongyu/senpi 2026.9.11 → 2026.9.12-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  3. package/dist/core/agent-session-runtime.js +5 -1
  4. package/dist/core/agent-session-runtime.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +13 -9
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +54 -121
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/cursor-history-admission.d.ts +50 -0
  10. package/dist/core/cursor-history-admission.d.ts.map +1 -0
  11. package/dist/core/cursor-history-admission.js +130 -0
  12. package/dist/core/cursor-history-admission.js.map +1 -0
  13. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
  14. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +2 -1
  15. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
  16. package/dist/core/session-manager.d.ts +2 -0
  17. package/dist/core/session-manager.d.ts.map +1 -1
  18. package/dist/core/session-manager.js +22 -13
  19. package/dist/core/session-manager.js.map +1 -1
  20. package/dist/core/session-write-reservation.d.ts +9 -0
  21. package/dist/core/session-write-reservation.d.ts.map +1 -1
  22. package/dist/core/session-write-reservation.js +33 -0
  23. package/dist/core/session-write-reservation.js.map +1 -1
  24. package/dist/modes/interactive/components/ask-user-async-widget.d.ts +2 -0
  25. package/dist/modes/interactive/components/ask-user-async-widget.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/ask-user-async-widget.js +17 -1
  27. package/dist/modes/interactive/components/ask-user-async-widget.js.map +1 -1
  28. package/dist/modes/interactive/interactive-mode.js +2 -2
  29. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  30. package/dist/modes/rpc/rpc-types.d.ts +2 -1
  31. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  32. package/dist/modes/rpc/rpc-types.js +1 -0
  33. package/dist/modes/rpc/rpc-types.js.map +1 -1
  34. package/dist/modes/rpc/session-path-reservations.d.ts +30 -0
  35. package/dist/modes/rpc/session-path-reservations.d.ts.map +1 -0
  36. package/dist/modes/rpc/session-path-reservations.js +58 -0
  37. package/dist/modes/rpc/session-path-reservations.js.map +1 -0
  38. package/dist/modes/rpc/session-registry.d.ts +3 -1
  39. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  40. package/dist/modes/rpc/session-registry.js.map +1 -1
  41. package/dist/modes/rpc/session-worker-client.d.ts +10 -8
  42. package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
  43. package/dist/modes/rpc/session-worker-client.js +14 -23
  44. package/dist/modes/rpc/session-worker-client.js.map +1 -1
  45. package/dist/modes/rpc/session-worker-credit.d.ts +17 -0
  46. package/dist/modes/rpc/session-worker-credit.d.ts.map +1 -0
  47. package/dist/modes/rpc/session-worker-credit.js +39 -0
  48. package/dist/modes/rpc/session-worker-credit.js.map +1 -0
  49. package/dist/modes/rpc/session-worker-protocol.d.ts +10 -0
  50. package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -1
  51. package/dist/modes/rpc/session-worker-protocol.js +2 -0
  52. package/dist/modes/rpc/session-worker-protocol.js.map +1 -1
  53. package/dist/modes/rpc/session-worker-signals.d.ts +7 -0
  54. package/dist/modes/rpc/session-worker-signals.d.ts.map +1 -0
  55. package/dist/modes/rpc/session-worker-signals.js +19 -0
  56. package/dist/modes/rpc/session-worker-signals.js.map +1 -0
  57. package/dist/modes/rpc/session-worker.js +12 -16
  58. package/dist/modes/rpc/session-worker.js.map +1 -1
  59. package/dist/modes/rpc/worker-session-registry.d.ts +12 -0
  60. package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -1
  61. package/dist/modes/rpc/worker-session-registry.js +51 -24
  62. package/dist/modes/rpc/worker-session-registry.js.map +1 -1
  63. package/docs/rpc.md +14 -6
  64. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  65. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  66. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  67. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +11 -0
  68. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -1
  69. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +18 -0
  70. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -1
  71. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +1 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +5 -1
  74. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts +2 -0
  76. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts.map +1 -1
  77. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js +96 -51
  78. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js.map +1 -1
  79. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts +389 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts.map +1 -1
  81. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js +132 -4
  82. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js.map +1 -1
  83. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts +34 -4
  84. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts.map +1 -1
  85. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js +41 -8
  86. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js.map +1 -1
  87. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts +20 -0
  88. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts.map +1 -1
  89. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js +20 -0
  90. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js.map +1 -1
  91. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts +18 -6
  92. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts.map +1 -1
  93. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js +77 -35
  94. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js.map +1 -1
  95. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts +9 -3
  96. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts.map +1 -1
  97. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js +30 -38
  98. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js.map +1 -1
  99. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.d.ts +16 -0
  100. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.d.ts.map +1 -0
  101. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.js +69 -0
  102. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.js.map +1 -0
  103. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.d.ts +26 -0
  104. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.d.ts.map +1 -0
  105. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.js +61 -0
  106. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.js.map +1 -0
  107. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts +6 -4
  108. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts.map +1 -1
  109. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js +6 -4
  110. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js.map +1 -1
  111. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts +13 -5
  112. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts.map +1 -1
  113. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js +76 -21
  114. package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js.map +1 -1
  115. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts +5 -0
  116. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts.map +1 -1
  117. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js +7 -2
  118. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js.map +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts +32 -0
  120. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts.map +1 -0
  121. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js +62 -0
  122. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js.map +1 -0
  123. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.d.ts.map +1 -1
  124. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js +2 -1
  125. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js.map +1 -1
  126. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  128. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  130. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +11 -1
  131. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
  132. package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
  133. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -1
  134. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +2 -1
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.json +1 -1
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.d.ts.map +1 -1
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js +3 -3
  143. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js.map +1 -1
  144. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts +4 -2
  145. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts.map +1 -1
  146. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js +18 -9
  147. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js.map +1 -1
  148. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts +12 -0
  149. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts.map +1 -0
  150. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js +93 -0
  151. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js.map +1 -0
  152. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  153. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  154. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  155. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  156. package/package.json +7 -7
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Unary Cascade RPCs (`GetUserJwt`, `AssignModel`, `GetCliModelConfigs`).
3
+ *
4
+ * Unlike the streaming chat call these carry a bare protobuf body both ways.
5
+ * Edges variously answer with plain or gzipped protobuf, so decoding tries the
6
+ * bytes as-is before falling back to gunzip. Discovery reaches this module from
7
+ * the provider factory, which is root-reachable, so the gunzip path uses the
8
+ * web `DecompressionStream` rather than `node:zlib`.
9
+ */
10
+ import { type DescMessage, type MessageInitShape, type MessageShape } from "@bufbuild/protobuf";
11
+ export declare class DevinUnaryError extends Error {
12
+ readonly rpc: string;
13
+ readonly status: number;
14
+ constructor(rpc: string, status: number, detail: string);
15
+ }
16
+ export interface DevinUnaryInput<TRequest extends DescMessage, TResponse extends DescMessage> {
17
+ baseUrl: string;
18
+ path: string;
19
+ requestSchema: TRequest;
20
+ request: MessageInitShape<TRequest>;
21
+ responseSchema: TResponse;
22
+ signal?: AbortSignal;
23
+ }
24
+ export declare function postDevinUnary<TRequest extends DescMessage, TResponse extends DescMessage>(input: DevinUnaryInput<TRequest, TResponse>): Promise<MessageShape<TResponse>>;
25
+ export declare function decodeDevinUnary<TSchema extends DescMessage>(schema: TSchema, payload: Uint8Array): Promise<MessageShape<TSchema> | undefined>;
26
+ //# sourceMappingURL=unary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unary.d.ts","sourceRoot":"","sources":["../../../src/api/devin-agent/unary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEN,KAAK,WAAW,EAEhB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAEjB,MAAM,oBAAoB,CAAC;AAG5B,qBAAa,eAAgB,SAAQ,KAAK;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAKtD;CACD;AAED,MAAM,WAAW,eAAe,CAAC,QAAQ,SAAS,WAAW,EAAE,SAAS,SAAS,WAAW;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,QAAQ,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,cAAc,EAAE,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,QAAQ,SAAS,WAAW,EAAE,SAAS,SAAS,WAAW,EAC/F,KAAK,EAAE,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,GACzC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAclC;AAED,wBAAsB,gBAAgB,CAAC,OAAO,SAAS,WAAW,EACjE,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,CAU5C"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Unary Cascade RPCs (`GetUserJwt`, `AssignModel`, `GetCliModelConfigs`).
3
+ *
4
+ * Unlike the streaming chat call these carry a bare protobuf body both ways.
5
+ * Edges variously answer with plain or gzipped protobuf, so decoding tries the
6
+ * bytes as-is before falling back to gunzip. Discovery reaches this module from
7
+ * the provider factory, which is root-reachable, so the gunzip path uses the
8
+ * web `DecompressionStream` rather than `node:zlib`.
9
+ */
10
+ import { create, fromBinary, toBinary, } from "@bufbuild/protobuf";
11
+ import { DEVIN_UNARY_HEADERS } from "./paths.js";
12
+ export class DevinUnaryError extends Error {
13
+ constructor(rpc, status, detail) {
14
+ super(`Devin ${rpc} failed (HTTP ${status})${detail ? `: ${detail}` : ""}`);
15
+ this.name = "DevinUnaryError";
16
+ this.rpc = rpc;
17
+ this.status = status;
18
+ }
19
+ }
20
+ export async function postDevinUnary(input) {
21
+ const response = await fetch(input.baseUrl + input.path, {
22
+ method: "POST",
23
+ headers: DEVIN_UNARY_HEADERS,
24
+ body: copyOf(toBinary(input.requestSchema, create(input.requestSchema, input.request))),
25
+ signal: input.signal,
26
+ });
27
+ const payload = new Uint8Array(await response.arrayBuffer());
28
+ if (!response.ok) {
29
+ throw new DevinUnaryError(rpcName(input.path), response.status, new TextDecoder().decode(payload).slice(0, 500));
30
+ }
31
+ const decoded = await decodeDevinUnary(input.responseSchema, payload);
32
+ if (!decoded)
33
+ throw new DevinUnaryError(rpcName(input.path), response.status, "response is not a protobuf message");
34
+ return decoded;
35
+ }
36
+ export async function decodeDevinUnary(schema, payload) {
37
+ try {
38
+ return fromBinary(schema, payload);
39
+ }
40
+ catch {
41
+ try {
42
+ return fromBinary(schema, await gunzip(payload));
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ }
48
+ }
49
+ async function gunzip(payload) {
50
+ const inflated = new Blob([copyOf(payload)]).stream().pipeThrough(new DecompressionStream("gzip"));
51
+ return new Uint8Array(await new Response(inflated).arrayBuffer());
52
+ }
53
+ function rpcName(path) {
54
+ return path.slice(path.lastIndexOf("/") + 1);
55
+ }
56
+ function copyOf(source) {
57
+ const out = new Uint8Array(source.byteLength);
58
+ out.set(source);
59
+ return out;
60
+ }
61
+ //# sourceMappingURL=unary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unary.js","sourceRoot":"","sources":["../../../src/api/devin-agent/unary.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unary Cascade RPCs (`GetUserJwt`, `AssignModel`, `GetCliModelConfigs`).\n *\n * Unlike the streaming chat call these carry a bare protobuf body both ways.\n * Edges variously answer with plain or gzipped protobuf, so decoding tries the\n * bytes as-is before falling back to gunzip. Discovery reaches this module from\n * the provider factory, which is root-reachable, so the gunzip path uses the\n * web `DecompressionStream` rather than `node:zlib`.\n */\n\nimport {\n\tcreate,\n\ttype DescMessage,\n\tfromBinary,\n\ttype MessageInitShape,\n\ttype MessageShape,\n\ttoBinary,\n} from \"@bufbuild/protobuf\";\nimport { DEVIN_UNARY_HEADERS } from \"./paths.ts\";\n\nexport class DevinUnaryError extends Error {\n\treadonly rpc: string;\n\treadonly status: number;\n\n\tconstructor(rpc: string, status: number, detail: string) {\n\t\tsuper(`Devin ${rpc} failed (HTTP ${status})${detail ? `: ${detail}` : \"\"}`);\n\t\tthis.name = \"DevinUnaryError\";\n\t\tthis.rpc = rpc;\n\t\tthis.status = status;\n\t}\n}\n\nexport interface DevinUnaryInput<TRequest extends DescMessage, TResponse extends DescMessage> {\n\tbaseUrl: string;\n\tpath: string;\n\trequestSchema: TRequest;\n\trequest: MessageInitShape<TRequest>;\n\tresponseSchema: TResponse;\n\tsignal?: AbortSignal;\n}\n\nexport async function postDevinUnary<TRequest extends DescMessage, TResponse extends DescMessage>(\n\tinput: DevinUnaryInput<TRequest, TResponse>,\n): Promise<MessageShape<TResponse>> {\n\tconst response = await fetch(input.baseUrl + input.path, {\n\t\tmethod: \"POST\",\n\t\theaders: DEVIN_UNARY_HEADERS,\n\t\tbody: copyOf(toBinary(input.requestSchema, create(input.requestSchema, input.request))),\n\t\tsignal: input.signal,\n\t});\n\tconst payload = new Uint8Array(await response.arrayBuffer());\n\tif (!response.ok) {\n\t\tthrow new DevinUnaryError(rpcName(input.path), response.status, new TextDecoder().decode(payload).slice(0, 500));\n\t}\n\tconst decoded = await decodeDevinUnary(input.responseSchema, payload);\n\tif (!decoded) throw new DevinUnaryError(rpcName(input.path), response.status, \"response is not a protobuf message\");\n\treturn decoded;\n}\n\nexport async function decodeDevinUnary<TSchema extends DescMessage>(\n\tschema: TSchema,\n\tpayload: Uint8Array,\n): Promise<MessageShape<TSchema> | undefined> {\n\ttry {\n\t\treturn fromBinary(schema, payload);\n\t} catch {\n\t\ttry {\n\t\t\treturn fromBinary(schema, await gunzip(payload));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nasync function gunzip(payload: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\tconst inflated = new Blob([copyOf(payload)]).stream().pipeThrough(new DecompressionStream(\"gzip\"));\n\treturn new Uint8Array(await new Response(inflated).arrayBuffer());\n}\n\nfunction rpcName(path: string): string {\n\treturn path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\nfunction copyOf(source: Uint8Array<ArrayBufferLike>): Uint8Array<ArrayBuffer> {\n\tconst out = new Uint8Array(source.byteLength);\n\tout.set(source);\n\treturn out;\n}\n"]}
@@ -1,6 +1,8 @@
1
- /** Cascade wire surface: paths, identity metadata, frame codec and request building. */
1
+ /** Cascade wire surface: paths, identity metadata, frame codec, unary RPCs and request building. */
2
2
  export { type DevinFrame, decodeDevinFrames, encodeDevinFrame as encodeDevinRequestFrame } from "./frames.ts";
3
- export { devinCliMetadata, normalizeDevinSessionToken } from "./metadata.ts";
4
- export { DEVIN_CHAT_MESSAGE_PATH, DEVIN_CLI_MODEL_CONFIGS_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_MAX_FRAME_PAYLOAD, } from "./paths.ts";
5
- export { buildDevinChatRequest, DEVIN_DEFAULT_STOP_PATTERNS, type DevinChatRequestInput } from "./request.ts";
3
+ export { DEVIN_CLI_IDENTITY, DEVIN_DISCOVERY_IDENTITY, DEVIN_SUPPORTED_MODEL_DISPLAYS, devinCliMetadata, devinDiscoveryMetadata, normalizeDevinSessionToken, } from "./metadata.ts";
4
+ export { DEVIN_ASSIGN_MODEL_PATH, DEVIN_CHAT_HEADERS, DEVIN_CHAT_MESSAGE_PATH, DEVIN_CLI_MODEL_CONFIGS_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_MAX_FRAME_PAYLOAD, DEVIN_UNARY_HEADERS, DEVIN_USER_JWT_PATH, } from "./paths.ts";
5
+ export { buildDevinChatRequest, buildDevinRouterPrompt, DEVIN_DEFAULT_STOP_PATTERNS, type DevinChatRequestInput, type DevinModelAssignment, } from "./request.ts";
6
+ export { type DevinTrailerError, readDevinTrailerError } from "./trailer.ts";
7
+ export { DevinUnaryError, type DevinUnaryInput, decodeDevinUnary, postDevinUnary } from "./unary.ts";
6
8
  //# sourceMappingURL=wire.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/api/devin-agent/wire.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,KAAK,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,uBAAuB,EACvB,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/api/devin-agent/wire.ts"],"names":[],"mappings":"AAAA,oGAAoG;AAEpG,OAAO,EAAE,KAAK,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,EACvB,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GACzB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -1,6 +1,8 @@
1
- /** Cascade wire surface: paths, identity metadata, frame codec and request building. */
1
+ /** Cascade wire surface: paths, identity metadata, frame codec, unary RPCs and request building. */
2
2
  export { decodeDevinFrames, encodeDevinFrame as encodeDevinRequestFrame } from "./frames.js";
3
- export { devinCliMetadata, normalizeDevinSessionToken } from "./metadata.js";
4
- export { DEVIN_CHAT_MESSAGE_PATH, DEVIN_CLI_MODEL_CONFIGS_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_MAX_FRAME_PAYLOAD, } from "./paths.js";
5
- export { buildDevinChatRequest, DEVIN_DEFAULT_STOP_PATTERNS } from "./request.js";
3
+ export { DEVIN_CLI_IDENTITY, DEVIN_DISCOVERY_IDENTITY, DEVIN_SUPPORTED_MODEL_DISPLAYS, devinCliMetadata, devinDiscoveryMetadata, normalizeDevinSessionToken, } from "./metadata.js";
4
+ export { DEVIN_ASSIGN_MODEL_PATH, DEVIN_CHAT_HEADERS, DEVIN_CHAT_MESSAGE_PATH, DEVIN_CLI_MODEL_CONFIGS_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_MAX_FRAME_PAYLOAD, DEVIN_UNARY_HEADERS, DEVIN_USER_JWT_PATH, } from "./paths.js";
5
+ export { buildDevinChatRequest, buildDevinRouterPrompt, DEVIN_DEFAULT_STOP_PATTERNS, } from "./request.js";
6
+ export { readDevinTrailerError } from "./trailer.js";
7
+ export { DevinUnaryError, decodeDevinUnary, postDevinUnary } from "./unary.js";
6
8
  //# sourceMappingURL=wire.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/api/devin-agent/wire.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAmB,iBAAiB,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,uBAAuB,EACvB,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAA8B,MAAM,cAAc,CAAC","sourcesContent":["/** Cascade wire surface: paths, identity metadata, frame codec and request building. */\n\nexport { type DevinFrame, decodeDevinFrames, encodeDevinFrame as encodeDevinRequestFrame } from \"./frames.ts\";\nexport { devinCliMetadata, normalizeDevinSessionToken } from \"./metadata.ts\";\nexport {\n\tDEVIN_CHAT_MESSAGE_PATH,\n\tDEVIN_CLI_MODEL_CONFIGS_PATH,\n\tDEVIN_DEFAULT_BASE_URL,\n\tDEVIN_MAX_FRAME_PAYLOAD,\n} from \"./paths.ts\";\nexport { buildDevinChatRequest, DEVIN_DEFAULT_STOP_PATTERNS, type DevinChatRequestInput } from \"./request.ts\";\n"]}
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/api/devin-agent/wire.ts"],"names":[],"mappings":"AAAA,oGAAoG;AAEpG,OAAO,EAAmB,iBAAiB,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,EACvB,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,GAG3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAA0B,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAwB,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/** Cascade wire surface: paths, identity metadata, frame codec, unary RPCs and request building. */\n\nexport { type DevinFrame, decodeDevinFrames, encodeDevinFrame as encodeDevinRequestFrame } from \"./frames.ts\";\nexport {\n\tDEVIN_CLI_IDENTITY,\n\tDEVIN_DISCOVERY_IDENTITY,\n\tDEVIN_SUPPORTED_MODEL_DISPLAYS,\n\tdevinCliMetadata,\n\tdevinDiscoveryMetadata,\n\tnormalizeDevinSessionToken,\n} from \"./metadata.ts\";\nexport {\n\tDEVIN_ASSIGN_MODEL_PATH,\n\tDEVIN_CHAT_HEADERS,\n\tDEVIN_CHAT_MESSAGE_PATH,\n\tDEVIN_CLI_MODEL_CONFIGS_PATH,\n\tDEVIN_DEFAULT_BASE_URL,\n\tDEVIN_MAX_FRAME_PAYLOAD,\n\tDEVIN_UNARY_HEADERS,\n\tDEVIN_USER_JWT_PATH,\n} from \"./paths.ts\";\nexport {\n\tbuildDevinChatRequest,\n\tbuildDevinRouterPrompt,\n\tDEVIN_DEFAULT_STOP_PATTERNS,\n\ttype DevinChatRequestInput,\n\ttype DevinModelAssignment,\n} from \"./request.ts\";\nexport { type DevinTrailerError, readDevinTrailerError } from \"./trailer.ts\";\nexport { DevinUnaryError, type DevinUnaryInput, decodeDevinUnary, postDevinUnary } from \"./unary.ts\";\n"]}
@@ -1,11 +1,19 @@
1
1
  /**
2
2
  * Devin (Cognition/Codeium Cascade) agent API.
3
3
  *
4
- * One `GetChatMessage` server-streaming Connect call per assistant turn. The
5
- * request is a single gzipped frame; the response is a frame sequence of
6
- * deltas, closed by a JSON trailer frame. Cascade reports text, thinking and
7
- * tool calls as separate delta fields on the same message, so this adapter owns
8
- * the block bookkeeping senpi's event protocol expects.
4
+ * One assistant turn is up to three RPCs, exactly as the released Devin CLI
5
+ * performs them:
6
+ * 1. `GetUserJwt` mints the short-lived user JWT the chat call must carry and
7
+ * names the account's API host, which can differ from the seeded one;
8
+ * 2. `AssignModel` resolves a server-side router into a concrete model uid plus
9
+ * the JWT that authorizes it (router models only);
10
+ * 3. `GetChatMessage` streams the turn: a single gzipped Connect frame out, a
11
+ * frame sequence of deltas back, closed by a JSON trailer that either
12
+ * carries the rejection or is empty.
13
+ *
14
+ * Cascade reports text, thinking and tool calls as separate delta fields on the
15
+ * same message, so this adapter owns the block bookkeeping senpi's event
16
+ * protocol expects.
9
17
  */
10
18
  import type { StreamFunction } from "../types.ts";
11
19
  import type { DevinAgentOptions } from "./devin-agent/types.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"devin-agent.d.ts","sourceRoot":"","sources":["../../src/api/devin-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAoC,cAAc,EAAY,MAAM,aAAa,CAAC;AAI9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAUhE,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,aAAa,EAAE,iBAAiB,CAQnE,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,aAAa,EAAE,iBAAiB,CAAU,CAAC"}
1
+ {"version":3,"file":"devin-agent.d.ts","sourceRoot":"","sources":["../../src/api/devin-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAoC,cAAc,EAAY,MAAM,aAAa,CAAC;AAW9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAiBhE,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,aAAa,EAAE,iBAAiB,CAQnE,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,aAAa,EAAE,iBAAiB,CAAU,CAAC"}
@@ -1,16 +1,24 @@
1
1
  /**
2
2
  * Devin (Cognition/Codeium Cascade) agent API.
3
3
  *
4
- * One `GetChatMessage` server-streaming Connect call per assistant turn. The
5
- * request is a single gzipped frame; the response is a frame sequence of
6
- * deltas, closed by a JSON trailer frame. Cascade reports text, thinking and
7
- * tool calls as separate delta fields on the same message, so this adapter owns
8
- * the block bookkeeping senpi's event protocol expects.
4
+ * One assistant turn is up to three RPCs, exactly as the released Devin CLI
5
+ * performs them:
6
+ * 1. `GetUserJwt` mints the short-lived user JWT the chat call must carry and
7
+ * names the account's API host, which can differ from the seeded one;
8
+ * 2. `AssignModel` resolves a server-side router into a concrete model uid plus
9
+ * the JWT that authorizes it (router models only);
10
+ * 3. `GetChatMessage` streams the turn: a single gzipped Connect frame out, a
11
+ * frame sequence of deltas back, closed by a JSON trailer that either
12
+ * carries the rejection or is empty.
13
+ *
14
+ * Cascade reports text, thinking and tool calls as separate delta fields on the
15
+ * same message, so this adapter owns the block bookkeeping senpi's event
16
+ * protocol expects.
9
17
  */
10
18
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
11
- import { GetChatMessageRequestSchema, GetChatMessageResponseSchema } from "./devin-agent/gen/cascade_pb.js";
12
- import { applyDevinResponse } from "./devin-agent/stream-state.js";
13
- import { buildDevinChatRequest, DEVIN_CHAT_MESSAGE_PATH, DEVIN_DEFAULT_BASE_URL, decodeDevinFrames, encodeDevinRequestFrame, normalizeDevinSessionToken, } from "./devin-agent/wire.js";
19
+ import { AssignModelRequestSchema, AssignModelResponseSchema, GetChatMessageRequestSchema, GetChatMessageResponseSchema, GetUserJwtRequestSchema, GetUserJwtResponseSchema, } from "./devin-agent/gen/cascade_pb.js";
20
+ import { applyDevinResponse, createDevinStreamState } from "./devin-agent/stream-state.js";
21
+ import { buildDevinChatRequest, buildDevinRouterPrompt, DEVIN_ASSIGN_MODEL_PATH, DEVIN_CHAT_HEADERS, DEVIN_CHAT_MESSAGE_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_USER_JWT_PATH, decodeDevinFrames, devinCliMetadata, encodeDevinRequestFrame, postDevinUnary, readDevinTrailerError, } from "./devin-agent/wire.js";
14
22
  export const stream = (model, context, options) => {
15
23
  const events = new AssistantMessageEventStream();
16
24
  void run(model, context, events, options);
@@ -35,28 +43,31 @@ async function run(model, context, events, options) {
35
43
  stopReason: "stop",
36
44
  timestamp: Date.now(),
37
45
  };
38
- const state = { textIndex: undefined, thinkingIndex: undefined, toolCalls: new Map() };
46
+ const state = createDevinStreamState();
39
47
  const signal = options?.signal;
40
48
  try {
41
49
  events.push({ type: "start", partial: output });
50
+ const baseUrl = (model.baseUrl || DEVIN_DEFAULT_BASE_URL).replace(/\/+$/, "");
51
+ const cascadeId = options?.cascadeId ?? crypto.randomUUID();
52
+ const session = await mintUserJwt(baseUrl, options?.apiKey, signal);
53
+ const assignment = model.compat?.modelRouter === true
54
+ ? await assignModel(model, context, session.chatBaseUrl, cascadeId, options?.apiKey, signal)
55
+ : undefined;
56
+ if (assignment)
57
+ output.responseModel = assignment.modelUid;
42
58
  const request = buildDevinChatRequest({
43
59
  model,
44
60
  context,
45
61
  apiKey: options?.apiKey,
46
- cascadeId: options?.cascadeId,
47
- maxTokens: options?.maxTokens,
48
- temperature: options?.temperature,
62
+ userJwt: session.userJwt,
63
+ cascadeId,
64
+ ...(assignment ? { assignment } : {}),
65
+ ...(options?.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),
66
+ ...(options?.temperature !== undefined ? { temperature: options.temperature } : {}),
49
67
  });
50
- const baseUrl = model.baseUrl || DEVIN_DEFAULT_BASE_URL;
51
- const response = await fetch(baseUrl + DEVIN_CHAT_MESSAGE_PATH, {
68
+ const response = await fetch(session.chatBaseUrl + DEVIN_CHAT_MESSAGE_PATH, {
52
69
  method: "POST",
53
- headers: {
54
- "content-type": "application/connect+proto",
55
- "connect-protocol-version": "1",
56
- "connect-content-encoding": "gzip",
57
- "connect-accept-encoding": "gzip",
58
- authorization: `Bearer ${normalizeDevinSessionToken(options?.apiKey)}`,
59
- },
70
+ headers: DEVIN_CHAT_HEADERS,
60
71
  body: encodeDevinRequestFrame(GetChatMessageRequestSchema, request),
61
72
  signal,
62
73
  });
@@ -67,6 +78,11 @@ async function run(model, context, events, options) {
67
78
  for await (const frame of decodeDevinFrames(response.body, GetChatMessageResponseSchema)) {
68
79
  if (frame.message)
69
80
  applyDevinResponse(frame.message, output, events, state);
81
+ if (frame.trailer !== undefined) {
82
+ const rejection = readDevinTrailerError(frame.trailer);
83
+ if (rejection)
84
+ throw new Error(rejection.formatted);
85
+ }
70
86
  }
71
87
  finalizeBlocks(output, events, state);
72
88
  // Cascade can close a turn that carries a tool call without ever sending an
@@ -96,6 +112,45 @@ async function run(model, context, events, options) {
96
112
  events.end();
97
113
  }
98
114
  }
115
+ async function mintUserJwt(baseUrl, apiKey, signal) {
116
+ const response = await postDevinUnary({
117
+ baseUrl,
118
+ path: DEVIN_USER_JWT_PATH,
119
+ requestSchema: GetUserJwtRequestSchema,
120
+ request: { metadata: devinCliMetadata(apiKey) },
121
+ responseSchema: GetUserJwtResponseSchema,
122
+ ...(signal ? { signal } : {}),
123
+ });
124
+ if (!response.userJwt)
125
+ throw new Error("Devin GetUserJwt returned an empty user JWT");
126
+ const customHost = response.customApiServerUrl.trim().replace(/\/+$/, "");
127
+ return { userJwt: response.userJwt, chatBaseUrl: customHost || baseUrl };
128
+ }
129
+ /**
130
+ * A router uid is never a legal chat model uid, so a failed assignment fails the
131
+ * turn instead of falling back to sending the router id to GetChatMessage.
132
+ */
133
+ async function assignModel(model, context, baseUrl, cascadeId, apiKey, signal) {
134
+ const routerPrompt = buildDevinRouterPrompt(context.messages);
135
+ const response = await postDevinUnary({
136
+ baseUrl,
137
+ path: DEVIN_ASSIGN_MODEL_PATH,
138
+ requestSchema: AssignModelRequestSchema,
139
+ request: {
140
+ metadata: devinCliMetadata(apiKey),
141
+ modelRouterUid: model.upstreamModelId ?? model.id,
142
+ cascadeId,
143
+ ...(routerPrompt ? { chatMessagePrompt: routerPrompt } : {}),
144
+ },
145
+ responseSchema: AssignModelResponseSchema,
146
+ ...(signal ? { signal } : {}),
147
+ });
148
+ const assignment = response.assignment;
149
+ if (!assignment?.modelUid || !assignment.assignmentJwt) {
150
+ throw new Error("Devin AssignModel returned no model uid and assignment JWT");
151
+ }
152
+ return { modelUid: assignment.modelUid, assignmentJwt: assignment.assignmentJwt };
153
+ }
99
154
  /** Narrows a settled stop reason to the three senpi accepts on a done event. */
100
155
  function doneReasonOf(stopReason) {
101
156
  switch (stopReason) {
@@ -1 +1 @@
1
- {"version":3,"file":"devin-agent.js","sourceRoot":"","sources":["../../src/api/devin-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin (Cognition/Codeium Cascade) agent API.\n *\n * One `GetChatMessage` server-streaming Connect call per assistant turn. The\n * request is a single gzipped frame; the response is a frame sequence of\n * deltas, closed by a JSON trailer frame. Cascade reports text, thinking and\n * tool calls as separate delta fields on the same message, so this adapter owns\n * the block bookkeeping senpi's event protocol expects.\n */\n\nimport type { AssistantMessage, Context, Model, StreamFunction, ToolCall } from \"../types.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport { GetChatMessageRequestSchema, GetChatMessageResponseSchema } from \"./devin-agent/gen/cascade_pb.ts\";\nimport { applyDevinResponse, type DevinStreamState } from \"./devin-agent/stream-state.ts\";\nimport type { DevinAgentOptions } from \"./devin-agent/types.ts\";\nimport {\n\tbuildDevinChatRequest,\n\tDEVIN_CHAT_MESSAGE_PATH,\n\tDEVIN_DEFAULT_BASE_URL,\n\tdecodeDevinFrames,\n\tencodeDevinRequestFrame,\n\tnormalizeDevinSessionToken,\n} from \"./devin-agent/wire.ts\";\n\nexport const stream: StreamFunction<\"devin-agent\", DevinAgentOptions> = (\n\tmodel: Model<\"devin-agent\">,\n\tcontext: Context,\n\toptions?: DevinAgentOptions,\n) => {\n\tconst events = new AssistantMessageEventStream();\n\tvoid run(model, context, events, options);\n\treturn events;\n};\n\nexport const streamSimple: StreamFunction<\"devin-agent\", DevinAgentOptions> = stream;\n\nasync function run(\n\tmodel: Model<\"devin-agent\">,\n\tcontext: Context,\n\tevents: AssistantMessageEventStream,\n\toptions: DevinAgentOptions | undefined,\n): Promise<void> {\n\tconst output: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"devin-agent\",\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n\tconst state: DevinStreamState = { textIndex: undefined, thinkingIndex: undefined, toolCalls: new Map() };\n\tconst signal = options?.signal;\n\n\ttry {\n\t\tevents.push({ type: \"start\", partial: output });\n\t\tconst request = buildDevinChatRequest({\n\t\t\tmodel,\n\t\t\tcontext,\n\t\t\tapiKey: options?.apiKey,\n\t\t\tcascadeId: options?.cascadeId,\n\t\t\tmaxTokens: options?.maxTokens,\n\t\t\ttemperature: options?.temperature,\n\t\t});\n\t\tconst baseUrl = model.baseUrl || DEVIN_DEFAULT_BASE_URL;\n\t\tconst response = await fetch(baseUrl + DEVIN_CHAT_MESSAGE_PATH, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/connect+proto\",\n\t\t\t\t\"connect-protocol-version\": \"1\",\n\t\t\t\t\"connect-content-encoding\": \"gzip\",\n\t\t\t\t\"connect-accept-encoding\": \"gzip\",\n\t\t\t\tauthorization: `Bearer ${normalizeDevinSessionToken(options?.apiKey)}`,\n\t\t\t},\n\t\t\tbody: encodeDevinRequestFrame(GetChatMessageRequestSchema, request),\n\t\t\tsignal,\n\t\t});\n\t\tawait options?.onResponse?.({ status: response.status, headers: headersOf(response) }, model);\n\n\t\tif (!response.ok || !response.body) {\n\t\t\tthrow new Error(`Devin request failed (HTTP ${response.status})${await detail(response)}`);\n\t\t}\n\n\t\tfor await (const frame of decodeDevinFrames(response.body, GetChatMessageResponseSchema)) {\n\t\t\tif (frame.message) applyDevinResponse(frame.message, output, events, state);\n\t\t}\n\n\t\tfinalizeBlocks(output, events, state);\n\t\t// Cascade can close a turn that carries a tool call without ever sending an\n\t\t// explicit stop reason. Only the default \"stop\" is upgraded: a server-reported\n\t\t// \"length\" means the turn was truncated, and a truncated tool call must not be\n\t\t// advertised as a complete one.\n\t\tif (output.stopReason === \"stop\" && output.content.some(isToolCall)) output.stopReason = \"toolUse\";\n\t\t// Cascade reports a server error or a content filter as a stop reason on an\n\t\t// otherwise well-formed stream. senpi's protocol has no \"done because it\n\t\t// failed\", so that turn terminates as an error event, not a done event.\n\t\tif (output.stopReason === \"error\") {\n\t\t\toutput.errorMessage ??= \"Devin ended the turn with a server error or a content filter\";\n\t\t\tevents.push({ type: \"error\", reason: \"error\", error: output });\n\t\t\tevents.end();\n\t\t\treturn;\n\t\t}\n\t\tevents.push({ type: \"done\", reason: doneReasonOf(output.stopReason), message: output });\n\t\tevents.end();\n\t} catch (error) {\n\t\tfinalizeBlocks(output, events, state);\n\t\tconst aborted = signal?.aborted === true || (error instanceof Error && error.name === \"AbortError\");\n\t\toutput.stopReason = aborted ? \"aborted\" : \"error\";\n\t\toutput.errorMessage = aborted ? \"Request was aborted\" : messageOf(error);\n\t\tevents.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\tevents.end();\n\t}\n}\n\n/** Narrows a settled stop reason to the three senpi accepts on a done event. */\nfunction doneReasonOf(stopReason: AssistantMessage[\"stopReason\"]): \"stop\" | \"length\" | \"toolUse\" {\n\tswitch (stopReason) {\n\t\tcase \"length\":\n\t\t\treturn \"length\";\n\t\tcase \"toolUse\":\n\t\t\treturn \"toolUse\";\n\t\tdefault:\n\t\t\treturn \"stop\";\n\t}\n}\n\nfunction headersOf(response: Response): Record<string, string> {\n\tconst headers: Record<string, string> = {};\n\tresponse.headers.forEach((value, key) => {\n\t\theaders[key] = value;\n\t});\n\treturn headers;\n}\n\nfunction isToolCall(content: AssistantMessage[\"content\"][number]): content is ToolCall {\n\treturn content.type === \"toolCall\";\n}\n\nfunction finalizeBlocks(output: AssistantMessage, events: AssistantMessageEventStream, state: DevinStreamState): void {\n\tif (state.thinkingIndex !== undefined) {\n\t\tconst block = output.content[state.thinkingIndex];\n\t\tif (block?.type === \"thinking\") {\n\t\t\tevents.push({\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: state.thinkingIndex,\n\t\t\t\tcontent: block.thinking,\n\t\t\t\tpartial: output,\n\t\t\t});\n\t\t}\n\t\tstate.thinkingIndex = undefined;\n\t}\n\tif (state.textIndex !== undefined) {\n\t\tconst block = output.content[state.textIndex];\n\t\tif (block?.type === \"text\") {\n\t\t\tevents.push({ type: \"text_end\", contentIndex: state.textIndex, content: block.text, partial: output });\n\t\t}\n\t\tstate.textIndex = undefined;\n\t}\n\tfor (const [, entry] of state.toolCalls) {\n\t\tconst block = output.content[entry.contentIndex];\n\t\tif (block?.type === \"toolCall\") {\n\t\t\tevents.push({ type: \"toolcall_end\", contentIndex: entry.contentIndex, toolCall: block, partial: output });\n\t\t}\n\t}\n\tstate.toolCalls.clear();\n}\n\nasync function detail(response: Response): Promise<string> {\n\ttry {\n\t\tconst body = await response.text();\n\t\treturn body ? `: ${body.slice(0, 500)}` : \"\";\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction messageOf(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n"]}
1
+ {"version":3,"file":"devin-agent.js","sourceRoot":"","sources":["../../src/api/devin-agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin (Cognition/Codeium Cascade) agent API.\n *\n * One assistant turn is up to three RPCs, exactly as the released Devin CLI\n * performs them:\n * 1. `GetUserJwt` mints the short-lived user JWT the chat call must carry and\n * names the account's API host, which can differ from the seeded one;\n * 2. `AssignModel` resolves a server-side router into a concrete model uid plus\n * the JWT that authorizes it (router models only);\n * 3. `GetChatMessage` streams the turn: a single gzipped Connect frame out, a\n * frame sequence of deltas back, closed by a JSON trailer that either\n * carries the rejection or is empty.\n *\n * Cascade reports text, thinking and tool calls as separate delta fields on the\n * same message, so this adapter owns the block bookkeeping senpi's event\n * protocol expects.\n */\n\nimport type { AssistantMessage, Context, Model, StreamFunction, ToolCall } from \"../types.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport {\n\tAssignModelRequestSchema,\n\tAssignModelResponseSchema,\n\tGetChatMessageRequestSchema,\n\tGetChatMessageResponseSchema,\n\tGetUserJwtRequestSchema,\n\tGetUserJwtResponseSchema,\n} from \"./devin-agent/gen/cascade_pb.ts\";\nimport { applyDevinResponse, createDevinStreamState, type DevinStreamState } from \"./devin-agent/stream-state.ts\";\nimport type { DevinAgentOptions } from \"./devin-agent/types.ts\";\nimport {\n\tbuildDevinChatRequest,\n\tbuildDevinRouterPrompt,\n\tDEVIN_ASSIGN_MODEL_PATH,\n\tDEVIN_CHAT_HEADERS,\n\tDEVIN_CHAT_MESSAGE_PATH,\n\tDEVIN_DEFAULT_BASE_URL,\n\tDEVIN_USER_JWT_PATH,\n\ttype DevinModelAssignment,\n\tdecodeDevinFrames,\n\tdevinCliMetadata,\n\tencodeDevinRequestFrame,\n\tpostDevinUnary,\n\treadDevinTrailerError,\n} from \"./devin-agent/wire.ts\";\n\nexport const stream: StreamFunction<\"devin-agent\", DevinAgentOptions> = (\n\tmodel: Model<\"devin-agent\">,\n\tcontext: Context,\n\toptions?: DevinAgentOptions,\n) => {\n\tconst events = new AssistantMessageEventStream();\n\tvoid run(model, context, events, options);\n\treturn events;\n};\n\nexport const streamSimple: StreamFunction<\"devin-agent\", DevinAgentOptions> = stream;\n\ninterface DevinSession {\n\tuserJwt: string;\n\t/** Host the account is provisioned on; GetUserJwt may move it off the seed. */\n\tchatBaseUrl: string;\n}\n\nasync function run(\n\tmodel: Model<\"devin-agent\">,\n\tcontext: Context,\n\tevents: AssistantMessageEventStream,\n\toptions: DevinAgentOptions | undefined,\n): Promise<void> {\n\tconst output: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tcontent: [],\n\t\tapi: \"devin-agent\",\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n\tconst state = createDevinStreamState();\n\tconst signal = options?.signal;\n\n\ttry {\n\t\tevents.push({ type: \"start\", partial: output });\n\t\tconst baseUrl = (model.baseUrl || DEVIN_DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\t\tconst cascadeId = options?.cascadeId ?? crypto.randomUUID();\n\t\tconst session = await mintUserJwt(baseUrl, options?.apiKey, signal);\n\t\tconst assignment =\n\t\t\tmodel.compat?.modelRouter === true\n\t\t\t\t? await assignModel(model, context, session.chatBaseUrl, cascadeId, options?.apiKey, signal)\n\t\t\t\t: undefined;\n\t\tif (assignment) output.responseModel = assignment.modelUid;\n\n\t\tconst request = buildDevinChatRequest({\n\t\t\tmodel,\n\t\t\tcontext,\n\t\t\tapiKey: options?.apiKey,\n\t\t\tuserJwt: session.userJwt,\n\t\t\tcascadeId,\n\t\t\t...(assignment ? { assignment } : {}),\n\t\t\t...(options?.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n\t\t\t...(options?.temperature !== undefined ? { temperature: options.temperature } : {}),\n\t\t});\n\t\tconst response = await fetch(session.chatBaseUrl + DEVIN_CHAT_MESSAGE_PATH, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: DEVIN_CHAT_HEADERS,\n\t\t\tbody: encodeDevinRequestFrame(GetChatMessageRequestSchema, request),\n\t\t\tsignal,\n\t\t});\n\t\tawait options?.onResponse?.({ status: response.status, headers: headersOf(response) }, model);\n\n\t\tif (!response.ok || !response.body) {\n\t\t\tthrow new Error(`Devin request failed (HTTP ${response.status})${await detail(response)}`);\n\t\t}\n\n\t\tfor await (const frame of decodeDevinFrames(response.body, GetChatMessageResponseSchema)) {\n\t\t\tif (frame.message) applyDevinResponse(frame.message, output, events, state);\n\t\t\tif (frame.trailer !== undefined) {\n\t\t\t\tconst rejection = readDevinTrailerError(frame.trailer);\n\t\t\t\tif (rejection) throw new Error(rejection.formatted);\n\t\t\t}\n\t\t}\n\n\t\tfinalizeBlocks(output, events, state);\n\t\t// Cascade can close a turn that carries a tool call without ever sending an\n\t\t// explicit stop reason. Only the default \"stop\" is upgraded: a server-reported\n\t\t// \"length\" means the turn was truncated, and a truncated tool call must not be\n\t\t// advertised as a complete one.\n\t\tif (output.stopReason === \"stop\" && output.content.some(isToolCall)) output.stopReason = \"toolUse\";\n\t\t// Cascade reports a server error or a content filter as a stop reason on an\n\t\t// otherwise well-formed stream. senpi's protocol has no \"done because it\n\t\t// failed\", so that turn terminates as an error event, not a done event.\n\t\tif (output.stopReason === \"error\") {\n\t\t\toutput.errorMessage ??= \"Devin ended the turn with a server error or a content filter\";\n\t\t\tevents.push({ type: \"error\", reason: \"error\", error: output });\n\t\t\tevents.end();\n\t\t\treturn;\n\t\t}\n\t\tevents.push({ type: \"done\", reason: doneReasonOf(output.stopReason), message: output });\n\t\tevents.end();\n\t} catch (error) {\n\t\tfinalizeBlocks(output, events, state);\n\t\tconst aborted = signal?.aborted === true || (error instanceof Error && error.name === \"AbortError\");\n\t\toutput.stopReason = aborted ? \"aborted\" : \"error\";\n\t\toutput.errorMessage = aborted ? \"Request was aborted\" : messageOf(error);\n\t\tevents.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\tevents.end();\n\t}\n}\n\nasync function mintUserJwt(\n\tbaseUrl: string,\n\tapiKey: string | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<DevinSession> {\n\tconst response = await postDevinUnary({\n\t\tbaseUrl,\n\t\tpath: DEVIN_USER_JWT_PATH,\n\t\trequestSchema: GetUserJwtRequestSchema,\n\t\trequest: { metadata: devinCliMetadata(apiKey) },\n\t\tresponseSchema: GetUserJwtResponseSchema,\n\t\t...(signal ? { signal } : {}),\n\t});\n\tif (!response.userJwt) throw new Error(\"Devin GetUserJwt returned an empty user JWT\");\n\tconst customHost = response.customApiServerUrl.trim().replace(/\\/+$/, \"\");\n\treturn { userJwt: response.userJwt, chatBaseUrl: customHost || baseUrl };\n}\n\n/**\n * A router uid is never a legal chat model uid, so a failed assignment fails the\n * turn instead of falling back to sending the router id to GetChatMessage.\n */\nasync function assignModel(\n\tmodel: Model<\"devin-agent\">,\n\tcontext: Context,\n\tbaseUrl: string,\n\tcascadeId: string,\n\tapiKey: string | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<DevinModelAssignment> {\n\tconst routerPrompt = buildDevinRouterPrompt(context.messages);\n\tconst response = await postDevinUnary({\n\t\tbaseUrl,\n\t\tpath: DEVIN_ASSIGN_MODEL_PATH,\n\t\trequestSchema: AssignModelRequestSchema,\n\t\trequest: {\n\t\t\tmetadata: devinCliMetadata(apiKey),\n\t\t\tmodelRouterUid: model.upstreamModelId ?? model.id,\n\t\t\tcascadeId,\n\t\t\t...(routerPrompt ? { chatMessagePrompt: routerPrompt } : {}),\n\t\t},\n\t\tresponseSchema: AssignModelResponseSchema,\n\t\t...(signal ? { signal } : {}),\n\t});\n\tconst assignment = response.assignment;\n\tif (!assignment?.modelUid || !assignment.assignmentJwt) {\n\t\tthrow new Error(\"Devin AssignModel returned no model uid and assignment JWT\");\n\t}\n\treturn { modelUid: assignment.modelUid, assignmentJwt: assignment.assignmentJwt };\n}\n\n/** Narrows a settled stop reason to the three senpi accepts on a done event. */\nfunction doneReasonOf(stopReason: AssistantMessage[\"stopReason\"]): \"stop\" | \"length\" | \"toolUse\" {\n\tswitch (stopReason) {\n\t\tcase \"length\":\n\t\t\treturn \"length\";\n\t\tcase \"toolUse\":\n\t\t\treturn \"toolUse\";\n\t\tdefault:\n\t\t\treturn \"stop\";\n\t}\n}\n\nfunction headersOf(response: Response): Record<string, string> {\n\tconst headers: Record<string, string> = {};\n\tresponse.headers.forEach((value, key) => {\n\t\theaders[key] = value;\n\t});\n\treturn headers;\n}\n\nfunction isToolCall(content: AssistantMessage[\"content\"][number]): content is ToolCall {\n\treturn content.type === \"toolCall\";\n}\n\nfunction finalizeBlocks(output: AssistantMessage, events: AssistantMessageEventStream, state: DevinStreamState): void {\n\tif (state.thinkingIndex !== undefined) {\n\t\tconst block = output.content[state.thinkingIndex];\n\t\tif (block?.type === \"thinking\") {\n\t\t\tevents.push({\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: state.thinkingIndex,\n\t\t\t\tcontent: block.thinking,\n\t\t\t\tpartial: output,\n\t\t\t});\n\t\t}\n\t\tstate.thinkingIndex = undefined;\n\t}\n\tif (state.textIndex !== undefined) {\n\t\tconst block = output.content[state.textIndex];\n\t\tif (block?.type === \"text\") {\n\t\t\tevents.push({ type: \"text_end\", contentIndex: state.textIndex, content: block.text, partial: output });\n\t\t}\n\t\tstate.textIndex = undefined;\n\t}\n\tfor (const [, entry] of state.toolCalls) {\n\t\tconst block = output.content[entry.contentIndex];\n\t\tif (block?.type === \"toolCall\") {\n\t\t\tevents.push({ type: \"toolcall_end\", contentIndex: entry.contentIndex, toolCall: block, partial: output });\n\t\t}\n\t}\n\tstate.toolCalls.clear();\n}\n\nasync function detail(response: Response): Promise<string> {\n\ttry {\n\t\tconst body = await response.text();\n\t\treturn body ? `: ${body.slice(0, 500)}` : \"\";\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction messageOf(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n"]}
@@ -7,6 +7,11 @@
7
7
  * grant - Devin re-issues the token through a fresh login - so refresh returns
8
8
  * the stored credential untouched.
9
9
  *
10
+ * api.devin.ai is the LOGIN host only. The token it mints is spent against the
11
+ * Cascade model host seeded on every Devin model, so toAuth must never return a
12
+ * baseUrl: the registry would overlay it onto the model and every chat would
13
+ * 404 on the REST API.
14
+ *
10
15
  * NOTE: the callback module uses node:http and is CLI-only, never browser.
11
16
  */
12
17
  import type { OAuthAuth } from "../types.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"devin.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA0FvF,eAAO,MAAM,UAAU,EAAE,SAWxB,CAAC"}
1
+ {"version":3,"file":"devin.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA0FvF,eAAO,MAAM,UAAU,EAAE,SAWxB,CAAC"}
@@ -7,10 +7,15 @@
7
7
  * grant - Devin re-issues the token through a fresh login - so refresh returns
8
8
  * the stored credential untouched.
9
9
  *
10
+ * api.devin.ai is the LOGIN host only. The token it mints is spent against the
11
+ * Cascade model host seeded on every Devin model, so toAuth must never return a
12
+ * baseUrl: the registry would overlay it onto the model and every chat would
13
+ * 404 on the REST API.
14
+ *
10
15
  * NOTE: the callback module uses node:http and is CLI-only, never browser.
11
16
  */
12
17
  import { startDevinCallbackServer } from "./devin-callback.js";
13
- import { DEVIN_API_ENDPOINT, exchangeDevinAuthorizationCode } from "./devin-token.js";
18
+ import { exchangeDevinAuthorizationCode } from "./devin-token.js";
14
19
  import { generatePKCE } from "./pkce.js";
15
20
  const AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
16
21
  const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
@@ -105,7 +110,7 @@ export const devinOAuth = {
105
110
  return credential;
106
111
  },
107
112
  async toAuth(credential) {
108
- return { apiKey: credential.access, baseUrl: DEVIN_API_ENDPOINT };
113
+ return { apiKey: credential.access };
109
114
  },
110
115
  };
111
116
  //# sourceMappingURL=devin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"devin.js","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,aAAa,GAAG,wCAAwC,CAAC;AAC/D,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvC,SAAS,uBAAuB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC;QACJ,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACR,YAAY;IACb,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;IACxF,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAAgE;IACrF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACnC,GAAG,CAAC,MAAM,GAAG,IAAI,eAAe,CAAC;QAChC,aAAa,EAAE,MAAM;QACrB,YAAY,EAAE,KAAK,CAAC,WAAW;QAC/B,cAAc,EAAE,KAAK,CAAC,SAAS;QAC/B,qBAAqB,EAAE,MAAM;QAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,gBAAgB;KACxB,CAAC,CAAC,QAAQ,EAAE,CAAC;IACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,WAAoC;IAC7D,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC;QAC/C,KAAK;QACL,QAAQ;QACR,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,cAAc,EAAE,gBAAgB;KAChC,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,IAAI,WAA+B,CAAC;IACpC,IAAI,WAA8B,CAAC;IAEnC,IAAI,CAAC;QACJ,WAAW,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,6CAA6C,QAAQ,CAAC,WAAW,EAAE;SAC5E,CAAC,CAAC;QACH,WAAW,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;YAC1E,YAAY,EACX,4GAA4G;SAC7G,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,WAAW;aAC/B,MAAM,CAAC;YACP,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,wFAAwF;YACjG,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,MAAM,EAAE,WAAW,CAAC,MAAM;SAC1B,CAAC;aACD,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,WAAW,GAAG,KAAK,CAAC;YACpB,QAAQ,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAChB,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,QAAQ,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;QAEJ,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,iBAAiB,EAAE,CAAC;QACtD,IAAI,WAAW;YAAE,MAAM,WAAW,CAAC;QACnC,IAAI,UAAU;YAAE,OAAO,UAAU,CAAC;QAElC,MAAM,aAAa,CAAC;QACpB,IAAI,WAAW;YAAE,MAAM,WAAW,CAAC;QACnC,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACzD,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,wDAAwD,EAAE,CAAC,CAAC;QAC5G,OAAO,MAAM,8BAA8B,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjF,CAAC;YAAS,CAAC;QACV,WAAW,CAAC,KAAK,EAAE,CAAC;QACpB,QAAQ,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,cAAc,EAAE,IAAI;IACpB,UAAU,EAAE,oBAAoB;IAChC,KAAK,EAAE,UAAU;IACjB,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO;QAChC,OAAO,UAAU,CAAC;IACnB,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,UAAU;QACtB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IACnE,CAAC;CACD,CAAC","sourcesContent":["/**\n * Devin (Cognition) CLI OAuth flow.\n *\n * Mirrors the Devin CLI login: the authorize page at app.devin.ai redeems a\n * PKCE S256 challenge against the fixed loopback redirect, and the resulting\n * code is exchanged for a single long-lived CLI token. There is no refresh\n * grant - Devin re-issues the token through a fresh login - so refresh returns\n * the stored credential untouched.\n *\n * NOTE: the callback module uses node:http and is CLI-only, never browser.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { startDevinCallbackServer } from \"./devin-callback.ts\";\nimport { DEVIN_API_ENDPOINT, exchangeDevinAuthorizationCode } from \"./devin-token.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://app.devin.ai/auth/cli/continue\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\tif (value.includes(\"code=\")) return new URLSearchParams(value).get(\"code\") ?? undefined;\n\treturn value;\n}\n\nfunction authorizeUrl(input: { redirectUri: string; challenge: string; state: string }): string {\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: input.redirectUri,\n\t\tcode_challenge: input.challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: input.state,\n\t\tprompt: \"select_account\",\n\t}).toString();\n\treturn url.toString();\n}\n\nasync function loginDevin(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst callback = await startDevinCallbackServer({\n\t\tstate,\n\t\tverifier,\n\t\tsignal: interaction.signal,\n\t\tloginTimeoutMs: LOGIN_TIMEOUT_MS,\n\t});\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for the Devin OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl({ redirectUri: callback.callbackUrl, challenge, state }),\n\t\t\tinstructions:\n\t\t\t\t\"Sign in to Devin in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging the authorization code for a Devin token...\" });\n\t\treturn await exchangeDevinAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const devinOAuth: OAuthAuth = {\n\tname: \"Devin\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with Devin\",\n\tlogin: loginDevin,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access, baseUrl: DEVIN_API_ENDPOINT };\n\t},\n};\n"]}
1
+ {"version":3,"file":"devin.js","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin (Cognition) CLI OAuth flow.\n *\n * Mirrors the Devin CLI login: the authorize page at app.devin.ai redeems a\n * PKCE S256 challenge against the fixed loopback redirect, and the resulting\n * code is exchanged for a single long-lived CLI token. There is no refresh\n * grant - Devin re-issues the token through a fresh login - so refresh returns\n * the stored credential untouched.\n *\n * api.devin.ai is the LOGIN host only. The token it mints is spent against the\n * Cascade model host seeded on every Devin model, so toAuth must never return a\n * baseUrl: the registry would overlay it onto the model and every chat would\n * 404 on the REST API.\n *\n * NOTE: the callback module uses node:http and is CLI-only, never browser.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { startDevinCallbackServer } from \"./devin-callback.ts\";\nimport { exchangeDevinAuthorizationCode } from \"./devin-token.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://app.devin.ai/auth/cli/continue\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\tif (value.includes(\"code=\")) return new URLSearchParams(value).get(\"code\") ?? undefined;\n\treturn value;\n}\n\nfunction authorizeUrl(input: { redirectUri: string; challenge: string; state: string }): string {\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: input.redirectUri,\n\t\tcode_challenge: input.challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: input.state,\n\t\tprompt: \"select_account\",\n\t}).toString();\n\treturn url.toString();\n}\n\nasync function loginDevin(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst callback = await startDevinCallbackServer({\n\t\tstate,\n\t\tverifier,\n\t\tsignal: interaction.signal,\n\t\tloginTimeoutMs: LOGIN_TIMEOUT_MS,\n\t});\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for the Devin OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl({ redirectUri: callback.callbackUrl, challenge, state }),\n\t\t\tinstructions:\n\t\t\t\t\"Sign in to Devin in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging the authorization code for a Devin token...\" });\n\t\treturn await exchangeDevinAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const devinOAuth: OAuthAuth = {\n\tname: \"Devin\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with Devin\",\n\tlogin: loginDevin,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access };\n\t},\n};\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Context ceilings Cursor itself reported, keyed by model id.
3
+ *
4
+ * `GetUsableModels` carries no window, so `CURSOR_MODEL_CAPABILITIES` is a
5
+ * committed guess at what each family accepts. The server states the truth on
6
+ * every conversation checkpoint (`tokenDetails.maxTokens`), so once a model has
7
+ * been observed, that value outranks the catalog for every later request.
8
+ *
9
+ * This module stays browser-safe: `providers/cursor.ts` and
10
+ * `cursor/store-migration.ts` materialize catalog windows and are bundled for
11
+ * the browser. Node processes install file persistence through
12
+ * `installCursorContextLimitPersistence` - see `utils/cursor-context-limit.ts`.
13
+ */
14
+ export type CursorContextLimitPersistence = {
15
+ /** Limits observed by earlier processes. Called at most once per install. */
16
+ readonly load: () => ReadonlyMap<string, number>;
17
+ /** Called only when a recorded limit actually changed the store. */
18
+ readonly save: (limits: ReadonlyMap<string, number>) => void;
19
+ };
20
+ /** Installs the process-wide persistence port. Idempotent per port identity. */
21
+ export declare function installCursorContextLimitPersistence(port: CursorContextLimitPersistence): void;
22
+ /**
23
+ * Records the server-reported ceiling for `modelId`. The first checkpoint of a
24
+ * conversation reports 0, so non-positive and non-finite values are ignored.
25
+ */
26
+ export declare function recordCursorContextLimit(modelId: string, maxTokens: number | undefined): void;
27
+ export declare function getCursorContextLimit(modelId: string): number | undefined;
28
+ /** The window to trust for `modelId`: what the server reported, else the catalog. */
29
+ export declare function resolveCursorContextWindow(modelId: string, catalogWindow: number): number;
30
+ /** Drops in-memory state so the next read re-hydrates from the installed port. */
31
+ export declare function resetCursorContextLimitStoreForTest(): void;
32
+ //# sourceMappingURL=context-limit-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-limit-store.d.ts","sourceRoot":"","sources":["../../src/cursor/context-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,6BAA6B,GAAG;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CAC7D,CAAC;AAMF,gFAAgF;AAChF,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,6BAA6B,GAAG,IAAI,CAI9F;AAYD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAM7F;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE;AAED,qFAAqF;AACrF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED,kFAAkF;AAClF,wBAAgB,mCAAmC,IAAI,IAAI,CAG1D"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Context ceilings Cursor itself reported, keyed by model id.
3
+ *
4
+ * `GetUsableModels` carries no window, so `CURSOR_MODEL_CAPABILITIES` is a
5
+ * committed guess at what each family accepts. The server states the truth on
6
+ * every conversation checkpoint (`tokenDetails.maxTokens`), so once a model has
7
+ * been observed, that value outranks the catalog for every later request.
8
+ *
9
+ * This module stays browser-safe: `providers/cursor.ts` and
10
+ * `cursor/store-migration.ts` materialize catalog windows and are bundled for
11
+ * the browser. Node processes install file persistence through
12
+ * `installCursorContextLimitPersistence` - see `utils/cursor-context-limit.ts`.
13
+ */
14
+ const observedLimits = new Map();
15
+ let persistence;
16
+ let hydrated = false;
17
+ /** Installs the process-wide persistence port. Idempotent per port identity. */
18
+ export function installCursorContextLimitPersistence(port) {
19
+ if (persistence === port)
20
+ return;
21
+ persistence = port;
22
+ hydrated = false;
23
+ }
24
+ function hydrate() {
25
+ if (hydrated)
26
+ return;
27
+ // Set before loading: a load that observes nothing must not retry per read.
28
+ hydrated = true;
29
+ if (!persistence)
30
+ return;
31
+ for (const [modelId, maxTokens] of persistence.load()) {
32
+ if (!observedLimits.has(modelId))
33
+ observedLimits.set(modelId, maxTokens);
34
+ }
35
+ }
36
+ /**
37
+ * Records the server-reported ceiling for `modelId`. The first checkpoint of a
38
+ * conversation reports 0, so non-positive and non-finite values are ignored.
39
+ */
40
+ export function recordCursorContextLimit(modelId, maxTokens) {
41
+ if (maxTokens === undefined || !Number.isFinite(maxTokens) || maxTokens <= 0)
42
+ return;
43
+ hydrate();
44
+ if (observedLimits.get(modelId) === maxTokens)
45
+ return;
46
+ observedLimits.set(modelId, maxTokens);
47
+ persistence?.save(observedLimits);
48
+ }
49
+ export function getCursorContextLimit(modelId) {
50
+ hydrate();
51
+ return observedLimits.get(modelId);
52
+ }
53
+ /** The window to trust for `modelId`: what the server reported, else the catalog. */
54
+ export function resolveCursorContextWindow(modelId, catalogWindow) {
55
+ return getCursorContextLimit(modelId) ?? catalogWindow;
56
+ }
57
+ /** Drops in-memory state so the next read re-hydrates from the installed port. */
58
+ export function resetCursorContextLimitStoreForTest() {
59
+ observedLimits.clear();
60
+ hydrated = false;
61
+ }
62
+ //# sourceMappingURL=context-limit-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-limit-store.js","sourceRoot":"","sources":["../../src/cursor/context-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AACjD,IAAI,WAAsD,CAAC;AAC3D,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB,gFAAgF;AAChF,MAAM,UAAU,oCAAoC,CAAC,IAAmC;IACvF,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO;IACjC,WAAW,GAAG,IAAI,CAAC;IACnB,QAAQ,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,SAAS,OAAO;IACf,IAAI,QAAQ;QAAE,OAAO;IACrB,4EAA4E;IAC5E,QAAQ,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC,WAAW;QAAE,OAAO;IACzB,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1E,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,SAA6B;IACtF,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO;IACrF,OAAO,EAAE,CAAC;IACV,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO;IACtD,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACpD,OAAO,EAAE,CAAC;IACV,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,0BAA0B,CAAC,OAAe,EAAE,aAAqB;IAChF,OAAO,qBAAqB,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC;AACxD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,mCAAmC;IAClD,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,QAAQ,GAAG,KAAK,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Context ceilings Cursor itself reported, keyed by model id.\n *\n * `GetUsableModels` carries no window, so `CURSOR_MODEL_CAPABILITIES` is a\n * committed guess at what each family accepts. The server states the truth on\n * every conversation checkpoint (`tokenDetails.maxTokens`), so once a model has\n * been observed, that value outranks the catalog for every later request.\n *\n * This module stays browser-safe: `providers/cursor.ts` and\n * `cursor/store-migration.ts` materialize catalog windows and are bundled for\n * the browser. Node processes install file persistence through\n * `installCursorContextLimitPersistence` - see `utils/cursor-context-limit.ts`.\n */\n\nexport type CursorContextLimitPersistence = {\n\t/** Limits observed by earlier processes. Called at most once per install. */\n\treadonly load: () => ReadonlyMap<string, number>;\n\t/** Called only when a recorded limit actually changed the store. */\n\treadonly save: (limits: ReadonlyMap<string, number>) => void;\n};\n\nconst observedLimits = new Map<string, number>();\nlet persistence: CursorContextLimitPersistence | undefined;\nlet hydrated = false;\n\n/** Installs the process-wide persistence port. Idempotent per port identity. */\nexport function installCursorContextLimitPersistence(port: CursorContextLimitPersistence): void {\n\tif (persistence === port) return;\n\tpersistence = port;\n\thydrated = false;\n}\n\nfunction hydrate(): void {\n\tif (hydrated) return;\n\t// Set before loading: a load that observes nothing must not retry per read.\n\thydrated = true;\n\tif (!persistence) return;\n\tfor (const [modelId, maxTokens] of persistence.load()) {\n\t\tif (!observedLimits.has(modelId)) observedLimits.set(modelId, maxTokens);\n\t}\n}\n\n/**\n * Records the server-reported ceiling for `modelId`. The first checkpoint of a\n * conversation reports 0, so non-positive and non-finite values are ignored.\n */\nexport function recordCursorContextLimit(modelId: string, maxTokens: number | undefined): void {\n\tif (maxTokens === undefined || !Number.isFinite(maxTokens) || maxTokens <= 0) return;\n\thydrate();\n\tif (observedLimits.get(modelId) === maxTokens) return;\n\tobservedLimits.set(modelId, maxTokens);\n\tpersistence?.save(observedLimits);\n}\n\nexport function getCursorContextLimit(modelId: string): number | undefined {\n\thydrate();\n\treturn observedLimits.get(modelId);\n}\n\n/** The window to trust for `modelId`: what the server reported, else the catalog. */\nexport function resolveCursorContextWindow(modelId: string, catalogWindow: number): number {\n\treturn getCursorContextLimit(modelId) ?? catalogWindow;\n}\n\n/** Drops in-memory state so the next read re-hydrates from the installed port. */\nexport function resetCursorContextLimitStoreForTest(): void {\n\tobservedLimits.clear();\n\thydrated = false;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"store-migration.d.ts","sourceRoot":"","sources":["../../src/cursor/store-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAyCzC;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,CAAC,cAAc,CAAC,EAAE,CAiC3G"}
1
+ {"version":3,"file":"store-migration.d.ts","sourceRoot":"","sources":["../../src/cursor/store-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA0CzC;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,CAAC,cAAc,CAAC,EAAE,CAiC3G"}