@m1a0rz/agent-identity 0.2.5 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +62 -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 +82 -34
  8. package/dist/src/commands/identity-commands.d.ts.map +1 -1
  9. package/dist/src/commands/identity-commands.js +14 -5
  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 +10 -22
  16. package/dist/src/hooks/before-tool-call.d.ts +20 -14
  17. package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
  18. package/dist/src/hooks/before-tool-call.js +207 -127
  19. package/dist/src/hooks/llm-input.d.ts +2 -0
  20. package/dist/src/hooks/llm-input.d.ts.map +1 -1
  21. package/dist/src/hooks/llm-input.js +15 -4
  22. package/dist/src/hooks/sessions-send-propagation.d.ts +1 -0
  23. package/dist/src/hooks/sessions-send-propagation.d.ts.map +1 -1
  24. package/dist/src/hooks/sessions-send-propagation.js +4 -2
  25. package/dist/src/hooks/sessions-spawn-propagation.d.ts.map +1 -1
  26. package/dist/src/hooks/sessions-spawn-propagation.js +4 -2
  27. package/dist/src/hooks/subagent-ended-cleanup.d.ts +1 -0
  28. package/dist/src/hooks/subagent-ended-cleanup.d.ts.map +1 -1
  29. package/dist/src/hooks/subagent-ended-cleanup.js +4 -1
  30. package/dist/src/risk/low-risk-tools.d.ts.map +1 -1
  31. package/dist/src/risk/low-risk-tools.js +0 -2
  32. package/dist/src/services/tip-acquisition.d.ts +0 -1
  33. package/dist/src/services/tip-acquisition.d.ts.map +1 -1
  34. package/dist/src/services/tip-acquisition.js +2 -2
  35. package/dist/src/services/tip-propagation.d.ts.map +1 -1
  36. package/dist/src/services/tip-propagation.js +1 -2
  37. package/dist/src/services/tip-with-refresh.d.ts +1 -1
  38. package/dist/src/services/tip-with-refresh.d.ts.map +1 -1
  39. package/dist/src/services/tip-with-refresh.js +3 -5
  40. package/dist/src/store/credential-env-snapshot.d.ts +38 -0
  41. package/dist/src/store/credential-env-snapshot.d.ts.map +1 -0
  42. package/dist/src/store/credential-env-snapshot.js +101 -0
  43. package/dist/src/store/encryption.d.ts +30 -0
  44. package/dist/src/store/encryption.d.ts.map +1 -0
  45. package/dist/src/store/encryption.js +147 -0
  46. package/dist/src/store/group-sender-store.d.ts +35 -0
  47. package/dist/src/store/group-sender-store.d.ts.map +1 -0
  48. package/dist/src/store/group-sender-store.js +112 -0
  49. package/dist/src/store/session-store.d.ts.map +1 -1
  50. package/dist/src/store/session-store.js +91 -8
  51. package/dist/src/store/tip-store.d.ts +11 -6
  52. package/dist/src/store/tip-store.d.ts.map +1 -1
  53. package/dist/src/store/tip-store.js +23 -54
  54. package/dist/src/tools/identity-approve-tool.d.ts.map +1 -1
  55. package/dist/src/tools/identity-approve-tool.js +3 -1
  56. package/dist/src/tools/identity-fetch.d.ts.map +1 -1
  57. package/dist/src/tools/identity-fetch.js +3 -1
  58. package/dist/src/tools/identity-list-credentials.d.ts +2 -0
  59. package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
  60. package/dist/src/tools/identity-list-credentials.js +8 -4
  61. package/dist/src/tools/identity-login.d.ts.map +1 -1
  62. package/dist/src/tools/identity-login.js +2 -1
  63. package/dist/src/tools/identity-logout.d.ts.map +1 -1
  64. package/dist/src/tools/identity-logout.js +2 -1
  65. package/dist/src/tools/identity-set-binding.d.ts.map +1 -1
  66. package/dist/src/tools/identity-set-binding.js +2 -1
  67. package/dist/src/tools/identity-status.d.ts.map +1 -1
  68. package/dist/src/tools/identity-status.js +2 -1
  69. package/dist/src/tools/identity-unset-binding.d.ts.map +1 -1
  70. package/dist/src/tools/identity-unset-binding.js +2 -1
  71. package/dist/src/tools/identity-whoami.d.ts.map +1 -1
  72. package/dist/src/tools/identity-whoami.js +2 -1
  73. package/dist/src/utils/derive-session-key.d.ts +7 -0
  74. package/dist/src/utils/derive-session-key.d.ts.map +1 -1
  75. package/dist/src/utils/derive-session-key.js +84 -15
  76. package/package.json +1 -1
  77. package/skills/SKILL.md +75 -150
