@m1a0rz/agent-identity 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README-cn.md +19 -9
  2. package/README.md +22 -12
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +23 -22
  5. package/dist/src/actions/identity-actions.d.ts +7 -1
  6. package/dist/src/actions/identity-actions.d.ts.map +1 -1
  7. package/dist/src/actions/identity-actions.js +89 -36
  8. package/dist/src/commands/identity-commands.d.ts.map +1 -1
  9. package/dist/src/commands/identity-commands.js +11 -6
  10. package/dist/src/hooks/after-tool-call.d.ts +12 -0
  11. package/dist/src/hooks/after-tool-call.d.ts.map +1 -0
  12. package/dist/src/hooks/after-tool-call.js +31 -0
  13. package/dist/src/hooks/before-agent-start.d.ts +3 -3
  14. package/dist/src/hooks/before-agent-start.d.ts.map +1 -1
  15. package/dist/src/hooks/before-agent-start.js +7 -21
  16. package/dist/src/hooks/before-tool-call.d.ts +19 -14
  17. package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
  18. package/dist/src/hooks/before-tool-call.js +194 -132
  19. package/dist/src/hooks/llm-input.js +3 -3
  20. package/dist/src/hooks/sessions-send-propagation.js +1 -1
  21. package/dist/src/hooks/sessions-spawn-propagation.js +1 -1
  22. package/dist/src/hooks/subagent-ended-cleanup.js +2 -2
  23. package/dist/src/risk/low-risk-tools.d.ts.map +1 -1
  24. package/dist/src/risk/low-risk-tools.js +0 -2
  25. package/dist/src/routes/oidc-login.d.ts.map +1 -1
  26. package/dist/src/routes/oidc-login.js +49 -7
  27. package/dist/src/services/identity-service.d.ts +3 -2
  28. package/dist/src/services/identity-service.d.ts.map +1 -1
  29. package/dist/src/services/identity-service.js +3 -2
  30. package/dist/src/services/oidc-client.d.ts +25 -8
  31. package/dist/src/services/oidc-client.d.ts.map +1 -1
  32. package/dist/src/services/oidc-client.js +55 -1
  33. package/dist/src/services/session-refresh.d.ts.map +1 -1
  34. package/dist/src/services/session-refresh.js +11 -2
  35. package/dist/src/services/tip-acquisition.d.ts +0 -1
  36. package/dist/src/services/tip-acquisition.d.ts.map +1 -1
  37. package/dist/src/services/tip-acquisition.js +2 -2
  38. package/dist/src/services/tip-propagation.d.ts.map +1 -1
  39. package/dist/src/services/tip-propagation.js +1 -2
  40. package/dist/src/services/tip-with-refresh.d.ts +1 -1
  41. package/dist/src/services/tip-with-refresh.d.ts.map +1 -1
  42. package/dist/src/services/tip-with-refresh.js +3 -5
  43. package/dist/src/store/credential-env-snapshot.d.ts +38 -0
  44. package/dist/src/store/credential-env-snapshot.d.ts.map +1 -0
  45. package/dist/src/store/credential-env-snapshot.js +101 -0
  46. package/dist/src/store/encryption.d.ts +30 -0
  47. package/dist/src/store/encryption.d.ts.map +1 -0
  48. package/dist/src/store/encryption.js +147 -0
  49. package/dist/src/store/oidc-state-store.d.ts +8 -1
  50. package/dist/src/store/oidc-state-store.d.ts.map +1 -1
  51. package/dist/src/store/oidc-state-store.js +3 -1
  52. package/dist/src/store/{group-sender-store.d.ts → sender-session-store.d.ts} +14 -7
  53. package/dist/src/store/sender-session-store.d.ts.map +1 -0
  54. package/dist/src/store/{group-sender-store.js → sender-session-store.js} +41 -12
  55. package/dist/src/store/session-store.d.ts.map +1 -1
  56. package/dist/src/store/session-store.js +91 -8
  57. package/dist/src/store/tip-store.d.ts +11 -6
  58. package/dist/src/store/tip-store.d.ts.map +1 -1
  59. package/dist/src/store/tip-store.js +23 -54
  60. package/dist/src/tools/identity-approve-tool.js +1 -1
  61. package/dist/src/tools/identity-fetch.d.ts.map +1 -1
  62. package/dist/src/tools/identity-fetch.js +2 -1
  63. package/dist/src/tools/identity-list-credentials.d.ts +2 -0
  64. package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
  65. package/dist/src/tools/identity-list-credentials.js +7 -4
  66. package/dist/src/tools/identity-login.js +1 -1
  67. package/dist/src/tools/identity-logout.js +1 -1
  68. package/dist/src/tools/identity-set-binding.js +1 -1
  69. package/dist/src/tools/identity-status.js +1 -1
  70. package/dist/src/tools/identity-unset-binding.js +1 -1
  71. package/dist/src/tools/identity-whoami.js +1 -1
  72. package/dist/src/utils/derive-session-key.d.ts +34 -4
  73. package/dist/src/utils/derive-session-key.d.ts.map +1 -1
  74. package/dist/src/utils/derive-session-key.js +50 -5
  75. package/package.json +4 -3
  76. package/skills/SKILL.md +75 -150
  77. package/dist/src/store/group-sender-store.d.ts.map +0 -1
