@m1a0rz/agent-identity 0.2.4 → 0.3.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/index.d.ts.map +1 -1
  2. package/dist/index.js +44 -5
  3. package/dist/src/commands/identity-commands.d.ts.map +1 -1
  4. package/dist/src/commands/identity-commands.js +5 -1
  5. package/dist/src/hooks/before-agent-start.d.ts.map +1 -1
  6. package/dist/src/hooks/before-agent-start.js +6 -4
  7. package/dist/src/hooks/before-tool-call.d.ts +1 -0
  8. package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
  9. package/dist/src/hooks/before-tool-call.js +24 -6
  10. package/dist/src/hooks/llm-input.d.ts +2 -0
  11. package/dist/src/hooks/llm-input.d.ts.map +1 -1
  12. package/dist/src/hooks/llm-input.js +15 -4
  13. package/dist/src/hooks/sessions-send-propagation.d.ts +1 -0
  14. package/dist/src/hooks/sessions-send-propagation.d.ts.map +1 -1
  15. package/dist/src/hooks/sessions-send-propagation.js +4 -2
  16. package/dist/src/hooks/sessions-spawn-propagation.d.ts.map +1 -1
  17. package/dist/src/hooks/sessions-spawn-propagation.js +4 -2
  18. package/dist/src/hooks/subagent-ended-cleanup.d.ts +1 -0
  19. package/dist/src/hooks/subagent-ended-cleanup.d.ts.map +1 -1
  20. package/dist/src/hooks/subagent-ended-cleanup.js +3 -0
  21. package/dist/src/store/group-sender-store.d.ts +35 -0
  22. package/dist/src/store/group-sender-store.d.ts.map +1 -0
  23. package/dist/src/store/group-sender-store.js +112 -0
  24. package/dist/src/store/session-store.d.ts.map +1 -1
  25. package/dist/src/store/session-store.js +15 -1
  26. package/dist/src/tools/identity-approve-tool.d.ts.map +1 -1
  27. package/dist/src/tools/identity-approve-tool.js +3 -1
  28. package/dist/src/tools/identity-fetch.d.ts.map +1 -1
  29. package/dist/src/tools/identity-fetch.js +2 -1
  30. package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
  31. package/dist/src/tools/identity-list-credentials.js +2 -1
  32. package/dist/src/tools/identity-login.d.ts.map +1 -1
  33. package/dist/src/tools/identity-login.js +2 -1
  34. package/dist/src/tools/identity-logout.d.ts.map +1 -1
  35. package/dist/src/tools/identity-logout.js +2 -1
  36. package/dist/src/tools/identity-set-binding.d.ts.map +1 -1
  37. package/dist/src/tools/identity-set-binding.js +2 -1
  38. package/dist/src/tools/identity-status.d.ts.map +1 -1
  39. package/dist/src/tools/identity-status.js +2 -1
  40. package/dist/src/tools/identity-unset-binding.d.ts.map +1 -1
  41. package/dist/src/tools/identity-unset-binding.js +2 -1
  42. package/dist/src/tools/identity-whoami.d.ts.map +1 -1
  43. package/dist/src/tools/identity-whoami.js +2 -1
  44. package/dist/src/utils/derive-session-key.d.ts +7 -0
  45. package/dist/src/utils/derive-session-key.d.ts.map +1 -1
  46. package/dist/src/utils/derive-session-key.js +84 -15
  47. package/package.json +1 -1
@@ -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;AA8D7D,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QAuWtD"}
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"}
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ 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
24
  import { createBeforeToolCallHandler } from "./src/hooks/before-tool-call.js";
23
25
  import * as skillPathStore from "./src/store/skill-path-store.js";
24
26
  import { createOIDCCallbackHandler, createOIDCCallbackHandlerLazy, } from "./src/routes/oidc-login.js";
