@jait/gateway 0.1.415 → 0.1.419

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/dist/providers/acp-provider.d.ts +4 -2
  2. package/dist/providers/acp-provider.d.ts.map +1 -1
  3. package/dist/providers/acp-provider.js +262 -123
  4. package/dist/providers/acp-provider.js.map +1 -1
  5. package/dist/providers/contracts.d.ts +2 -2
  6. package/dist/providers/contracts.d.ts.map +1 -1
  7. package/dist/providers/registry.js +1 -1
  8. package/dist/providers/registry.js.map +1 -1
  9. package/dist/routes/chat.d.ts.map +1 -1
  10. package/dist/routes/chat.js +37 -21
  11. package/dist/routes/chat.js.map +1 -1
  12. package/package.json +1 -1
  13. package/web-dist/assets/{_basePickBy-Vej0n7ts.js → _basePickBy-DPfVIRAk.js} +1 -1
  14. package/web-dist/assets/{_baseUniq-IJ0sJU1m.js → _baseUniq-CoY2kA2J.js} +1 -1
  15. package/web-dist/assets/{arc-BeHu1aOb.js → arc-C1T2VsVa.js} +1 -1
  16. package/web-dist/assets/{architectureDiagram-2XIMDMQ5-Bp6z5ps0.js → architectureDiagram-2XIMDMQ5-yPpU9I5l.js} +1 -1
  17. package/web-dist/assets/{blockDiagram-WCTKOSBZ-Cx_zzsRg.js → blockDiagram-WCTKOSBZ-C1pfdQSr.js} +1 -1
  18. package/web-dist/assets/{c4Diagram-IC4MRINW-DUa0YpGC.js → c4Diagram-IC4MRINW-BVSh5Fqx.js} +1 -1
  19. package/web-dist/assets/channel-BDkYsH0o.js +1 -0
  20. package/web-dist/assets/{chunk-4BX2VUAB-tpj87VmD.js → chunk-4BX2VUAB-Ccr_kv0Q.js} +1 -1
  21. package/web-dist/assets/{chunk-55IACEB6-B_ZFHP09.js → chunk-55IACEB6-Z-XxFc_X.js} +1 -1
  22. package/web-dist/assets/{chunk-FMBD7UC4-CTvcgmSZ.js → chunk-FMBD7UC4-Pkno2glL.js} +1 -1
  23. package/web-dist/assets/{chunk-JSJVCQXG-B7h9KpNR.js → chunk-JSJVCQXG-BJ3W969q.js} +1 -1
  24. package/web-dist/assets/{chunk-KX2RTZJC-D9Vq1pQV.js → chunk-KX2RTZJC-BzELc_J8.js} +1 -1
  25. package/web-dist/assets/{chunk-NQ4KR5QH-C7aaLWfv.js → chunk-NQ4KR5QH-D7dBHFba.js} +1 -1
  26. package/web-dist/assets/{chunk-QZHKN3VN-DL14VDvc.js → chunk-QZHKN3VN-BplMEvVR.js} +1 -1
  27. package/web-dist/assets/{chunk-WL4C6EOR-Dj9yLTN_.js → chunk-WL4C6EOR-FlLV5EOE.js} +1 -1
  28. package/web-dist/assets/classDiagram-VBA2DB6C-ougbB9yj.js +1 -0
  29. package/web-dist/assets/classDiagram-v2-RAHNMMFH-ougbB9yj.js +1 -0
  30. package/web-dist/assets/clone-ChknCnMs.js +1 -0
  31. package/web-dist/assets/{cose-bilkent-S5V4N54A-BWtWPOzl.js → cose-bilkent-S5V4N54A-m4ikUNqH.js} +1 -1
  32. package/web-dist/assets/{dagre-KLK3FWXG-BGfWBJVo.js → dagre-KLK3FWXG-C4WXsuqt.js} +1 -1
  33. package/web-dist/assets/{diagram-E7M64L7V-Cke9c3vx.js → diagram-E7M64L7V-Csk2Jgho.js} +1 -1
  34. package/web-dist/assets/{diagram-IFDJBPK2-BRRl8D9K.js → diagram-IFDJBPK2-BZr0a637.js} +1 -1
  35. package/web-dist/assets/{diagram-P4PSJMXO-BuZo-ISM.js → diagram-P4PSJMXO-dkyXMiS6.js} +1 -1
  36. package/web-dist/assets/{erDiagram-INFDFZHY-Plok75TS.js → erDiagram-INFDFZHY-DbQqScPT.js} +1 -1
  37. package/web-dist/assets/{flowDiagram-PKNHOUZH-DrTFnv1o.js → flowDiagram-PKNHOUZH-B1zA3pR2.js} +1 -1
  38. package/web-dist/assets/{ganttDiagram-A5KZAMGK-lbd5t50L.js → ganttDiagram-A5KZAMGK-DdyvmvzW.js} +1 -1
  39. package/web-dist/assets/{gitGraphDiagram-K3NZZRJ6-4bqi_Grw.js → gitGraphDiagram-K3NZZRJ6-BD-jKOoD.js} +1 -1
  40. package/web-dist/assets/{graph-BJxBQW7G.js → graph-DTz_lMuX.js} +1 -1
  41. package/web-dist/assets/{index-BGe0OrqB.js → index-CiJZgv18.js} +1 -1
  42. package/web-dist/assets/{index-D-Q3NBqs.js → index-D3wvFOt1.js} +1 -1
  43. package/web-dist/assets/{index-DT-yEdLN.js → index-b6Lfsnr6.js} +162 -162
  44. package/web-dist/assets/{infoDiagram-LFFYTUFH-mOUWUKTg.js → infoDiagram-LFFYTUFH-DtGnxrtE.js} +1 -1
  45. package/web-dist/assets/{ishikawaDiagram-PHBUUO56-C-ISusUx.js → ishikawaDiagram-PHBUUO56-Djz5cOl0.js} +1 -1
  46. package/web-dist/assets/{journeyDiagram-4ABVD52K-znHcSODe.js → journeyDiagram-4ABVD52K-DeksSAXF.js} +1 -1
  47. package/web-dist/assets/{kanban-definition-K7BYSVSG-B9ekOJxL.js → kanban-definition-K7BYSVSG-gYeJU2K7.js} +1 -1
  48. package/web-dist/assets/{layout-B_RPzsC2.js → layout-orO3aKWY.js} +1 -1
  49. package/web-dist/assets/{linear-DZLBRrsE.js → linear-_QMRlue7.js} +1 -1
  50. package/web-dist/assets/{mindmap-definition-YRQLILUH-D3XCkmFm.js → mindmap-definition-YRQLILUH-8wRw1CND.js} +1 -1
  51. package/web-dist/assets/{pieDiagram-SKSYHLDU-B2rKp15l.js → pieDiagram-SKSYHLDU-BifZPLKU.js} +1 -1
  52. package/web-dist/assets/{quadrantDiagram-337W2JSQ-B3DzAIDT.js → quadrantDiagram-337W2JSQ-CdZKrW_Y.js} +1 -1
  53. package/web-dist/assets/{requirementDiagram-Z7DCOOCP-C4aT5lvg.js → requirementDiagram-Z7DCOOCP-QDuW92Qu.js} +1 -1
  54. package/web-dist/assets/{sankeyDiagram-WA2Y5GQK-CwdIKXWh.js → sankeyDiagram-WA2Y5GQK-DFZCLAHQ.js} +1 -1
  55. package/web-dist/assets/{sequenceDiagram-2WXFIKYE-C85lQ8Bt.js → sequenceDiagram-2WXFIKYE-D5dAX5pt.js} +1 -1
  56. package/web-dist/assets/{stateDiagram-RAJIS63D-C6gFkSrw.js → stateDiagram-RAJIS63D-BU9FdkI0.js} +1 -1
  57. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-LBDX1YCg.js +1 -0
  58. package/web-dist/assets/{timeline-definition-YZTLITO2-B6sQ4e6y.js → timeline-definition-YZTLITO2-CX4lUF5E.js} +1 -1
  59. package/web-dist/assets/{treemap-KZPCXAKY-DqwxJSZi.js → treemap-KZPCXAKY-ByDWAYkU.js} +1 -1
  60. package/web-dist/assets/{vennDiagram-LZ73GAT5-BkJxDniY.js → vennDiagram-LZ73GAT5-B1sk3TR5.js} +1 -1
  61. package/web-dist/assets/{xychartDiagram-JWTSCODW-BbHdBYjX.js → xychartDiagram-JWTSCODW-GkXX82wb.js} +1 -1
  62. package/web-dist/index.html +1 -1
  63. package/web-dist/assets/channel-Dd_6YMtj.js +0 -1
  64. package/web-dist/assets/classDiagram-VBA2DB6C-DGa9Vmq1.js +0 -1
  65. package/web-dist/assets/classDiagram-v2-RAHNMMFH-DGa9Vmq1.js +0 -1
  66. package/web-dist/assets/clone-BZHgrnhF.js +0 -1
  67. package/web-dist/assets/stateDiagram-v2-FVOUBMTO-BsX0KLYS.js +0 -1