package/README-cn.md CHANGED
@@ -11,7 +11,9 @@ UserPool OIDC 登录、TIP (Trusted Identity Provider) 令牌(通过 Identity
11
11
  - **OIDC 登录**:`/identity login` 返回 IdP 授权 URL。用户打开 URL 后,IdP 重定向到 `/identity/oauth/callback`。
12
12
  - **TIP 令牌**:当会话中有已登录用户时,`before_agent_start` 钩子会获取 TIP 令牌。
13
13
  - **凭据 3LO**:`/identity fetch <provider>` 返回授权 URL。IdP 重定向到 Identity 提供的回调地址(控制台配置)。
14
- - **凭据绑定**:`/identity set <provider> <envVar>` 将存储的凭据绑定到环境变量。工具运行时在 `before_agent_start` 中注入到 `process.env`。
14
+ - **凭据绑定**:`/identity set <provider> <envVar>` 将存储的凭据绑定到环境变量。凭据按工具调用粒度安全注入,并发多用户会话之间互相隔离。
15
+ - **加密会话存储**:`sessions.json` 使用 AES-256-GCM 加密存储在磁盘上。旧版明文文件首次加载时自动迁移。
16
+ - **内存 TIP 缓存**:TIP 令牌仅存储在内存中(不持久化到磁盘)。TIP 是短效令牌,可随时从用户 session token 重新获取。
15
17
  - **动态 UserPool**:通过 `userPoolName` + `clientName` 解析 OIDC 配置(无需手动配置 clientId)。
16
18
  - **凭证加载**:从环境变量、文件或 STS AssumeRole 加载 AK/SK(veadk 风格)。
17
19
 
@@ -251,16 +253,24 @@ TIP token 通过 `GetWorkloadAccessTokenForJWT` 获取。工作负载行为:
251
253
 
252
254
  ## 钩子
253
255
 
254
- - **before_agent_start** - 获取 TIP token;按 credential-env-bindings(按 session)将凭据注入到 `process.env`
255
- - **subagent_spawned** - 在子 agent 创建时将 TIP 传播到子会话
256
- - **before_tool_call** - authz.toolCheck、authz.skillReadCheck 或 authz.requireRiskApproval 时可选 AuthZTIP + CheckPermission 工具/skill;高风险工具需审批。评估命令/路径风险(规则 + 可选 LLM via authz.enableLlmRiskCheck)。
256
+ - **before_agent_start** - 仅为主 agent 获取 TIP token
257
+ - **subagent_spawned** - 在子 agent 创建时将 TIP 传播到子会话。
258
+ - **before_tool_call** - 群组上下文注入、可选 AuthZTIP 检查、CheckPermission、风险审批)、工具调用级凭据注入。
259
+ - **after_tool_call** - 清理工具调用级凭据注入状态。
257
260
 
258
261
  ## 数据存储
259
262
 
260
263
  插件数据位于 `~/.openclaw/plugins/identity/`:
261
264
 
