@automatalabs/acp-agents 0.21.2 → 0.22.1

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 (47) hide show
  1. package/dist/acp-client.d.ts +51 -0
  2. package/dist/acp-client.d.ts.map +1 -1
  3. package/dist/acp-client.js +127 -2
  4. package/dist/auth/auth-profiles.d.ts +63 -0
  5. package/dist/auth/auth-profiles.d.ts.map +1 -0
  6. package/dist/auth/auth-profiles.js +45 -0
  7. package/dist/auth/auth-store.d.ts +112 -0
  8. package/dist/auth/auth-store.d.ts.map +1 -0
  9. package/dist/auth/auth-store.js +186 -0
  10. package/dist/auth/auth-types.d.ts +93 -0
  11. package/dist/auth/auth-types.d.ts.map +1 -0
  12. package/dist/auth/auth-types.js +108 -0
  13. package/dist/backend.d.ts +7 -0
  14. package/dist/backend.d.ts.map +1 -1
  15. package/dist/backends/claude.d.ts +2 -0
  16. package/dist/backends/claude.d.ts.map +1 -1
  17. package/dist/backends/claude.js +3 -0
  18. package/dist/backends/codex.d.ts +3 -0
  19. package/dist/backends/codex.d.ts.map +1 -1
  20. package/dist/backends/codex.js +4 -0
  21. package/dist/backends/opencode.d.ts +2 -0
  22. package/dist/backends/opencode.d.ts.map +1 -1
  23. package/dist/backends/opencode.js +3 -0
  24. package/dist/capabilities.d.ts +7 -1
  25. package/dist/capabilities.d.ts.map +1 -1
  26. package/dist/capabilities.js +17 -0
  27. package/dist/client-handlers.d.ts +9 -0
  28. package/dist/client-handlers.d.ts.map +1 -1
  29. package/dist/client-handlers.js +17 -0
  30. package/dist/errors-map.d.ts.map +1 -1
  31. package/dist/errors-map.js +26 -2
  32. package/dist/index.d.ts +11 -5
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +8 -2
  35. package/dist/permissions.d.ts +27 -0
  36. package/dist/permissions.d.ts.map +1 -1
  37. package/dist/permissions.js +23 -1
  38. package/dist/pool.d.ts +20 -0
  39. package/dist/pool.d.ts.map +1 -1
  40. package/dist/pool.js +58 -16
  41. package/dist/protocol-coverage.d.ts +75 -0
  42. package/dist/protocol-coverage.d.ts.map +1 -1
  43. package/dist/protocol-coverage.js +58 -0
  44. package/dist/runner.d.ts +120 -3
  45. package/dist/runner.d.ts.map +1 -1
  46. package/dist/runner.js +307 -20
  47. package/package.json +3 -3
package/dist/runner.d.ts CHANGED
@@ -6,6 +6,8 @@ import { type AcpEventListener, type AcpEventName } from "./events.js";
6
6
  import type { Backend } from "./backend.js";
7
7
  import { InteractiveSession, type InteractiveSessionOptions } from "./interactive.js";
8
8
  import { type BackendRegistry, type CustomBackendConfig } from "./registry.js";
9
+ import { type AuthMethodDescriptor, type AuthResolution, type AuthResolver } from "./auth/auth-types.js";
10
+ import { type AuthMethodType, type BackendAuthState } from "./auth/auth-store.js";
9
11
  import type { ElicitationResolver, PermissionResolver } from "./permissions.js";
10
12
  interface LifecycleRoutingOptions {
11
13
  /** Model spec used only to select the backend process. */
@@ -73,6 +75,60 @@ export interface ReattachSessionOptions extends InteractiveSessionOptions {
73
75
  /** Alias for onPermissionRequest for hosts that name the resolver by role. */
74
76
  permissionResolver?: PermissionResolver;
75
77
  }
78
+ /** Options for AcpAgentRunner.completeAuth() (§1.3, §4.1). */
79
+ export interface CompleteAuthOptions extends AuthMethodsOptions {
80
+ /** A method id from describeAuthMethods(). */
81
+ methodId: string;
82
+ /** The host-collected resolution (env values / gateway meta / completed / cancelled) (§1.3). */
83
+ resolution: AuthResolution;
84
+ /** Event/telemetry label used in strict capability errors. */
85
+ label?: string;
86
+ signal?: AbortSignal;
87
+ }
88
+ /** The outcome of a host-completed auth step (§1.3, §4.1). Carries no secret. */
89
+ export type AuthOutcome = {
90
+ status: "authenticated" | "cancelled";
91
+ methodId: string;
92
+ recycled: boolean;
93
+ };
94
+ /** Redacted status view surfaced by the controller, MCP tool, and web (canonical shape; §2.14). */
95
+ export interface AuthStatusSnapshot {
96
+ backendId: string;
97
+ poolKey: string;
98
+ state: BackendAuthState;
99
+ authenticated: boolean;
100
+ canResume: boolean;
101
+ methods: {
102
+ id: string;
103
+ type: AuthMethodType;
104
+ name?: string;
105
+ }[];
106
+ }
107
+ /** The `runner.auth` controller — the auth verbs as one addressable object (§2.10, §4.1). */
108
+ export interface AuthController {
109
+ /** Alias of describeAuthMethods(). */
110
+ methods(opts?: AuthMethodsOptions): Promise<AuthMethodDescriptor[]>;
111
+ /** Alias of completeAuth(). */
112
+ authenticate(opts: CompleteAuthOptions): Promise<AuthOutcome>;
113
+ /** Clears the AuthStore for the backend, zeroizes secrets (§2.14), and recycles the pool. */
114
+ logout(opts?: LogoutOptions): Promise<void>;
115
+ /** Redacted, synchronous snapshot — ids/types/names + state only, NEVER secrets (§2.14). */
116
+ status(opts?: {
117
+ backend?: string;
118
+ }): AuthStatusSnapshot[];
119
+ /** Cold-resume re-arm predicate (§2.13): true iff state ∈ {authenticated,credentials_held} or diskBacked. */
120
+ canResume(backendId: string): boolean;
121
+ }
122
+ /** Structural capability interface the MCP composition root duck-types to register auth tools
123
+ * without widening the frozen `AgentRunner` seam (§4.1). `AcpAgentRunner` implements it. */
124
+ export interface AuthCapableRunner {
125
+ describeAuthMethods(opts?: AuthMethodsOptions): Promise<AuthMethodDescriptor[]>;
126
+ completeAuth(opts: CompleteAuthOptions): Promise<AuthOutcome>;
127
+ /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends), whether or not it
128
+ * yet has a BackendAuthMachine. */
129
+ listBackends(): string[];
130
+ readonly auth: AuthController;
131
+ }
76
132
  /** Constructor options for the runner: pool sizing, client-side handlers, and the custom-backend
77
133
  * registry. `backends` merges over (and wins against) env-declared AGENTPRISM_BACKENDS entries. */
