@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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -5
- package/dist/src/commands/identity-commands.d.ts.map +1 -1
- package/dist/src/commands/identity-commands.js +5 -1
- package/dist/src/hooks/before-agent-start.d.ts.map +1 -1
- package/dist/src/hooks/before-agent-start.js +6 -4
- package/dist/src/hooks/before-tool-call.d.ts +1 -0
- package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
- package/dist/src/hooks/before-tool-call.js +24 -6
- package/dist/src/hooks/llm-input.d.ts +2 -0
- package/dist/src/hooks/llm-input.d.ts.map +1 -1
- package/dist/src/hooks/llm-input.js +15 -4
- package/dist/src/hooks/sessions-send-propagation.d.ts +1 -0
- package/dist/src/hooks/sessions-send-propagation.d.ts.map +1 -1
- package/dist/src/hooks/sessions-send-propagation.js +4 -2
- package/dist/src/hooks/sessions-spawn-propagation.d.ts.map +1 -1
- package/dist/src/hooks/sessions-spawn-propagation.js +4 -2
- package/dist/src/hooks/subagent-ended-cleanup.d.ts +1 -0
- package/dist/src/hooks/subagent-ended-cleanup.d.ts.map +1 -1
- package/dist/src/hooks/subagent-ended-cleanup.js +3 -0
- package/dist/src/store/group-sender-store.d.ts +35 -0
- package/dist/src/store/group-sender-store.d.ts.map +1 -0
- package/dist/src/store/group-sender-store.js +112 -0
- package/dist/src/store/session-store.d.ts.map +1 -1
- package/dist/src/store/session-store.js +15 -1
- package/dist/src/tools/identity-approve-tool.d.ts.map +1 -1
- package/dist/src/tools/identity-approve-tool.js +3 -1
- package/dist/src/tools/identity-fetch.d.ts.map +1 -1
- package/dist/src/tools/identity-fetch.js +2 -1
- package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
- package/dist/src/tools/identity-list-credentials.js +2 -1
- package/dist/src/tools/identity-login.d.ts.map +1 -1
- package/dist/src/tools/identity-login.js +2 -1
- package/dist/src/tools/identity-logout.d.ts.map +1 -1
- package/dist/src/tools/identity-logout.js +2 -1
- package/dist/src/tools/identity-set-binding.d.ts.map +1 -1
- package/dist/src/tools/identity-set-binding.js +2 -1
- package/dist/src/tools/identity-status.d.ts.map +1 -1
- package/dist/src/tools/identity-status.js +2 -1
- package/dist/src/tools/identity-unset-binding.d.ts.map +1 -1
- package/dist/src/tools/identity-unset-binding.js +2 -1
- package/dist/src/tools/identity-whoami.d.ts.map +1 -1
- package/dist/src/tools/identity-whoami.js +2 -1
- package/dist/src/utils/derive-session-key.d.ts +7 -0
- package/dist/src/utils/derive-session-key.d.ts.map +1 -1
- package/dist/src/utils/derive-session-key.js +84 -15
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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;
|
|
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
|
|
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;
|
|
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,
|
|
40
|
+
const bindings = await loadCredentialEnvBindings(storeDir, effectiveKey);
|
|
39
41
|
for (const [provider, envVar] of Object.entries(bindings)) {
|
|
40
|
-
const cred = await getCredential(storeDir,
|
|
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,
|
|
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 ${
|
|
62
|
+
logWarn(logger, `failed to get TIP for ${effectiveKey}: ${String(err)}`);
|
|
61
63
|
}
|
|
62
64
|
};
|
|
63
65
|
}
|
|
@@ -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;
|
|
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,
|
|
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(
|
|
150
|
-
toolApprovalStore.consumeApproval(
|
|
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 +
|
|
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":"
|
|
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:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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 (!
|
|
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;
|
|
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,
|
|
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;
|
|
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,
|
|
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":"
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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
|
-
|
|
96
|
-
|
|
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
|
|
100
|
-
if (!
|
|
111
|
+
const ch = (channel ?? "").trim().toLowerCase();
|
|
112
|
+
if (!ch)
|
|
101
113
|
return null;
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
|
|
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.
|
|
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",
|