@@ -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,17 +21,20 @@
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;
33
36
  try {
34
- await deleteTIPToken(storeDir, targetSessionKey);
37
+ deleteTIPToken(targetSessionKey);
35
38
  await deleteSession(storeDir, targetSessionKey);
36
39
  logDebug(logger, `cleaned up TIP and session for ${targetSessionKey.slice(0, 24)}... on subagent_ended`);
37
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"low-risk-tools.d.ts","sourceRoot":"","sources":["../../../src/risk/low-risk-tools.ts"],"names":[],"mappings":"AAqCA,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAQhF"}
1
+ {"version":3,"file":"low-risk-tools.d.ts","sourceRoot":"","sources":["../../../src/risk/low-risk-tools.ts"],"names":[],"mappings":"AAmCA,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAQhF"}
@@ -20,8 +20,6 @@ const LOW_RISK_TOOL_NAMES = new Set([
20
20
  "memory_search",
21
21
  "memory_get",
22
22
  "session_status",
23
- "identity_whoami",
24
- "identity_status",
25
23
  "identity_list_tips",
26
24
  "identity_list_credentials",
27
25
  "identity_config",
@@ -4,7 +4,6 @@
4
4
  */
5
5
  import type { IdentityService } from "./identity-service.js";
6
6
  export type FetchAndStoreTIPParams = {
7
- storeDir: string;
8
7
  sessionKey: string;
9
8
  identityService: IdentityService;
10
9
  jwtForExchange: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tip-acquisition.d.ts","sourceRoot":"","sources":["../../../src/services/tip-acquisition.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BpF"}
1
+ {"version":3,"file":"tip-acquisition.d.ts","sourceRoot":"","sources":["../../../src/services/tip-acquisition.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BpF"}
@@ -16,7 +16,7 @@
16
16
  import { setTIPToken } from "../store/tip-store.js";
17
17
  import { resolveAgentId, resolveWorkloadNameForSession, } from "../utils/derive-session-key.js";
18
18
  export async function fetchAndStoreTIP(params) {
19
- const { storeDir, sessionKey, identityService, jwtForExchange, sub, ctxAgentId, configWorkloadName, targetWorkloadSessionKey, parentSessionKey, } = params;
19
+ const { sessionKey, identityService, jwtForExchange, sub, ctxAgentId, configWorkloadName, targetWorkloadSessionKey, parentSessionKey, } = params;
20
20
  const workloadKey = targetWorkloadSessionKey ?? sessionKey;
21
21
  const agentId = resolveAgentId({ agentId: ctxAgentId, sessionKey: workloadKey });
22
22
  const workloadName = resolveWorkloadNameForSession({
@@ -35,5 +35,5 @@ export async function fetchAndStoreTIP(params) {
35
35
  ...(parentSessionKey && { parentSessionKey }),
36
36
  issuedAt: Date.now(),
37
37
  };
38
- await setTIPToken(storeDir, sessionKey, entry);
38
+ setTIPToken(sessionKey, entry);
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tip-propagation.d.ts","sourceRoot":"","sources":["../../../src/services/tip-propagation.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAM3D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClD,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;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CpF"}
1
+ {"version":3,"file":"tip-propagation.d.ts","sourceRoot":"","sources":["../../../src/services/tip-propagation.ts"],"names":[],"mappings":"AAgBA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAM3D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAClD,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;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CpF"}
@@ -30,7 +30,6 @@ export async function propagateTIPToTarget(params) {
30
30
  }
31
31
  if (subagentTipPropagation === true) {
32
32
  await fetchAndStoreTIP({
33
- storeDir,
34
33
  sessionKey: targetSessionKey,
35
34
  identityService,
36
35
  jwtForExchange: callerTIP.token,
@@ -42,7 +41,7 @@ export async function propagateTIPToTarget(params) {
42
41
  logInfo(logger, `TIP propagated to ${targetSessionKey.slice(0, 24)}...`);
43
42
  }
44
43
  else {
45
- await setTIPToken(storeDir, targetSessionKey, {
44
+ setTIPToken(targetSessionKey, {
46
45
  ...callerTIP,
47
46
  ...(callerSessionKey && { parentSessionKey: callerSessionKey }),
48
47
  });
@@ -20,5 +20,5 @@ export type GetOrRefreshTIPOptions = {
20
20
  * Get TIP token for session. If missing or expired and refresh options provided,
21
21
  * attempts to fetch TIP (refreshing userToken if needed).
22
22
  */
23
- export declare function getOrRefreshTIPToken(storeDir: string, sessionKey: string, options?: GetOrRefreshTIPOptions): Promise<Awaited<ReturnType<typeof getTIPToken>>>;
23
+ export declare function getOrRefreshTIPToken(storeDir: string, sessionKey: string, options?: GetOrRefreshTIPOptions): Promise<ReturnType<typeof getTIPToken>>;
24
24
  //# sourceMappingURL=tip-with-refresh.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tip-with-refresh.d.ts","sourceRoot":"","sources":["../../../src/services/tip-with-refresh.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAMpD,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,EAAE,eAAe,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,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;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAgElD"}
1
+ {"version":3,"file":"tip-with-refresh.d.ts","sourceRoot":"","sources":["../../../src/services/tip-with-refresh.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAMpD,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,EAAE,eAAe,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,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;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CA8DzC"}
@@ -24,7 +24,7 @@ import { fetchAndStoreTIP } from "./tip-acquisition.js";
24
24
  * attempts to fetch TIP (refreshing userToken if needed).
25
25
  */
26
26
  export async function getOrRefreshTIPToken(storeDir, sessionKey, options) {
27
- const cached = await getTIPToken(storeDir, sessionKey);
27
+ const cached = getTIPToken(sessionKey);
28
28
  if (cached)
29
29
  return cached;
30
30
  if (!options) {
@@ -36,7 +36,6 @@ export async function getOrRefreshTIPToken(storeDir, sessionKey, options) {
36
36
  return null;
37
37
  try {
38
38
  await fetchAndStoreTIP({
39
- storeDir,
40
39
  sessionKey,
41
40
  identityService,
42
41
  jwtForExchange: session.userToken,
@@ -45,7 +44,7 @@ export async function getOrRefreshTIPToken(storeDir, sessionKey, options) {
45
44
  configWorkloadName,
46
45
  });
47
46
  logInfo(logger, `TIP acquired for ${sessionKey.slice(0, 24)}...`);
48
- return getTIPToken(storeDir, sessionKey);
47
+ return getTIPToken(sessionKey);
49
48
  }
50
49
  catch (err) {
51
50
  const canRefresh = isTokenExpiredError(err) && !!getOidcConfigForRefresh && !!session.refreshToken;
@@ -60,7 +59,6 @@ export async function getOrRefreshTIPToken(storeDir, sessionKey, options) {
60
59
  return null;
61
60
  try {
62
61
  await fetchAndStoreTIP({
63
- storeDir,
64
62
  sessionKey,
65
63
  identityService,
66
64
  jwtForExchange: refreshed,
@@ -69,7 +67,7 @@ export async function getOrRefreshTIPToken(storeDir, sessionKey, options) {
69
67
  configWorkloadName,
70
68
  });
71
69
  logInfo(logger, `TIP acquired after refresh for ${sessionKey.slice(0, 24)}...`);
72
- return getTIPToken(storeDir, sessionKey);
70
+ return getTIPToken(sessionKey);
73
71
  }
74
72
  catch {
75
73
  return null;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * In-memory env snapshot store for per-tool-call credential injection.
3
+ *
4
+ * before_tool_call applies a snapshot (saves baseline, sets env);
5
+ * after_tool_call restores the snapshot (reverts env to baseline).
6
+ * Keyed by "runId:toolCallId" so concurrent tool calls stay isolated.
7
+ *
8
+ * Each env key has its own promise-chain mutex so concurrent tool calls
9
+ * writing the SAME key are serialized, while calls using different keys
10
+ * run fully in parallel.
11
+ */
12
+ type EnvSnapshot = {
13
+ /** envVar → previous value (undefined = key was absent) */
14
+ baseline: Map<string, string | undefined>;
15
+ /** Release functions for per-key locks (call on restore) */
16
+ releasers: Array<() => void>;
17
+ createdAt: number;
18
+ };
19
+ export declare function snapshotKey(runId?: string, toolCallId?: string): string;
20
+ export declare function hasSnapshot(runId?: string, toolCallId?: string): boolean;
21
+ /**
22
+ * Acquire per-key locks, record baseline, then overwrite process.env.
23
+ * Keys are sorted before acquisition to prevent deadlocks when two
24
+ * concurrent calls compete for the same set of keys.
25
+ */
26
+ export declare function applyEnvSnapshot(credentials: Record<string, string>, runId?: string, toolCallId?: string): Promise<void>;
27
+ /**
28
+ * Revert process.env to baseline values, then release per-key locks
29
+ * so the next queued tool call for each key can proceed.
30
+ */
31
+ export declare function restoreEnvSnapshot(runId?: string, toolCallId?: string): void;
32
+ /** Exposed for testing. */
33
+ export declare const __testing: {
34
+ snapshots: Map<string, EnvSnapshot>;
35
+ envKeyChains: Map<string, Promise<void>>;
36
+ };
37
+ export {};
38
+ //# sourceMappingURL=credential-env-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-env-snapshot.d.ts","sourceRoot":"","sources":["../../../src/store/credential-env-snapshot.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AAEH,KAAK,WAAW,GAAG;IACjB,2DAA2D;IAC3D,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1C,4DAA4D;IAC5D,SAAS,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAyBF,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAExE;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAc5E;AAoBD,2BAA2B;AAC3B,eAAO,MAAM,SAAS;;;CAA8B,CAAC"}
@@ -0,0 +1,101 @@
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
+ const SNAPSHOT_TTL_MS = 2 * 60 * 1000;
17
+ const MAX_SNAPSHOTS = 512;
18
+ const snapshots = new Map();
19
+ /**
20
+ * Per-key promise chain mutex. Each env key has a chain; to acquire a key
21
+ * the caller appends to the chain and awaits the previous holder. Releasing
22
+ * resolves the promise so the next waiter proceeds.
23
+ */
24
+ const envKeyChains = new Map();
25
+ async function acquireEnvKey(key) {
26
+ const prev = envKeyChains.get(key) ?? Promise.resolve();
27
+ let release;
28
+ const next = new Promise((resolve) => {
29
+ release = resolve;
30
+ });
31
+ envKeyChains.set(key, next);
32
+ await prev;
33
+ return release;
34
+ }
35
+ export function snapshotKey(runId, toolCallId) {
36
+ return `${runId ?? "no-run"}:${toolCallId ?? "no-tcid"}`;
37
+ }
38
+ export function hasSnapshot(runId, toolCallId) {
39
+ return snapshots.has(snapshotKey(runId, toolCallId));
40
+ }
41
+ /**
42
+ * Acquire per-key locks, record baseline, then overwrite process.env.
43
+ * Keys are sorted before acquisition to prevent deadlocks when two
44
+ * concurrent calls compete for the same set of keys.
45
+ */
46
+ export async function applyEnvSnapshot(credentials, runId, toolCallId) {
47
+ const key = snapshotKey(runId, toolCallId);
48
+ const baseline = new Map();
49
+ const releasers = [];
50
+ const sortedEntries = Object.entries(credentials).sort(([a], [b]) => a.localeCompare(b));
51
+ for (const [envVar, value] of sortedEntries) {
52
+ const release = await acquireEnvKey(envVar);
53
+ releasers.push(release);
54
+ baseline.set(envVar, process.env[envVar]);
55
+ process.env[envVar] = value;
56
+ }
57
+ snapshots.set(key, { baseline, releasers, createdAt: Date.now() });
58
+ pruneSnapshots();
59
+ }
60
+ /**
61
+ * Revert process.env to baseline values, then release per-key locks
62
+ * so the next queued tool call for each key can proceed.
63
+ */
64
+ export function restoreEnvSnapshot(runId, toolCallId) {
65
+ const key = snapshotKey(runId, toolCallId);
66
+ const snapshot = snapshots.get(key);
67
+ if (!snapshot)
68
+ return;
69
+ snapshots.delete(key);
70
+ for (const [envVar, oldValue] of snapshot.baseline) {
71
+ if (oldValue === undefined)
72
+ delete process.env[envVar];
73
+ else
74
+ process.env[envVar] = oldValue;
75
+ }
76
+ for (const release of snapshot.releasers) {
77
+ release();
78
+ }
79
+ }
80
+ /** Evict expired snapshots, restoring env and releasing locks to prevent leaks. */
81
+ function pruneSnapshots() {
82
+ if (snapshots.size < MAX_SNAPSHOTS)
83
+ return;
84
+ const now = Date.now();
85
+ for (const [key, snap] of snapshots) {
86
+ if (now - snap.createdAt > SNAPSHOT_TTL_MS) {
87
+ for (const [envVar, oldValue] of snap.baseline) {
88
+ if (oldValue === undefined)
89
+ delete process.env[envVar];
90
+ else
91
+ process.env[envVar] = oldValue;
92
+ }
93
+ for (const release of snap.releasers) {
94
+ release();
95
+ }
96
+ snapshots.delete(key);
97
+ }
98
+ }
99
+ }
100
+ /** Exposed for testing. */
101
+ export const __testing = { snapshots, envKeyChains };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Schedule key derivation. Call once at plugin startup (synchronous context
3
+ * is fine — the returned promise is awaited lazily by getEncryptionKey).
4
+ */
5
+ export declare function initEncryptionKey(storeDir: string): void;
6
+ /**
7
+ * Get the derived encryption key, awaiting initialization if it hasn't
8
+ * completed yet. Callers in async hooks simply `await getEncryptionKey()`.
9
+ */
10
+ export declare function getEncryptionKey(): Promise<Buffer>;
11
+ export declare function encrypt(key: Buffer, plaintext: string): Buffer;
12
+ export declare function decrypt(key: Buffer, raw: Buffer): string;
13
+ /**
14
+ * Read and decrypt a file. Returns null if the file is missing or
15
+ * decryption fails (e.g. key changed after config migration).
16
+ */
17
+ export declare function readEncrypted(key: Buffer, filePath: string): Promise<string | null>;
18
+ /**
19
+ * Encrypt and write data to a file with restrictive permissions.
20
+ */
21
+ export declare function writeEncrypted(key: Buffer, filePath: string, data: string): Promise<void>;
22
+ /** Encrypt a single field value, returning a prefixed base64 string. */
23
+ export declare function encryptField(key: Buffer, plaintext: string): string;
24
+ /** Decrypt a field value previously encrypted by encryptField. */
25
+ export declare function decryptField(key: Buffer, value: string): string;
26
+ /** Check whether a string looks like an encrypted field value. */
27
+ export declare function isEncryptedField(value: unknown): boolean;
28
+ /** Reset cached key and init promise (for testing only). */
29
+ export declare function __resetCachedKey(): void;
30
+ //# sourceMappingURL=encryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/store/encryption.ts"],"names":[],"mappings":"AAgEA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAIxD;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAGf;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGnE;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAExD;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
@@ -0,0 +1,147 @@
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
+ * Encryption layer for sensitive on-disk data (sessions.json).
18
+ *
19
+ * Derives a 256-bit AES key via HKDF-SHA512 from:
20
+ * 1. A per-installation random salt (.key-salt, generated once)
21
+ * 2. A hardcoded pepper (compiled into plugin code)
22
+ *
23
+ * Files are encrypted with AES-256-GCM (authenticated encryption).
24
+ * On-disk format: [IV 12B][AuthTag 16B][Ciphertext ...]
25
+ */
26
+ import crypto from "node:crypto";
27
+ import fs from "node:fs/promises";
28
+ import path from "node:path";
29
+ const IV_LEN = 12;
30
+ const TAG_LEN = 16;
31
+ const KEY_LEN = 32;
32
+ const SALT_LEN = 32;
33
+ const SALT_FILENAME = ".key-salt";
34
+ const PEPPER = "openclaw-identity-plugin-v1-AES256GCM-7f3a9c";
35
+ const HKDF_INFO = "openclaw-identity-enc";
36
+ let cachedKey = null;
37
+ let initPromise = null;
38
+ async function readOrCreateSalt(storeDir) {
39
+ const saltPath = path.join(storeDir, SALT_FILENAME);
40
+ try {
41
+ const existing = await fs.readFile(saltPath);
42
+ if (existing.length === SALT_LEN)
43
+ return existing;
44
+ }
45
+ catch {
46
+ // salt file missing — will create below
47
+ }
48
+ const salt = crypto.randomBytes(SALT_LEN);
49
+ await fs.mkdir(storeDir, { recursive: true });
50
+ await fs.writeFile(saltPath, salt, { mode: 0o600 });
51
+ return salt;
52
+ }
53
+ async function deriveKey(storeDir) {
54
+ const salt = await readOrCreateSalt(storeDir);
55
+ const ikm = Buffer.concat([salt, Buffer.from(PEPPER, "utf8")]);
56
+ return Buffer.from(crypto.hkdfSync("sha512", ikm, salt, HKDF_INFO, KEY_LEN));
57
+ }
58
+ /**
59
+ * Schedule key derivation. Call once at plugin startup (synchronous context
60
+ * is fine — the returned promise is awaited lazily by getEncryptionKey).
61
+ */
62
+ export function initEncryptionKey(storeDir) {
63
+ if (cachedKey || initPromise)
64
+ return;
65
+ initPromise = deriveKey(storeDir).then((key) => {
66
+ cachedKey = key;
67
+ return key;
68
+ });
69
+ }
70
+ /**
71
+ * Get the derived encryption key, awaiting initialization if it hasn't
72
+ * completed yet. Callers in async hooks simply `await getEncryptionKey()`.
73
+ */
74
+ export async function getEncryptionKey() {
75
+ if (cachedKey)
76
+ return cachedKey;
77
+ if (initPromise)
78
+ return initPromise;
79
+ throw new Error("Encryption key not initialized — call initEncryptionKey first");
80
+ }
81
+ export function encrypt(key, plaintext) {
82
+ const iv = crypto.randomBytes(IV_LEN);
83
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
84
+ const encrypted = Buffer.concat([
85
+ cipher.update(plaintext, "utf8"),
86
+ cipher.final(),
87
+ ]);
88
+ const tag = cipher.getAuthTag();
89
+ return Buffer.concat([iv, tag, encrypted]);
90
+ }
91
+ export function decrypt(key, raw) {
92
+ if (raw.length < IV_LEN + TAG_LEN + 1) {
93
+ throw new Error("Encrypted data too short");
94
+ }
95
+ const iv = raw.subarray(0, IV_LEN);
96
+ const tag = raw.subarray(IV_LEN, IV_LEN + TAG_LEN);
97
+ const ciphertext = raw.subarray(IV_LEN + TAG_LEN);
98
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
99
+ decipher.setAuthTag(tag);
100
+ return Buffer.concat([
101
+ decipher.update(ciphertext),
102
+ decipher.final(),
103
+ ]).toString("utf8");
104
+ }
105
+ /**
106
+ * Read and decrypt a file. Returns null if the file is missing or
107
+ * decryption fails (e.g. key changed after config migration).
108
+ */
109
+ export async function readEncrypted(key, filePath) {
110
+ try {
111
+ const raw = await fs.readFile(filePath);
112
+ return decrypt(key, raw);
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ /**
119
+ * Encrypt and write data to a file with restrictive permissions.
120
+ */
121
+ export async function writeEncrypted(key, filePath, data) {
122
+ const encrypted = encrypt(key, data);
123
+ await fs.writeFile(filePath, encrypted, { mode: 0o600 });
124
+ }
125
+ const FIELD_PREFIX = "enc:v1:";
126
+ /** Encrypt a single field value, returning a prefixed base64 string. */
127
+ export function encryptField(key, plaintext) {
128
+ const buf = encrypt(key, plaintext);
129
+ return FIELD_PREFIX + buf.toString("base64");
130
+ }
131
+ /** Decrypt a field value previously encrypted by encryptField. */
132
+ export function decryptField(key, value) {
133
+ if (!value.startsWith(FIELD_PREFIX)) {
134
+ throw new Error("Not an encrypted field value");
135
+ }
136
+ const raw = Buffer.from(value.slice(FIELD_PREFIX.length), "base64");
137
+ return decrypt(key, raw);
138
+ }
139
+ /** Check whether a string looks like an encrypted field value. */
140
+ export function isEncryptedField(value) {
141
+ return typeof value === "string" && value.startsWith(FIELD_PREFIX);
142
+ }
143
+ /** Reset cached key and init promise (for testing only). */
144
+ export function __resetCachedKey() {
145
+ cachedKey = null;
146
+ initPromise = null;
147
+ }
@@ -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"}