78
134
  export interface AcpRunnerOptions extends AcpPoolOptions {
@@ -85,13 +141,27 @@ export interface AcpRunnerOptions extends AcpPoolOptions {
85
141
  /** Runner-wide ACP elicitation responder. When set, initialize advertises unstable
86
142
  * elicitation form/url support on every connection; sessions may override the resolver. */
87
143
  onElicitation?: ElicitationResolver;
144
+ /** Which auth method TYPES this host can complete (§1.2). When set, initialize advertises the
145
+ * matching client auth capability (`auth.terminal` + top-level `_meta["terminal-auth"]`, and/or
146
+ * `auth._meta.gateway`) on every connection, fixed for the connection lifetime. Unset (and
147
+ * `onAuth` unset) omits the `auth` capability entirely — the default-OFF, zero-behavior-change
148
+ * baseline. When `onAuth` is set but this is unset it derives to `{ terminal: false, gateway: true }`
149
+ * (§1.2). A native-TTY CLI host passes `{ terminal: true, gateway: true }`. */
150
+ authCapabilities?: {
151
+ terminal?: boolean;
152
+ gateway?: boolean;
153
+ };
154
+ /** Inline auth resolver (§1.3, §2.11). When set, a -32000 at session/new resolves-and-retries-once
155
+ * and the run NEVER pauses; when unset, a -32000 run pauses with reason:"auth_required" (§2.12,
156
+ * PR4). Mutually exclusive with pause by construction. */
157
+ onAuth?: AuthResolver;
88
158
  }
89
159
  /**
90
160
  * ACP-backed AgentRunner implementation. The caller that constructs an AcpAgentRunner owns it:
91
161
  * pass it into managers/runs as needed, then call dispose() (or use `await using`) when that
92
162
  * owner is done with the pooled and dedicated backend processes.
93
163
  */
94
- export declare class AcpAgentRunner implements AgentRunner {
164
+ export declare class AcpAgentRunner implements AgentRunner, AuthCapableRunner {
95
165
  private readonly pool;
96
166
  /** The resolved custom-backend registry (env + option, validated at construction). */
97
167
  private readonly backends;
@@ -105,6 +175,18 @@ export declare class AcpAgentRunner implements AgentRunner {
105
175
  private readonly clientHandlers;
106
176
  private readonly permissionResolver;
107
177
  private readonly elicitationResolver;
178
+ /** Client auth advertisement, derived ONCE at construction and fixed for every connection this
179
+ * runner opens (pooled and dedicated). Undefined => the `auth` capability is omitted (§1.2). */
180
+ private readonly authCapabilities;
181
+ /** Inline auth resolver (§2.11). When set, run() resolves a -32000 and retries once instead of
182
+ * surfacing AUTH_REQUIRED. Undefined => the (PR4) pause-and-resume path. */
183
+ private readonly onAuth;
184
+ /** The single per-runner auth store (§2.2). Holds every backend's `BackendAuthMachine`; the only
185
+ * home for credential material in the library. Threaded into the pool and every dedicated
186
+ * connection so all connection types reconcile to the same intent. */
187
+ private readonly authStore;
188
+ /** The auth verbs as one addressable object (§2.10). */
189
+ readonly auth: AuthController;
108
190
  private readonly structuredOutputTools;
109
191
  /** FIFO turn queue per pooled connection for injected-tool schema runs (see the injection
110
192
  * site for why concurrent injected sessions on one process cannot be isolated). */
@@ -143,15 +225,33 @@ export declare class AcpAgentRunner implements AgentRunner {
143
225
  openSession(opts: InteractiveSessionOptions): Promise<InteractiveSession>;
144
226
  /** Return the selected backend's initialize-advertised authentication methods. */
145
227
  authMethods(opts?: AuthMethodsOptions): Promise<AuthMethod[]>;
146
- /** Drive ACP authenticate on the selected backend. */
228
+ /** Drive ACP authenticate on the selected backend. REBUILT off dispose-after-authenticate (§2.9):
229
+ * instead of opening a dedicated connection and disposing it in `finally` — which lost any
230
+ * in-process (gateway) credential the agent stored on that process (gap 3) — this records the
231
+ * credential into the durable `AuthStore` and recycles the pool. A method with `_meta` records an
232
+ * in-process/disk intent replayed on every pooled connection's initialize; a bare method with no
233
+ * `_meta` fires the one-shot `agent-login` RPC so the agent runs its own login. */
147
234
  authenticate(opts: AuthenticateOptions): Promise<AuthenticateResponse | void>;
235
+ /** Proactively enumerate the selected backend's advertised methods, already type-dispatched (§1.3)
236
+ * and label-enriched by the backend's `AuthProfile.describe` (§3.1, identity for a profile-less
237
+ * custom backend). A read-only probe: opens a dedicated connection, reads the initialize-advertised
238
+ * methods, runs the base dispatcher through the profile seam, and disposes. */
239
+ describeAuthMethods(opts?: AuthMethodsOptions): Promise<AuthMethodDescriptor[]>;
240
+ /** Record the host-collected resolution into the AuthStore, advance the generation, and recycle
241
+ * the pool (§2.9/§2.6) so a subsequent run() always lands on a current connection. */
242
+ completeAuth(opts: CompleteAuthOptions): Promise<AuthOutcome>;
243
+ /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends). */
244
+ listBackends(): string[];
148
245
  /** List configurable providers from the selected backend. */
149
246
  listProviders(opts?: ListProvidersOptions): Promise<ListProvidersResponse>;
150
247
  /** Configure one provider on the selected backend. */
151
248
  setProvider(opts: SetProviderOptions): Promise<SetProviderResponse | void>;
152
249
  /** Disable one provider on the selected backend. */
153
250
  disableProvider(opts: DisableProviderOptions): Promise<DisableProviderResponse | void>;
154
- /** Logout through the selected backend. */
251
+ /** Logout through the selected backend. REBUILT (§2.9): first clear the AuthStore machine
252
+ * (zeroizing `authenticateMeta`/`envValues`, §2.14) and recycle the pool so no pooled process
253
+ * replays a stale gateway credential, THEN issue the agent `logout` RPC only where advertised
254
+ * (gated on `supportsLogout`; opencode advertises none → store-clear + recycle, no RPC, §3.4). */
155
255
  logout(opts?: LogoutOptions): Promise<LogoutResponse | void>;
156
256
  /** List persisted ACP sessions from the selected backend. */
157
257
  listSessions(opts?: ListSessionsOptions): Promise<ListSessionsResponse>;
@@ -171,6 +271,23 @@ export declare class AcpAgentRunner implements AgentRunner {
171
271
  [Symbol.asyncDispose](): Promise<void>;
172
272
  private createInteractiveSession;
173
273
  private createDedicatedConnection;
274
+ /** Read the selected backend's initialize-advertised auth methods on a dedicated connection and
275
+ * build their type-dispatched descriptors (§1.3). A read-only probe; the connection is disposed. */
276
+ private probeAuthMethods;
277
+ /** The shared write path (§2.9) behind completeAuth, the inline resolver (§2.11), and the rebuilt
278
+ * legacy authenticate(). Records the payload/methodType from the outcome but DERIVES `klass` from
279
+ * the chosen method's type + `_meta` shape (§2.1) — never from the resolution outcome. */
280
+ private applyResolution;
281
+ /** Inline resolve-and-retry-once at the run() session-acquisition seam (§2.11). Builds the
282
+ * AuthContext from the backend's advertised methods, invokes `onAuth`, and applies the result.
283
+ * Returns false on a cancelled/absent resolution (the caller propagates AUTH_REQUIRED). */
284
+ private resolveInlineAuth;
285
+ /** Redacted status snapshots (§2.10/§4.1). Enumerates every configured backend when `backend` is
286
+ * omitted; never exposes secrets. */
287
+ private authStatus;
288
+ private snapshotFor;
289
+ /** Cold-resume re-arm predicate (§2.13). */
290
+ canResume(backendId: string): boolean;
174
291
  /** Build the backend choice, model-selection spec, tool policy, and session/new options in one
175
292
  * place for run() and openSession() so new AcpSessionOptions fields cannot drift by path. */
176
293
  private prepareSession;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAmBA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEhB,KAAK,UAAU,EAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EAEV,sBAAsB,EACtB,uBAAuB,EAEvB,qBAAqB,EAErB,oBAAoB,EAEpB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EAEpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAoB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAKtF,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAc,MAAM,kBAAkB,CAAC;AAkD5F,UAAU,uBAAuB;IAC/B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IAClE,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,kDAAkD;AAClD,MAAM,WAAW,oBAAqB,SAAQ,uBAAuB;IACnE,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,UAAU,0BAA2B,SAAQ,uBAAuB;IAClE,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAoB,SAAQ,0BAA0B;IACrE,mEAAmE;IACnE,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC3C;AAED,kDAAkD;AAClD,MAAM,WAAW,oBAAqB,SAAQ,0BAA0B;CAAG;AAE3E,gDAAgD;AAChD,MAAM,WAAW,kBAAmB,SAAQ,0BAA0B;IACpE,UAAU,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC7C,OAAO,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACvC,OAAO,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACvC,OAAO,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;CACzC;AAED,oDAAoD;AACpD,MAAM,WAAW,sBAAuB,SAAQ,0BAA0B;IACxE,UAAU,EAAE,sBAAsB,CAAC,YAAY,CAAC,CAAC;CAClD;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAc,SAAQ,0BAA0B;CAAG;AAEpE,oEAAoE;AACpE,MAAM,WAAW,sBAAuB,SAAQ,yBAAyB;IACvE,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAMD,8DAA8D;AAC9D,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,UAAU,EAAE,cAAc,CAAC;IAC3B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,eAAe,GAAG,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzG,mGAAmG;AACnG,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,gBAAgB,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,cAAc,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChE;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACpE,+BAA+B;IAC/B,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9D,6FAA6F;IAC7F,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,4FAA4F;IAC5F,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB,EAAE,CAAC;IAC1D,6GAA6G;IAC7G,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED;6FAC6F;AAC7F,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,CAAC,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAChF,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9D;wCACoC;IACpC,YAAY,IAAI,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;CAC/B;AAED;oGACoG;AACpG,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD;yFACqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/C;mFAC+E;IAC/E,mBAAmB,CAAC,EAAE,kBAAkB,CAAC;IACzC;gGAC4F;IAC5F,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;;;oFAKgF;IAChF,gBAAgB,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7D;;+DAE2D;IAC3D,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,cAAe,YAAW,WAAW,EAAE,iBAAiB;IACnE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C;;2BAEuB;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgE;IAC1F;6FACyF;IACzF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAiC;IACpE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAkC;IACtE;qGACiG;IACjG,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAwD;IACzF;iFAC6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD;;2EAEuE;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAkC;IACxE;wFACoF;IACpF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAwC;IAC5E;;yFAEqF;IACrF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmD;IACvF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,GAAE,gBAAqB;IA+B1C;;;;;;;;;OASG;IACH,EAAE,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAI9E,+EAA+E;IAC/E,IAAI,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIhF,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIzE,kBAAkB,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI;IAI7C,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM;IAIzC;;;;;;OAMG;IACG,WAAW,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAM/E,kFAAkF;IAC5E,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAavE;;;;;wFAKoF;IAC9E,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAgBnF;;;oFAGgF;IAC1E,mBAAmB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAMzF;2FACuF;IACjF,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;IASnE,+EAA+E;IAC/E,YAAY,IAAI,MAAM,EAAE;IAMxB,6DAA6D;IACvD,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAmBpF,sDAAsD;IAChD,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IA0BhF,oDAAoD;IAC9C,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAqB5F;;;uGAGmG;IAC7F,MAAM,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA8BtE,6DAA6D;IACvD,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsBjF,gEAAgE;IAC1D,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B9D,iFAAiF;IAC3E,WAAW,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAO5E,kGAAkG;IAC5F,aAAa,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAOxE,GAAG,CAAC,CAAC,SAAS,OAAO,GAAG,SAAS,GAAG,SAAS,EACjD,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,UAAU,CAAC,CAAC,CAAM,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAgM1B;qGACiG;IACjG;gGAC4F;YAC9E,yBAAyB;IAWjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;YAI9B,wBAAwB;IAiEtC,OAAO,CAAC,yBAAyB;IAejC;yGACqG;YACvF,gBAAgB;IAY9B;;+FAE2F;YAC7E,eAAe;IAyE7B;;gGAE4F;YAC9E,iBAAiB;IAmB/B;0CACsC;IACtC,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;IAmBnB,4CAA4C;IAC5C,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKrC;kGAC8F;IAC9F,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,cAAc;IAMtB,gGAAgG;IAChG,OAAO,CAAC,WAAW;CAGpB;AAED;;;qDAGqD;AACrD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAE1E;AA6ED;;;;oCAIoC;AACpC,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,OAAO,CAM1G"}
package/dist/runner.js CHANGED
@@ -17,7 +17,7 @@
17
17
  // Timeout and abort are the ENGINE's job: we honor opts.signal (wired to ACP session/cancel)
18
18
  // and re-throw on abort, but never implement our own timeout.
19
19
  import { isAbsolute } from "node:path";
20
- import { WorkflowError, WorkflowErrorCode, } from "@automatalabs/shared-types";
20
+ import { isWorkflowError, WorkflowError, WorkflowErrorCode, } from "@automatalabs/shared-types";
21
21
  import { PooledConnection } from "./acp-client.js";
22
22
  import { AcpAgentPool } from "./pool.js";
23
23
  import { TypedEventEmitter, } from "./events.js";
@@ -28,6 +28,8 @@ import { OpenCodeBackend } from "./backends/opencode.js";
28
28
  import { CustomAcpBackend } from "./backends/custom.js";
29
29
  import { registryWithRunBackends, resolveBackendRegistry, } from "./registry.js";
30
30
  import { mapThrownError } from "./errors-map.js";
31
+ import { buildAuthDescriptor, } from "./auth/auth-types.js";
32
+ import { AuthStore, classifyCredential, } from "./auth/auth-store.js";
31
33
  import { resolveStructuredOutput } from "./structured-output.js";
32
34
  import { STRUCTURED_OUTPUT_SERVER_NAME, StructuredOutputToolHost, } from "./structured-tool.js";
33
35
  import { buildRunPrompt, mergeTurnMeta, promptWithImages, validatePromptImages, } from "./prompt.js";
@@ -51,6 +53,18 @@ export class AcpAgentRunner {
51
53
  clientHandlers;
52
54
  permissionResolver;
53
55
  elicitationResolver;
56
+ /** Client auth advertisement, derived ONCE at construction and fixed for every connection this
57
+ * runner opens (pooled and dedicated). Undefined => the `auth` capability is omitted (§1.2). */
58
+ authCapabilities;
59
+ /** Inline auth resolver (§2.11). When set, run() resolves a -32000 and retries once instead of
60
+ * surfacing AUTH_REQUIRED. Undefined => the (PR4) pause-and-resume path. */
61
+ onAuth;
62
+ /** The single per-runner auth store (§2.2). Holds every backend's `BackendAuthMachine`; the only
63
+ * home for credential material in the library. Threaded into the pool and every dedicated
64
+ * connection so all connection types reconcile to the same intent. */
65
+ authStore = new AuthStore();
66
+ /** The auth verbs as one addressable object (§2.10). */
67
+ auth;
54
68
  structuredOutputTools = new StructuredOutputToolHost();
55
69
  /** FIFO turn queue per pooled connection for injected-tool schema runs (see the injection
56
70
  * site for why concurrent injected sessions on one process cannot be isolated). */
@@ -66,13 +80,31 @@ export class AcpAgentRunner {
66
80
  this.clientHandlers = options.clientHandlers;
67
81
  this.permissionResolver = options.onPermissionRequest;
68
82
  this.elicitationResolver = options.onElicitation;
83
+ this.onAuth = options.onAuth;
84
+ // Default derivation (§1.2): explicit `authCapabilities` wins; else, when an `onAuth` resolver is
85
+ // present, derive `{ terminal: false, gateway: true }` (gateway is cheap and non-destructive;
86
+ // terminal needs a real TTY a generic programmatic host lacks); else omit `auth` entirely — the
87
+ // default-OFF, byte-identical baseline.
88
+ this.authCapabilities =
89
+ options.authCapabilities ?? (options.onAuth ? { terminal: false, gateway: true } : undefined);
69
90
  this.pool = new AcpAgentPool(options, {
70
91
  onEvent: this.emitEvent,
71
92
  permissionResolver: options.onPermissionRequest,
72
93
  elicitationResolver: options.onElicitation,
73
94
  advertiseElicitation: Boolean(options.onElicitation),
95
+ authCapabilities: this.authCapabilities,
96
+ authStore: this.authStore,
74
97
  });
75
98
  this.backends = resolveBackendRegistry(options.backends);
99
+ this.auth = {
100
+ methods: (opts) => this.describeAuthMethods(opts),
101
+ authenticate: (opts) => this.completeAuth(opts),
102
+ logout: async (opts) => {
103
+ await this.logout(opts ?? {});
104
+ },
105
+ status: (opts) => this.authStatus(opts),
106
+ canResume: (backendId) => this.canResume(backendId),
107
+ };
76
108
  }
77
109
  /**
78
110
  * Listen in on the live ACP stream. `name` is an ACP `sessionUpdate` discriminant
@@ -125,28 +157,55 @@ export class AcpAgentRunner {
125
157
  await disposeBestEffort(connection);
126
158
  }
127
159
  }
128
- /** Drive ACP authenticate on the selected backend. */
160
+ /** Drive ACP authenticate on the selected backend. REBUILT off dispose-after-authenticate (§2.9):
161
+ * instead of opening a dedicated connection and disposing it in `finally` — which lost any
162
+ * in-process (gateway) credential the agent stored on that process (gap 3) — this records the
163
+ * credential into the durable `AuthStore` and recycles the pool. A method with `_meta` records an
164
+ * in-process/disk intent replayed on every pooled connection's initialize; a bare method with no
165
+ * `_meta` fires the one-shot `agent-login` RPC so the agent runs its own login. */
129
166
  async authenticate(opts) {
130
167
  if (this.disposed)
131
168
  throw new Error("ACP agent runner is disposed");
132
169
  validateRequiredString(opts.methodId, opts.label, "authenticate", "methodId");
133
170
  opts.signal?.throwIfAborted();
134
171
  const backend = selectBackend(opts, this.backends);
135
- const connection = this.createDedicatedConnection(backend, () => undefined);
136
- try {
137
- const request = {
138
- methodId: opts.methodId,
139
- ...(opts.meta ? { _meta: opts.meta } : {}),
140
- };
141
- const response = await connection.authenticate(request, opts.label);
142
- opts.signal?.throwIfAborted();
143
- if (this.disposed)
144
- throw new Error("ACP agent runner is disposed");
145
- return response;
146
- }
147
- finally {
148
- await disposeBestEffort(connection);
149
- }
172
+ const { methods } = await this.probeAuthMethods(backend);
173
+ const resolution = opts.meta
174
+ ? { outcome: "meta", methodId: opts.methodId, meta: opts.meta }
175
+ : { outcome: "agent-login", methodId: opts.methodId };
176
+ await this.applyResolution(backend, resolution, methods, opts.methodId, opts.label);
177
+ opts.signal?.throwIfAborted();
178
+ if (this.disposed)
179
+ throw new Error("ACP agent runner is disposed");
180
+ return;
181
+ }
182
+ /** Proactively enumerate the selected backend's advertised methods, already type-dispatched (§1.3)
183
+ * and label-enriched by the backend's `AuthProfile.describe` (§3.1, identity for a profile-less
184
+ * custom backend). A read-only probe: opens a dedicated connection, reads the initialize-advertised
185
+ * methods, runs the base dispatcher through the profile seam, and disposes. */
186
+ async describeAuthMethods(opts = {}) {
187
+ if (this.disposed)
188
+ throw new Error("ACP agent runner is disposed");
189
+ const backend = selectBackend(opts, this.backends);
190
+ return (await this.probeAuthMethods(backend)).descriptors;
191
+ }
192
+ /** Record the host-collected resolution into the AuthStore, advance the generation, and recycle
193
+ * the pool (§2.9/§2.6) so a subsequent run() always lands on a current connection. */
194
+ async completeAuth(opts) {
195
+ if (this.disposed)
196
+ throw new Error("ACP agent runner is disposed");
197
+ validateRequiredString(opts.methodId, opts.label, "completeAuth", "methodId");
198
+ opts.signal?.throwIfAborted();
199
+ const backend = selectBackend(opts, this.backends);
200
+ const { methods } = await this.probeAuthMethods(backend);
201
+ return this.applyResolution(backend, opts.resolution, methods, opts.methodId, opts.label);
202
+ }
203
+ /** Ids of every configured backend (built-ins + AcpRunnerOptions.backends). */
204
+ listBackends() {
205
+ const ids = new Set(["claude", "codex", "opencode"]);
206
+ for (const name of this.backends.keys())
207
+ ids.add(name);
208
+ return [...ids];
150
209
  }
151
210
  /** List configurable providers from the selected backend. */
152
211
  async listProviders(opts = {}) {
@@ -220,14 +279,28 @@ export class AcpAgentRunner {
220
279
  await disposeBestEffort(connection);
221
280
  }
222
281
  }
223
- /** Logout through the selected backend. */
282
+ /** Logout through the selected backend. REBUILT (§2.9): first clear the AuthStore machine
283
+ * (zeroizing `authenticateMeta`/`envValues`, §2.14) and recycle the pool so no pooled process
284
+ * replays a stale gateway credential, THEN issue the agent `logout` RPC only where advertised
285
+ * (gated on `supportsLogout`; opencode advertises none → store-clear + recycle, no RPC, §3.4). */
224
286
  async logout(opts = {}) {
225
287
  if (this.disposed)
226
288
  throw new Error("ACP agent runner is disposed");
227
289
  opts.signal?.throwIfAborted();
228
290
  const backend = selectBackend(opts, this.backends);
291
+ const poolKey = backend.poolKey ?? backend.id;
292
+ this.authStore.machineFor(poolKey, backend.authProfile).send({ t: "logout" });
293
+ this.pool.recycle(poolKey);
229
294
  const connection = this.createDedicatedConnection(backend, () => undefined);
230
295
  try {
296
+ // await ready + negotiate before reading the logout advertisement.
297
+ await connection.authMethods();
298
+ if (!connection.capabilities?.supportsLogout) {
299
+ opts.signal?.throwIfAborted();
300
+ if (this.disposed)
301
+ throw new Error("ACP agent runner is disposed");
302
+ return; // logout unadvertised — store already cleared + recycled, no RPC (§3.4)
303
+ }
231
304
  const request = {
232
305
  ...(opts.meta ? { _meta: opts.meta } : {}),
233
306
  };
@@ -328,7 +401,7 @@ export class AcpAgentRunner {
328
401
  let structuredToolActive = false;
329
402
  let releaseStructuredToolTurn;
330
403
  try {
331
- session = await this.pool.acquirePrepared(prepared.backend, async (connection) => {
404
+ const prepare = async (connection) => {
332
405
  let sessionOptions = prepared.sessionOptions;
333
406
  if (shouldInjectStructuredOutputTool(schema, prepared.backend, connection.capabilities)) {
334
407
  // Injected runs are SERIALIZED per connection, and the server name stays CONSTANT.
@@ -357,7 +430,43 @@ export class AcpAgentRunner {
357
430
  };
358
431
  }
359
432
  return sessionOptions;
360
- }, { signal: opts.signal, label: opts.label });
433
+ };
434
+ // Inline resolve-and-retry-once (§2.11): when `onAuth` is set, a -32000 at session/new is
435
+ // resolved via the resolver and the acquire retried EXACTLY once — the run never pauses. A
436
+ // second -32000 propagates as AUTH_REQUIRED. When `onAuth` is unset this loop runs once and
437
+ // the error propagates unchanged (the PR4 pause-and-resume path), byte-identical to today.
438
+ let authRetried = false;
439
+ for (;;) {
440
+ try {
441
+ session = await this.pool.acquirePrepared(prepared.backend, prepare, {
442
+ signal: opts.signal,
443
+ label: opts.label,
444
+ });
445
+ break;
446
+ }
447
+ catch (error) {
448
+ if (this.onAuth && !authRetried && !opts.signal?.aborted && isAuthRequiredError(error)) {
449
+ authRetried = true;
450
+ // Discard the failed attempt's partial structured-tool registration so the retry's
451
+ // prepare re-registers cleanly (the failure happened at session/new, after prepare ran).
452
+ releaseStructuredToolTurn?.();
453
+ releaseStructuredToolTurn = undefined;
454
+ try {
455
+ structuredTool?.release();
456
+ }
457
+ catch {
458
+ // best-effort cleanup between attempts.
459
+ }
460
+ structuredTool = undefined;
461
+ structuredToolActive = false;
462
+ const resolved = await this.resolveInlineAuth(prepared.backend, opts, error);
463
+ if (!resolved)
464
+ throw error; // cancelled/unresolved -> propagate AUTH_REQUIRED
465
+ continue;
466
+ }
467
+ throw error;
468
+ }
469
+ }
361
470
  const activeSession = session;
362
471
  opts.signal?.throwIfAborted();
363
472
  // Hand the host the re-attach identity BEFORE any turn runs: the session id plus the
@@ -563,9 +672,139 @@ export class AcpAgentRunner {
563
672
  permissionResolver: this.permissionResolver,
564
673
  elicitationResolver: this.elicitationResolver,
565
674
  advertiseElicitation: Boolean(this.elicitationResolver),
675
+ authCapabilities: this.authCapabilities,
676
+ // Same store as the pool: a dedicated connection re-primes the durable intent at its own
677
+ // initialize (§2.5) — the direct fix for the dispose-after-authenticate bug (gap 3).
678
+ authStore: this.authStore,
566
679
  clientHandlers: this.clientHandlers,
567
680
  });
568
681
  }
682
+ /** Read the selected backend's initialize-advertised auth methods on a dedicated connection and
683
+ * build their type-dispatched descriptors (§1.3). A read-only probe; the connection is disposed. */
684
+ async probeAuthMethods(backend) {
685
+ const connection = this.createDedicatedConnection(backend, () => undefined);
686
+ try {
687
+ const methods = await connection.authMethods();
688
+ return { methods, descriptors: describeMethods(methods, backend) };
689
+ }
690
+ finally {
691
+ await disposeBestEffort(connection);
692
+ }
693
+ }
694
+ /** The shared write path (§2.9) behind completeAuth, the inline resolver (§2.11), and the rebuilt
695
+ * legacy authenticate(). Records the payload/methodType from the outcome but DERIVES `klass` from
696
+ * the chosen method's type + `_meta` shape (§2.1) — never from the resolution outcome. */
697
+ async applyResolution(backend, resolution, advertised, methodIdHint, label) {
698
+ const poolKey = backend.poolKey ?? backend.id;
699
+ const machine = this.authStore.machineFor(poolKey, backend.authProfile);
700
+ if (resolution.outcome === "cancelled") {
701
+ return { status: "cancelled", methodId: methodIdHint ?? "", recycled: false };
702
+ }
703
+ const methodId = resolutionMethodId(resolution) ?? methodIdHint ?? inferMethodId(resolution, advertised);
704
+ if (!methodId) {
705
+ throw new WorkflowError("completeAuth requires a methodId to record the resolution", WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false, agentLabel: label });
706
+ }
707
+ const chosen = advertised.find((m) => m.id === methodId);
708
+ const methodType = chosen ? authMethodType(chosen) : resolution.outcome === "env" ? "env_var" : "agent";
709
+ const advertisedMeta = chosen ? authMethodMeta(chosen) : undefined;
710
+ if (resolution.outcome === "agent-login") {
711
+ // The sole path from which the bare-`agent` login RPC ever fires (§2.9 step 3): the agent runs
712
+ // its own login on a live connection and persists to its native store, so every subsequent
713
+ // fresh initialize re-reads it and needs no replay.
714
+ const connection = this.createDedicatedConnection(backend, () => undefined);
715
+ try {
716
+ await connection.authenticate({ methodId }, label);
717
+ }
718
+ catch (error) {
719
+ throw mapThrownError(error, { label, backendId: backend.id, authMethods: [...advertised] });
720
+ }
721
+ finally {
722
+ await disposeBestEffort(connection);
723
+ }
724
+ const intent = { backendId: backend.id, poolKey, methodId, methodType, klass: "disk", diskBacked: true };
725
+ machine.send({ t: "host_authenticate", intent });
726
+ machine.send({ t: "apply_ok", connectionId: "host", generation: machine.generation });
727
+ this.pool.recycle(poolKey);
728
+ return { status: "authenticated", methodId, recycled: true };
729
+ }
730
+ // meta / env / completed: derive klass from the chosen advertised method (§2.1), never the outcome.
731
+ const { klass, diskBacked } = classifyCredential(methodType, advertisedMeta);
732
+ // A `meta` payload passes through the backend's pure-data `AuthProfile.buildMeta` (§3.1) so the
733
+ // agent's expected authenticate `_meta` shape is honored; a custom backend (no profile), or one
734
+ // whose advertised method could not be matched, records the host-supplied `meta` verbatim
735
+ // (conformance-by-absence). buildMeta is SECRET-preserving — it only reshapes the payload, never
736
+ // logs it (§2.14/Principle 9).
737
+ let authenticateMeta;
738
+ if (resolution.outcome === "meta") {
739
+ const buildMeta = backend.authProfile?.buildMeta;
740
+ authenticateMeta = buildMeta && chosen ? buildMeta(chosen, resolution) : resolution.meta;
741
+ }
742
+ const envValues = resolution.outcome === "env" ? resolution.values : undefined;
743
+ const intent = {
744
+ backendId: backend.id,
745
+ poolKey,
746
+ methodId,
747
+ methodType,
748
+ klass,
749
+ diskBacked,
750
+ ...(authenticateMeta ? { authenticateMeta } : {}),
751
+ ...(envValues ? { envValues } : {}),
752
+ };
753
+ machine.send({ t: "host_authenticate", intent });
754
+ this.pool.recycle(poolKey);
755
+ return { status: "authenticated", methodId, recycled: true };
756
+ }
757
+ /** Inline resolve-and-retry-once at the run() session-acquisition seam (§2.11). Builds the
758
+ * AuthContext from the backend's advertised methods, invokes `onAuth`, and applies the result.
759
+ * Returns false on a cancelled/absent resolution (the caller propagates AUTH_REQUIRED). */
760
+ async resolveInlineAuth(backend, opts, error) {
761
+ const { methods, descriptors } = await this.probeAuthMethods(backend);
762
+ // Mark the machine's authenticated->auth_required transition on the protocol signal (§2.3).
763
+ this.authStore
764
+ .machineFor(backend.poolKey ?? backend.id, backend.authProfile)
765
+ .send({ t: "auth_required_tripped", connectionId: "run", error });
766
+ const ctx = {
767
+ backendId: backend.id,
768
+ ...(opts.label ? { label: opts.label } : {}),
769
+ methods: descriptors,
770
+ cause: "required",
771
+ ...(opts.signal ? { signal: opts.signal } : {}),
772
+ };
773
+ const resolution = await this.onAuth(ctx);
774
+ if (resolution.outcome === "cancelled")
775
+ return false;
776
+ await this.applyResolution(backend, resolution, methods, undefined, opts.label);
777
+ return true;
778
+ }
779
+ /** Redacted status snapshots (§2.10/§4.1). Enumerates every configured backend when `backend` is
780
+ * omitted; never exposes secrets. */
781
+ authStatus(opts) {
782
+ const ids = opts?.backend ? [opts.backend] : this.listBackends();
783
+ return ids.map((id) => this.snapshotFor(id));
784
+ }
785
+ snapshotFor(backendId) {
786
+ const backend = selectBackend({ model: backendId }, this.backends);
787
+ const poolKey = backend.poolKey ?? backend.id;
788
+ const machine = this.authStore.existing(poolKey);
789
+ const state = machine?.state ?? "unauthenticated";
790
+ return {
791
+ backendId: backend.id,
792
+ poolKey,
793
+ state,
794
+ authenticated: state === "authenticated",
795
+ canResume: machine?.canResume() ?? false,
796
+ methods: (machine?.advertised ?? []).map((m) => ({
797
+ id: m.id,
798
+ type: authMethodType(m),
799
+ ...(m.name ? { name: m.name } : {}),
800
+ })),
801
+ };
802
+ }
803
+ /** Cold-resume re-arm predicate (§2.13). */
804
+ canResume(backendId) {
805
+ const backend = selectBackend({ model: backendId }, this.backends);
806
+ return this.authStore.existing(backend.poolKey ?? backend.id)?.canResume() ?? false;
807
+ }
569
808
  /** Build the backend choice, model-selection spec, tool policy, and session/new options in one
570
809
  * place for run() and openSession() so new AcpSessionOptions fields cannot drift by path. */
571
810
  prepareSession(opts, config) {
@@ -773,6 +1012,54 @@ function validateRequiredString(value, label, methodName, fieldName) {
773
1012
  throw new WorkflowError(`${methodName} requires ${fieldName} to be a non-empty string`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false, agentLabel: label });
774
1013
  }
775
1014
  }
1015
+ /** The mapped run error is a WorkflowError; the auth-required classification already ran in the
1016
+ * pool via mapThrownError (§1.5), so the inline seam keys on the code, not the raw -32000. */
1017
+ function isAuthRequiredError(error) {
1018
+ return isWorkflowError(error) && error.code === WorkflowErrorCode.AUTH_REQUIRED;
1019
+ }
1020
+ /** The SDK types a missing `type` discriminant as `agent`. */
1021
+ /** Dispatch each advertised method through the base §1.3 dispatcher, then hand the result to the
1022
+ * backend's pure-data `AuthProfile.describe` (§3.1) for label enrichment. A custom backend has NO
1023
+ * profile, so the base descriptor is returned verbatim (conformance-by-absence, §3.5). The profile
1024
+ * may only relabel; it never changes the `type` discriminant the base dispatcher chose (§3.1). */
1025
+ function describeMethods(methods, backend) {
1026
+ const spawn = backend.spawnConfig();
1027
+ const profile = backend.authProfile;
1028
+ return methods.map((method) => {
1029
+ const base = buildAuthDescriptor(method, spawn);
1030
+ return profile ? profile.describe(method, base) : base;
1031
+ });
1032
+ }
1033
+ function authMethodType(method) {
1034
+ return (("type" in method ? method.type : undefined) ?? "agent");
1035
+ }
1036
+ /** The advertised method's `_meta`, or undefined. This is agent-PUBLISHED metadata (e.g.
1037
+ * `gateway.protocol`), never a credential — the credential is only the resolver's payload. */
1038
+ function authMethodMeta(method) {
1039
+ const meta = method._meta;
1040
+ return meta ?? undefined;
1041
+ }
1042
+ /** A resolution that names its own method (meta / agent-login), else undefined. */
1043
+ function resolutionMethodId(resolution) {
1044
+ if (resolution.outcome === "meta" || resolution.outcome === "agent-login")
1045
+ return resolution.methodId;
1046
+ if (resolution.outcome === "env" || resolution.outcome === "completed")
1047
+ return resolution.methodId;
1048
+ return undefined;
1049
+ }
1050
+ /** Infer the target method for an env/completed resolution that did not name one: match by outcome
1051
+ * against the advertised methods (there is typically exactly one env_var / terminal method). */
1052
+ function inferMethodId(resolution, advertised) {
1053
+ if (resolution.outcome === "env") {
1054
+ return advertised.find((m) => authMethodType(m) === "env_var")?.id ?? advertised.find((m) => authMethodType(m) === "agent")?.id;
1055
+ }
1056
+ if (resolution.outcome === "completed") {
1057
+ return (advertised.find((m) => authMethodType(m) === "terminal")?.id ??
1058
+ advertised.find((m) => authMethodType(m) === "agent")?.id ??
1059
+ advertised[0]?.id);
1060
+ }
1061
+ return undefined;
1062
+ }
776
1063
  async function disposeBestEffort(connection) {
777
1064
  try {
778
1065
  await connection.dispose();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automatalabs/acp-agents",
3
- "version": "0.21.2",
3
+ "version": "0.22.1",
4
4
  "license": "Apache-2.0",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -29,10 +29,10 @@
29
29
  "dependencies": {
30
30
  "@agentclientprotocol/claude-agent-acp": "0.57.0",
31
31
  "@agentclientprotocol/sdk": "^1.2.1",
32
- "@automatalabs/codex-acp": "1.5.1",
32
+ "@automatalabs/codex-acp": "1.5.2",
33
33
  "@modelcontextprotocol/sdk": "^1.29",
34
34
  "typebox": "1.3.2",
35
- "@automatalabs/shared-types": "0.13.0"
35
+ "@automatalabs/shared-types": "0.14.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsc -b",