evolcore 0.0.23 → 0.0.24
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 +50 -0
- package/README.md +3 -1
- package/bin/codex-managed-acl.ps1 +7 -2
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +17 -6
- package/dist/agents/baseagent-availability.js +4 -4
- package/dist/agents/baseagent.js +29 -1
- package/dist/agents/claude-runner.js +148 -13
- package/dist/agents/codex/app-server-client.js +18 -8
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +215 -0
- package/dist/agents/codex-runner.js +438 -59
- package/dist/agents/ecagent-runner.js +17 -3
- package/dist/agents/gemini-runner.js +60 -2
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +41 -0
- package/dist/aun/msg/managed-operation.js +6 -1
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +4 -5
- package/dist/channels/aun.js +147 -76
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +60 -20
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +148 -17
- package/dist/cli/index.js +2 -2
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/config/builtin-roles.js +2 -0
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +72 -19
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +10 -2
- package/dist/core/auth/remote-access-store.js +3 -2
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +87 -25
- package/dist/core/command/menu-catalog.js +3 -1
- package/dist/core/command/menu-handler.js +274 -70
- package/dist/core/command/menu-protocol.js +6 -2
- package/dist/core/command/slash-gate.js +1 -3
- package/dist/core/command/slash-handler.js +157 -48
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +73 -21
- package/dist/core/model/catalog-service.js +3 -1
- package/dist/core/model/model-catalog.js +10 -0
- package/dist/core/model/model-permission.js +4 -1
- package/dist/core/permission/approval-gateway.js +169 -33
- package/dist/core/permission/tool-policy.js +13 -6
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +174 -28
- package/dist/ipc.js +68 -21
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/error-utils.js +11 -4
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +24 -1
- package/dist/utils/windows-autostart.js +1 -1
- package/kits/templates/roles/admin.json +2 -0
- package/package.json +7 -3
- package/AGENTS.md +0 -62
- package/dist/utils/codex-cli.js +0 -43
|
@@ -26,6 +26,13 @@ const MAX_TOOL_OUTPUT = 200_000;
|
|
|
26
26
|
const MAX_SCAN_FILES = 5_000;
|
|
27
27
|
const MAX_SEARCH_RESULTS = 500;
|
|
28
28
|
const DEFAULT_BASH_TIMEOUT_MS = 120_000;
|
|
29
|
+
/**
|
|
30
|
+
* 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
|
|
31
|
+
* Ecagent/OpenAI API 不认识这些后缀,必须传入标准模型 ID
|
|
32
|
+
*/
|
|
33
|
+
function stripContextWindowSuffix(modelId) {
|
|
34
|
+
return modelId.replace(/\[(?:1m|200k)\]$/i, '');
|
|
35
|
+
}
|
|
29
36
|
class AsyncEventQueue {
|
|
30
37
|
values = [];
|
|
31
38
|
waiters = [];
|
|
@@ -714,7 +721,7 @@ export class EcagentRunner {
|
|
|
714
721
|
configuredHeaders: config.headers,
|
|
715
722
|
}),
|
|
716
723
|
};
|
|
717
|
-
this.model = config.model;
|
|
724
|
+
this.model = stripContextWindowSuffix(config.model);
|
|
718
725
|
this.effort = config.effort;
|
|
719
726
|
this.agentAid = runtime?.agentAid;
|
|
720
727
|
this.onSessionIdUpdate = callbacks?.onSessionIdUpdate;
|
|
@@ -724,7 +731,7 @@ export class EcagentRunner {
|
|
|
724
731
|
throw new Error('ecagent requires an EvolCore Agent AID');
|
|
725
732
|
return agentSessionsDir(this.agentAid);
|
|
726
733
|
}
|
|
727
|
-
setModel(model) { this.model = model; }
|
|
734
|
+
setModel(model) { this.model = stripContextWindowSuffix(model); }
|
|
728
735
|
getModel() { return this.model; }
|
|
729
736
|
resolveModelId(model) { return model; }
|
|
730
737
|
setEffort(effort) { this.effort = effort; }
|
|
@@ -995,6 +1002,13 @@ export class EcagentRunner {
|
|
|
995
1002
|
return runtime;
|
|
996
1003
|
}
|
|
997
1004
|
mapHarnessEvent(queue, event, stats, model, effort) {
|
|
1005
|
+
if (event.type === 'message_update'
|
|
1006
|
+
&& (event.assistantMessageEvent.type === 'thinking_start'
|
|
1007
|
+
|| event.assistantMessageEvent.type === 'thinking_delta'
|
|
1008
|
+
|| event.assistantMessageEvent.type === 'thinking_end')) {
|
|
1009
|
+
queue.push({ type: 'heartbeat', source: 'reasoning', turn: stats.numTurns + 1 });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
998
1012
|
if (event.type === 'message_update' && event.assistantMessageEvent.type === 'text_delta') {
|
|
999
1013
|
if (!stats.firstTextAt)
|
|
1000
1014
|
stats.firstTextAt = Date.now();
|
|
@@ -1202,7 +1216,7 @@ export class EcagentRunner {
|
|
|
1202
1216
|
? cached
|
|
1203
1217
|
: await this.openExistingRuntime(agentSessionId, cwd);
|
|
1204
1218
|
this.onCompactStart?.(sessionId);
|
|
1205
|
-
const compactModel = modelOverride?.model || this.model;
|
|
1219
|
+
const compactModel = stripContextWindowSuffix(modelOverride?.model || this.model);
|
|
1206
1220
|
const compactEffort = modelOverride?.effortMode === 'model_default'
|
|
1207
1221
|
? undefined
|
|
1208
1222
|
: modelOverride?.effort ?? this.effort;
|
|
@@ -24,6 +24,7 @@ import { normalizeExecutionPermissionMode, normalizePermissionMode } from '../co
|
|
|
24
24
|
import { workspaceContainsHClassPaths } from '../core/permission/protected-paths.js';
|
|
25
25
|
import { buildBubblewrapCommand } from '../core/permission/sandbox-runtime.js';
|
|
26
26
|
import { getManagedTaskTempDir } from '../cli/task-context.js';
|
|
27
|
+
import { auditPermissionLifecycle } from '../core/auth/authorization-audit.js';
|
|
27
28
|
// Strip ANSI escape codes from Gemini CLI text output.
|
|
28
29
|
// Gemini embeds raw terminal colors from tool stdout (e.g. vitest, npm)
|
|
29
30
|
// into its assistant text, unlike Claude SDK which strips them internally.
|
|
@@ -32,6 +33,13 @@ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?(?:\x1b\\|\x07)/g;
|
|
|
32
33
|
function stripAnsi(text) {
|
|
33
34
|
return text.replace(ANSI_RE, '');
|
|
34
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 规范化模型 ID:去掉 Claude Code SDK 专有的上下文窗口后缀 [1m]/[200k]
|
|
38
|
+
* Gemini API 不认识这些后缀,必须传入标准模型 ID
|
|
39
|
+
*/
|
|
40
|
+
function stripContextWindowSuffix(modelId) {
|
|
41
|
+
return modelId.replace(/\[(?:1m|200k)\]$/i, '');
|
|
42
|
+
}
|
|
35
43
|
// ── MIME → 扩展名映射 ──
|
|
36
44
|
const MIME_EXT = {
|
|
37
45
|
'image/png': '.png',
|
|
@@ -286,11 +294,11 @@ export class GeminiRunner {
|
|
|
286
294
|
permissionContexts = new Map();
|
|
287
295
|
constructor(config, callbacks) {
|
|
288
296
|
this.resolved = resolveGoogleConfig(config);
|
|
289
|
-
this.model = this.resolved.model;
|
|
297
|
+
this.model = stripContextWindowSuffix(this.resolved.model);
|
|
290
298
|
this.onSessionIdUpdate = callbacks.onSessionIdUpdate;
|
|
291
299
|
}
|
|
292
300
|
// ── ModelSwitcher ──
|
|
293
|
-
setModel(model) { this.model = model; }
|
|
301
|
+
setModel(model) { this.model = stripContextWindowSuffix(model); }
|
|
294
302
|
getModel() { return this.model; }
|
|
295
303
|
async listModels() { const catalog = await getCatalog(undefined, 'gemini'); assertCatalogSelectable(catalog); return catalog.models.map(m => m.id); }
|
|
296
304
|
// ── Effort (not applicable) ──
|
|
@@ -687,6 +695,56 @@ export class GeminiRunner {
|
|
|
687
695
|
logger.info(`[GeminiRunner] interaction.auto_resolved kind=${normalizeGeminiToolName(toolName)} session=${sessionId}`);
|
|
688
696
|
return;
|
|
689
697
|
}
|
|
698
|
+
const lifecycleRequestId = requestId ?? `gemini-approval-${crypto.randomUUID()}`;
|
|
699
|
+
const auditSummary = `${toolName} runtime authorization denied by Gemini headless policy`;
|
|
700
|
+
auditPermissionLifecycle({
|
|
701
|
+
phase: 'request',
|
|
702
|
+
requestId: lifecycleRequestId,
|
|
703
|
+
sessionId,
|
|
704
|
+
toolName,
|
|
705
|
+
decision: 'pending',
|
|
706
|
+
decisionSource: 'approval',
|
|
707
|
+
permissionMode: context.permissionMode,
|
|
708
|
+
agentAid: context.selfAid,
|
|
709
|
+
agentName: context.agentName,
|
|
710
|
+
role: context.role,
|
|
711
|
+
taskId: context.taskId,
|
|
712
|
+
summary: auditSummary,
|
|
713
|
+
});
|
|
714
|
+
auditPermissionLifecycle({
|
|
715
|
+
phase: 'response',
|
|
716
|
+
requestId: lifecycleRequestId,
|
|
717
|
+
sessionId,
|
|
718
|
+
toolName,
|
|
719
|
+
decision: 'deny',
|
|
720
|
+
decisionSource: 'policy',
|
|
721
|
+
policyCode: 'permission_mode_denied',
|
|
722
|
+
reason: auditSummary,
|
|
723
|
+
permissionMode: context.permissionMode,
|
|
724
|
+
agentAid: context.selfAid,
|
|
725
|
+
agentName: context.agentName,
|
|
726
|
+
role: context.role,
|
|
727
|
+
taskId: context.taskId,
|
|
728
|
+
summary: auditSummary,
|
|
729
|
+
response: 'deny',
|
|
730
|
+
});
|
|
731
|
+
auditPermissionLifecycle({
|
|
732
|
+
phase: 'final',
|
|
733
|
+
requestId: lifecycleRequestId,
|
|
734
|
+
sessionId,
|
|
735
|
+
toolName,
|
|
736
|
+
decision: 'deny',
|
|
737
|
+
decisionSource: 'policy',
|
|
738
|
+
policyCode: 'permission_mode_denied',
|
|
739
|
+
reason: auditSummary,
|
|
740
|
+
permissionMode: context.permissionMode,
|
|
741
|
+
agentAid: context.selfAid,
|
|
742
|
+
agentName: context.agentName,
|
|
743
|
+
role: context.role,
|
|
744
|
+
taskId: context.taskId,
|
|
745
|
+
summary: auditSummary,
|
|
746
|
+
response: 'deny',
|
|
747
|
+
});
|
|
690
748
|
try {
|
|
691
749
|
await context.recordExecutionAnomaly?.({
|
|
692
750
|
code: 'runtime_permission_denied',
|
|
@@ -63,7 +63,7 @@ export function usageForContext(usage) {
|
|
|
63
63
|
return lastIteration ?? usage;
|
|
64
64
|
}
|
|
65
65
|
/** Models whose base ID uses a 1M context window when sent to the SDK with [1m]. */
|
|
66
|
-
export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-4-8', 'claude-sonnet-4-6'];
|
|
66
|
+
export const ONE_M_CONTEXT_MODEL_PREFIXES = ['claude-opus-5', 'claude-opus-4-8', 'claude-sonnet-4-6'];
|
|
67
67
|
const ONE_M_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet'];
|
|
68
68
|
const CLAUDE_CONTEXT_MODEL_ALIASES = ['opus', 'sonnet', 'haiku'];
|
|
69
69
|
export function isClaudeContextUsageModel(model) {
|
|
@@ -29,8 +29,9 @@ function parseAid(value) {
|
|
|
29
29
|
return { ok: true, aid };
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* The daemon accepts only the
|
|
33
|
-
* tasks.
|
|
32
|
+
* The daemon accepts only the narrowly scoped AID commands exposed to managed
|
|
33
|
+
* tasks. Public lookup/get may name any valid AID; local show/publish remain
|
|
34
|
+
* subject to the self-AID boundary enforced by the command handler.
|
|
34
35
|
*/
|
|
35
36
|
export function resolveManagedAidOperation(argv) {
|
|
36
37
|
const args = argv[0] === 'aid' ? argv.slice(1) : argv;
|
|
@@ -67,7 +68,21 @@ export function resolveManagedAidOperation(argv) {
|
|
|
67
68
|
return {
|
|
68
69
|
ok: false,
|
|
69
70
|
code: 'NOT_ALLOWED',
|
|
70
|
-
reason: 'Managed AID operations only allow exact
|
|
71
|
+
reason: 'Managed AID operations only allow exact show, lookup, agentmd get, or agentmd put commands with an optional --format json suffix',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function managedAidShowViewForRole(role) {
|
|
75
|
+
return role === 'owner' || role === 'admin' ? 'full' : 'redacted';
|
|
76
|
+
}
|
|
77
|
+
export function redactAidShowResult(value) {
|
|
78
|
+
return {
|
|
79
|
+
aid: value.aid,
|
|
80
|
+
redacted: true,
|
|
81
|
+
hasAgentMd: value.hasAgentMd,
|
|
82
|
+
certExpiresAt: value.certExpiresAt,
|
|
83
|
+
certSubject: value.certSubject,
|
|
84
|
+
certExpired: value.certExpired,
|
|
85
|
+
agentMdSignature: value.agentMdSignature,
|
|
71
86
|
};
|
|
72
87
|
}
|
|
73
88
|
const defaultDependencies = () => ({
|
|
@@ -80,12 +95,19 @@ const defaultDependencies = () => ({
|
|
|
80
95
|
readFileSync: fs.readFileSync,
|
|
81
96
|
});
|
|
82
97
|
/** Execute a validated AID operation from the daemon-owned host filesystem. */
|
|
83
|
-
export async function executeManagedAidOperation(command,
|
|
98
|
+
export async function executeManagedAidOperation(command, options = {}) {
|
|
99
|
+
const { showView = 'full', ...overrides } = options;
|
|
84
100
|
const deps = { ...defaultDependencies(), ...overrides };
|
|
85
101
|
const aunPath = deps.root;
|
|
86
102
|
switch (command.kind) {
|
|
87
|
-
case 'show':
|
|
88
|
-
|
|
103
|
+
case 'show': {
|
|
104
|
+
const value = await deps.aidShow(command.aid, { aunPath });
|
|
105
|
+
return {
|
|
106
|
+
kind: command.kind,
|
|
107
|
+
aid: command.aid,
|
|
108
|
+
value: showView === 'redacted' ? redactAidShowResult(value) : value,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
89
111
|
case 'lookup':
|
|
90
112
|
return { kind: command.kind, aid: command.aid, value: await deps.aidLookup(command.aid) };
|
|
91
113
|
case 'agentmd.get':
|
package/dist/aun/aid/store.js
CHANGED
|
@@ -7,6 +7,7 @@ import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
|
|
|
7
7
|
// pages so a large backlog is drained without monopolizing the sync loop.
|
|
8
8
|
const FULL_GROUP_PULL_MAX_PAGES = 10;
|
|
9
9
|
const GROUP_PULL_PAGINATION_PATCH = Symbol('evolcore.groupPullPaginationPatch');
|
|
10
|
+
const RELIABLE_MESSAGE_DELIVERY_PATCH = Symbol('evolcore.reliableMessageDeliveryPatch');
|
|
10
11
|
/**
|
|
11
12
|
* Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
|
|
12
13
|
*
|
|
@@ -106,6 +107,46 @@ export function enableFullGroupPullPagination(client) {
|
|
|
106
107
|
target._pullGroupV2 = wrappedPull;
|
|
107
108
|
return true;
|
|
108
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Make SDK pull delivery wait until EvolCore's application handlers finish.
|
|
112
|
+
*
|
|
113
|
+
* fastaun 0.5.13 publishes message events with dispatcher.enqueue(), then
|
|
114
|
+
* advances its sequence tracker immediately. That is a transport watermark,
|
|
115
|
+
* so a daemon restart can acknowledge a message before the application has
|
|
116
|
+
* durably claimed it. The SDK keeps _publishAppEvent private, but the public
|
|
117
|
+
* client is intentionally adapted here in one place: preserve the SDK's
|
|
118
|
+
* normal publication path and wait for its dispatcher queue for pulled
|
|
119
|
+
* message events before the pull operation commits the sequence.
|
|
120
|
+
*/
|
|
121
|
+
export function enableReliableMessageDelivery(client) {
|
|
122
|
+
const target = client;
|
|
123
|
+
if (target[RELIABLE_MESSAGE_DELIVERY_PATCH])
|
|
124
|
+
return false;
|
|
125
|
+
const publish = target._publishAppEvent;
|
|
126
|
+
const dispatcher = target._dispatcher;
|
|
127
|
+
if (typeof publish !== 'function' || !dispatcher || typeof dispatcher.flush !== 'function')
|
|
128
|
+
return false;
|
|
129
|
+
const wrappedPublish = async function (event, payload, source, ...rest) {
|
|
130
|
+
const result = await publish.call(this, event, payload, source, ...rest);
|
|
131
|
+
// Pull/recovery paths must not advance SeqTracker until the application
|
|
132
|
+
// handlers have completed. Push delivery has no pull commit to gate and
|
|
133
|
+
// remains asynchronous as intended by the SDK.
|
|
134
|
+
const reliable = (event === 'message.received' || event === 'group.message_created')
|
|
135
|
+
&& source !== 'push'
|
|
136
|
+
&& source !== 'inline-push';
|
|
137
|
+
if (reliable)
|
|
138
|
+
await dispatcher.flush();
|
|
139
|
+
return result;
|
|
140
|
+
};
|
|
141
|
+
Object.defineProperty(target, RELIABLE_MESSAGE_DELIVERY_PATCH, {
|
|
142
|
+
configurable: false,
|
|
143
|
+
enumerable: false,
|
|
144
|
+
value: true,
|
|
145
|
+
writable: false,
|
|
146
|
+
});
|
|
147
|
+
target._publishAppEvent = wrappedPublish;
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
109
150
|
/** 加载本地 AID 值对象(用于离线签名/验签,无需连接)。load 失败抛 AidLoadError。 */
|
|
110
151
|
export function loadAid(store, aid) {
|
|
111
152
|
const r = store.load(aid);
|
|
@@ -797,7 +797,12 @@ export function resolveManagedFsOperation(argv) {
|
|
|
797
797
|
const destination = remoteOrError(positionals[1]);
|
|
798
798
|
if (!('groupId' in destination))
|
|
799
799
|
return destination;
|
|
800
|
-
|
|
800
|
+
// Windows absolute paths contain a drive-letter colon, but are local
|
|
801
|
+
// upload sources rather than managed group references.
|
|
802
|
+
const isWindowsAbsolutePath = /^[A-Za-z]:[\\/]/.test(positionals[0]);
|
|
803
|
+
const source = positionals[0].includes(':') && !isWindowsAbsolutePath
|
|
804
|
+
? remoteOrError(positionals[0])
|
|
805
|
+
: undefined;
|
|
801
806
|
if (source && !('groupId' in source))
|
|
802
807
|
return source;
|
|
803
808
|
if (source && source.groupId !== destination.groupId)
|
package/dist/aun/outbox.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
|
-
import
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { agentActivityOutboxPath, agentOutboxPath, legacyAgentActivityOutboxPath, legacyAgentOutboxPath, legacyFlatOutboxPath, } from '../paths.js';
|
|
3
4
|
import { atomicRead, atomicWrite } from '../utils/atomic-write.js';
|
|
4
5
|
import { isDeliveryTarget, isDeliveryTargetForChannel, sameDeliveryTarget } from '../core/message/message-utils.js';
|
|
5
6
|
export const DEFAULT_OUTBOX_MAX_ENTRIES = 200;
|
|
@@ -10,11 +11,13 @@ const MAX_TERMINAL_ENTRIES_PER_AID = 100;
|
|
|
10
11
|
const QUEUE_CONFIG = {
|
|
11
12
|
default: {
|
|
12
13
|
file: agentOutboxPath,
|
|
14
|
+
legacyFiles: aid => [legacyAgentOutboxPath(aid), legacyFlatOutboxPath(aid)],
|
|
13
15
|
maxEntries: DEFAULT_OUTBOX_MAX_ENTRIES,
|
|
14
16
|
defaultTtl: DEFAULT_OUTBOX_TTL_MS,
|
|
15
17
|
},
|
|
16
18
|
activity: {
|
|
17
19
|
file: agentActivityOutboxPath,
|
|
20
|
+
legacyFiles: aid => [legacyAgentActivityOutboxPath(aid)],
|
|
18
21
|
maxEntries: ACTIVITY_OUTBOX_MAX_ENTRIES,
|
|
19
22
|
defaultTtl: ACTIVITY_OUTBOX_TTL_MS,
|
|
20
23
|
},
|
|
@@ -28,6 +31,55 @@ export function defaultTtl(queue = 'default') {
|
|
|
28
31
|
function outboxFile(aid, queue = 'default') {
|
|
29
32
|
return queueConfig(queue).file(aid);
|
|
30
33
|
}
|
|
34
|
+
const migratedOutboxes = new Set();
|
|
35
|
+
function parseEntriesFile(file) {
|
|
36
|
+
try {
|
|
37
|
+
const content = atomicRead(file)?.trim();
|
|
38
|
+
if (!content)
|
|
39
|
+
return [];
|
|
40
|
+
return content.split('\n').map(line => {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(line);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}).filter((entry) => entry !== null);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Import old outbox layouts without deleting their recovery copies. */
|
|
54
|
+
function ensureOutboxMigrated(aid, queue) {
|
|
55
|
+
const canonical = outboxFile(aid, queue);
|
|
56
|
+
const key = canonical;
|
|
57
|
+
if (migratedOutboxes.has(key))
|
|
58
|
+
return;
|
|
59
|
+
const merged = fs.existsSync(canonical) ? parseEntriesFile(canonical) : [];
|
|
60
|
+
const ids = new Set(merged.map(entry => entry.id));
|
|
61
|
+
let changed = false;
|
|
62
|
+
for (const legacy of queueConfig(queue).legacyFiles(aid)) {
|
|
63
|
+
if (legacy === canonical || !fs.existsSync(legacy))
|
|
64
|
+
continue;
|
|
65
|
+
for (const entry of parseEntriesFile(legacy)) {
|
|
66
|
+
if (ids.has(entry.id))
|
|
67
|
+
continue;
|
|
68
|
+
ids.add(entry.id);
|
|
69
|
+
merged.push(entry);
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!changed) {
|
|
74
|
+
migratedOutboxes.add(key);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const content = merged.length > 0
|
|
78
|
+
? merged.map(entry => JSON.stringify(entry)).join('\n') + '\n'
|
|
79
|
+
: '';
|
|
80
|
+
atomicWrite(canonical, content);
|
|
81
|
+
migratedOutboxes.add(key);
|
|
82
|
+
}
|
|
31
83
|
function entryQueue(entry) {
|
|
32
84
|
return entry.queue ?? 'default';
|
|
33
85
|
}
|
|
@@ -50,25 +102,11 @@ export function isDeliveryForChannel(value, channelId) {
|
|
|
50
102
|
return isDeliveryTargetForChannel(value, channelId);
|
|
51
103
|
}
|
|
52
104
|
function readEntries(aid, queue = 'default') {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const content = atomicRead(file)?.trim();
|
|
56
|
-
if (!content)
|
|
57
|
-
return [];
|
|
58
|
-
return content.split('\n').map(line => {
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(line);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}).filter((e) => e !== null);
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
105
|
+
ensureOutboxMigrated(aid, queue);
|
|
106
|
+
return parseEntriesFile(outboxFile(aid, queue));
|
|
70
107
|
}
|
|
71
108
|
function writeEntries(aid, entries, queue = 'default') {
|
|
109
|
+
ensureOutboxMigrated(aid, queue);
|
|
72
110
|
const file = outboxFile(aid, queue);
|
|
73
111
|
const boundedEntries = trimFinalEntries(entries);
|
|
74
112
|
const content = boundedEntries.length > 0
|
package/dist/aun/rpc/client.js
CHANGED
|
@@ -9,11 +9,10 @@ export async function createShortConnection(aid, opts) {
|
|
|
9
9
|
return client.call(method, params);
|
|
10
10
|
},
|
|
11
11
|
group: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
revokeInvite: (params) => client.group.revokeInvite(params),
|
|
12
|
+
createInviteCode: (params) => client.group.createInviteCode(params),
|
|
13
|
+
listInviteCodes: (params) => client.group.listInviteCodes(params),
|
|
14
|
+
useInviteCode: (params) => client.group.useInviteCode(params),
|
|
15
|
+
revokeInviteCode: (params) => client.group.revokeInviteCode(params),
|
|
17
16
|
},
|
|
18
17
|
async close() {
|
|
19
18
|
try {
|