262
- - `sessions.json` - sessionKey → userToken 映射(加载/保存时清理过期)
263
- - `tip-tokens.json` - sessionKey → TIP token 缓存(加载/保存时清理过期)
264
- - Credentials - 仅内存,按 session(api_key、oauth2);gateway 重启后丢失;logout 时清除
265
- - `credential-env-bindings.json` - 按 session:`{ [sessionKey]: { [provider]: envVar } }`
266
- - OIDC state - 仅内存(临时,5 分钟 TTL)
265
+ | 文件 | 描述 |
266
+ |------|------|
267
+ | `sessions.json` | 加密存储的 sessionKey userToken 映射。加载/保存时清理过期条目。 |
268
+ | `credential-env-bindings.json` | 按 session:`{ [sessionKey]: { [provider]: envVar } }` |
269
+
270
+ **仅内存存储(不持久化到磁盘):**
271
+
272
+ | 数据 | 描述 |
273
+ |------|------|
274
+ | TIP 令牌 | sessionKey → TIP token 缓存。短效,按需从 session token 重新获取。 |
275
+ | 凭据 | 按 session(api_key、oauth2)。gateway 重启后丢失;logout 时清除。 |
276
+ | OIDC state | 临时数据,5 分钟 TTL。 |
package/README.md CHANGED
@@ -9,11 +9,13 @@ Integrates with [Volcengine Agent Identity and Permission Management](https://ww
9
9
  ## Features
10
10
 
11
11
  - **OIDC Login**: `/identity login` returns IdP auth URL (no HTTP start endpoint). User opens URL, IdP redirects to `/identity/oauth/callback`.
12
- - **TIP Token**: `before_agent_start` hook fetches TIP token when session has a logged-in user
12
+ - **TIP Token**: `before_agent_start` hook fetches TIP token when session has a logged-in user.
13
13
  - **Credential 3LO**: `/identity fetch <provider>` returns auth URL. IdP redirects to Identity-provided callback (control-plane config).
14
- - **Credential Binding**: `/identity set <provider> <envVar>` binds stored credential to env var. Credentials are injected into `process.env` in `before_agent_start` when tools run.
15
- - **Dynamic UserPool**: Resolve OIDC config by `userPoolName` + `clientName` (no manual clientId)
16
- - **Credentials**: Load AK/SK from env, file, or STS AssumeRole (veadk-style)
14
+ - **Credential Binding**: `/identity set <provider> <envVar>` binds stored credential to env var. Credentials are securely injected per-tool-call, isolated between concurrent multi-user sessions.
15
+ - **Encrypted Session Storage**: `sessions.json` is encrypted at rest (AES-256-GCM). Plaintext sessions from older versions are auto-migrated on first load.
16
+ - **In-memory TIP Cache**: TIP tokens are stored only in memory (no disk persistence). They are short-lived and re-obtained from the user's session token on demand.
17
+ - **Dynamic UserPool**: Resolve OIDC config by `userPoolName` + `clientName` (no manual clientId).
18
+ - **Credentials**: Load AK/SK from env, file, or STS AssumeRole (veadk-style).
17
19
 
18
20
  ## HTTP Endpoints
19
21
 
@@ -251,16 +253,24 @@ Follow-up messages (login success, credential fetch done) are not delivered when
251
253
 
252
254
  ## Hooks
253
255
 
254
- - **before_agent_start** - Fetch TIP token; inject credentials into `process.env` per credential-env-bindings (per-session)
255
- - **subagent_spawned** - Propagate TIP to child session on subagent spawn
256
- - **before_tool_call** - Optional AuthZ when authz.toolCheck, authz.skillReadCheck, or authz.requireRiskApproval. TIP + CheckPermission for tools/skills; risk approval for high-risk tools. Evaluates user-provided commands/paths (rules + optional LLM via authz.enableLlmRiskCheck).
256
+ - **before_agent_start** - Fetch TIP token for main agent only.
257
+ - **subagent_spawned** - Propagate TIP to child session on subagent spawn.
258
+ - **before_tool_call** - Group context injection, optional AuthZ (TIP check, CheckPermission, risk approval), and per-tool-call credential injection.
259
+ - **after_tool_call** - Clean up per-tool-call credential injection state.
257
260
 
258
261
  ## Data Storage
259
262
 
260
263
  Plugin data at `~/.openclaw/plugins/identity/`:
261
264
 
262
- - `sessions.json` - sessionKey → userToken mapping (expired pruned on load/save)
263
- - `tip-tokens.json` - sessionKey → TIP token cache (expired pruned on load/save)
264
- - Credentials - in-memory only, per-session (api_key, oauth2); lost on gateway restart; cleared on logout
265
- - `credential-env-bindings.json` - per-session: `{ [sessionKey]: { [provider]: envVar } }`
266
- - OIDC state - in-memory only (ephemeral, 5 min TTL)
265
+ | File | Description |
266
+ |------|-------------|
267
+ | `sessions.json` | Encrypted sessionKey userToken mapping. Expired entries pruned on load/save. |
268
+ | `credential-env-bindings.json` | Per-session: `{ [sessionKey]: { [provider]: envVar } }` |
269
+
270
+ **In-memory only (not persisted to disk):**
271
+
272
+ | Data | Description |
273
+ |------|-------------|
274
+ | TIP tokens | sessionKey → TIP token cache. Short-lived, re-obtained from session token on demand. |
275
+ | Credentials | Per-session (api_key, oauth2). Lost on gateway restart; cleared on logout. |
276
+ | OIDC state | Ephemeral, 5 min TTL. |
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAmE7D,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QA0YtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAqE7D,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QA0YtD"}
package/dist/index.js CHANGED
@@ -19,9 +19,10 @@ import { createLlmInputHandler } from "./src/hooks/llm-input.js";
19
19
  import { createSessionsSendPropagationHandler } from "./src/hooks/sessions-send-propagation.js";
20
20
  import { createSessionsSpawnPropagationHandler } from "./src/hooks/sessions-spawn-propagation.js";
21
21
  import { createSubagentEndedCleanupHandler } from "./src/hooks/subagent-ended-cleanup.js";
22
- import { setSender, clearSender } from "./src/store/group-sender-store.js";
23
- import { deriveSessionKey, isGroupOrChannelSessionKey, } from "./src/utils/derive-session-key.js";
22
+ import { setSender, clearSender } from "./src/store/sender-session-store.js";
23
+ import { deriveSessionKey, needsSenderIsolation, } from "./src/utils/derive-session-key.js";
24
24
  import { createBeforeToolCallHandler } from "./src/hooks/before-tool-call.js";
25
+ import { createAfterToolCallHandler } from "./src/hooks/after-tool-call.js";
25
26
  import * as skillPathStore from "./src/store/skill-path-store.js";
26
27
  import { createOIDCCallbackHandler, createOIDCCallbackHandlerLazy, } from "./src/routes/oidc-login.js";
27
28
  import { IdentityClient, resolveOIDCConfig, } from "./src/services/identity-client.js";
@@ -42,7 +43,8 @@ import { createIdentityStatusTool } from "./src/tools/identity-status.js";
42
43
  import { createIdentityUnsetBindingTool } from "./src/tools/identity-unset-binding.js";
43
44
  import { createIdentityWhoamiTool } from "./src/tools/identity-whoami.js";
44
45
  import { parseSessionKeyToDeliveryTarget, } from "./src/utils/derive-session-key.js";
45
- import { logInfo, logWarn } from "./src/utils/logger.js";
46
+ import { logDebug, logInfo, logWarn } from "./src/utils/logger.js";
47
+ import { initEncryptionKey } from "./src/store/encryption.js";
46
48
  const PLUGIN_STORE_DIR = "~/.openclaw/plugins/identity";
47
49
  /**
48
50
  * Whether Identity should be enabled.
@@ -64,6 +66,7 @@ function hasAnyIdentityConfig(identity) {
64
66
  export default function register(api) {
65
67
  const pluginConfig = (api.pluginConfig ?? {});
66
68
  const storeDir = api.resolvePath(PLUGIN_STORE_DIR);
69
+ initEncryptionKey(storeDir);
67
70
  const identityCfg = pluginConfig.identity;
68
71
  const hasIdentity = hasAnyIdentityConfig(identityCfg);
69
72
  const userpool = pluginConfig.userpool;
@@ -320,7 +323,7 @@ export default function register(api) {
320
323
  accountId: ctx.accountId,
321
324
  config: api.runtime.config.loadConfig(),
322
325
  });
323
- if (!sessionKey || !isGroupOrChannelSessionKey(sessionKey))
326
+ if (!sessionKey || !needsSenderIsolation(sessionKey))
324
327
  return;
325
328
  setSender(sessionKey, {
326
329
  senderId,
@@ -330,7 +333,7 @@ export default function register(api) {
330
333
  messageId: metadata?.messageId,
331
334
  capturedAt: Date.now(),
332
335
  });
333
- logInfo(api.logger, `group sender captured session=${sessionKey} sender=${senderId}`);
336
+ logDebug(api.logger, `sender captured session=${sessionKey} sender=${senderId}`);
334
337
  }, { priority: 200 });
335
338
  api.on("session_end", (_event, ctx) => {
336
339
  if (ctx.sessionKey)
@@ -366,10 +369,7 @@ export default function register(api) {
366
369
  logger: api.logger,
367
370
  }));
368
371
  }
369
- const toolCheck = authz?.toolCheck ?? false;
370
372
  const skillReadCheck = authz?.skillReadCheck ?? false;
371
- const requireRiskApproval = authz?.requireRiskApproval ?? false;
372
- const hasAuthz = toolCheck || skillReadCheck || requireRiskApproval;
373
373
  api.on("llm_input", createLlmInputHandler({
374
374
  enabled: skillReadCheck,
375
375
  logger: api.logger,
@@ -380,18 +380,19 @@ export default function register(api) {
380
380
  skillPathStore.clearSessionById(ctx.sessionId);
381
381
  });
382
382
  }
383
- if (hasAuthz) {
384
- api.on("before_tool_call", createBeforeToolCallHandler({
385
- storeDir,
386
- identityClient: hasIdentity ? identityClient : undefined,
387
- namespaceName: authz?.namespaceName ?? "default",
388
- logger: api.logger,
389
- sendToSession,
390
- authz,
391
- approvalTtlMs,
392
- identityService: hasIdentity ? identityService : undefined,
393
- getOidcConfigForRefresh: getOidcConfigForRefresh ?? undefined,
394
- configWorkloadName: identityCfg?.workloadName,
395
- }));
396
- }
383
+ // before_tool_call: authz, credential injection, group sender context
384
+ api.on("before_tool_call", createBeforeToolCallHandler({
385
+ storeDir,
386
+ identityClient: hasIdentity ? identityClient : undefined,
387
+ namespaceName: authz?.namespaceName ?? "default",
388
+ logger: api.logger,
389
+ sendToSession,
390
+ authz,
391
+ approvalTtlMs,
392
+ identityService: hasIdentity ? identityService : undefined,
393
+ getOidcConfigForRefresh: getOidcConfigForRefresh ?? undefined,
394
+ configWorkloadName: identityCfg?.workloadName,
395
+ }));
396
+ // Companion after_tool_call: restore env snapshot set by credential injection
397
+ api.on("after_tool_call", createAfterToolCallHandler({ logger: api.logger }));
397
398
  }
@@ -86,7 +86,11 @@ export type ListCredentialsResult = {
86
86
  hasMore: boolean;
87
87
  totalCount?: number;
88
88
  };
89
- export declare function runListCredentials(deps: IdentityActionsDeps, sessionKey: string, page?: number): Promise<ListCredentialsResult>;
89
+ export type ListCredentialsFilter = {
90
+ name?: string;
91
+ flow?: string;
92
+ };
93
+ export declare function runListCredentials(deps: IdentityActionsDeps, sessionKey: string, page?: number, filter?: ListCredentialsFilter): Promise<ListCredentialsResult>;
90
94
  export type ListTipsResult = {
91
95
  tips: Array<{
92
96
  sessionKey: string;
@@ -124,6 +128,8 @@ export declare function runFetch(deps: IdentityActionsDeps, sessionKey: string,
124
128
  scopes?: string[];
125
129
  deliveryTarget?: SessionKeyDeliveryTarget | null;
126
130
  config?: OpenClawConfig;
131
+ /** "tool" = agent tool call, "command" = slash command. Defaults to "command". */
132
+ source?: "tool" | "command";
127
133
  }): Promise<FetchResult>;
