evolcore 0.0.33 → 0.0.35-test.0
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/CHANGELOG.md +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +25 -8
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +131 -6
- package/dist/core/session/session-title.js +26 -1
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +17 -5
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +13 -16
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +12 -12
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -128,9 +128,13 @@ function printItem(item) {
|
|
|
128
128
|
const running = obj.running ? ' [运行中]' : '';
|
|
129
129
|
// 内部话题(daemon 会话记录)不是真实联系人,标注出来避免误认
|
|
130
130
|
const internal = obj.internal ? ' [内部会话记录·无消息]' : '';
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
// 主会话的 threadId 是空串;显示成 /main 比留一个空尾巴好读。
|
|
132
|
+
console.log(`${obj.peer}/${obj.threadId || 'main'}${running}${internal}`);
|
|
133
|
+
// 名称只放名称本身:状态、标签各自单独一行,避免污染标题(标题会被 summary set --name 引用)。
|
|
134
|
+
// 主会话(threadId 为空)加 `[主会话]` 前缀,与普通话题区分开;前缀只在展示层加,
|
|
135
|
+
// 不进索引与 session 的 name 字段,避免被 summary set --name 当成真实标题。
|
|
136
|
+
const namePrefix = obj.threadId ? '' : '[主会话] ';
|
|
137
|
+
console.log(` 名称: ${namePrefix}${obj.name}`);
|
|
134
138
|
if (obj.topicStatus)
|
|
135
139
|
console.log(` 状态: ${obj.topicStatus}`);
|
|
136
140
|
if (obj.label)
|
|
@@ -150,6 +154,8 @@ function printItem(item) {
|
|
|
150
154
|
console.log(` 最后消息: ${obj.lastMsgSnippet}`);
|
|
151
155
|
if (obj.msgTotal !== undefined)
|
|
152
156
|
console.log(` 摘要: ${obj.summarySnippet ?? '未总结'}`);
|
|
157
|
+
if (obj.sessionId)
|
|
158
|
+
console.log(` 会话: ${obj.sessionId}`);
|
|
153
159
|
console.log('');
|
|
154
160
|
return;
|
|
155
161
|
}
|
|
@@ -228,7 +234,11 @@ export async function runThreadCommand(args) {
|
|
|
228
234
|
const slash = ref.lastIndexOf('/');
|
|
229
235
|
if (slash < 0)
|
|
230
236
|
return { peer: ref };
|
|
231
|
-
|
|
237
|
+
const raw = ref.slice(slash + 1);
|
|
238
|
+
// 主会话的 threadId 是空串;`<peer>/main` 是同义写法,`list` 也这样显示。
|
|
239
|
+
if (raw === 'main')
|
|
240
|
+
return { peer: ref.slice(0, slash), threadId: '' };
|
|
241
|
+
return { peer: ref.slice(0, slash), threadId: raw };
|
|
232
242
|
}
|
|
233
243
|
switch (sub) {
|
|
234
244
|
case 'list': {
|
|
@@ -308,7 +318,7 @@ export async function runThreadCommand(args) {
|
|
|
308
318
|
case 'create': {
|
|
309
319
|
const peer = args[1];
|
|
310
320
|
if (!peer || peer.startsWith('-')) {
|
|
311
|
-
console.error(
|
|
321
|
+
console.error(`用法: ec thread create <peer> --name '...'`);
|
|
312
322
|
process.exitCode = 1;
|
|
313
323
|
return;
|
|
314
324
|
}
|
|
@@ -334,7 +344,7 @@ export async function runThreadCommand(args) {
|
|
|
334
344
|
const ref = args[1];
|
|
335
345
|
const newName = args[2];
|
|
336
346
|
if (!ref || newName === undefined) {
|
|
337
|
-
console.error(
|
|
347
|
+
console.error(`用法: ec thread rename <ref> '<名称>'`);
|
|
338
348
|
process.exitCode = 1;
|
|
339
349
|
return;
|
|
340
350
|
}
|
|
@@ -524,7 +534,7 @@ export async function runThreadCommand(args) {
|
|
|
524
534
|
if (newName === undefined) {
|
|
525
535
|
console.error('✗ 必须指定 --name <新标题>');
|
|
526
536
|
console.error(' 不改名请显式传 --name -');
|
|
527
|
-
console.error(` 例: ec thread summary set ${ref}
|
|
537
|
+
console.error(` 例: ec thread summary set ${ref} '<摘要内容>' --name '<话题的核心动作>'`);
|
|
528
538
|
process.exitCode = 1;
|
|
529
539
|
return;
|
|
530
540
|
}
|
|
@@ -146,7 +146,7 @@ Create 参数模式支持:
|
|
|
146
146
|
--target-chat-type <private|group> (AUN 目标必填;channelId 同时作为群聊 ID)
|
|
147
147
|
--target-session <main|thread> [--target-thread-id <threadId>]
|
|
148
148
|
--trigger-thread <by-run|by-trigger> (兼容旧值 per-run)
|
|
149
|
-
--name <名称>
|
|
149
|
+
--name <名称> --description <文本>
|
|
150
150
|
|
|
151
151
|
通用输出参数:
|
|
152
152
|
--json | --format json
|
|
@@ -187,6 +187,8 @@ async function listTriggers(args, json, response) {
|
|
|
187
187
|
}
|
|
188
188
|
for (const t of triggers) {
|
|
189
189
|
console.log(`${t.enabled ? 'on ' : 'off'} ${t.id} ${t.name} ${sourceLabel(t)}`);
|
|
190
|
+
if (t.description)
|
|
191
|
+
console.log(` ${t.description}`);
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
194
|
async function showTrigger(args, json, response) {
|
|
@@ -199,6 +201,8 @@ async function showTrigger(args, json, response) {
|
|
|
199
201
|
console.log(`${t.name} (${t.id})`);
|
|
200
202
|
console.log(`agent: ${t.agentAid}`);
|
|
201
203
|
console.log(`enabled: ${t.enabled}`);
|
|
204
|
+
if (t.description)
|
|
205
|
+
console.log(`description: ${t.description}`);
|
|
202
206
|
console.log(`source: ${sourceLabel(t)}`);
|
|
203
207
|
if (t.limits?.maxRuns !== undefined || t.limits?.maxDuration !== undefined) {
|
|
204
208
|
console.log(`limits: ${[
|
|
@@ -331,6 +335,7 @@ export async function buildTriggerCreateRequest(args, fileScope, defaultAgentAid
|
|
|
331
335
|
$schema_version: 3.1,
|
|
332
336
|
agentAid,
|
|
333
337
|
name: parsed.name || `trigger-${now.toString(36)}`,
|
|
338
|
+
...(parsed.description ? { description: parsed.description } : {}),
|
|
334
339
|
enabled: hasFlag(args, '--enable'),
|
|
335
340
|
createdAt: now,
|
|
336
341
|
updatedAt: now,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SYSTEM_SESSION_TYPES } from '../core/system-session/types.js';
|
|
1
2
|
const BEHAVIOR_TOP_FIELDS = new Set([
|
|
2
3
|
'active_baseagent',
|
|
3
4
|
'chatmode',
|
|
@@ -183,6 +184,9 @@ export function resolveConfigFieldRule(fieldPath, scope) {
|
|
|
183
184
|
if (top === 'sessionManifests') {
|
|
184
185
|
if (parts.length === 1)
|
|
185
186
|
return readonlyObject('sessionManifests');
|
|
187
|
+
// 系统会话类型的 manifest 由代码决定,不可配置
|
|
188
|
+
if (parts.length === 2 && SYSTEM_SESSION_TYPES.includes(parts[1]))
|
|
189
|
+
return { class: 'unknown' };
|
|
186
190
|
if (parts.length === 2 && /^[A-Za-z0-9_-]+$/.test(parts[1]))
|
|
187
191
|
return scalar('sessionManifests', 'manifest-file');
|
|
188
192
|
return { class: 'unknown' };
|
|
@@ -17,6 +17,7 @@ import crypto from 'crypto';
|
|
|
17
17
|
import { resolvePaths, agentConfig as agentConfigPath, agentContactConfig, agentRelationConfig, agentDir, agentRelationsDir, agentRolesIndex, agentRoleConfig, } from '../paths.js';
|
|
18
18
|
import { atomicReadJson, atomicWriteJson } from '../utils/atomic-write.js';
|
|
19
19
|
import { fileCache } from '../core/daemon-file-cache.js';
|
|
20
|
+
import { registerFile } from '../core/file-integrity/store.js';
|
|
20
21
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
21
22
|
import { normalizeAidDomain } from '../aun/aid/domain.js';
|
|
22
23
|
import { isExplicitGroupId } from '../aun/group/index.js';
|
|
@@ -764,6 +765,15 @@ export function normalizeProcessConfigCompat(value) {
|
|
|
764
765
|
if (changed)
|
|
765
766
|
normalized = { ...normalized, idleMonitor: nextIdleMonitor };
|
|
766
767
|
}
|
|
768
|
+
const delegationDiagnostics = normalized.delegationDiagnostics;
|
|
769
|
+
if (delegationDiagnostics && typeof delegationDiagnostics === 'object' && !Array.isArray(delegationDiagnostics)
|
|
770
|
+
&& (Object.prototype.hasOwnProperty.call(delegationDiagnostics, 'eventRetentionDays')
|
|
771
|
+
|| Object.prototype.hasOwnProperty.call(delegationDiagnostics, 'stateRetentionDays'))) {
|
|
772
|
+
const nextDiagnostics = { ...delegationDiagnostics };
|
|
773
|
+
delete nextDiagnostics.eventRetentionDays;
|
|
774
|
+
delete nextDiagnostics.stateRetentionDays;
|
|
775
|
+
normalized = { ...normalized, delegationDiagnostics: nextDiagnostics };
|
|
776
|
+
}
|
|
767
777
|
if (debug && typeof debug === 'object' && !Array.isArray(debug)
|
|
768
778
|
&& Object.prototype.hasOwnProperty.call(debug, 'flusherDiag')) {
|
|
769
779
|
const nextDebug = { ...debug };
|
|
@@ -1049,6 +1059,15 @@ function finishConfigWrite(target, sel, file) {
|
|
|
1049
1059
|
if (sel?.self && (target === ConfigTarget.Agent || target === ConfigTarget.RoleRegistry || target === ConfigTarget.Role)) {
|
|
1050
1060
|
clearRoleStoreCache(sel.self);
|
|
1051
1061
|
}
|
|
1062
|
+
// 配置落盘后登记到完整性台账;台账不可用时只告警不阻断
|
|
1063
|
+
if (fs.existsSync(file)) {
|
|
1064
|
+
try {
|
|
1065
|
+
registerFile(file);
|
|
1066
|
+
}
|
|
1067
|
+
catch (err) {
|
|
1068
|
+
configWarn(`[file-integrity] register failed for ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1052
1071
|
const event = { target, selector: { ...(sel ?? {}) } };
|
|
1053
1072
|
for (const listener of configWriteListeners) {
|
|
1054
1073
|
try {
|
|
@@ -6,6 +6,7 @@ import { isExplicitGroupId } from '../aun/group/index.js';
|
|
|
6
6
|
import { agentConfig, agentContactAuditPath, agentContactConfig, agentDir, resolvePaths } from '../paths.js';
|
|
7
7
|
import { atomicReadJson, atomicWrite, atomicWriteJson } from '../utils/atomic-write.js';
|
|
8
8
|
import { fileCache } from '../core/daemon-file-cache.js';
|
|
9
|
+
import { unregisterFile } from '../core/file-integrity/store.js';
|
|
9
10
|
import { ensureManagedLockParent, resolveManagedLockPath } from '../core/runtime-lock.js';
|
|
10
11
|
import { ConfigTarget, notifyConfigWrite, validateConfig } from './config-manager.js';
|
|
11
12
|
import { parseContactAlias } from './contact-alias.js';
|
|
@@ -584,6 +585,10 @@ function applyTransactionChange(change) {
|
|
|
584
585
|
for (const candidate of [change.file, `${change.file}_`, `${change.file}__`]) {
|
|
585
586
|
fs.rmSync(candidate, { force: true });
|
|
586
587
|
}
|
|
588
|
+
try {
|
|
589
|
+
unregisterFile(change.file);
|
|
590
|
+
}
|
|
591
|
+
catch { /* best-effort */ }
|
|
587
592
|
}
|
|
588
593
|
else {
|
|
589
594
|
atomicWrite(change.file, change.after);
|
|
@@ -208,7 +208,10 @@ function resolveFieldCommand(argv, subcommand, options = {}) {
|
|
|
208
208
|
else {
|
|
209
209
|
if (!route)
|
|
210
210
|
return { ok: false, code: 'UNKNOWN_FIELD', reason: `Unknown config field: ${field}` };
|
|
211
|
-
|
|
211
|
+
const coerced = coerceConfigValue(rawValue, route, field);
|
|
212
|
+
if (!coerced.ok)
|
|
213
|
+
return { ok: false, code: 'INVALID_CONFIG_VALUE', reason: coerced.reason };
|
|
214
|
+
value = coerced.value;
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
return {
|
|
@@ -506,18 +509,32 @@ function buildGlobalCanonicalArgv(command) {
|
|
|
506
509
|
argv.push('--format', command.format);
|
|
507
510
|
return argv;
|
|
508
511
|
}
|
|
509
|
-
function coerceConfigValue(raw, route) {
|
|
512
|
+
function coerceConfigValue(raw, route, field) {
|
|
510
513
|
if (route.merge === 'list')
|
|
511
|
-
return [raw];
|
|
514
|
+
return { ok: true, value: [raw] };
|
|
512
515
|
if (route.enum)
|
|
513
|
-
return raw;
|
|
516
|
+
return { ok: true, value: raw };
|
|
517
|
+
if (route.merge === 'dict' && field === route.field) {
|
|
518
|
+
const trimmed = raw.trim();
|
|
519
|
+
let parsed;
|
|
520
|
+
try {
|
|
521
|
+
parsed = JSON.parse(trimmed);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
return { ok: false, reason: `Config field ${route.field} requires a JSON object value` };
|
|
525
|
+
}
|
|
526
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
527
|
+
return { ok: false, reason: `Config field ${route.field} requires a JSON object value` };
|
|
528
|
+
}
|
|
529
|
+
return { ok: true, value: parsed };
|
|
530
|
+
}
|
|
514
531
|
if (raw === 'true')
|
|
515
|
-
return true;
|
|
532
|
+
return { ok: true, value: true };
|
|
516
533
|
if (raw === 'false')
|
|
517
|
-
return false;
|
|
534
|
+
return { ok: true, value: false };
|
|
518
535
|
if (/^-?\d+(\.\d+)?$/.test(raw))
|
|
519
|
-
return Number(raw);
|
|
520
|
-
return raw;
|
|
536
|
+
return { ok: true, value: Number(raw) };
|
|
537
|
+
return { ok: true, value: raw };
|
|
521
538
|
}
|
|
522
539
|
function parseOwnersValue(raw) {
|
|
523
540
|
let values;
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { atomicReadJson, atomicWriteJson } from '../utils/atomic-write.js';
|
|
4
4
|
import { agentRelationConfig, agentRelationsDir, agentRoleConfig, agentRolesIndex } from '../paths.js';
|
|
5
|
+
import { registerFile, unregisterFile } from '../core/file-integrity/store.js';
|
|
5
6
|
import { loadSchema } from './schema-registry.js';
|
|
6
7
|
import { clearRoleStoreCache, getStoredRoleRegistry, isRoleName, readRoleStore, roleRegistryExists, } from './role-store.js';
|
|
7
8
|
import { expectedRoleRank, isManagementRole } from './builtin-roles.js';
|
|
@@ -38,6 +39,10 @@ export function writeRoleRegistry(selfAid, value) {
|
|
|
38
39
|
const file = agentRolesIndex(selfAid);
|
|
39
40
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
40
41
|
atomicWriteJson(file, { ...value, $schema_version: 1 });
|
|
42
|
+
try {
|
|
43
|
+
registerFile(file);
|
|
44
|
+
}
|
|
45
|
+
catch { /* best-effort */ }
|
|
41
46
|
clearRoleStoreCache(selfAid);
|
|
42
47
|
}
|
|
43
48
|
export function writeRoleDefinition(selfAid, roleId, value) {
|
|
@@ -52,6 +57,10 @@ export function writeRoleDefinition(selfAid, roleId, value) {
|
|
|
52
57
|
const file = agentRoleConfig(selfAid, roleId);
|
|
53
58
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
54
59
|
atomicWriteJson(file, normalized);
|
|
60
|
+
try {
|
|
61
|
+
registerFile(file);
|
|
62
|
+
}
|
|
63
|
+
catch { /* best-effort */ }
|
|
55
64
|
clearRoleStoreCache(selfAid);
|
|
56
65
|
}
|
|
57
66
|
/** Remove an agent-local override so the builtin role template is used again. */
|
|
@@ -62,6 +71,10 @@ export function resetRoleDefinition(selfAid, roleId) {
|
|
|
62
71
|
const changed = fs.existsSync(file);
|
|
63
72
|
if (changed) {
|
|
64
73
|
fs.rmSync(file, { force: true });
|
|
74
|
+
try {
|
|
75
|
+
unregisterFile(file);
|
|
76
|
+
}
|
|
77
|
+
catch { /* best-effort */ }
|
|
65
78
|
clearRoleStoreCache(selfAid);
|
|
66
79
|
}
|
|
67
80
|
return changed;
|
|
@@ -43,9 +43,23 @@ function envFileValue(file, name) {
|
|
|
43
43
|
catch { }
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
46
|
+
// Registry values do not depend on the agent/project scope. Without this, every
|
|
47
|
+
// agent pays its own reg.exe launches (2 hives x 3 names, ~50ms each), which
|
|
48
|
+
// serializes daemon startup at roughly 150ms per Claude runner.
|
|
49
|
+
const windowsValueCache = new Map();
|
|
46
50
|
function windowsValue(hive, name) {
|
|
47
51
|
if (process.platform !== 'win32')
|
|
48
52
|
return undefined;
|
|
53
|
+
const key = `${hive}:${name}`;
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const hit = windowsValueCache.get(key);
|
|
56
|
+
if (hit && hit.expires > now)
|
|
57
|
+
return hit.value;
|
|
58
|
+
const value = readWindowsValue(hive, name);
|
|
59
|
+
windowsValueCache.set(key, { value, expires: now + 3000 });
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function readWindowsValue(hive, name) {
|
|
49
63
|
const registryPath = hive === 'user' ? 'HKCU\\Environment'
|
|
50
64
|
: 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment';
|
|
51
65
|
try {
|
|
@@ -129,9 +143,11 @@ export function setRuntimeEnvironmentOverride(aid, values) {
|
|
|
129
143
|
else
|
|
130
144
|
overrides.delete(scope);
|
|
131
145
|
cache.clear();
|
|
146
|
+
windowsValueCache.clear();
|
|
132
147
|
}
|
|
133
148
|
export function resetRuntimeEnvironmentForTests() {
|
|
134
149
|
overrides.clear();
|
|
135
150
|
cache.clear();
|
|
151
|
+
windowsValueCache.clear();
|
|
136
152
|
captureStartupValues();
|
|
137
153
|
}
|
package/dist/config-store.js
CHANGED
|
@@ -19,6 +19,7 @@ import fs from 'fs';
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
import { resolvePaths, agentDir, } from './paths.js';
|
|
21
21
|
import { atomicReadJson, atomicWriteJson } from './utils/atomic-write.js';
|
|
22
|
+
import { registerFile as registerFileIntegrity } from './core/file-integrity/store.js';
|
|
22
23
|
import { checkAgentDir, isValidAid } from './aun/aid/validation.js';
|
|
23
24
|
import { normalizeAidDomain } from './aun/aid/domain.js';
|
|
24
25
|
import { isValidChannelName } from './core/channel-loader.js';
|
|
@@ -60,6 +61,18 @@ export function loadDaemonConfig() {
|
|
|
60
61
|
export function isRestartRecoveryEnabled(config = loadDaemonConfig()) {
|
|
61
62
|
return config.restartRecovery?.enabled === true;
|
|
62
63
|
}
|
|
64
|
+
export function isDelegationDiagnosticsEnabled() {
|
|
65
|
+
const config = cfgRead(ConfigTarget.Process, undefined, { cache: true });
|
|
66
|
+
const diagnostics = config?.delegationDiagnostics;
|
|
67
|
+
if (diagnostics !== undefined && (!diagnostics || typeof diagnostics !== 'object' || Array.isArray(diagnostics))) {
|
|
68
|
+
throw new Error('daemon.json.delegationDiagnostics must be an object');
|
|
69
|
+
}
|
|
70
|
+
const enabled = diagnostics?.enabled;
|
|
71
|
+
if (enabled !== undefined && typeof enabled !== 'boolean') {
|
|
72
|
+
throw new Error('daemon.json.delegationDiagnostics.enabled must be a boolean');
|
|
73
|
+
}
|
|
74
|
+
return enabled !== false;
|
|
75
|
+
}
|
|
63
76
|
export function isSkillWatchEnabled(config = loadDaemonConfig()) {
|
|
64
77
|
return config.skills?.watch === true;
|
|
65
78
|
}
|
|
@@ -92,6 +105,10 @@ export function saveDaemonConfig(value) {
|
|
|
92
105
|
|| current.aun?.defaultAidDomain !== value.aun?.defaultAidDomain;
|
|
93
106
|
const normalized = normalizeProcessConfigCompat(normalizeDaemonServices(stripLegacyAunGatewayConfig(value, configPath, message => logger.warn(message))));
|
|
94
107
|
atomicWriteJson(configPath, validateDaemonConfig({ ...normalized, $schema_version: currentVersion('daemon') }, validateAidDomain));
|
|
108
|
+
try {
|
|
109
|
+
registerFileIntegrity(configPath);
|
|
110
|
+
}
|
|
111
|
+
catch { /* best-effort */ }
|
|
95
112
|
}
|
|
96
113
|
function validateDaemonConfig(value, validateAidDomain) {
|
|
97
114
|
if (value.restartRecovery !== undefined) {
|
|
@@ -118,6 +135,19 @@ function validateDaemonConfig(value, validateAidDomain) {
|
|
|
118
135
|
throw new Error('daemon.json.skills.watch must be a boolean');
|
|
119
136
|
}
|
|
120
137
|
}
|
|
138
|
+
if (value.delegationDiagnostics !== undefined) {
|
|
139
|
+
if (!value.delegationDiagnostics || typeof value.delegationDiagnostics !== 'object' || Array.isArray(value.delegationDiagnostics)) {
|
|
140
|
+
throw new Error('daemon.json.delegationDiagnostics must be an object');
|
|
141
|
+
}
|
|
142
|
+
const unknownFields = Object.keys(value.delegationDiagnostics)
|
|
143
|
+
.filter(key => key !== 'enabled');
|
|
144
|
+
if (unknownFields.length > 0) {
|
|
145
|
+
throw new Error('daemon.json.delegationDiagnostics contains unsupported fields: ' + unknownFields.join(', '));
|
|
146
|
+
}
|
|
147
|
+
if (value.delegationDiagnostics.enabled !== undefined && typeof value.delegationDiagnostics.enabled !== 'boolean') {
|
|
148
|
+
throw new Error('daemon.json.delegationDiagnostics.enabled must be a boolean');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
121
151
|
const minEvolVersion = value.aun?.minEvolVersion;
|
|
122
152
|
if (minEvolVersion !== undefined && !parseStableSemver(minEvolVersion)) {
|
|
123
153
|
throw new Error('daemon.json.aun.minEvolVersion must use stable X.Y.Z format');
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { buildToolLifecycleEventKey, normalizeToolCorrelation } from './event-key.js';
|
|
5
5
|
// Logger rotation is hourly/daily, but the suffix is intentionally generic so
|
|
6
6
|
// this check also covers future rotation formats without changing the audit.
|
|
7
|
-
const STRUCTURED_LOG_RE = /^(?:daemon|events|channel-in|channel-out|messages|command-audit)(?:-[^.]+)?\.log$/;
|
|
7
|
+
const STRUCTURED_LOG_RE = /^(?:daemon|events|channel-in|channel-out|messages|command-audit|delegation-audit)(?:-[^.]+)?\.log$/;
|
|
8
8
|
const MIXED_TEXT_LOG_RE = /^daemon(?:-[^.]+)?\.log$/;
|
|
9
9
|
const LEGACY_JSONL_FILE = /\.jsonl$/i;
|
|
10
10
|
/** Validate and de-duplicate structured event streams by lifecycle identity. */
|
|
@@ -80,6 +80,8 @@ export function inspectStructuredLogs(logDir, options = {}) {
|
|
|
80
80
|
return { files: files.length, records: records.length, uniqueRecords: records.length - duplicates, duplicates, issues };
|
|
81
81
|
}
|
|
82
82
|
function structuredRecordKey(value) {
|
|
83
|
+
if (typeof value.eventId === 'string' && value.eventId.startsWith('del-audit-'))
|
|
84
|
+
return value.eventId;
|
|
83
85
|
const nested = nestedRecord(value);
|
|
84
86
|
const eventKey = firstDefined(value, nested, ['eventKey', 'event_key']);
|
|
85
87
|
const eventPhase = recordPhase(value);
|
|
@@ -6,6 +6,7 @@ import { logger } from '../../utils/logger.js';
|
|
|
6
6
|
import { canonicalizeEvolcoreCommandArgv } from '../permission/ec-command-parser.js';
|
|
7
7
|
import { MAIN_CLI_BIN } from '../../product.js';
|
|
8
8
|
import { DebugDelegationRegistry, DEBUG_TOKEN_PREFIX } from './debug-context.js';
|
|
9
|
+
import { DelegationDiagnostics, } from './delegation-diagnostics.js';
|
|
9
10
|
export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
|
|
10
11
|
export const CODEX_SESSION_TOKEN_ENV = 'EVOLCORE_CODEX_SESSION_TOKEN';
|
|
11
12
|
export const AGENT_DELEGATION_COMMAND_TTL_MS = 5 * 60_000;
|
|
@@ -22,10 +23,15 @@ export function readDelegationCredential(env = process.env) {
|
|
|
22
23
|
}
|
|
23
24
|
export class AgentDelegationRegistry {
|
|
24
25
|
runtimeEpoch = crypto.randomBytes(16).toString('hex');
|
|
26
|
+
diagnostics;
|
|
25
27
|
grantsByHash = new Map();
|
|
26
28
|
activeHashBySession = new Map();
|
|
29
|
+
sessionTokenHashesBySession = new Map();
|
|
27
30
|
approvedCommands = new Map();
|
|
28
31
|
debugRegistry = new DebugDelegationRegistry();
|
|
32
|
+
constructor() {
|
|
33
|
+
this.diagnostics = new DelegationDiagnostics(this.runtimeEpoch);
|
|
34
|
+
}
|
|
29
35
|
getRuntimeEpoch() {
|
|
30
36
|
return this.runtimeEpoch;
|
|
31
37
|
}
|
|
@@ -34,13 +40,45 @@ export class AgentDelegationRegistry {
|
|
|
34
40
|
if (input.executionIdentity && !isValidFullAccessExecutionIdentity(input.executionIdentity)) {
|
|
35
41
|
throw new Error('invalid fullaccess task execution identity');
|
|
36
42
|
}
|
|
37
|
-
this.revokeSession(input.sessionId);
|
|
43
|
+
this.revokeSession(input.sessionId, 'replaced');
|
|
38
44
|
const token = crypto.randomBytes(32).toString('base64url');
|
|
39
45
|
const tokenHash = hashDelegationToken(token);
|
|
40
46
|
this.grantsByHash.set(tokenHash, { ...input, tokenHash });
|
|
41
47
|
this.activeHashBySession.set(input.sessionId, tokenHash);
|
|
48
|
+
this.diagnostics.put('task.issued', {
|
|
49
|
+
kind: 'task', tokenHash, sessionId: input.sessionId, taskId: input.taskId,
|
|
50
|
+
runtimeEpoch: this.runtimeEpoch, status: 'active', credentialType: 'task',
|
|
51
|
+
...(input.selfAid ? { selfAid: input.selfAid } : {}),
|
|
52
|
+
...(input.peerKey ? { peerKey: input.peerKey } : {}),
|
|
53
|
+
...(input.actorId ? { actorId: input.actorId } : {}),
|
|
54
|
+
...(input.issuedRole ? { issuedRole: input.issuedRole } : {}),
|
|
55
|
+
...(input.permissionMode ? { permissionMode: input.permissionMode } : {}),
|
|
56
|
+
...(input.messageId ? { messageId: input.messageId } : {}),
|
|
57
|
+
});
|
|
42
58
|
return token;
|
|
43
59
|
}
|
|
60
|
+
registerSessionCredential(input) {
|
|
61
|
+
const activeTokenHash = this.activeHashBySession.get(input.sessionId);
|
|
62
|
+
const grant = activeTokenHash ? this.grantsByHash.get(activeTokenHash) : undefined;
|
|
63
|
+
if (!grant || grant.taskId !== input.taskId || !input.sessionToken)
|
|
64
|
+
return false;
|
|
65
|
+
const tokenHash = hashDelegationToken(input.sessionToken);
|
|
66
|
+
let sessionTokens = this.sessionTokenHashesBySession.get(input.sessionId);
|
|
67
|
+
if (!sessionTokens) {
|
|
68
|
+
sessionTokens = new Set();
|
|
69
|
+
this.sessionTokenHashesBySession.set(input.sessionId, sessionTokens);
|
|
70
|
+
}
|
|
71
|
+
sessionTokens.add(tokenHash);
|
|
72
|
+
this.diagnostics.put('session.bound', {
|
|
73
|
+
kind: 'session', tokenHash,
|
|
74
|
+
sessionId: input.sessionId, taskId: input.taskId, runtimeEpoch: this.runtimeEpoch,
|
|
75
|
+
status: 'active', credentialType: 'codex-session', activeTokenHash,
|
|
76
|
+
threadId: input.threadId, selfAid: grant.selfAid, peerKey: grant.peerKey,
|
|
77
|
+
actorId: grant.actorId, issuedRole: grant.issuedRole, permissionMode: grant.permissionMode,
|
|
78
|
+
...(grant.messageId ? { messageId: grant.messageId } : {}),
|
|
79
|
+
});
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
44
82
|
/** 发行 debug token(dbg: 前缀),仅 debug 模式下调用 */
|
|
45
83
|
issueDebug(input) {
|
|
46
84
|
return this.debugRegistry.issue(input);
|
|
@@ -60,12 +98,7 @@ export class AgentDelegationRegistry {
|
|
|
60
98
|
}
|
|
61
99
|
validateToken(token, sessionId, commandHash, consumeApproval) {
|
|
62
100
|
if (!token) {
|
|
63
|
-
return
|
|
64
|
-
ok: false,
|
|
65
|
-
code: 'DELEGATION_REQUIRED',
|
|
66
|
-
reason: 'An active task delegation token is required',
|
|
67
|
-
diagnosticCause: 'missing_token',
|
|
68
|
-
};
|
|
101
|
+
return this.diagnostics.failure('missing_token', sessionId);
|
|
69
102
|
}
|
|
70
103
|
const tokenHash = hashDelegationToken(token);
|
|
71
104
|
const grant = this.grantsByHash.get(tokenHash);
|
|
@@ -77,10 +110,11 @@ export class AgentDelegationRegistry {
|
|
|
77
110
|
assertAgentPolicyAuthorization(grant.executionIdentity);
|
|
78
111
|
}
|
|
79
112
|
catch {
|
|
80
|
-
this.revokeSession(sessionId);
|
|
81
|
-
return
|
|
113
|
+
this.revokeSession(sessionId, 'policy_revoked');
|
|
114
|
+
return this.diagnostics.failure('task_revoked', sessionId, tokenHash);
|
|
82
115
|
}
|
|
83
116
|
}
|
|
117
|
+
this.diagnostics.accepted(sessionId, tokenHash, commandHash);
|
|
84
118
|
return { ok: true, grant };
|
|
85
119
|
}
|
|
86
120
|
const approved = commandHash
|
|
@@ -88,20 +122,9 @@ export class AgentDelegationRegistry {
|
|
|
88
122
|
: undefined;
|
|
89
123
|
if (approved)
|
|
90
124
|
return { ok: true, grant: approved };
|
|
91
|
-
const diagnosticCause =
|
|
92
|
-
? grant.sessionId !== sessionId
|
|
93
|
-
? 'session_mismatch'
|
|
94
|
-
: 'task_replaced'
|
|
95
|
-
: commandHash
|
|
96
|
-
? 'approved_command_unavailable'
|
|
97
|
-
: 'unknown_token';
|
|
125
|
+
const diagnosticCause = this.diagnostics.classify(tokenHash, sessionId, commandHash);
|
|
98
126
|
logger.warn(`[AgentDelegation] validate failed: cause=${diagnosticCause} session=${sessionId} grantSession=${grant?.sessionId ?? 'none'} taskId=${grant?.taskId ?? 'none'} hasCommandHash=${!!commandHash} tokenKnown=${!!grant} activeForSession=${this.activeHashBySession.get(sessionId) ?? 'none'} grantTokenHash=${grant?.tokenHash ?? 'none'}`);
|
|
99
|
-
return
|
|
100
|
-
ok: false,
|
|
101
|
-
code: 'INVALID_DELEGATION',
|
|
102
|
-
reason: 'The task delegation token is invalid, revoked, or belongs to another session',
|
|
103
|
-
diagnosticCause,
|
|
104
|
-
};
|
|
127
|
+
return this.diagnostics.failure(diagnosticCause, sessionId, tokenHash, commandHash);
|
|
105
128
|
}
|
|
106
129
|
armApprovedCommand(input) {
|
|
107
130
|
const activeTokenHash = this.activeHashBySession.get(input.sessionId);
|
|
@@ -115,10 +138,15 @@ export class AgentDelegationRegistry {
|
|
|
115
138
|
return false;
|
|
116
139
|
this.deleteExpiredApprovedCommands();
|
|
117
140
|
const sessionTokenHash = hashDelegationToken(sessionToken);
|
|
141
|
+
const credential = this.diagnostics.find('session', sessionTokenHash, input.sessionId);
|
|
142
|
+
if (credential?.taskId !== input.taskId || credential.runtimeEpoch !== this.runtimeEpoch) {
|
|
143
|
+
this.registerSessionCredential({ sessionToken, sessionId: input.sessionId, taskId: input.taskId });
|
|
144
|
+
}
|
|
118
145
|
const key = approvedCommandKey(sessionTokenHash, input.sessionId, input.commandHash);
|
|
119
146
|
const existing = this.approvedCommands.get(key);
|
|
120
147
|
const ttlMs = Math.max(1, Math.min(input.ttlMs ?? AGENT_DELEGATION_COMMAND_TTL_MS, AGENT_DELEGATION_COMMAND_TTL_MS));
|
|
121
148
|
this.approvedCommands.set(key, {
|
|
149
|
+
sessionTokenHash,
|
|
122
150
|
sessionId: input.sessionId,
|
|
123
151
|
taskId: input.taskId,
|
|
124
152
|
activeTokenHash,
|
|
@@ -130,25 +158,42 @@ export class AgentDelegationRegistry {
|
|
|
130
158
|
? existing.remainingUses + 1
|
|
131
159
|
: 1,
|
|
132
160
|
});
|
|
161
|
+
this.recordApproval('approval.issued', this.approvedCommands.get(key), 'active');
|
|
133
162
|
return true;
|
|
134
163
|
}
|
|
135
|
-
revokeTask(sessionId, taskId) {
|
|
164
|
+
revokeTask(sessionId, taskId, reason = 'revoked') {
|
|
136
165
|
const tokenHash = this.activeHashBySession.get(sessionId);
|
|
137
166
|
if (!tokenHash)
|
|
138
167
|
return;
|
|
139
168
|
const grant = this.grantsByHash.get(tokenHash);
|
|
140
169
|
if (!grant || grant.taskId !== taskId)
|
|
141
170
|
return;
|
|
142
|
-
this.
|
|
143
|
-
this.activeHashBySession.delete(sessionId);
|
|
144
|
-
this.deleteApprovedCommandsForSession(sessionId);
|
|
171
|
+
this.revokeSession(sessionId, reason);
|
|
145
172
|
}
|
|
146
|
-
revokeSession(sessionId) {
|
|
173
|
+
revokeSession(sessionId, reason = 'revoked') {
|
|
147
174
|
const tokenHash = this.activeHashBySession.get(sessionId);
|
|
148
|
-
|
|
175
|
+
for (const sessionTokenHash of this.sessionTokenHashesBySession.get(sessionId) ?? []) {
|
|
176
|
+
const credential = this.diagnostics.find('session', sessionTokenHash, sessionId);
|
|
177
|
+
if (credential)
|
|
178
|
+
this.diagnostics.put('session.revoked', {
|
|
179
|
+
...credential, status: reason === 'replaced' ? 'replaced' : 'revoked', revocationReason: reason,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
this.sessionTokenHashesBySession.delete(sessionId);
|
|
183
|
+
if (tokenHash) {
|
|
184
|
+
const grant = this.grantsByHash.get(tokenHash);
|
|
185
|
+
if (grant)
|
|
186
|
+
this.diagnostics.put('task.revoked', {
|
|
187
|
+
kind: 'task', tokenHash, sessionId, taskId: grant.taskId, runtimeEpoch: this.runtimeEpoch,
|
|
188
|
+
status: reason === 'replaced' ? 'replaced' : 'revoked', revocationReason: reason,
|
|
189
|
+
credentialType: 'task', selfAid: grant.selfAid, peerKey: grant.peerKey,
|
|
190
|
+
actorId: grant.actorId, issuedRole: grant.issuedRole, permissionMode: grant.permissionMode,
|
|
191
|
+
...(grant.messageId ? { messageId: grant.messageId } : {}),
|
|
192
|
+
});
|
|
149
193
|
this.grantsByHash.delete(tokenHash);
|
|
194
|
+
}
|
|
150
195
|
this.activeHashBySession.delete(sessionId);
|
|
151
|
-
this.deleteApprovedCommandsForSession(sessionId);
|
|
196
|
+
this.deleteApprovedCommandsForSession(sessionId, reason);
|
|
152
197
|
}
|
|
153
198
|
readApprovedCommand(sessionTokenHash, sessionId, commandHash, consume) {
|
|
154
199
|
if (!isDelegationCommandHash(commandHash))
|
|
@@ -158,6 +203,7 @@ export class AgentDelegationRegistry {
|
|
|
158
203
|
if (!approved)
|
|
159
204
|
return undefined;
|
|
160
205
|
if (approved.expiresAt <= Date.now()) {
|
|
206
|
+
this.recordApproval('approval.expired', approved, 'expired');
|
|
161
207
|
this.approvedCommands.delete(key);
|
|
162
208
|
return undefined;
|
|
163
209
|
}
|
|
@@ -166,30 +212,57 @@ export class AgentDelegationRegistry {
|
|
|
166
212
|
if (!grant
|
|
167
213
|
|| activeTokenHash !== approved.activeTokenHash
|
|
168
214
|
|| grant.taskId !== approved.taskId) {
|
|
215
|
+
this.recordApproval('approval.revoked', approved, 'replaced', 'replaced');
|
|
169
216
|
this.approvedCommands.delete(key);
|
|
170
217
|
return undefined;
|
|
171
218
|
}
|
|
219
|
+
this.diagnostics.accepted(sessionId, sessionTokenHash, commandHash);
|
|
172
220
|
if (consume) {
|
|
173
|
-
|
|
221
|
+
approved.remainingUses--;
|
|
222
|
+
this.recordApproval('approval.consumed', approved, approved.remainingUses <= 0 ? 'consumed' : 'active');
|
|
223
|
+
if (approved.remainingUses <= 0)
|
|
174
224
|
this.approvedCommands.delete(key);
|
|
175
|
-
else
|
|
176
|
-
approved.remainingUses--;
|
|
177
225
|
}
|
|
178
226
|
return grant;
|
|
179
227
|
}
|
|
180
228
|
deleteExpiredApprovedCommands() {
|
|
181
229
|
const now = Date.now();
|
|
182
230
|
for (const [key, approved] of this.approvedCommands) {
|
|
183
|
-
if (approved.expiresAt <= now)
|
|
231
|
+
if (approved.expiresAt <= now) {
|
|
232
|
+
this.recordApproval('approval.expired', approved, 'expired');
|
|
184
233
|
this.approvedCommands.delete(key);
|
|
234
|
+
}
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
|
-
deleteApprovedCommandsForSession(sessionId) {
|
|
237
|
+
deleteApprovedCommandsForSession(sessionId, reason) {
|
|
188
238
|
for (const [key, approved] of this.approvedCommands) {
|
|
189
|
-
if (approved.sessionId === sessionId)
|
|
239
|
+
if (approved.sessionId === sessionId) {
|
|
240
|
+
this.recordApproval('approval.revoked', approved, reason === 'replaced' ? 'replaced' : 'revoked', reason);
|
|
190
241
|
this.approvedCommands.delete(key);
|
|
242
|
+
}
|
|
191
243
|
}
|
|
192
244
|
}
|
|
245
|
+
recordApproval(event, approved, status, revocationReason) {
|
|
246
|
+
this.diagnostics.put(event, {
|
|
247
|
+
kind: 'approval', tokenHash: approved.sessionTokenHash, sessionId: approved.sessionId,
|
|
248
|
+
taskId: approved.taskId, runtimeEpoch: this.runtimeEpoch, status, credentialType: 'codex-session',
|
|
249
|
+
activeTokenHash: approved.activeTokenHash, commandHash: approved.commandHash,
|
|
250
|
+
expiresAt: approved.expiresAt, remainingUses: approved.remainingUses,
|
|
251
|
+
...(revocationReason ? { revocationReason } : {}),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
recordDiagnosticFailure(cause, sessionId, token, commandHash) {
|
|
255
|
+
const tokenHash = token ? hashDelegationToken(token) : undefined;
|
|
256
|
+
let diagnosticCause = cause;
|
|
257
|
+
if (cause === 'command_hash_mismatch') {
|
|
258
|
+
diagnosticCause = !tokenHash ? 'missing_token'
|
|
259
|
+
: sessionId ? this.diagnostics.classifyCommandHashMismatch(tokenHash, sessionId) : 'unknown_token';
|
|
260
|
+
}
|
|
261
|
+
return this.diagnostics.failure(diagnosticCause, sessionId, tokenHash, commandHash);
|
|
262
|
+
}
|
|
263
|
+
close() {
|
|
264
|
+
this.diagnostics.close();
|
|
265
|
+
}
|
|
193
266
|
}
|
|
194
267
|
export function authorizeDelegatedAunMsgSend(registry, input) {
|
|
195
268
|
if (!input.sessionId) {
|