@@ -1,6 +1,6 @@
1
1
  import { type SessionNotification } from "@agentclientprotocol/sdk";
2
2
  import type { CliProviderAdapter, ProviderEvent, ProviderAuthStatus, ProviderLoginResult, ProviderLogoutResult, ProviderId, ProviderInfo, ProviderModelInfo, ProviderSession, RuntimeMode, StartSessionOptions } from "./contracts.js";
3
- type AcpProviderAuthKind = "codex" | "claude-code";
3
+ type AcpProviderAuthKind = "acp";
4
4
  export interface AcpProviderConfig {
5
5
  id: ProviderId;
6
6
  name: string;
@@ -19,11 +19,13 @@ export declare class AcpProvider implements CliProviderAdapter {
19
19
  private readonly sessions;
20
20
  private readonly emitter;
21
21
  private authLoginProcess;
22
+ private cachedModels;
22
23
  constructor(config: AcpProviderConfig);
23
24
  checkAvailability(): Promise<boolean>;
24
25
  listModels(): Promise<ProviderModelInfo[]>;
25
26
  getAuthStatus(): Promise<ProviderAuthStatus>;
26
27
  startLogin(): Promise<ProviderLoginResult>;
28
+ private checkProviderAuthenticated;
27
29
  logout(): Promise<ProviderLogoutResult>;
28
30
  startSession(options: StartSessionOptions): Promise<ProviderSession>;
29
31
  sendTurn(sessionId: string, message: string, attachments?: string[]): Promise<void>;
@@ -34,7 +36,7 @@ export declare class AcpProvider implements CliProviderAdapter {
34
36
  emitEvent(event: ProviderEvent): void;
35
37
  handleSessionUpdate(sessionId: string, params: SessionNotification): void;
36
38
  private getSession;
37
- private getLoginCommand;
39
+ private probeAcpAuth;
38
40
  }
39
41
  export declare function loadAcpProviderConfigs(): AcpProviderConfig[];
40
42
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"acp-provider.d.ts","sourceRoot":"","sources":["../../src/providers/acp-provider.ts"],"names":[],"mappings":"AAMA,OAAO,EAUL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,kBAAkB,EAElB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAWxB,KAAK,mBAAmB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACpC;AAgDD,qBAAa,WAAY,YAAW,kBAAkB;IACpD,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8E;IACrG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,MAAM,EAAE,iBAAiB;IAmB/B,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAmBrC,UAAU,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAI1C,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC;IA4B5C,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IA0B1C,MAAM,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAmCvC,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IAsFpE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BnF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzF,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnD,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAK5D,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIrC,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAkEzE,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,eAAe;CAUxB;AAgGD,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CA4B5D"}
1
+ {"version":3,"file":"acp-provider.d.ts","sourceRoot":"","sources":["../../src/providers/acp-provider.ts"],"names":[],"mappings":"AAMA,OAAO,EAWL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,kBAAkB,EAElB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AASxB,KAAK,mBAAmB,GAAG,KAAK,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACpC;AAwDD,qBAAa,WAAY,YAAW,kBAAkB;IACpD,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8E;IACrG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,YAAY,CAAoC;gBAE5C,MAAM,EAAE,iBAAiB;IAmB/B,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAmBrC,UAAU,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAmC1C,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAmB5C,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;YAwDlC,0BAA0B;IAWlC,MAAM,IAAI,OAAO,CAAC,oBAAoB,CAAC;IA4BvC,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IAgGpE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BnF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzF,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAK5D,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIrC,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAkEzE,OAAO,CAAC,UAAU;YAMJ,YAAY;CAgD3B;AAkHD,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CA4D5D"}
@@ -1,23 +1,34 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
2
  import { EventEmitter } from "node:events";
3
- import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { Readable, Writable } from "node:stream";
7
7
  import { ClientSideConnection, PROTOCOL_VERSION, ndJsonStream, } from "@agentclientprotocol/sdk";
8
8
  import { uuidv7 } from "../db/uuidv7.js";
9
- import { DEVICE_PROVIDER_AUTH, NO_PROVIDER_AUTH, killChildTree as killAuthChildTree, runAuthCommand, startDeviceLoginCommand, unsupportedLogin, unsupportedLogout, } from "./provider-auth.js";
9
+ import { NO_PROVIDER_AUTH, killChildTree as killAuthChildTree, runAuthCommand, unsupportedLogin, unsupportedLogout, } from "./provider-auth.js";
10
10
  class JaitAcpClient {
11
11
  provider;
12
12
  sessionId;
13
13
  approvals;
14
- constructor(provider, sessionId, approvals) {
14
+ runtimeMode;
15
+ constructor(provider, sessionId, approvals, runtimeMode) {
15
16
  this.provider = provider;
16
17
  this.sessionId = sessionId;
17
18
  this.approvals = approvals;
19
+ this.runtimeMode = runtimeMode;
18
20
  }
19
21
  async requestPermission(params) {
22
+ const allowOptionId = params.options.find((option) => option.kind.startsWith("allow"))?.optionId ?? params.options[0]?.optionId ?? "allow";
23
+ const rejectOptionId = params.options.find((option) => option.kind.startsWith("reject"))?.optionId ?? params.options.at(-1)?.optionId ?? "reject";
24
+ // In full-access mode, auto-approve all tool requests without prompting.
25
+ if (this.runtimeMode === "full-access") {
26
+ return { outcome: { outcome: "selected", optionId: allowOptionId } };
27
+ }
20
28
  const requestId = uuidv7();
29
+ const response = new Promise((resolve) => {
30
+ this.approvals.set(requestId, { allowOptionId, rejectOptionId, resolve });
31
+ });
21
32
  this.provider.emitEvent({
22
33
  type: "tool.approval-required",
23
34
  sessionId: this.sessionId,
@@ -25,11 +36,7 @@ class JaitAcpClient {
25
36
  args: params.toolCall.rawInput,
26
37
  requestId,
27
38
  });
28
- const allowOptionId = params.options.find((option) => option.kind.startsWith("allow"))?.optionId ?? params.options[0]?.optionId ?? "allow";
29
- const rejectOptionId = params.options.find((option) => option.kind.startsWith("reject"))?.optionId ?? params.options.at(-1)?.optionId ?? "reject";
30
- return new Promise((resolve) => {
31
- this.approvals.set(requestId, { allowOptionId, rejectOptionId, resolve });
32
- });
39
+ return response;
33
40
  }
34
41
  async sessionUpdate(params) {
35
42
  this.provider.handleSessionUpdate(this.sessionId, params);
@@ -43,9 +50,10 @@ export class AcpProvider {
43
50
  sessions = new Map();
44
51
  emitter = new EventEmitter();
45
52
  authLoginProcess = null;
53
+ cachedModels = null;
46
54
  constructor(config) {
47
55
  this.id = config.id;
48
- this.authKind = config.auth === false ? null : config.auth ?? inferAcpAuthKind(config.id);
56
+ this.authKind = config.auth === false ? null : "acp";
49
57
  this.config = {
50
58
  ...config,
51
59
  args: config.args ?? [],
@@ -58,7 +66,7 @@ export class AcpProvider {
58
66
  description: config.description,
59
67
  available: false,
60
68
  modes: this.config.modes,
61
- auth: this.authKind ? DEVICE_PROVIDER_AUTH : NO_PROVIDER_AUTH,
69
+ auth: this.authKind ? { login: true, logout: false, deviceCode: false } : NO_PROVIDER_AUTH,
62
70
  };
63
71
  }
64
72
  async checkAvailability() {
@@ -78,91 +86,143 @@ export class AcpProvider {
78
86
  return true;
79
87
  }
80
88
  async listModels() {
81
- return [];
82
- }
83
- async getAuthStatus() {
84
- switch (this.authKind) {
85
- case "codex": {
86
- const authenticated = !!process.env.OPENAI_API_KEY?.trim() || checkCodexAuthFile();
87
- return {
88
- ...DEVICE_PROVIDER_AUTH,
89
- authenticated,
90
- detail: authenticated
91
- ? "Codex CLI credentials are configured."
92
- : "Codex CLI is not authenticated.",
93
- };
89
+ if (this.cachedModels)
90
+ return this.cachedModels;
91
+ try {
92
+ const probe = await this.probeAcpAuth();
93
+ try {
94
+ const newSession = await probe.connection.newSession({
95
+ cwd: process.cwd(),
96
+ mcpServers: [],
97
+ });
98
+ const models = [];
99
+ const modelState = newSession.models;
100
+ if (modelState && modelState.availableModels?.length) {
101
+ for (const model of modelState.availableModels) {
102
+ models.push({
103
+ id: model.modelId,
104
+ name: model.name,
105
+ description: model.description ?? undefined,
106
+ isDefault: model.modelId === modelState.currentModelId,
107
+ });
108
+ }
109
+ }
110
+ await probe.connection.closeSession?.({ sessionId: newSession.sessionId }).catch(() => { });
111
+ this.cachedModels = models;
112
+ return models;
94
113
  }
95
- case "claude-code": {
96
- const status = await runAuthCommand(this.id, "claude", ["auth", "status"], 10_000);
97
- const authenticated = !!process.env.ANTHROPIC_API_KEY?.trim() || status.ok;
98
- return {
99
- ...DEVICE_PROVIDER_AUTH,
100
- authenticated,
101
- detail: authenticated
102
- ? "Claude Code credentials are configured."
103
- : status.rawOutput ?? "Claude Code is not authenticated.",
104
- };
114
+ finally {
115
+ probe.child.kill();
105
116
  }
106
- default:
107
- return { ...NO_PROVIDER_AUTH, authenticated: null, detail: "Auth is managed by the ACP agent." };
117
+ }
118
+ catch {
119
+ return [];
108
120
  }
109
121
  }
122
+ async getAuthStatus() {
123
+ if (!this.authKind) {
124
+ return { ...NO_PROVIDER_AUTH, authenticated: null, detail: "Auth is managed by the ACP agent." };
125
+ }
126
+ const probe = await this.probeAcpAuth().catch(() => null);
127
+ const login = (probe?.authMethods.length ?? 0) > 0;
128
+ const logout = Boolean(probe?.initialized.agentCapabilities?.auth?.logout);
129
+ probe?.child.kill();
130
+ const authenticated = await this.checkProviderAuthenticated();
131
+ return {
132
+ login,
133
+ logout,
134
+ deviceCode: false,
135
+ authenticated,
136
+ detail: probe ? formatAcpAuthDetail(this.info.name, authenticated) : "Could not read ACP authentication capabilities.",
137
+ };
138
+ }
110
139
  async startLogin() {
111
- const login = this.getLoginCommand();
112
- if (!login)
140
+ if (!this.authKind)
113
141
  return unsupportedLogin(this.id, "Auth is managed by the ACP agent.");
114
142
  if (this.authLoginProcess) {
115
143
  killAuthChildTree(this.authLoginProcess);
116
144
  this.authLoginProcess = null;
117
145
  }
118
- const { result, child } = await startDeviceLoginCommand({
119
- providerId: this.id,
120
- label: login.label,
121
- commandLine: login.commandLine,
122
- args: login.args,
123
- timeoutMs: 30_000,
124
- });
125
- if (child) {
146
+ const probe = await this.probeAcpAuth();
147
+ const method = chooseAcpAuthMethod(probe.authMethods);
148
+ if (!method) {
149
+ probe.child.kill();
150
+ return unsupportedLogin(this.id, "ACP agent did not advertise a login method.");
151
+ }
152
+ if (isTerminalAuthMethod(method)) {
153
+ probe.child.kill();
154
+ const args = [...this.config.args, ...(method.args ?? [])];
155
+ const child = spawn(this.config.command, args, {
156
+ cwd: process.cwd(),
157
+ stdio: "ignore",
158
+ env: { ...process.env, ...this.config.env, ...method.env },
159
+ });
126
160
  this.authLoginProcess = child;
127
161
  child.on("exit", () => {
128
162
  if (this.authLoginProcess === child)
129
163
  this.authLoginProcess = null;
164
+ this.cachedModels = null;
130
165
  void this.checkAvailability();
131
166
  });
167
+ return {
168
+ ok: true,
169
+ status: "started",
170
+ providerId: this.id,
171
+ message: `${method.name} login started through ACP.`,
172
+ };
132
173
  }
133
- return result;
174
+ const child = probe.child;
175
+ this.authLoginProcess = child;
176
+ void probe.connection.authenticate({ methodId: method.id })
177
+ .catch(() => { })
178
+ .finally(() => {
179
+ if (this.authLoginProcess === child)
180
+ this.authLoginProcess = null;
181
+ child.kill();
182
+ this.cachedModels = null;
183
+ void this.checkAvailability();
184
+ });
185
+ return {
186
+ ok: true,
187
+ status: "started",
188
+ providerId: this.id,
189
+ message: `${method.name} login started through ACP.`,
190
+ };
191
+ }
192
+ async checkProviderAuthenticated() {
193
+ if (this.id === "codex") {
194
+ return Boolean(process.env.OPENAI_API_KEY?.trim()) || checkCodexAuthFile();
195
+ }
196
+ if (this.id === "claude-code") {
197
+ const status = await runAuthCommand(this.id, "claude", ["auth", "status"], 10_000).catch(() => null);
198
+ return Boolean(process.env.ANTHROPIC_API_KEY?.trim()) || Boolean(status?.ok);
199
+ }
200
+ return null;
134
201
  }
135
202
  async logout() {
136
203
  if (this.authLoginProcess) {
137
204
  killAuthChildTree(this.authLoginProcess);
138
205
  this.authLoginProcess = null;
139
206
  }
140
- switch (this.authKind) {
141
- case "codex": {
142
- const result = await runAuthCommand(this.id, "codex", ["logout"]);
143
- const cleared = result.ok ? clearCodexAuthFile() : true;
144
- await this.checkAvailability().catch(() => false);
145
- return {
146
- ...result,
147
- ok: result.ok && cleared,
148
- status: result.ok && cleared ? result.status : "error",
149
- message: result.ok
150
- ? cleared
151
- ? "Codex logout completed."
152
- : "Codex logout ran, but stored credentials could not be removed."
153
- : result.message,
154
- };
155
- }
156
- case "claude-code": {
157
- const result = await runAuthCommand(this.id, "claude", ["auth", "logout"]);
158
- await this.checkAvailability().catch(() => false);
159
- return {
160
- ...result,
161
- message: result.ok ? "Claude Code logout completed." : result.message,
162
- };
207
+ if (!this.authKind) {
208
+ return unsupportedLogout(this.id, "Auth is managed by the ACP agent.");
209
+ }
210
+ const probe = await this.probeAcpAuth();
211
+ try {
212
+ if (!probe.initialized.agentCapabilities?.auth?.logout) {
213
+ return unsupportedLogout(this.id, "ACP agent did not advertise logout support.");
163
214
  }
164
- default:
165
- return unsupportedLogout(this.id, "Auth is managed by the ACP agent.");
215
+ await probe.connection.unstable_logout({});
216
+ return {
217
+ ok: true,
218
+ status: "completed",
219
+ providerId: this.id,
220
+ message: `${this.info.name} logout completed.`,
221
+ };
222
+ }
223
+ finally {
224
+ probe.child.kill();
225
+ await this.checkAvailability().catch(() => false);
166
226
  }
167
227
  }
168
228
  async startSession(options) {
@@ -179,7 +239,9 @@ export class AcpProvider {
179
239
  cwd: options.workingDirectory,
180
240
  stdio: ["pipe", "pipe", "pipe"],
181
241
  env: { ...process.env, ...this.config.env, ...options.env },
242
+ shell: true,
182
243
  });
244
+ child.on("error", () => { }); // prevent unhandled ENOENT on Windows
183
245
  child.stderr?.on("data", (chunk) => {
184
246
  const text = chunk.toString("utf8").trim();
185
247
  if (text) {
@@ -191,7 +253,7 @@ export class AcpProvider {
191
253
  const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
192
254
  const connection = new ClientSideConnection((agent) => {
193
255
  capturedAgent = agent;
194
- return new JaitAcpClient(this, sessionId, approvals);
256
+ return new JaitAcpClient(this, sessionId, approvals, options.mode);
195
257
  }, stream);
196
258
  child.once("exit", (code, signal) => {
197
259
  const current = this.sessions.get(sessionId);
@@ -208,23 +270,31 @@ export class AcpProvider {
208
270
  });
209
271
  this.sessions.delete(sessionId);
210
272
  });
211
- const initialized = await connection.initialize({
212
- protocolVersion: PROTOCOL_VERSION,
213
- clientInfo: { name: "Jait", version: "0.1" },
214
- clientCapabilities: {
215
- fs: { readTextFile: false, writeTextFile: false },
216
- terminal: false,
217
- },
218
- });
219
- const newSession = await connection.newSession({
220
- cwd: options.workingDirectory,
221
- mcpServers: (options.mcpServers ?? []).map(toAcpMcpServer),
222
- });
223
- if (options.mode) {
224
- await connection.setSessionMode({ sessionId: newSession.sessionId, modeId: options.mode }).catch(() => { });
273
+ let initialized;
274
+ let newSession;
275
+ try {
276
+ initialized = await connection.initialize({
277
+ protocolVersion: PROTOCOL_VERSION,
278
+ clientInfo: { name: "Jait", version: "0.1" },
279
+ clientCapabilities: {
280
+ fs: { readTextFile: false, writeTextFile: false },
281
+ terminal: false,
282
+ },
283
+ });
284
+ newSession = await connection.newSession({
285
+ cwd: options.workingDirectory,
286
+ mcpServers: (options.mcpServers ?? []).map(toAcpMcpServer),
287
+ });
288
+ if (options.mode) {
289
+ await connection.setSessionMode({ sessionId: newSession.sessionId, modeId: options.mode }).catch(() => { });
290
+ }
291
+ if (options.model) {
292
+ await connection.unstable_setSessionModel({ sessionId: newSession.sessionId, modelId: options.model }).catch(() => { });
293
+ }
225
294
  }
226
- if (options.model) {
227
- await connection.unstable_setSessionModel({ sessionId: newSession.sessionId, modelId: options.model }).catch(() => { });
295
+ catch (error) {
296
+ child.kill();
297
+ throw error;
228
298
  }
229
299
  session.status = "running";
230
300
  const state = {
@@ -289,7 +359,7 @@ export class AcpProvider {
289
359
  const state = this.sessions.get(sessionId);
290
360
  if (!state)
291
361
  return;
292
- await state.connection.closeSession?.({ sessionId: state.acpSessionId }).catch(() => { });
362
+ await withTimeout(state.connection.closeSession?.({ sessionId: state.acpSessionId }) ?? Promise.resolve(), 2_000).catch(() => { });
293
363
  state.session.status = "completed";
294
364
  state.session.completedAt = new Date().toISOString();
295
365
  state.child.kill();
@@ -374,23 +444,60 @@ export class AcpProvider {
374
444
  throw new Error(`Unknown ACP session: ${sessionId}`);
375
445
  return state;
376
446
  }
377
- getLoginCommand() {
378
- switch (this.authKind) {
379
- case "codex":
380
- return { label: "Codex", commandLine: "codex", args: ["login", "--device-auth"] };
381
- case "claude-code":
382
- return { label: "Claude Code", commandLine: "claude", args: ["auth", "login", "--claudeai"] };
383
- default:
384
- return null;
447
+ async probeAcpAuth() {
448
+ const child = spawn(this.config.command, this.config.args, {
449
+ cwd: process.cwd(),
450
+ stdio: ["pipe", "pipe", "pipe"],
451
+ env: { ...process.env, ...this.config.env },
452
+ shell: true,
453
+ });
454
+ // Prevent unhandled 'error' events from crashing the process (e.g. ENOENT on Windows)
455
+ const spawnError = new Promise((_, reject) => {
456
+ child.on("error", (err) => reject(err));
457
+ });
458
+ const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
459
+ const connection = new ClientSideConnection(() => ({
460
+ async requestPermission() {
461
+ return { outcome: { outcome: "cancelled" } };
462
+ },
463
+ async sessionUpdate() { },
464
+ }), stream);
465
+ try {
466
+ const initialized = await Promise.race([
467
+ connection.initialize({
468
+ protocolVersion: PROTOCOL_VERSION,
469
+ clientInfo: { name: "Jait", version: "0.1" },
470
+ clientCapabilities: {
471
+ auth: { terminal: true },
472
+ fs: { readTextFile: false, writeTextFile: false },
473
+ terminal: false,
474
+ },
475
+ }),
476
+ spawnError,
477
+ ]);
478
+ return { child, connection, initialized, authMethods: initialized.authMethods ?? [] };
479
+ }
480
+ catch (error) {
481
+ child.kill();
482
+ throw error;
385
483
  }
386
484
  }
387
485
  }
388
- function inferAcpAuthKind(id) {
389
- if (id === "codex")
390
- return "codex";
391
- if (id === "claude-code")
392
- return "claude-code";
393
- return null;
486
+ function chooseAcpAuthMethod(methods) {
487
+ return (methods.find(isTerminalAuthMethod) ??
488
+ methods.find((method) => method.id === "chat-gpt") ??
489
+ methods[0] ??
490
+ null);
491
+ }
492
+ function isTerminalAuthMethod(method) {
493
+ return "type" in method && method.type === "terminal";
494
+ }
495
+ function formatAcpAuthDetail(providerName, authenticated) {
496
+ if (authenticated === true)
497
+ return `${providerName} credentials are configured. Login and logout are managed through ACP.`;
498
+ if (authenticated === false)
499
+ return `${providerName} credentials are not configured. Login and logout are managed through ACP.`;
500
+ return `${providerName} authentication is managed through ACP.`;
394
501
  }
395
502
  function getCodexAuthPath() {
396
503
  const codexHome = process.env.CODEX_HOME ?? join(homedir(), ".codex");
@@ -423,26 +530,6 @@ function checkCodexAuthFile() {
423
530
  return false;
424
531
  }
425
532
  }
426
- function clearCodexAuthFile() {
427
- const auth = readCodexAuthFile();
428
- if (!auth)
429
- return true;
430
- delete auth.OPENAI_API_KEY;
431
- delete auth.tokens;
432
- const remaining = Object.entries(auth).filter(([, value]) => value !== undefined && value !== null);
433
- try {
434
- if (remaining.length === 0) {
435
- unlinkSync(getCodexAuthPath());
436
- }
437
- else {
438
- writeFileSync(getCodexAuthPath(), `${JSON.stringify(Object.fromEntries(remaining), null, 2)}\n`, "utf-8");
439
- }
440
- return true;
441
- }
442
- catch {
443
- return false;
444
- }
445
- }
446
533
  function toAcpMcpServer(server) {
447
534
  if (server.transport === "stdio") {
448
535
  return {
@@ -452,6 +539,14 @@ function toAcpMcpServer(server) {
452
539
  env: Object.entries(server.env ?? {}).map(([name, value]) => ({ name, value })),
453
540
  };
454
541
  }
542
+ if (server.transport === "http") {
543
+ return {
544
+ type: "http",
545
+ name: server.name,
546
+ url: server.url ?? "",
547
+ headers: [],
548
+ };
549
+ }
455
550
  return {
456
551
  type: "sse",
457
552
  name: server.name,
@@ -474,6 +569,18 @@ function stringifyUnknown(value) {
474
569
  return String(value);
475
570
  }
476
571
  }
572
+ function withTimeout(promise, timeoutMs) {
573
+ return new Promise((resolve, reject) => {
574
+ const timeout = setTimeout(() => reject(new Error("Operation timed out")), timeoutMs);
575
+ promise.then((value) => {
576
+ clearTimeout(timeout);
577
+ resolve(value);
578
+ }, (error) => {
579
+ clearTimeout(timeout);
580
+ reject(error);
581
+ });
582
+ });
583
+ }
477
584
  export function loadAcpProviderConfigs() {
478
585
  const defaults = [
479
586
  {
@@ -490,6 +597,38 @@ export function loadAcpProviderConfigs() {
490
597
  command: "npx",
491
598
  args: ["-y", "@agentclientprotocol/claude-agent-acp"],
492
599
  },
600
+ {
601
+ id: "cursor",
602
+ name: "Cursor",
603
+ description: "Cursor Agent via Agent Client Protocol",
604
+ command: "npx",
605
+ args: ["-y", "@blowmage/cursor-agent-acp"],
606
+ auth: false,
607
+ },
608
+ {
609
+ id: "pi",
610
+ name: "Pi",
611
+ description: "Pi coding agent via Agent Client Protocol",
612
+ command: "npx",
613
+ args: ["-y", "pi-acp"],
614
+ auth: false,
615
+ },
616
+ {
617
+ id: "pi-gemini",
618
+ name: "Pi Gemini",
619
+ description: "Gemini-backed Pi ACP provider",
620
+ command: "npx",
621
+ args: ["-y", "pi-gemini-acp"],
622
+ auth: false,
623
+ },
624
+ {
625
+ id: "deepagents",
626
+ name: "DeepAgents",
627
+ description: "DeepAgents via Agent Client Protocol",
628
+ command: "npx",
629
+ args: ["-y", "deepagents-acp"],
630
+ auth: false,
631
+ },
493
632
  ];
494
633
  const raw = process.env.JAIT_ACP_PROVIDERS?.trim();
495
634
  if (!raw)