128
134
  export type SetBindingResult = {
129
135
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../../src/actions/identity-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../../src/actions/identity-actions.ts"],"names":[],"mappings":"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"}
@@ -15,12 +15,12 @@
15
15
  */
16
16
  import { logDebug, logInfo, logWarn } from "../utils/logger.js";
17
17
  import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
18
- import { fetchOIDCDiscovery, buildAuthorizationUrl, generateState, } from "../services/oidc-client.js";
18
+ import { fetchOIDCDiscovery, buildAuthorizationUrl, generateState, generatePKCE, generateNonce, } from "../services/oidc-client.js";
19
19
  import { loadCredentialEnvBindings, loadAllCredentialEnvBindings, setCredentialEnvBinding, deleteCredentialEnvBinding, } from "../store/credential-env-bindings.js";
20
20
  import { loadCredentials, setCredential, getCredential, deleteCredentialsForSession, } from "../store/credential-store.js";
21
21
  import { getSession, deleteSession } from "../store/session-store.js";
22
22
  import { createState } from "../store/oidc-state-store.js";
23
- import { loadTIPTokens, saveTIPTokens } from "../store/tip-store.js";
23
+ import { getAllTIPTokens, deleteTIPToken } from "../store/tip-store.js";
24
24
  import { extractDelegationChainFromJwt } from "../utils/auth.js";
25
25
  import { resolveAgentId, } from "../utils/derive-session-key.js";
26
26
  function inferFlowFromProvider(info) {
@@ -32,6 +32,7 @@ function inferFlowFromProvider(info) {
32
32
  }
33
33
  const OAUTH_POLL_INTERVAL_MS = 2500;
34
34
  const OAUTH_POLL_TIMEOUT_MS = 10 * 60 * 1000;
35
+ const OAUTH_QUICK_RETRY_MS = 1000;
35
36
  function sleep(ms) {
36
37
  return new Promise((r) => setTimeout(r, ms));
37
38
  }
@@ -133,13 +134,18 @@ export async function runLogin(deps, sessionKey, options) {
133
134
  const oidcConfig = await getOidcConfig();
134
135
  const discovery = await fetchOIDCDiscovery(oidcConfig.discoveryUrl);
135
136
  const state = await generateState();
136
- await createState(storeDir, sessionKey, "", state, deliveryTarget);
137
+ const { codeVerifier, codeChallenge } = await generatePKCE();
138
+ const nonce = await generateNonce();
139
+ await createState(storeDir, sessionKey, "", state, deliveryTarget, { codeVerifier, nonce });
137
140
  const authUrl = buildAuthorizationUrl({
138
141
  authorizationEndpoint: discovery.authorization_endpoint,
139
142
  clientId: oidcConfig.clientId,
140
143
  redirectUri: oidcConfig.callbackUrl,
141
144
  scope: oidcConfig.scope ?? "openid profile email offline_access",
142
145
  state,
146
+ codeChallenge,
147
+ codeChallengeMethod: "S256",
148
+ nonce,
143
149
  });
144
150
  logInfo(logger, `login returning IdP URL for sessionKey=${sessionKey.slice(0, 24)}...`);
145
151
  return { kind: "auth_url", authUrl };
@@ -156,30 +162,40 @@ export async function runLogout(deps, sessionKey) {
156
162
  const { storeDir, logger } = deps;
157
163
  logDebug(logger, `logout sessionKey=${sessionKey.slice(0, 24)}...`);
158
164
  await deleteSession(storeDir, sessionKey);
159
- const tokens = await loadTIPTokens(storeDir);
160
- delete tokens[sessionKey];
161
- await saveTIPTokens(storeDir, tokens);
165
+ deleteTIPToken(sessionKey);
162
166
  deleteCredentialsForSession(storeDir, sessionKey);
163
167
  return { ok: true };
164
168
  }
165
169
  const LIST_PAGE_SIZE = 10;
166
- export async function runListCredentials(deps, sessionKey, page = 1) {
167
- const { storeDir, identityClient, logger } = deps;
170
+ export async function runListCredentials(deps, sessionKey, page = 1, filter) {
171
+ const { storeDir, identityClient, identityService, logger } = deps;
168
172
  const creds = await loadCredentials(storeDir, sessionKey);
169
173
  const bindings = await loadCredentialEnvBindings(storeDir, sessionKey);
170
174
  let providers = [];
171
175
  let totalCount = 0;
172
176
  if (identityClient) {
173
- try {
174
- const result = await identityClient.listCredentialProviders({
175
- PageNumber: page,
176
- PageSize: LIST_PAGE_SIZE,
177
- });
178
- providers = result.CredentialProviders ?? result.Data ?? [];
179
- totalCount = result.TotalCount ?? 0;
177
+ const session = await getSession(storeDir, sessionKey);
178
+ if (!session || !identityService.parseUserToken(session.userToken).valid) {
179
+ logWarn(logger, `list-credentials: no valid session for key=${sessionKey.slice(0, 24)}...`);
180
180
  }
181
- catch (e) {
182
- logWarn(logger, `list-credentials API error: ${String(e)}`);
181
+ else {
182
+ try {
183
+ const apiFilter = {};
184
+ if (filter?.name)
185
+ apiFilter.Name = filter.name;
186
+ if (filter?.flow)
187
+ apiFilter.Flow = filter.flow;
188
+ const result = await identityClient.listCredentialProviders({
189
+ PageNumber: page,
190
+ PageSize: LIST_PAGE_SIZE,
191
+ ...(Object.keys(apiFilter).length > 0 ? { Filter: apiFilter } : {}),
192
+ });
193
+ providers = result.CredentialProviders ?? result.Data ?? [];
194
+ totalCount = result.TotalCount ?? 0;
195
+ }
196
+ catch (e) {
197
+ logWarn(logger, `list-credentials API error: ${String(e)}`);
198
+ }
183
199
  }
184
200
  }
185
201
  const providerNames = new Set(providers.map((p) => p.Name));
@@ -232,7 +248,7 @@ export async function runListCredentials(deps, sessionKey, page = 1) {
232
248
  }
233
249
  export async function runListTips(deps) {
234
250
  const { storeDir } = deps;
235
- const tokens = await loadTIPTokens(storeDir);
251
+ const tokens = getAllTIPTokens();
236
252
  const bindingsBySession = await loadAllCredentialEnvBindings(storeDir);
237
253
  const now = Date.now();
238
254
  const tips = [];
@@ -292,27 +308,10 @@ export async function runConfig(deps) {
292
308
  }
293
309
  export async function runFetch(deps, sessionKey, params) {
294
310
  const { identityClient, storeDir, sendCredentialMessage, logger } = deps;
295
- const { provider, flow, flowExplicit, redirectUrl, scopes, deliveryTarget, config } = params;
311
+ const { provider, flow, flowExplicit, redirectUrl, scopes, deliveryTarget, config, source = "command" } = params;
296
312
  if (!identityClient) {
297
313
  return { kind: "error", message: "Identity service not configured. Cannot add credentials." };
298
314
  }
299
- let effectiveFlow = flow;
300
- if (!flowExplicit) {
301
- try {
302
- const listResult = await identityClient.listCredentialProviders({
303
- PageNumber: 1,
304
- PageSize: 20,
305
- Filter: { Name: provider },
306
- });
307
- const list = listResult.CredentialProviders ?? listResult.Data ?? [];
308
- const info = list.find((p) => p.Name === provider);
309
- if (info)
310
- effectiveFlow = inferFlowFromProvider(info);
311
- }
312
- catch {
313
- // keep default
314
- }
315
- }
316
315
  const ctxAgentId = resolveAgentId({ sessionKey, config: config });
317
316
  const tipRefreshOptions = deps.getOidcConfigForRefresh
318
317
  ? {
@@ -330,6 +329,23 @@ export async function runFetch(deps, sessionKey, params) {
330
329
  message: "Login first with `/identity login` to establish identity before adding credentials.",
331
330
  };
332
331
  }
332
+ let effectiveFlow = flow;
333
+ if (!flowExplicit) {
334
+ try {
335
+ const listResult = await identityClient.listCredentialProviders({
336
+ PageNumber: 1,
337
+ PageSize: 20,
338
+ Filter: { Name: provider },
339
+ });
340
+ const list = listResult.CredentialProviders ?? listResult.Data ?? [];
341
+ const info = list.find((p) => p.Name === provider);
342
+ if (info)
343
+ effectiveFlow = inferFlowFromProvider(info);
344
+ }
345
+ catch {
346
+ // keep default
347
+ }
348
+ }
333
349
  try {
334
350
  if (effectiveFlow === "apikey") {
335
351
  const result = await identityClient.getResourceApiKey({
@@ -361,6 +377,43 @@ export async function runFetch(deps, sessionKey, params) {
361
377
  return { kind: "success", message: `✓ Credential for \`${provider}\` added (direct token).` };
362
378
  }
363
379
  if (oauthResult.authorizationUrl) {
380
+ // Quick retry: the server may have a cached token that wasn't ready on
381
+ // the first call (e.g. user previously authorized). A short pause then
382
+ // re-check avoids unnecessary polling / returning authUrl to the user.
383
+ await sleep(OAUTH_QUICK_RETRY_MS);
384
+ try {
385
+ const retry = await identityClient.getResourceOauth2Token({
386
+ providerName: provider,
387
+ identityToken: tip.token,
388
+ flow: oauth2Flow,
389
+ redirectUrl,
390
+ scopes: scopes?.length ? scopes : undefined,
391
+ });
392
+ if (retry.accessToken) {
393
+ await setCredential(storeDir, sessionKey, provider, {
394
+ type: "oauth2",
395
+ status: "authenticated",
396
+ accessToken: retry.accessToken,
397
+ expiresAt: Date.now() + 3600 * 1000,
398
+ });
399
+ return { kind: "success", message: `✓ Credential for \`${provider}\` added (cached token).` };
400
+ }
401
+ }
402
+ catch {
403
+ // quick retry failed — proceed with authUrl flow
404
+ }
405
+ if (source === "tool") {
406
+ // Agent tool context: return authUrl for the agent to display.
407
+ // No background polling — the agent should re-call identity_fetch
408
+ // after the user completes authorization in the browser.
409
+ logInfo(logger, `fetch returning auth URL for provider=${provider} (tool, no poll)`);
410
+ return {
411
+ kind: "auth_url",
412
+ authUrl: oauthResult.authorizationUrl,
413
+ message: `Open this URL to authorize \`${provider}\`. After you complete authorization in the browser, tell me and I will fetch the credential.`,
414
+ };
415
+ }
416
+ // Slash command context: start background polling and notify via channel message.
364
417
  logInfo(logger, `fetch returning auth URL for provider=${provider}, starting poll`);
365
418
  const target = deliveryTarget ?? sessionKey;
366
419
  pollOAuthAndNotify({
@@ -1 +1 @@
1
- {"version":3,"file":"identity-commands.d.ts","sourceRoot":"","sources":["../../../src/commands/identity-commands.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAUL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,gCAAgC,CAAC;AAYxC,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAooBvD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB;;;;;mBAjf3C,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EA0fpE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB;;;;;mBA7frC,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EAsgBpE"}
1
+ {"version":3,"file":"identity-commands.d.ts","sourceRoot":"","sources":["../../../src/commands/identity-commands.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAUL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,gCAAgC,CAAC;AAYxC,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAyoBvD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB;;;;;mBAlf3C,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EA2fpE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB;;;;;mBA9frC,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EAugBpE"}
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { runStatus, runLogin, runLogout, runListCredentials, runListTips, runConfig, runFetch, runSetBinding, runUnsetBinding, } from "../actions/identity-actions.js";
17
17
  import { deriveSessionKey, deriveDeliveryTargetFromContext, } from "../utils/derive-session-key.js";
18
- import { buildEffectiveSessionKey } from "../store/group-sender-store.js";
18
+ import { buildEffectiveSessionKey } from "../store/sender-session-store.js";
19
19
  import { logDebug } from "../utils/logger.js";
20
20
  import { diagnoseRisk } from "../risk/diagnose-risk.js";
21
21
  import { getRiskPatterns } from "../risk/classify-risk.js";
@@ -88,7 +88,7 @@ function parseSubcommand(args) {
88
88
  rest: raw.slice(space + 1).trim(),
89
89
  };
90
90
  }
91
- /** Parse list args: optional page. E.g. "list", "list 2", "list --page 2". */
91
+ /** Parse list args: optional page and filters. E.g. "list", "list 2", "list --name=github --flow=M2M". */
92
92
  function parseListArgs(rest) {
93
93
  const parts = rest.split(/\s+/).filter(Boolean);
94
94
  const flags = {};
@@ -104,7 +104,11 @@ function parseListArgs(rest) {
104
104
  }
105
105
  }
106
106
  const page = flags.page ? parseInt(flags.page, 10) : positional[0];
107
- return { page: Number.isFinite(page) && page >= 1 ? page : 1 };
107
+ return {
108
+ page: Number.isFinite(page) && page >= 1 ? page : 1,
109
+ name: flags.name || undefined,
110
+ flow: flags.flow || undefined,
111
+ };
108
112
  }
109
113
  /** Parse fetch args: provider and flags (--flow, --redirectUrl, --scopes). flow can be omitted to auto-infer from provider. */
110
114
  function parseFetchArgs(rest) {
@@ -165,7 +169,7 @@ function createIdentityHandler(deps) {
165
169
  config: ctx.config,
166
170
  });
167
171
  const sessionKey = baseSessionKey
168
- ? buildEffectiveSessionKey(baseSessionKey, ctx.senderId)
172
+ ? buildEffectiveSessionKey(baseSessionKey, ctx.senderId, ctx.channel)
169
173
  : null;
170
174
  const needsSession = [
171
175
  "login",
@@ -331,8 +335,9 @@ async function handleLogout(deps, sessionKey) {
331
335
  return { text: "✓ Logged out." };
332
336
  }
333
337
  async function handleListCredentials(deps, sessionKey, rest) {
334
- const { page } = parseListArgs(rest);
335
- const result = await runListCredentials(deps, sessionKey, page);
338
+ const { page, name, flow } = parseListArgs(rest);
339
+ const filter = name || flow ? { name, flow } : undefined;
340
+ const result = await runListCredentials(deps, sessionKey, page, filter);
336
341
  const lines = [];
337
342
  if (result.providers.length > 0) {
338
343
  lines.push(`**Available (page ${result.page}):**`);
@@ -0,0 +1,12 @@
1
+ import { type PluginLogger } from "../utils/logger.js";
2
+ export type AfterToolCallDeps = {
3
+ logger?: PluginLogger;
4
+ };
5
+ export declare function createAfterToolCallHandler(deps: AfterToolCallDeps): (event: {
6
+ toolName: string;
7
+ runId?: string;
8
+ toolCallId?: string;
9
+ }, _ctx: {
10
+ sessionKey?: string;
11
+ }) => void;
12
+ //# sourceMappingURL=after-tool-call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"after-tool-call.d.ts","sourceRoot":"","sources":["../../../src/hooks/after-tool-call.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,iBAAiB,IAI9D,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,MAAM;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC5B,IAAI,CASR"}
@@ -0,0 +1,31 @@
1
+ /*
2
+ * Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * after_tool_call hook: restore process.env snapshot and release per-key locks
18
+ * set by before_tool_call credential injection.
19
+ */
20
+ import { hasSnapshot, restoreEnvSnapshot } from "../store/credential-env-snapshot.js";
21
+ import { logDebug } from "../utils/logger.js";
22
+ export function createAfterToolCallHandler(deps) {
23
+ const { logger } = deps;
24
+ return (event, _ctx) => {
25
+ if (!hasSnapshot(event.runId, event.toolCallId))
26
+ return;
27
+ logDebug(logger, `restoring env snapshot for tool=${event.toolName} ` +
28
+ `run=${event.runId ?? "?"} toolCallId=${event.toolCallId ?? "?"}`);
29
+ restoreEnvSnapshot(event.runId, event.toolCallId);
30
+ };
31
+ }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * before_agent_start hook: fetch TIP token for main agent only.
3
- * 1. Inject credentials into process.env per credential-env-bindings
4
- * 2. Subagent: skip (TIP comes from sessions_send propagation)
5
- * 3. Main: getOrRefreshTIPToken (fetches TIP, refreshes userToken if expired)
3
+ * Credential env injection is handled per-tool-call in before_tool_call
4
+ * to avoid process.env race conditions between concurrent runs.
6
5
  */
7
6
  import type { IdentityService } from "../services/identity-service.js";
8
7
  import type { OIDCConfigForRefresh } from "../services/session-refresh.js";
@@ -13,6 +12,7 @@ export type BeforeAgentStartDeps = {
13
12
  getOidcConfigForRefresh?: () => Promise<OIDCConfigForRefresh>;
14
13
  logger: {
15
14
  info?: (msg: string) => void;
15
+ debug?: (msg: string) => void;
16
16
  warn?: (msg: string) => void;
17
17
  };
18
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"before-agent-start.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-agent-start.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAQ3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACxE,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,oBAAoB,IAapE,QAAQ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,EAChD,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7C,OAAO,CAAC;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA8B/C"}
1
+ {"version":3,"file":"before-agent-start.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-agent-start.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAM3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvG,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,oBAAoB,IAWpE,QAAQ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,EAChD,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7C,OAAO,CAAC;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuB/C"}
@@ -14,15 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
17
- import { logWarn } from "../utils/logger.js";
18
- import { loadCredentialEnvBindings } from "../store/credential-env-bindings.js";
19
- import { getCredential, resolveCredentialValue } from "../store/credential-store.js";
17
+ import { logDebug, logWarn } from "../utils/logger.js";
20
18
  import { isSubagentSessionKey } from "../utils/derive-session-key.js";
21
- import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
+ import { resolveEffectiveSessionKey } from "../store/sender-session-store.js";
22
20
  export function createBeforeAgentStartHandler(deps) {
23
21
  const { storeDir, identityService, configWorkloadName, getOidcConfigForRefresh, logger } = deps;
24
- // Always pass identityService so we can try fetch with session.userToken when TIP is missing/expired.
25
- // getOidcConfigForRefresh is only needed for refresh when userToken itself has expired.
26
22
  const tipRefreshOptions = {
27
23
  identityService,
28
24
  getOidcConfigForRefresh,
@@ -36,27 +32,17 @@ export function createBeforeAgentStartHandler(deps) {
36
32
  if (isSubagentSessionKey(sessionKey))
37
33
  return;
38
34
  const effectiveKey = resolveEffectiveSessionKey(sessionKey);
39
- try {
40
- const bindings = await loadCredentialEnvBindings(storeDir, effectiveKey);
41
- for (const [provider, envVar] of Object.entries(bindings)) {
42
- const cred = await getCredential(storeDir, effectiveKey, provider);
43
- const value = cred ? resolveCredentialValue(cred) : undefined;
44
- if (value)
45
- process.env[envVar] = value;
46
- else
47
- delete process.env[envVar];
48
- }
49
- }
50
- catch {
51
- /* best-effort */
52
- }
35
+ logDebug(logger, `before_agent_start: fetching TIP for key=${effectiveKey}`);
53
36
  try {
54
37
  const tip = await getOrRefreshTIPToken(storeDir, effectiveKey, {
55
38
  ...tipRefreshOptions,
56
39
  ctxAgentId: ctx.agentId,
57
40
  });
58
- if (!tip)
41
+ if (!tip) {
42
+ logDebug(logger, `before_agent_start: no TIP available for key=${effectiveKey}`);
59
43
  return;
44
+ }
45
+ logDebug(logger, `before_agent_start: TIP ready for key=${effectiveKey} sub=${tip.sub}`);
60
46
  }
61
47
  catch (err) {
62
48
  logWarn(logger, `failed to get TIP for ${effectiveKey}: ${String(err)}`);