@@ -293,10 +295,47 @@ export default function register(api) {
293
295
  api.registerTool(createIdentityListRiskPatternsTool(), { optional: true });
294
296
  const authz = pluginConfig.authz;
295
297
  const approvalTtlMs = (authz?.approvalTtlSeconds ?? 300) * 1000;
296
- // Optional: agent must NOT have this (would self-approve). Use /identity approve for human approval.
297
298
  api.registerTool(createIdentityApproveTool({ approvalTtlMs, logger: api.logger }), {
298
299
  optional: true,
299
300
  });
301
+ // Capture group sender on every inbound message so before_tool_call can
302
+ // identify which user triggered the current run.
303
+ api.on("message_received", (event, ctx) => {
304
+ const channel = ctx.channelId ??
305
+ event.metadata?.provider;
306
+ if (!channel)
307
+ return;
308
+ const to = ctx.conversationId ??
309
+ event.metadata?.to;
310
+ const from = event.from;
311
+ const metadata = event.metadata;
312
+ const senderId = metadata?.senderId;
313
+ if (!senderId)
314
+ return;
315
+ const sessionKey = deriveSessionKey({
316
+ channel,
317
+ senderId,
318
+ from,
319
+ to,
320
+ accountId: ctx.accountId,
321
+ config: api.runtime.config.loadConfig(),
322
+ });
323
+ if (!sessionKey || !isGroupOrChannelSessionKey(sessionKey))
324
+ return;
325
+ setSender(sessionKey, {
326
+ senderId,
327
+ senderName: metadata?.senderName,
328
+ from,
329
+ channelId: channel,
330
+ messageId: metadata?.messageId,
331
+ capturedAt: Date.now(),
332
+ });
333
+ logInfo(api.logger, `group sender captured session=${sessionKey} sender=${senderId}`);
334
+ }, { priority: 200 });
335
+ api.on("session_end", (_event, ctx) => {
336
+ if (ctx.sessionKey)
337
+ clearSender(ctx.sessionKey);
338
+ });
300
339
  // Hooks
301
340
  if (hasIdentity) {
302
341
  api.on("before_agent_start", createBeforeAgentStartHandler({
@@ -331,11 +370,11 @@ export default function register(api) {
331
370
  const skillReadCheck = authz?.skillReadCheck ?? false;
332
371
  const requireRiskApproval = authz?.requireRiskApproval ?? false;
333
372
  const hasAuthz = toolCheck || skillReadCheck || requireRiskApproval;
373
+ api.on("llm_input", createLlmInputHandler({
374
+ enabled: skillReadCheck,
375
+ logger: api.logger,
376
+ }));
334
377
  if (skillReadCheck) {
335
- api.on("llm_input", createLlmInputHandler({
336
- enabled: true,
337
- logger: api.logger,
338
- }));
339
378
  api.on("session_end", (_event, ctx) => {
340
379
  if (ctx.sessionId)
341
380
  skillPathStore.clearSessionById(ctx.sessionId);
@@ -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;AAWxC,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;AAioBvD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB;;;;;mBA9e3C,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EAufpE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB;;;;;mBA1frC,oBAAoB,KAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;EAmgBpE"}
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"}
@@ -15,6 +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
19
  import { logDebug } from "../utils/logger.js";
19
20
  import { diagnoseRisk } from "../risk/diagnose-risk.js";
20
21
  import { getRiskPatterns } from "../risk/classify-risk.js";
@@ -155,7 +156,7 @@ function createIdentityHandler(deps) {
155
156
  return async (ctx) => {
156
157
  const { sub, rest } = parseSubcommand(ctx.args);
157
158
  logDebug(logger, `command sub=${sub} rest=${rest.slice(0, 40)}...`);
158
- const sessionKey = deriveSessionKey({
159
+ const baseSessionKey = deriveSessionKey({
159
160
  channel: ctx.channel,
160
161
  senderId: ctx.senderId,
161
162
  from: ctx.from,
@@ -163,6 +164,9 @@ function createIdentityHandler(deps) {
163
164
  accountId: ctx.accountId,
164
165
  config: ctx.config,
165
166
  });
167
+ const sessionKey = baseSessionKey
168
+ ? buildEffectiveSessionKey(baseSessionKey, ctx.senderId)
169
+ : null;
166
170
  const needsSession = [
167
171
  "login",
168
172
  "logout",
@@ -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;AAO3E,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,CA4B/C"}
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"}
@@ -18,6 +18,7 @@ import { logWarn } from "../utils/logger.js";
18
18
  import { loadCredentialEnvBindings } from "../store/credential-env-bindings.js";
19
19
  import { getCredential, resolveCredentialValue } from "../store/credential-store.js";
20
20
  import { isSubagentSessionKey } from "../utils/derive-session-key.js";
21
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
21
22
  export function createBeforeAgentStartHandler(deps) {
22
23
  const { storeDir, identityService, configWorkloadName, getOidcConfigForRefresh, logger } = deps;
23
24
  // Always pass identityService so we can try fetch with session.userToken when TIP is missing/expired.
@@ -34,10 +35,11 @@ export function createBeforeAgentStartHandler(deps) {
34
35
  return;
35
36
  if (isSubagentSessionKey(sessionKey))
36
37
  return;
38
+ const effectiveKey = resolveEffectiveSessionKey(sessionKey);
37
39
  try {
38
- const bindings = await loadCredentialEnvBindings(storeDir, sessionKey);
40
+ const bindings = await loadCredentialEnvBindings(storeDir, effectiveKey);
39
41
  for (const [provider, envVar] of Object.entries(bindings)) {
40
- const cred = await getCredential(storeDir, sessionKey, provider);
42
+ const cred = await getCredential(storeDir, effectiveKey, provider);
41
43
  const value = cred ? resolveCredentialValue(cred) : undefined;
42
44
  if (value)
43
45
  process.env[envVar] = value;
@@ -49,7 +51,7 @@ export function createBeforeAgentStartHandler(deps) {
49
51
  /* best-effort */
50
52
  }
51
53
  try {
52
- const tip = await getOrRefreshTIPToken(storeDir, sessionKey, {
54
+ const tip = await getOrRefreshTIPToken(storeDir, effectiveKey, {
53
55
  ...tipRefreshOptions,
54
56
  ctxAgentId: ctx.agentId,
55
57
  });
@@ -57,7 +59,7 @@ export function createBeforeAgentStartHandler(deps) {
57
59
  return;
58
60
  }
59
61
  catch (err) {
60
- logWarn(logger, `failed to get TIP for ${sessionKey}: ${String(err)}`);
62
+ logWarn(logger, `failed to get TIP for ${effectiveKey}: ${String(err)}`);
61
63
  }
62
64
  };
63
65
  }
@@ -32,6 +32,7 @@ export type BeforeToolCallDeps = {
32
32
  };
33
33
  export declare function createBeforeToolCallHandler(deps: BeforeToolCallDeps): (event: {
34
34
  toolName: string;
35
+ runId?: string;
35
36
  params: Record<string, unknown>;
36
37
  }, ctx: {
37
38
  agentId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"before-tool-call.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-tool-call.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAUhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,uGAAuG;IACvG,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACxE,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,oBAAoB;IACpB,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAwCF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,IA8BhE,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC5D,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC/D,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyK7D"}
1
+ {"version":3,"file":"before-tool-call.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-tool-call.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAYhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,uGAAuG;IACvG,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACxE,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,oBAAoB;IACpB,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAwCF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,IA8BhE,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC5E,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC/D,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA2L7D"}
@@ -21,6 +21,8 @@ import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
21
21
  import { supportsSyncApproval } from "../utils/approval-channel.js";
22
22
  import { extractDelegationChainFromJwt } from "../utils/auth.js";
23
23
  import { logDebug } from "../utils/logger.js";
24
+ import { getSender, resolveEffectiveSessionKeyForRun } from "../store/group-sender-store.js";
25
+ import { isGroupOrChannelSessionKey } from "../utils/derive-session-key.js";
24
26
  function resolveAuthzFlags(authz) {
25
27
  return {
26
28
  toolCheck: authz?.toolCheck ?? false,
@@ -65,6 +67,22 @@ export function createBeforeToolCallHandler(deps) {
65
67
  logDebug(logger, `before_tool_call toolName=${toolName}`);
66
68
  if (!sessionKey)
67
69
  return;
70
+ const effectiveKey = resolveEffectiveSessionKeyForRun(sessionKey, event.runId);
71
+ if (isGroupOrChannelSessionKey(sessionKey)) {
72
+ const groupSender = getSender(sessionKey);
73
+ if (groupSender) {
74
+ params._enhancedContext = {
75
+ senderId: groupSender.senderId,
76
+ senderName: groupSender.senderName,
77
+ from: groupSender.from,
78
+ channelId: groupSender.channelId,
79
+ messageId: groupSender.messageId,
80
+ sourceSessionKey: sessionKey,
81
+ source: "group-latest",
82
+ capturedAt: groupSender.capturedAt,
83
+ };
84
+ }
85
+ }
68
86
  const pathStr = params?.path ?? params?.file_path;
69
87
  const isSkillRead = flags.skillReadCheck &&
70
88
  toolName.toLowerCase() === "read" &&
@@ -80,7 +98,7 @@ export function createBeforeToolCallHandler(deps) {
80
98
  logDebug(logger, `low-risk bypass for ${toolName}`);
81
99
  return;
82
100
  }
83
- const tip = await getOrRefreshTIPToken(storeDir, sessionKey, tipRefreshOptions
101
+ const tip = await getOrRefreshTIPToken(storeDir, effectiveKey, tipRefreshOptions
84
102
  ? { ...tipRefreshOptions, ctxAgentId: ctx.agentId }
85
103
  : undefined);
86
104
  if (!tip) {
@@ -146,18 +164,18 @@ export function createBeforeToolCallHandler(deps) {
146
164
  logDebug(logger, `AuthZ ok for ${toolName} (risk=${risk})`);
147
165
  return;
148
166
  }
149
- if (toolApprovalStore.hasRecentApproval(sessionKey, toolName, paramsRecord)) {
150
- toolApprovalStore.consumeApproval(sessionKey, toolName, paramsRecord);
167
+ if (toolApprovalStore.hasRecentApproval(effectiveKey, toolName, paramsRecord)) {
168
+ toolApprovalStore.consumeApproval(effectiveKey, toolName, paramsRecord);
151
169
  logDebug(logger, `AuthZ ok for ${toolName} (recent approval)`);
152
170
  return;
153
171
  }
154
172
  const fullHash = toolApprovalStore.hashToolParams(toolName, paramsRecord);
155
- const approvalId = toolApprovalStore.shortApprovalId(fullHash + sessionKey + Date.now());
173
+ const approvalId = toolApprovalStore.shortApprovalId(fullHash + effectiveKey + Date.now());
156
174
  const ttlSeconds = Math.floor(approvalTtlMs / 1000);
157
175
  if (supportsSyncApproval(sessionKey) && sendToSession) {
158
176
  toolApprovalStore.createPending({
159
177
  approvalId,
160
- sessionKey,
178
+ sessionKey: effectiveKey,
161
179
  toolName,
162
180
  params: paramsRecord,
163
181
  ttlMs: approvalTtlMs,
@@ -177,7 +195,7 @@ export function createBeforeToolCallHandler(deps) {
177
195
  }
178
196
  toolApprovalStore.createPending({
179
197
  approvalId,
180
- sessionKey,
198
+ sessionKey: effectiveKey,
181
199
  toolName,
182
200
  params: paramsRecord,
183
201
  ttlMs: approvalTtlMs,
@@ -1,10 +1,12 @@
1
1
  export type LlmInputHandlerDeps = {
2
+ /** Enable skill path parsing for authz.skillReadCheck. */
2
3
  enabled: boolean;
3
4
  logger?: {
4
5
  debug?: (msg: string) => void;
5
6
  };
6
7
  };
7
8
  export declare function createLlmInputHandler(deps: LlmInputHandlerDeps): (event: {
9
+ runId?: string;
8
10
  systemPrompt?: string;
9
11
  }, ctx: {
10
12
  sessionKey?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"llm-input.d.ts","sourceRoot":"","sources":["../../../src/hooks/llm-input.ts"],"names":[],"mappings":"AA0BA,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAC5C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,IAI3D,OAAO;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EAChC,KAAK;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,KACtE,IAAI,CAcR"}
1
+ {"version":3,"file":"llm-input.d.ts","sourceRoot":"","sources":["../../../src/hooks/llm-input.ts"],"names":[],"mappings":"AA4BA,MAAM,MAAM,mBAAmB,GAAG;IAChC,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAC5C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,IAI3D,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,KAAK;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,KACtE,IAAI,CAwBR"}
@@ -14,17 +14,28 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  /**
17
- * llm_input hook: parse <available_skills> from system prompt and store
18
- * path -> skill name mapping for skill read permission checks in before_tool_call.
19
- * Only runs when authz.skillReadCheck is enabled.
17
+ * llm_input hook:
18
+ * 1. Freeze effective sessionKey per runId for group-chat user isolation.
19
+ * 2. Parse <available_skills> from system prompt (when skillReadCheck enabled).
20
20
  */
21
21
  import { parseAvailableSkills } from "../utils/parse-available-skills.js";
22
22
  import * as skillPathStore from "../store/skill-path-store.js";
23
+ import { freezeRun, resolveEffectiveSessionKey } from "../store/group-sender-store.js";
24
+ import { isGroupOrChannelSessionKey } from "../utils/derive-session-key.js";
23
25
  import { logDebug } from "../utils/logger.js";
24
26
  export function createLlmInputHandler(deps) {
25
27
  const { enabled, logger } = deps;
26
28
  return (event, ctx) => {
27
- if (!enabled || !ctx.sessionKey)
29
+ if (!ctx.sessionKey)
30
+ return;
31
+ if (event.runId && isGroupOrChannelSessionKey(ctx.sessionKey)) {
32
+ const effectiveKey = resolveEffectiveSessionKey(ctx.sessionKey);
33
+ if (effectiveKey !== ctx.sessionKey) {
34
+ freezeRun(event.runId, effectiveKey);
35
+ logDebug(logger, `frozen run=${event.runId} → ${effectiveKey}`);
36
+ }
37
+ }
38
+ if (!enabled)
28
39
  return;
29
40
  const pathToName = parseAvailableSkills(event.systemPrompt);
30
41
  if (pathToName.size === 0)
@@ -21,6 +21,7 @@ export type SessionsSendPropagationDeps = {
21
21
  };
22
22
  export declare function createSessionsSendPropagationHandler(deps: SessionsSendPropagationDeps): (event: {
23
23
  toolName: string;
24
+ runId?: string;
24
25
  params: Record<string, unknown>;
25
26
  }, ctx: {
26
27
  agentId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"sessions-send-propagation.d.ts","sourceRoot":"","sources":["../../../src/hooks/sessions-send-propagation.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAK3E,MAAM,MAAM,2BAA2B,GAAG;IACxC,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,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AAEF,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,2BAA2B,IAWlF,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC5D,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC/D,OAAO,CAAC,IAAI,CAAC,CAoCjB"}
1
+ {"version":3,"file":"sessions-send-propagation.d.ts","sourceRoot":"","sources":["../../../src/hooks/sessions-send-propagation.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAM3E,MAAM,MAAM,2BAA2B,GAAG;IACxC,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,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AAEF,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,2BAA2B,IAWlF,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC5E,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC/D,OAAO,CAAC,IAAI,CAAC,CAsCjB"}
@@ -16,6 +16,7 @@
16
16
  import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
17
17
  import { propagateTIPToTarget } from "../services/tip-propagation.js";
18
18
  import { logWarn } from "../utils/logger.js";
19
+ import { resolveEffectiveSessionKeyForRun } from "../store/group-sender-store.js";
19
20
  export function createSessionsSendPropagationHandler(deps) {
20
21
  const { storeDir, identityService, configWorkloadName, getOidcConfigForRefresh, subagentTipPropagation, logger, } = deps;
21
22
  return async (event, ctx) => {
@@ -28,16 +29,17 @@ export function createSessionsSendPropagationHandler(deps) {
28
29
  if (!callerSessionKey || !targetSessionKey || callerSessionKey === targetSessionKey) {
29
30
  return;
30
31
  }
32
+ const effectiveCallerKey = resolveEffectiveSessionKeyForRun(callerSessionKey, event.runId);
31
33
  try {
32
34
  await propagateTIPToTarget({
33
35
  storeDir,
34
- callerSessionKey,
36
+ callerSessionKey: effectiveCallerKey,
35
37
  targetSessionKey,
36
38
  identityService,
37
39
  configWorkloadName,
38
40
  subagentTipPropagation,
39
41
  ctxAgentId: ctx.agentId,
40
- getCallerTIP: () => getOrRefreshTIPToken(storeDir, callerSessionKey, {
42
+ getCallerTIP: () => getOrRefreshTIPToken(storeDir, effectiveCallerKey, {
41
43
  identityService,
42
44
  getOidcConfigForRefresh,
43
45
  configWorkloadName,
@@ -1 +1 @@
1
- {"version":3,"file":"sessions-spawn-propagation.d.ts","sourceRoot":"","sources":["../../../src/hooks/sessions-spawn-propagation.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAK3E,MAAM,MAAM,4BAA4B,GAAG;IACzC,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,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AAEF,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,4BAA4B,IAWpF,OAAO;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAClE,KAAK;IAAE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9D,OAAO,CAAC,IAAI,CAAC,CA+BjB"}
1
+ {"version":3,"file":"sessions-spawn-propagation.d.ts","sourceRoot":"","sources":["../../../src/hooks/sessions-spawn-propagation.ts"],"names":[],"mappings":"AAgBA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAM3E,MAAM,MAAM,4BAA4B,GAAG;IACzC,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,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzE,CAAC;AAEF,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,4BAA4B,IAWpF,OAAO;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAClE,KAAK;IAAE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,KAC9D,OAAO,CAAC,IAAI,CAAC,CAiCjB"}
@@ -16,6 +16,7 @@
16
16
  import { getOrRefreshTIPToken } from "../services/tip-with-refresh.js";
17
17
  import { propagateTIPToTarget } from "../services/tip-propagation.js";
18
18
  import { logWarn } from "../utils/logger.js";
19
+ import { resolveEffectiveSessionKeyForRun } from "../store/group-sender-store.js";
19
20
  export function createSessionsSpawnPropagationHandler(deps) {
20
21
  const { storeDir, identityService, configWorkloadName, getOidcConfigForRefresh, subagentTipPropagation, logger, } = deps;
21
22
  return async (event, ctx) => {
@@ -24,16 +25,17 @@ export function createSessionsSpawnPropagationHandler(deps) {
24
25
  if (!callerSessionKey || !targetSessionKey || callerSessionKey === targetSessionKey) {
25
26
  return;
26
27
  }
28
+ const effectiveCallerKey = resolveEffectiveSessionKeyForRun(callerSessionKey, event.runId);
27
29
  try {
28
30
  await propagateTIPToTarget({
29
31
  storeDir,
30
- callerSessionKey,
32
+ callerSessionKey: effectiveCallerKey,
31
33
  targetSessionKey,
32
34
  identityService,
33
35
  configWorkloadName,
34
36
  subagentTipPropagation,
35
37
  ctxAgentId: event.agentId,
36
- getCallerTIP: () => getOrRefreshTIPToken(storeDir, callerSessionKey, {
38
+ getCallerTIP: () => getOrRefreshTIPToken(storeDir, effectiveCallerKey, {
37
39
  identityService,
38
40
  getOidcConfigForRefresh,
39
41
  configWorkloadName,
@@ -9,5 +9,6 @@ export type SubagentEndedCleanupDeps = {
9
9
  export declare function createSubagentEndedCleanupHandler(deps: SubagentEndedCleanupDeps): (event: {
10
10
  targetSessionKey: string;
11
11
  targetKind: string;
12
+ runId?: string;
12
13
  }) => Promise<void>;
13
14
  //# sourceMappingURL=subagent-ended-cleanup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-ended-cleanup.d.ts","sourceRoot":"","sources":["../../../src/hooks/subagent-ended-cleanup.ts"],"names":[],"mappings":"AA2BA,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,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,iCAAiC,CAAC,IAAI,EAAE,wBAAwB,IAI5E,OAAO;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,KACtD,OAAO,CAAC,IAAI,CAAC,CAcjB"}
1
+ {"version":3,"file":"subagent-ended-cleanup.d.ts","sourceRoot":"","sources":["../../../src/hooks/subagent-ended-cleanup.ts"],"names":[],"mappings":"AA4BA,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,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,iCAAiC,CAAC,IAAI,EAAE,wBAAwB,IAI5E,OAAO;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KACtE,OAAO,CAAC,IAAI,CAAC,CAgBjB"}
@@ -21,12 +21,15 @@
21
21
  */
22
22
  import { deleteSession } from "../store/session-store.js";
23
23
  import { deleteTIPToken } from "../store/tip-store.js";
24
+ import { clearFrozenRun } from "../store/group-sender-store.js";
24
25
  import { logDebug, logWarn } from "../utils/logger.js";
25
26
  export function createSubagentEndedCleanupHandler(deps) {
26
27
  const { storeDir, logger } = deps;
27
28
  return async (event) => {
28
29
  if (event.targetKind !== "subagent")
29
30
  return;
31
+ if (event.runId)
32
+ clearFrozenRun(event.runId);
30
33
  const targetSessionKey = event.targetSessionKey?.trim();
31
34
  if (!targetSessionKey)
32
35
  return;
@@ -0,0 +1,35 @@
1
+ export type SenderInfo = {
2
+ senderId: string;
3
+ senderName?: string;
4
+ from?: string;
5
+ channelId?: string;
6
+ messageId?: string;
7
+ capturedAt: number;
8
+ };
9
+ export declare function setSender(sessionKey: string, info: SenderInfo): void;
10
+ export declare function getSender(sessionKey: string): SenderInfo | undefined;
11
+ export declare function clearSender(sessionKey: string): void;
12
+ /**
13
+ * For group sessions, append :user:<senderId> so each member gets isolated
14
+ * TIP tokens, credentials and session state.
15
+ * For non-group sessions the key is returned unchanged.
16
+ */
17
+ export declare function resolveEffectiveSessionKey(sessionKey: string): string;
18
+ /**
19
+ * Same as resolveEffectiveSessionKey but takes an explicit senderId
20
+ * (used by commands where ctx.senderId is already available).
21
+ */
22
+ export declare function buildEffectiveSessionKey(sessionKey: string, senderId?: string): string;
23
+ /**
24
+ * Freeze the effective sessionKey for a runId.
25
+ * Once frozen, all hooks in this run use the same user-scoped key
26
+ * regardless of later message_received overwrites.
27
+ */
28
+ export declare function freezeRun(runId: string, effectiveKey: string): void;
29
+ /**
30
+ * Resolve effective sessionKey with run-level freeze support.
31
+ * Priority: frozen[runId] > store lookup > original key.
32
+ */
33
+ export declare function resolveEffectiveSessionKeyForRun(sessionKey: string, runId?: string): string;
34
+ export declare function clearFrozenRun(runId: string): void;
35
+ //# sourceMappingURL=group-sender-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-sender-store.d.ts","sourceRoot":"","sources":["../../../src/store/group-sender-store.ts"],"names":[],"mappings":"AA0BA,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAGpE;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAQpE;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtF;AASD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAInE;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD"}
@@ -0,0 +1,112 @@
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
+ * Lightweight in-memory store that maps group sessionKey → latest sender info.
18
+ * Written by the message_received handler, read by before_tool_call to inject
19
+ * sender identity into tool params so group-chat users can be distinguished.
20
+ */
21
+ import { isGroupOrChannelSessionKey } from "../utils/derive-session-key.js";
22
+ const SESSION_TTL_MS = 2 * 60 * 60 * 1000;
23
+ const store = new Map();
24
+ export function setSender(sessionKey, info) {
25
+ store.set(sessionKey, info);
26
+ pruneExpired();
27
+ }
28
+ export function getSender(sessionKey) {
29
+ const info = store.get(sessionKey);
30
+ if (!info)
31
+ return undefined;
32
+ if (Date.now() - info.capturedAt > SESSION_TTL_MS) {
33
+ store.delete(sessionKey);
34
+ return undefined;
35
+ }
36
+ return info;
37
+ }
38
+ export function clearSender(sessionKey) {
39
+ store.delete(sessionKey);
40
+ }
41
+ /**
42
+ * For group sessions, append :user:<senderId> so each member gets isolated
43
+ * TIP tokens, credentials and session state.
44
+ * For non-group sessions the key is returned unchanged.
45
+ */
46
+ export function resolveEffectiveSessionKey(sessionKey) {
47
+ if (!isGroupOrChannelSessionKey(sessionKey))
48
+ return sessionKey;
49
+ const sender = getSender(sessionKey);
50
+ if (!sender?.senderId)
51
+ return sessionKey;
52
+ return `${sessionKey}:user:${sender.senderId}`;
53
+ }
54
+ /**
55
+ * Same as resolveEffectiveSessionKey but takes an explicit senderId
56
+ * (used by commands where ctx.senderId is already available).
57
+ */
58
+ export function buildEffectiveSessionKey(sessionKey, senderId) {
59
+ if (!senderId || !isGroupOrChannelSessionKey(sessionKey))
60
+ return sessionKey;
61
+ return `${sessionKey}:user:${senderId}`;
62
+ }
63
+ // --------------- freeze-by-runId ---------------
64
+ const RUN_TTL_MS = 2 * 60 * 60 * 1000;
65
+ const frozenByRun = new Map();
66
+ /**
67
+ * Freeze the effective sessionKey for a runId.
68
+ * Once frozen, all hooks in this run use the same user-scoped key
69
+ * regardless of later message_received overwrites.
70
+ */
71
+ export function freezeRun(runId, effectiveKey) {
72
+ if (frozenByRun.has(runId))
73
+ return;
74
+ frozenByRun.set(runId, { effectiveKey, frozenAt: Date.now() });
75
+ pruneExpiredRuns();
76
+ }
77
+ /**
78
+ * Resolve effective sessionKey with run-level freeze support.
79
+ * Priority: frozen[runId] > store lookup > original key.
80
+ */
81
+ export function resolveEffectiveSessionKeyForRun(sessionKey, runId) {
82
+ if (runId) {
83
+ const frozen = frozenByRun.get(runId);
84
+ if (frozen && Date.now() - frozen.frozenAt <= RUN_TTL_MS) {
85
+ return frozen.effectiveKey;
86
+ }
87
+ }
88
+ return resolveEffectiveSessionKey(sessionKey);
89
+ }
90
+ export function clearFrozenRun(runId) {
91
+ frozenByRun.delete(runId);
92
+ }
93
+ function pruneExpired() {
94
+ if (store.size < 128)
95
+ return;
96
+ const now = Date.now();
97
+ for (const [key, info] of store) {
98
+ if (now - info.capturedAt > SESSION_TTL_MS) {
99
+ store.delete(key);
100
+ }
101
+ }
102
+ }
103
+ function pruneExpiredRuns() {
104
+ if (frozenByRun.size < 256)
105
+ return;
106
+ const now = Date.now();
107
+ for (const [runId, entry] of frozenByRun) {
108
+ if (now - entry.frozenAt > RUN_TTL_MS) {
109
+ frozenByRun.delete(runId);
110
+ }
111
+ }
112
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/store/session-store.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAeD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAc1F;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAU9B;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvF"}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/store/session-store.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAoBD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAc1F;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgB9B;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvF"}
@@ -23,6 +23,8 @@ import path from "node:path";
23
23
  const SESSIONS_FILENAME = "sessions.json";
24
24
  /** Max session age when no expiresAt (e.g. legacy entries). Default 30 days. */
25
25
  const MAX_SESSION_AGE_MS = 30 * 24 * 60 * 60 * 1000;
26
+ /** Absolute max session lifetime regardless of refresh token. Default 30 days. */
27
+ const MAX_SESSION_ABSOLUTE_AGE_MS = 30 * 24 * 60 * 60 * 1000;
26
28
  export async function ensureStoreDir(storeDir) {
27
29
  await fs.mkdir(storeDir, { recursive: true });
28
30
  }
@@ -30,6 +32,12 @@ function pruneExpiredSessions(sessions) {
30
32
  const now = Date.now();
31
33
  const pruned = {};
32
34
  for (const [k, v] of Object.entries(sessions)) {
35
+ if (now - v.loginAt > MAX_SESSION_ABSOLUTE_AGE_MS)
36
+ continue;
37
+ if (v.refreshToken) {
38
+ pruned[k] = v;
39
+ continue;
40
+ }
33
41
  if (v.expiresAt != null && v.expiresAt < now)
34
42
  continue;
35
43
  if (v.expiresAt == null && now - v.loginAt > MAX_SESSION_AGE_MS)
@@ -65,7 +73,13 @@ export async function getSession(storeDir, sessionKey) {
65
73
  const entry = sessions[sessionKey];
66
74
  if (!entry)
67
75
  return null;
68
- if (entry.expiresAt && entry.expiresAt < Date.now()) {
76
+ const now = Date.now();
77
+ if (now - entry.loginAt > MAX_SESSION_ABSOLUTE_AGE_MS) {
78
+ delete sessions[sessionKey];
79
+ await saveSessions(storeDir, sessions);
80
+ return null;
81
+ }
82
+ if (entry.expiresAt && entry.expiresAt < now && !entry.refreshToken) {
69
83
  delete sessions[sessionKey];
70
84
  await saveSessions(storeDir, sessions);
71
85
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"identity-approve-tool.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-approve-tool.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,MAAM,MAAM,uBAAuB,GAAG;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAC1E,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,uBAAuB,IAC7D,KAAK,iBAAiB;;;;;;;2BAOC,MAAM,UAAU;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;EAsBxE"}
1
+ {"version":3,"file":"identity-approve-tool.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-approve-tool.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOrD,MAAM,MAAM,uBAAuB,GAAG;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CAC1E,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,uBAAuB,IAC7D,KAAK,iBAAiB;;;;;;;2BAOC,MAAM,UAAU;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;EAuBxE"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import * as toolApprovalStore from "../store/tool-approval-store.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  import { logDebug, logWarn } from "../utils/logger.js";
20
21
  export function createIdentityApproveTool(deps) {
21
22
  return (ctx) => ({
@@ -33,7 +34,8 @@ export function createIdentityApproveTool(deps) {
33
34
  error: "approval_id is required",
34
35
  });
35
36
  }
36
- const ok = toolApprovalStore.approve(approvalId, deps.approvalTtlMs, ctx.sessionKey);
37
+ const approverKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : ctx.sessionKey;
38
+ const ok = toolApprovalStore.approve(approvalId, deps.approvalTtlMs, approverKey);
37
39
  if (ok) {
38
40
  logDebug(deps.logger, `approved tool call ${approvalId}`);
39
41
  return jsonResult({ ok: true, message: "Tool call approved" });
@@ -1 +1 @@
1
- {"version":3,"file":"identity-fetch.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-fetch.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAM1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,IACvD,KAAK,iBAAiB;;;;;;;;;;;;EA6D/B"}
1
+ {"version":3,"file":"identity-fetch.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-fetch.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAO1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,IACvD,KAAK,iBAAiB;;;;;;;;;;;;EA6D/B"}
@@ -18,6 +18,7 @@ import { optionalStringEnum } from "openclaw/plugin-sdk";
18
18
  import { jsonResult } from "openclaw/plugin-sdk";
19
19
  import { runFetch } from "../actions/identity-actions.js";
20
20
  import { getCredential, resolveCredentialValue } from "../store/credential-store.js";
21
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
21
22
  const FETCH_FLOWS = ["oauth2-user", "oauth2-m2m", "apikey"];
22
23
  export function createIdentityFetchTool(deps) {
23
24
  return (ctx) => ({
@@ -36,7 +37,7 @@ export function createIdentityFetchTool(deps) {
36
37
  })),
37
38
  }),
38
39
  execute: async (_toolCallId, params) => {
39
- const sessionKey = ctx.sessionKey;
40
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
40
41
  if (!sessionKey) {
41
42
  return jsonResult({ error: "No session context", success: false });
42
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-list-credentials.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-list-credentials.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,mBAAmB,IACjE,KAAK,iBAAiB;;;;;;;;EAsB/B"}
1
+ {"version":3,"file":"identity-list-credentials.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-list-credentials.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,mBAAmB,IACjE,KAAK,iBAAiB;;;;;;;;EAsB/B"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runListCredentials } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityListCredentialsTool(deps) {
20
21
  return (ctx) => ({
21
22
  name: "identity_list_credentials",
@@ -25,7 +26,7 @@ export function createIdentityListCredentialsTool(deps) {
25
26
  page: Type.Optional(Type.Number({ minimum: 1, default: 1 })),
26
27
  }),
27
28
  execute: async (_toolCallId, params) => {
28
- const sessionKey = ctx.sessionKey;
29
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
29
30
  if (!sessionKey) {
30
31
  return jsonResult({ error: "No session context", providers: [], storedOnly: [] });
31
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-login.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-login.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,IACvD,KAAK,iBAAiB;;;;;;EAiC/B"}
1
+ {"version":3,"file":"identity-login.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-login.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,IACvD,KAAK,iBAAiB;;;;;;EAiC/B"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runLogin } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityLoginTool(deps) {
20
21
  return (ctx) => ({
21
22
  name: "identity_login",
@@ -23,7 +24,7 @@ export function createIdentityLoginTool(deps) {
23
24
  description: "Start OIDC login (returns auth URL to open) or refresh TIP when already logged in. User must open the URL in a browser.",
24
25
  parameters: Type.Object({}),
25
26
  execute: async () => {
26
- const sessionKey = ctx.sessionKey;
27
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
27
28
  if (!sessionKey) {
28
29
  return jsonResult({ error: "No session context", authUrl: null });
29
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-logout.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-logout.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EAgB/B"}
1
+ {"version":3,"file":"identity-logout.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-logout.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EAgB/B"}
@@ -16,9 +16,10 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runLogout } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityLogoutTool(deps) {
20
21
  return (ctx) => {
21
- const sessionKey = ctx.sessionKey;
22
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
22
23
  return {
23
24
  name: "identity_logout",
24
25
  label: "Identity Logout",
@@ -1 +1 @@
1
- {"version":3,"file":"identity-set-binding.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-set-binding.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,IAC5D,KAAK,iBAAiB;;;;;;;;;EAwB/B"}
1
+ {"version":3,"file":"identity-set-binding.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-set-binding.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,IAC5D,KAAK,iBAAiB;;;;;;;;;EAwB/B"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runSetBinding } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentitySetBindingTool(deps) {
20
21
  return (ctx) => ({
21
22
  name: "identity_set_binding",
@@ -28,7 +29,7 @@ export function createIdentitySetBindingTool(deps) {
28
29
  }),
29
30
  }),
30
31
  execute: async (_toolCallId, params) => {
31
- const sessionKey = ctx.sessionKey;
32
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
32
33
  if (!sessionKey) {
33
34
  return jsonResult({ ok: false, error: "No session context" });
34
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-status.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-status.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EAiC/B"}
1
+ {"version":3,"file":"identity-status.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-status.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EAiC/B"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runStatus } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityStatusTool(deps) {
20
21
  return (ctx) => ({
21
22
  name: "identity_status",
@@ -23,7 +24,7 @@ export function createIdentityStatusTool(deps) {
23
24
  description: "Show login status, credentials, and env bindings for the current session.",
24
25
  parameters: Type.Object({}),
25
26
  execute: async () => {
26
- const sessionKey = ctx.sessionKey;
27
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
27
28
  if (!sessionKey) {
28
29
  return jsonResult({ error: "No session context", loggedIn: false });
29
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-unset-binding.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-unset-binding.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,mBAAmB,IAC9D,KAAK,iBAAiB;;;;;;;;EAiB/B"}
1
+ {"version":3,"file":"identity-unset-binding.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-unset-binding.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,mBAAmB,IAC9D,KAAK,iBAAiB;;;;;;;;EAiB/B"}
@@ -16,6 +16,7 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runUnsetBinding } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityUnsetBindingTool(deps) {
20
21
  return (ctx) => ({
21
22
  name: "identity_unset_binding",
@@ -25,7 +26,7 @@ export function createIdentityUnsetBindingTool(deps) {
25
26
  provider: Type.String({ description: "Provider name (e.g. google)" }),
26
27
  }),
27
28
  execute: async (_toolCallId, params) => {
28
- const sessionKey = ctx.sessionKey;
29
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
29
30
  if (!sessionKey) {
30
31
  return jsonResult({ ok: false, error: "No session context" });
31
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"identity-whoami.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-whoami.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAG1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EA+B/B"}
1
+ {"version":3,"file":"identity-whoami.d.ts","sourceRoot":"","sources":["../../../src/tools/identity-whoami.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAI1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,IACxD,KAAK,iBAAiB;;;;;;EA+B/B"}
@@ -16,9 +16,10 @@
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { jsonResult } from "openclaw/plugin-sdk";
18
18
  import { runStatus } from "../actions/identity-actions.js";
19
+ import { resolveEffectiveSessionKey } from "../store/group-sender-store.js";
19
20
  export function createIdentityWhoamiTool(deps) {
20
21
  return (ctx) => {
21
- const sessionKey = ctx.sessionKey;
22
+ const sessionKey = ctx.sessionKey ? resolveEffectiveSessionKey(ctx.sessionKey) : undefined;
22
23
  return {
23
24
  name: "identity_whoami",
24
25
  label: "Identity Whoami",
@@ -48,6 +48,13 @@ export declare function resolveAgentId(params: ResolveAgentIdParams): string;
48
48
  * Check if sessionKey is in subagent format (agent:X:subagent:uuid or nested).
49
49
  */
50
50
  export declare function isSubagentSessionKey(sessionKey: string | undefined | null): boolean;
51
+ /**
52
+ * Check whether sessionKey represents a group/channel conversation.
53
+ * Examples:
54
+ * - agent:main:telegram:group:-1001
55
+ * - agent:main:slack:channel:C123456
56
+ */
57
+ export declare function isGroupOrChannelSessionKey(sessionKey: string | undefined | null): boolean;
51
58
  export type ResolveWorkloadNameParams = {
52
59
  sessionKey?: string;
53
60
  agentId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"derive-session-key.d.ts","sourceRoot":"","sources":["../../../src/utils/derive-session-key.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAUF;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAE/D;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAM/F;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CASnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAKnF;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CAiBvF;AAyBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC9E;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAaF,8DAA8D;AAC9D,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,yBAAyB,GAC7B,wBAAwB,GAAG,IAAI,CAmBjC;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACpC,wBAAwB,GAAG,IAAI,CAyDjC"}
1
+ {"version":3,"file":"derive-session-key.d.ts","sourceRoot":"","sources":["../../../src/utils/derive-session-key.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC3D,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAUF;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAE/D;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAM/F;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CASnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAKnF;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAIzF;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CAiBvF;AAsGD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC9E;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAaF,8DAA8D;AAC9D,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,yBAAyB,GAC7B,wBAAwB,GAAG,IAAI,CAmBjC;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACpC,wBAAwB,GAAG,IAAI,CAyDjC"}
@@ -64,6 +64,18 @@ export function isSubagentSessionKey(sessionKey) {
64
64
  return true;
65
65
  return raw.includes(":subagent:");
66
66
  }
67
+ /**
68
+ * Check whether sessionKey represents a group/channel conversation.
69
+ * Examples:
70
+ * - agent:main:telegram:group:-1001
71
+ * - agent:main:slack:channel:C123456
72
+ */
73
+ export function isGroupOrChannelSessionKey(sessionKey) {
74
+ const raw = (sessionKey ?? "").trim().toLowerCase();
75
+ if (!raw)
76
+ return false;
77
+ return raw.includes(":group:") || raw.includes(":channel:");
78
+ }
67
79
  /**
68
80
  * Resolve workload name for GetWorkloadAccessToken when roleTrn is not set.
69
81
  * For subagent keys (agent:X:subagent:uuid or nested): use last segment (uuid) as workload name.
@@ -92,24 +104,81 @@ function normalizeAccountId(value) {
92
104
  return trimmed || DEFAULT_ACCOUNT_ID;
93
105
  }
94
106
  /**
95
- * Extract peer/chat id from from/to for group sessions.
96
- * Channel-specific heuristics (e.g. telegram:group:123 or telegram:123 for group chat).
97
- */
107
+ * Extract group peer id from channel-specific "from/to" shapes.
108
+ * Return null for direct messages.
109
+ */
98
110
  function extractGroupPeerId(channel, from, to) {
99
- const raw = (to ?? from ?? "").trim();
100
- if (!raw)
111
+ const ch = (channel ?? "").trim().toLowerCase();
112
+ if (!ch)
101
113
  return null;
102
- const lower = raw.toLowerCase();
103
- const channelPrefix = `${channel}:`;
104
- if (lower.startsWith(channelPrefix)) {
105
- const rest = raw.slice(channelPrefix.length);
106
- const parts = rest.split(":");
107
- if (parts[0] === "group" && parts[1])
108
- return parts[1];
109
- if (parts.length >= 1)
110
- return parts[0];
114
+ const fromRaw = (from ?? "").trim();
115
+ const toRaw = (to ?? "").trim();
116
+ const pickByPrefix = (value, prefix) => {
117
+ if (!value)
118
+ return null;
119
+ if (!value.toLowerCase().startsWith(prefix.toLowerCase()))
120
+ return null;
121
+ const id = value.slice(prefix.length).trim();
122
+ return id || null;
123
+ };
124
+ const pickFirst = (...values) => {
125
+ for (const v of values) {
126
+ if (v)
127
+ return v;
128
+ }
129
+ return null;
130
+ };
131
+ switch (ch) {
132
+ case "feishu":
133
+ // Group: to="chat:oc_xxx"; DM: to="user:ou_xxx"
134
+ return pickByPrefix(toRaw, "chat:");
135
+ case "telegram": {
136
+ // Group/topic: telegram:group:<chatId>[:topic:<threadId>]
137
+ const g1 = pickByPrefix(fromRaw, "telegram:group:");
138
+ if (g1)
139
+ return g1;
140
+ const g2 = pickByPrefix(toRaw, "telegram:group:");
141
+ if (g2)
142
+ return g2;
143
+ // Legacy fallback: telegram:-100... is usually group/supergroup
144
+ const f = pickByPrefix(fromRaw, "telegram:");
145
+ if (f && f.startsWith("-"))
146
+ return f;
147
+ const t = pickByPrefix(toRaw, "telegram:");
148
+ if (t && t.startsWith("-"))
149
+ return t;
150
+ return null;
151
+ }
152
+ case "slack":
153
+ // Group/channel: slack:channel:<id> / slack:group:<id>, to=channel:<id>
154
+ return pickFirst(pickByPrefix(fromRaw, "slack:channel:"), pickByPrefix(fromRaw, "slack:group:"), pickByPrefix(toRaw, "channel:"));
155
+ case "discord":
156
+ // Guild/thread channel: discord:channel:<id>, to=channel:<id>; DM uses user:<id>
157
+ return pickFirst(pickByPrefix(fromRaw, "discord:channel:"), pickByPrefix(toRaw, "channel:"));
158
+ case "signal":
159
+ // Group: group:<id>; DM: signal:<recipient>
160
+ return pickFirst(pickByPrefix(fromRaw, "group:"), pickByPrefix(toRaw, "group:"));
161
+ case "imessage":
162
+ // Group: imessage:group:<id>, to=chat_id:<id>; DM: imessage:<sender>
163
+ return pickFirst(pickByPrefix(fromRaw, "imessage:group:"), pickByPrefix(toRaw, "chat_id:"));
164
+ case "whatsapp":
165
+ // Group JID ends with @g.us; DM is E.164-like
166
+ if (/@g\.us$/i.test(fromRaw))
167
+ return fromRaw;
168
+ return null;
169
+ case "line":
170
+ // Group/room: line:group:<id> / line:room:<id>
171
+ return pickFirst(pickByPrefix(fromRaw, "line:group:"), pickByPrefix(fromRaw, "line:room:"), pickByPrefix(toRaw, "line:group:"), pickByPrefix(toRaw, "line:room:"));
172
+ case "matrix":
173
+ // Group/channel: matrix:channel:<roomId>; DM: matrix:<userId>
174
+ return pickByPrefix(fromRaw, "matrix:channel:");
175
+ case "msteams":
176
+ // Group/channel: msteams:channel:<id> / msteams:group:<id>, to=conversation:<id>
177
+ return pickFirst(pickByPrefix(fromRaw, "msteams:channel:"), pickByPrefix(fromRaw, "msteams:group:"), pickByPrefix(toRaw, "conversation:"));
178
+ default:
179
+ // Safe generic fallback: only explicit group/channel markers.
180
+ return pickFirst(pickByPrefix(fromRaw, `${ch}:group:`), pickByPrefix(fromRaw, `${ch}:channel:`), pickByPrefix(toRaw, `${ch}:group:`), pickByPrefix(toRaw, `${ch}:channel:`));
111
181
  }
112
- return null;
113
182
  }
114
183
  /**
115
184
  * Derive sessionKey from command context.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1a0rz/agent-identity",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Agent Identity: UserPool (用户池) login, TIP token (工作负载令牌), credential hosting (凭据托管 OAuth2/API key), optional tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",