evolcore 0.0.31 → 0.0.33
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 +35 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +51 -24
- package/dist/agents/ecagent-runner.js +9 -13
- package/dist/agents/gemini-runner.js +4 -0
- package/dist/aun/aid/store.js +0 -81
- package/dist/aun/group/fs-download.js +1 -10
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/managed-operation.js +130 -32
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/aun/service-proxy.js +12 -18
- package/dist/aun/storage/index.js +29 -0
- package/dist/channels/aun.js +38 -137
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +64 -31
- package/dist/cli/agent-create.js +43 -24
- package/dist/cli/agent.js +16 -70
- package/dist/cli/aun-commands.js +512 -32
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +1 -2
- package/dist/cli/daemon-commands.js +82 -13
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/diag.js +2 -2
- package/dist/cli/handoff-command.js +18 -5
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +93 -12
- package/dist/cli/model.js +14 -138
- package/dist/cli/queue-command.js +51 -0
- package/dist/cli/response.js +5 -54
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +85 -3
- package/dist/cli/stats.js +10 -4
- package/dist/cli/task-command.js +1 -0
- package/dist/cli/task-context.js +16 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +76 -50
- package/dist/config/builtin-roles.js +9 -7
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/agent-ipc-registration.js +65 -22
- package/dist/core/auth/agent-delegation.js +42 -2
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-authorization.js +1 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +44 -3
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +224 -45
- package/dist/core/command/diag-service.js +5 -1
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/ipc-registration.js +38 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +210 -9
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +207 -8
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +131 -3
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +379 -69
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/permission/sandbox-runtime.js +14 -1
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/task/task-recovery-service.js +2 -0
- package/dist/core/task/task-resume-operation.js +11 -0
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +10 -7
- package/dist/ipc.js +234 -17
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +59 -29
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/ipc-service.js +49 -35
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/script-sandbox.js +58 -77
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/AUTHORING.md +1 -1
- package/kits/docs/channels/aun.md +8 -0
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -2
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/evolcore/trigger.md +2 -2
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +0 -9
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +7 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +2 -1
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
package/dist/channels/daemon.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendExpireEntry, recordNextLaunchCleanup, cleanupPreviousByKind, runExpireCleanup, cleanupSessionRecord, } from '../core/background-session/session-cleanup.js';
|
|
1
2
|
import { ExecutionTimeoutError } from '../utils/error-utils.js';
|
|
2
3
|
import { normalizeExecutionMode } from '../trigger/validation.js';
|
|
3
4
|
import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
|
|
@@ -17,6 +18,7 @@ export class DaemonChannel {
|
|
|
17
18
|
thread: true,
|
|
18
19
|
};
|
|
19
20
|
pending = new Map();
|
|
21
|
+
bgPending = new Map();
|
|
20
22
|
watchdogMs;
|
|
21
23
|
totalExecutionMs;
|
|
22
24
|
constructor(sessionManager, messageQueue, opts = {}) {
|
|
@@ -26,6 +28,8 @@ export class DaemonChannel {
|
|
|
26
28
|
this.totalExecutionMs = normalizeTotalExecutionMs(opts.totalExecutionMs);
|
|
27
29
|
}
|
|
28
30
|
async converse(prompt, ctx) {
|
|
31
|
+
// 启动前跑一次 expire 扫描(成本低,只读少量小文件)
|
|
32
|
+
void runExpireCleanup(this.sessionManager).catch(() => { });
|
|
29
33
|
const runId = ctx.runId;
|
|
30
34
|
const pendingId = ctx.attemptId ?? runId;
|
|
31
35
|
const messageId = pendingId;
|
|
@@ -41,6 +45,11 @@ export class DaemonChannel {
|
|
|
41
45
|
? undefined
|
|
42
46
|
: setTimeout(() => this.totalExecutionTimeout(pendingId, remainingExecutionMs), remainingExecutionMs);
|
|
43
47
|
totalExecutionTimer?.unref?.();
|
|
48
|
+
// trigger 清理策略:once → expire(3天),其他 → expire(30天)
|
|
49
|
+
const isOnce = ctx.trigger.source?.type === 'once';
|
|
50
|
+
const triggerCleanupPolicy = isOnce
|
|
51
|
+
? { mode: 'expire', ttlMs: 3 * 24 * 3600 * 1000 }
|
|
52
|
+
: { mode: 'expire', ttlMs: 30 * 24 * 3600 * 1000 };
|
|
44
53
|
this.pending.set(pendingId, {
|
|
45
54
|
resolve,
|
|
46
55
|
startedAt,
|
|
@@ -52,6 +61,11 @@ export class DaemonChannel {
|
|
|
52
61
|
abortSignal: ctx.signal,
|
|
53
62
|
runId,
|
|
54
63
|
acc: { text: '', files: [], anomalies: [], toolCallCount: 0, seenToolCallIds: new Set() },
|
|
64
|
+
cleanupPolicy: triggerCleanupPolicy,
|
|
65
|
+
channelId: ctx.session.channelId,
|
|
66
|
+
agentAid: ctx.trigger.agentAid,
|
|
67
|
+
kind: `trigger:${ctx.trigger.id}`,
|
|
68
|
+
sessionId: ctx.session.id,
|
|
55
69
|
});
|
|
56
70
|
});
|
|
57
71
|
const message = {
|
|
@@ -85,8 +99,13 @@ export class DaemonChannel {
|
|
|
85
99
|
originChannelKey: ctx.trigger.origin?.channelKey,
|
|
86
100
|
originChannelType: ctx.trigger.origin?.channelType
|
|
87
101
|
?? (ctx.trigger.origin?.channelKey ? channelTypeFromKey(ctx.trigger.origin.channelKey) : undefined),
|
|
102
|
+
originChatType: ctx.trigger.origin?.delivery?.chatType,
|
|
103
|
+
originConversationId: ctx.trigger.origin?.delivery?.chatType === 'group'
|
|
104
|
+
? ctx.trigger.origin.channelId
|
|
105
|
+
: ctx.trigger.origin?.peerId,
|
|
88
106
|
originControl: ctx.trigger.origin?.channelKey === 'daemon',
|
|
89
107
|
authorizedBy: ctx.trigger.authorizedBy,
|
|
108
|
+
labels: ctx.trigger.labels,
|
|
90
109
|
},
|
|
91
110
|
};
|
|
92
111
|
const onAbort = () => {
|
|
@@ -128,6 +147,15 @@ export class DaemonChannel {
|
|
|
128
147
|
return promise;
|
|
129
148
|
}
|
|
130
149
|
async send(envelope, payload) {
|
|
150
|
+
// bg-session 路径:优先查 bgPending
|
|
151
|
+
const bgRunId = this.bgRunIdFromEnvelope(envelope);
|
|
152
|
+
if (bgRunId) {
|
|
153
|
+
const bgSlot = this.bgPending.get(bgRunId);
|
|
154
|
+
if (bgSlot && !bgSlot.terminated) {
|
|
155
|
+
this.handleBgSessionPayload(bgRunId, bgSlot, payload);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
131
159
|
const runId = this.runIdFromEnvelope(envelope);
|
|
132
160
|
if (!runId)
|
|
133
161
|
return;
|
|
@@ -270,6 +298,11 @@ export class DaemonChannel {
|
|
|
270
298
|
const pendingId = metadata?.triggerAttemptId ?? metadata?.triggerRunId;
|
|
271
299
|
return typeof pendingId === 'string' ? pendingId : undefined;
|
|
272
300
|
}
|
|
301
|
+
bgRunIdFromEnvelope(envelope) {
|
|
302
|
+
const metadata = envelope.replyContext?.metadata;
|
|
303
|
+
const runId = metadata?.bgSessionRunId;
|
|
304
|
+
return typeof runId === 'string' ? runId : undefined;
|
|
305
|
+
}
|
|
273
306
|
recordToolCall(slot, callId) {
|
|
274
307
|
if (typeof callId !== 'string' || !callId) {
|
|
275
308
|
slot.acc.toolCallCount += 1;
|
|
@@ -290,13 +323,14 @@ export class DaemonChannel {
|
|
|
290
323
|
?? (origin.channelKey ? channelTypeFromKey(origin.channelKey) : undefined);
|
|
291
324
|
if (!channelType)
|
|
292
325
|
return undefined;
|
|
326
|
+
const chatType = origin.delivery?.chatType ?? 'private';
|
|
293
327
|
return resolvePeerRoleDetail({
|
|
294
328
|
selfAid: trigger.agentAid,
|
|
295
329
|
channelKey: origin.channelKey,
|
|
296
330
|
channelType,
|
|
297
|
-
chatType
|
|
331
|
+
chatType,
|
|
298
332
|
actorId: origin.peerId,
|
|
299
|
-
conversationId: origin.peerId,
|
|
333
|
+
conversationId: chatType === 'group' ? origin.channelId : origin.peerId,
|
|
300
334
|
}).effectiveRole ?? undefined;
|
|
301
335
|
}
|
|
302
336
|
finish(runId, status, metadata) {
|
|
@@ -346,6 +380,29 @@ export class DaemonChannel {
|
|
|
346
380
|
toolCallCount: Math.max(slot.acc.toolCallCount, completedToolCallCount),
|
|
347
381
|
},
|
|
348
382
|
});
|
|
383
|
+
// trigger 清理策略
|
|
384
|
+
const policy = slot.cleanupPolicy;
|
|
385
|
+
if (policy.mode === 'immediate') {
|
|
386
|
+
void cleanupSessionRecord({ sessionId: slot.sessionId, channelId: slot.channelId, agentAid: slot.agentAid }, this.sessionManager);
|
|
387
|
+
}
|
|
388
|
+
else if (policy.mode === 'on_next_launch') {
|
|
389
|
+
recordNextLaunchCleanup(slot.kind, {
|
|
390
|
+
channelId: slot.channelId,
|
|
391
|
+
sessionId: slot.sessionId,
|
|
392
|
+
agentAid: slot.agentAid,
|
|
393
|
+
recordedAt: Date.now(),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
else if (policy.mode === 'expire') {
|
|
397
|
+
appendExpireEntry({
|
|
398
|
+
expireAt: Date.now() + policy.ttlMs,
|
|
399
|
+
sessionId: slot.sessionId,
|
|
400
|
+
channelId: slot.channelId,
|
|
401
|
+
kind: slot.kind,
|
|
402
|
+
agentAid: slot.agentAid,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// 'keep' 模式:不做任何处理
|
|
349
406
|
}
|
|
350
407
|
fail(runId, reason, text) {
|
|
351
408
|
const slot = this.pending.get(runId);
|
|
@@ -412,6 +469,204 @@ export class DaemonChannel {
|
|
|
412
469
|
}
|
|
413
470
|
queue.cancelActive?.(messageId);
|
|
414
471
|
}
|
|
472
|
+
// ── bg-session 生命周期 ──
|
|
473
|
+
handleBgSessionPayload(runId, slot, payload) {
|
|
474
|
+
if (slot.terminated)
|
|
475
|
+
return;
|
|
476
|
+
switch (payload.kind) {
|
|
477
|
+
case 'result.text':
|
|
478
|
+
if (payload.isFinal)
|
|
479
|
+
slot.text = payload.text;
|
|
480
|
+
else
|
|
481
|
+
slot.text += payload.text;
|
|
482
|
+
break;
|
|
483
|
+
case 'status.completed':
|
|
484
|
+
this.finishBgSession(runId, slot, 'completed');
|
|
485
|
+
break;
|
|
486
|
+
case 'result.error':
|
|
487
|
+
this.finishBgSession(runId, slot, 'error', payload.text ?? payload.reason ?? 'unknown error');
|
|
488
|
+
break;
|
|
489
|
+
case 'system.error':
|
|
490
|
+
this.finishBgSession(runId, slot, 'error', payload.text ?? payload.subtype ?? 'unknown error');
|
|
491
|
+
break;
|
|
492
|
+
case 'status.error':
|
|
493
|
+
this.finishBgSession(runId, slot, 'error', payload.metadata?.message ?? 'status error');
|
|
494
|
+
break;
|
|
495
|
+
case 'status.interrupted':
|
|
496
|
+
{
|
|
497
|
+
const reason = typeof payload.metadata?.reason === 'string'
|
|
498
|
+
? payload.metadata.reason
|
|
499
|
+
: 'interrupted';
|
|
500
|
+
const outcome = slot.abortSignal?.aborted ? 'cancelled' : 'interrupted';
|
|
501
|
+
this.finishBgSession(runId, slot, outcome, reason);
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
case 'status.timeout':
|
|
505
|
+
this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
|
|
506
|
+
break;
|
|
507
|
+
default:
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
finishBgSession(runId, slot, outcome, error) {
|
|
512
|
+
if (slot.terminated)
|
|
513
|
+
return;
|
|
514
|
+
slot.terminated = true;
|
|
515
|
+
if (slot.watchdogTimer)
|
|
516
|
+
clearTimeout(slot.watchdogTimer);
|
|
517
|
+
if (slot.abortListener)
|
|
518
|
+
slot.abortSignal?.removeEventListener('abort', slot.abortListener);
|
|
519
|
+
this.bgPending.delete(runId);
|
|
520
|
+
// 会话结束后按清理策略处理
|
|
521
|
+
const policy = slot.cleanupPolicy;
|
|
522
|
+
if (policy.mode === 'immediate') {
|
|
523
|
+
void cleanupSessionRecord({ sessionId: slot.sessionId, channelId: slot.channelId, agentAid: slot.agentAid }, this.sessionManager);
|
|
524
|
+
}
|
|
525
|
+
else if (policy.mode === 'on_next_launch') {
|
|
526
|
+
recordNextLaunchCleanup(slot.kind, {
|
|
527
|
+
channelId: slot.channelId,
|
|
528
|
+
sessionId: slot.sessionId,
|
|
529
|
+
agentAid: slot.agentAid,
|
|
530
|
+
recordedAt: Date.now(),
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
else if (policy.mode === 'expire') {
|
|
534
|
+
appendExpireEntry({
|
|
535
|
+
expireAt: Date.now() + policy.ttlMs,
|
|
536
|
+
sessionId: slot.sessionId,
|
|
537
|
+
channelId: slot.channelId,
|
|
538
|
+
kind: slot.kind,
|
|
539
|
+
agentAid: slot.agentAid,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
// 'keep' 模式:不做任何处理
|
|
543
|
+
slot.resolve({
|
|
544
|
+
sessionId: slot.sessionId,
|
|
545
|
+
outcome,
|
|
546
|
+
...(slot.text ? { text: slot.text } : {}),
|
|
547
|
+
...(error ? { error } : {}),
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 拉起一个独立后台会话。会话创建、首条消息入队后即返回 handle;
|
|
552
|
+
* 会话结束由 handle.result 通知。实现 BackgroundSessionLauncher 接口。
|
|
553
|
+
* 详见 docs/background-session/background-session-launcher.md
|
|
554
|
+
*/
|
|
555
|
+
async launchBackgroundSession(spec) {
|
|
556
|
+
const runId = `bg-${spec.kind}-${spec.instanceId}`;
|
|
557
|
+
const channelId = `bg:${spec.kind}:${spec.instanceId}`;
|
|
558
|
+
const cleanupPolicy = spec.cleanupPolicy ?? { mode: 'on_next_launch' };
|
|
559
|
+
// 启动前清理:on_next_launch 清理同 kind 历史,expire 扫描到期条目
|
|
560
|
+
if (cleanupPolicy.mode === 'on_next_launch') {
|
|
561
|
+
await cleanupPreviousByKind(spec.kind, this.sessionManager);
|
|
562
|
+
}
|
|
563
|
+
else if (cleanupPolicy.mode !== 'keep') {
|
|
564
|
+
// immediate 和 expire 模式都在启动时跑一次 expire 扫描
|
|
565
|
+
await runExpireCleanup(this.sessionManager);
|
|
566
|
+
}
|
|
567
|
+
const session = await this.sessionManager.getOrCreateSession(this.channelName, channelId, spec.projectPath, channelId, // threadId,每次独立,保证 by_run 语义
|
|
568
|
+
{
|
|
569
|
+
channelKey: this.channelKey,
|
|
570
|
+
peerId: `bg:${spec.kind}`,
|
|
571
|
+
peerName: spec.kind,
|
|
572
|
+
peerType: 'system',
|
|
573
|
+
bgKind: spec.kind,
|
|
574
|
+
bgInstanceId: spec.instanceId,
|
|
575
|
+
...(spec.metadata ?? {}),
|
|
576
|
+
}, spec.kind, undefined, 'private', spec.baseagent, spec.agentAid, this.channelName, 'system');
|
|
577
|
+
const promise = new Promise((resolve) => {
|
|
578
|
+
const slot = {
|
|
579
|
+
resolve,
|
|
580
|
+
sessionId: session.id,
|
|
581
|
+
channelId,
|
|
582
|
+
kind: spec.kind,
|
|
583
|
+
startedAt: Date.now(),
|
|
584
|
+
abortSignal: spec.signal,
|
|
585
|
+
text: '',
|
|
586
|
+
terminated: false,
|
|
587
|
+
cleanupPolicy,
|
|
588
|
+
agentAid: spec.agentAid,
|
|
589
|
+
watchdogTimer: setTimeout(() => {
|
|
590
|
+
this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
|
|
591
|
+
}, this.watchdogMs),
|
|
592
|
+
};
|
|
593
|
+
slot.watchdogTimer?.unref?.();
|
|
594
|
+
if (spec.signal) {
|
|
595
|
+
const onAbort = () => {
|
|
596
|
+
void this.cancelDelivery(runId).catch(() => { });
|
|
597
|
+
};
|
|
598
|
+
slot.abortListener = onAbort;
|
|
599
|
+
spec.signal.addEventListener('abort', onAbort, { once: true });
|
|
600
|
+
if (spec.signal.aborted)
|
|
601
|
+
onAbort();
|
|
602
|
+
}
|
|
603
|
+
this.bgPending.set(runId, slot);
|
|
604
|
+
});
|
|
605
|
+
const message = {
|
|
606
|
+
channel: this.channelName,
|
|
607
|
+
channelType: this.channelName,
|
|
608
|
+
channelId: session.channelId,
|
|
609
|
+
selfAID: spec.agentAid,
|
|
610
|
+
baseagent: spec.baseagent,
|
|
611
|
+
threadId: session.threadId || '',
|
|
612
|
+
chatType: 'private',
|
|
613
|
+
peerId: `bg:${spec.kind}`,
|
|
614
|
+
peerName: spec.kind,
|
|
615
|
+
peerType: 'system',
|
|
616
|
+
content: spec.prompt,
|
|
617
|
+
messageId: runId,
|
|
618
|
+
timestamp: Date.now(),
|
|
619
|
+
source: 'bg-session',
|
|
620
|
+
bgSessionMeta: {
|
|
621
|
+
kind: spec.kind,
|
|
622
|
+
runId,
|
|
623
|
+
instanceId: spec.instanceId,
|
|
624
|
+
boundSessionId: session.id,
|
|
625
|
+
},
|
|
626
|
+
replyContext: {
|
|
627
|
+
metadata: { bgSessionRunId: runId },
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
void this.messageQueue.enqueue?.(session.id, message, spec.projectPath, {
|
|
631
|
+
interruptible: false,
|
|
632
|
+
agentName: spec.agentAid,
|
|
633
|
+
sessionKeyField: session.sessionKey,
|
|
634
|
+
selfAID: spec.agentAid,
|
|
635
|
+
});
|
|
636
|
+
let settled = false;
|
|
637
|
+
void promise.then(() => { settled = true; });
|
|
638
|
+
const sessionId = session.id;
|
|
639
|
+
return {
|
|
640
|
+
sessionId,
|
|
641
|
+
result: promise,
|
|
642
|
+
waitForAgentSessionId: (opts) => this.waitForAgentSessionId(sessionId, () => settled, opts),
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 轮询 session 记录,等 runner 写入 agentSessionId。
|
|
647
|
+
* 会话刚创建时只有 evolcore sessionId,agentSessionId 要等 runner 初始化并开始第一轮才写入。
|
|
648
|
+
* 会话结束后再读一次即停止,避免对已结束的会话空等到超时。
|
|
649
|
+
*/
|
|
650
|
+
async waitForAgentSessionId(sessionId, isSettled, opts = {}) {
|
|
651
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 10_000);
|
|
652
|
+
const interval = opts.intervalMs ?? 250;
|
|
653
|
+
while (true) {
|
|
654
|
+
const settledBeforeRead = isSettled();
|
|
655
|
+
try {
|
|
656
|
+
const id = (await this.sessionManager.getSessionById(sessionId))?.agentSessionId;
|
|
657
|
+
if (id)
|
|
658
|
+
return id;
|
|
659
|
+
}
|
|
660
|
+
catch { /* 会话记录写到一半,下一轮再读 */ }
|
|
661
|
+
if (settledBeforeRead || Date.now() >= deadline)
|
|
662
|
+
return undefined;
|
|
663
|
+
await new Promise(r => setTimeout(r, interval));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/** 实现 BackgroundSessionLauncher 接口 */
|
|
667
|
+
launch(spec) {
|
|
668
|
+
return this.launchBackgroundSession(spec);
|
|
669
|
+
}
|
|
415
670
|
}
|
|
416
671
|
function isObject(value) {
|
|
417
672
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -41,6 +41,30 @@ function formatBytes(bytes) {
|
|
|
41
41
|
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
42
42
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
43
43
|
}
|
|
44
|
+
/** 终端显示宽度:CJK 及全角字符占 2 列,ASCII/拉丁占 1 列 */
|
|
45
|
+
function displayWidth(s) {
|
|
46
|
+
let w = 0;
|
|
47
|
+
for (const ch of s) {
|
|
48
|
+
const cp = ch.codePointAt(0);
|
|
49
|
+
if ((cp >= 0x1100 && cp <= 0x115F) ||
|
|
50
|
+
(cp >= 0x2E80 && cp <= 0x303E) ||
|
|
51
|
+
(cp >= 0x3040 && cp <= 0x33FF) ||
|
|
52
|
+
(cp >= 0x3400 && cp <= 0x9FFF) ||
|
|
53
|
+
(cp >= 0xAC00 && cp <= 0xD7AF) ||
|
|
54
|
+
(cp >= 0xF900 && cp <= 0xFAFF) ||
|
|
55
|
+
(cp >= 0xFF01 && cp <= 0xFF60) ||
|
|
56
|
+
(cp >= 0xFFE0 && cp <= 0xFFE6)) {
|
|
57
|
+
w += 2;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
w += 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return w;
|
|
64
|
+
}
|
|
65
|
+
function padEndDisplay(s, targetWidth) {
|
|
66
|
+
return s + ' '.repeat(Math.max(0, targetWidth - displayWidth(s)));
|
|
67
|
+
}
|
|
44
68
|
// ==================== Agent ====================
|
|
45
69
|
export async function cmdAgent(args) {
|
|
46
70
|
args = canonicalizeEvolcoreCommandArgv(['ec', 'agent', ...args]).slice(2);
|
|
@@ -78,8 +102,8 @@ Commands:
|
|
|
78
102
|
persona show 查看当前 Agent 的 persona.md
|
|
79
103
|
persona --text <text> 用文本设置当前 Agent 的 persona.md
|
|
80
104
|
persona --file <path> 用 .md/.txt 文件设置当前 Agent 的 persona.md
|
|
81
|
-
welcome show
|
|
82
|
-
welcome --text <text> 保存
|
|
105
|
+
welcome show 查看 Agent 自主准备的 Bootstrap welcome 文案
|
|
106
|
+
welcome --text <text> 保存 Agent 自主准备的 Bootstrap welcome 文案
|
|
83
107
|
rename <aid> <name> 修改 agent.md 中的显示名称
|
|
84
108
|
ready <aid> 标记 bootstrap 完成,进入 active 状态
|
|
85
109
|
reload [aid] 热重载配置(无参数=全量 resync)
|
|
@@ -148,8 +172,7 @@ Options:
|
|
|
148
172
|
process.exitCode = 1;
|
|
149
173
|
return;
|
|
150
174
|
}
|
|
151
|
-
const
|
|
152
|
-
const result = await runManagedAgentOperation(managedArgs);
|
|
175
|
+
const result = await runManagedAgentOperation(args);
|
|
153
176
|
if (!result.ok) {
|
|
154
177
|
if (formatJson)
|
|
155
178
|
console.log(JSON.stringify(result));
|
|
@@ -221,25 +244,28 @@ Options:
|
|
|
221
244
|
// 表头跟随系统语言
|
|
222
245
|
const isChinese = isHostChinese();
|
|
223
246
|
const headers = isChinese
|
|
224
|
-
? { name: '名称', personal: 'Personal名', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', lastActive: '最后活跃' }
|
|
225
|
-
: { name: 'NAME', personal: 'PERSONAL', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', lastActive: 'LAST ACTIVE' };
|
|
226
|
-
//
|
|
227
|
-
let maxNameLen = headers.name
|
|
228
|
-
let maxPersonalLen = headers.personal
|
|
229
|
-
let maxStatusLen = headers.status
|
|
230
|
-
let maxChannelsLen = headers.channels
|
|
231
|
-
let maxProjectLen = headers.project
|
|
232
|
-
let maxBaseagentLen = headers.baseagent
|
|
247
|
+
? { name: '名称', personal: 'Personal名', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', model: '模型', lastActive: '最后活跃' }
|
|
248
|
+
: { name: 'NAME', personal: 'PERSONAL', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', model: 'MODEL', lastActive: 'LAST ACTIVE' };
|
|
249
|
+
// 计算各列实际需要的显示宽度(CJK 字符占 2 列)
|
|
250
|
+
let maxNameLen = displayWidth(headers.name);
|
|
251
|
+
let maxPersonalLen = displayWidth(headers.personal);
|
|
252
|
+
let maxStatusLen = displayWidth(headers.status);
|
|
253
|
+
let maxChannelsLen = displayWidth(headers.channels);
|
|
254
|
+
let maxProjectLen = displayWidth(headers.project);
|
|
255
|
+
let maxBaseagentLen = displayWidth(headers.baseagent);
|
|
256
|
+
let maxModelLen = displayWidth(headers.model);
|
|
233
257
|
for (const info of result.agents) {
|
|
234
|
-
maxNameLen = Math.max(maxNameLen, info.name
|
|
235
|
-
maxPersonalLen = Math.max(maxPersonalLen, (info.personalName || '—')
|
|
236
|
-
const
|
|
237
|
-
|
|
258
|
+
maxNameLen = Math.max(maxNameLen, displayWidth(info.name));
|
|
259
|
+
maxPersonalLen = Math.max(maxPersonalLen, displayWidth(info.personalName || '—'));
|
|
260
|
+
const lcSuffix = info.lifecycle && info.lifecycle !== 'active' ? ` (${info.lifecycle})` : '';
|
|
261
|
+
const displayedStatus = `${info.status || 'unknown'}${info.stale ? ' (stale)' : ''}${lcSuffix}`;
|
|
262
|
+
maxStatusLen = Math.max(maxStatusLen, displayWidth(displayedStatus));
|
|
238
263
|
const channelsStr = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
239
|
-
maxChannelsLen = Math.max(maxChannelsLen, channelsStr
|
|
264
|
+
maxChannelsLen = Math.max(maxChannelsLen, displayWidth(channelsStr));
|
|
240
265
|
const projectStr = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
241
|
-
maxProjectLen = Math.max(maxProjectLen, projectStr
|
|
242
|
-
maxBaseagentLen = Math.max(maxBaseagentLen, (info.baseagent || '—')
|
|
266
|
+
maxProjectLen = Math.max(maxProjectLen, displayWidth(projectStr));
|
|
267
|
+
maxBaseagentLen = Math.max(maxBaseagentLen, displayWidth(info.baseagent || '—'));
|
|
268
|
+
maxModelLen = Math.max(maxModelLen, displayWidth(info.model || '—'));
|
|
243
269
|
}
|
|
244
270
|
// 加 2 作为列间距
|
|
245
271
|
const colName = maxNameLen + 2;
|
|
@@ -248,18 +274,25 @@ Options:
|
|
|
248
274
|
const colChannels = maxChannelsLen + 1;
|
|
249
275
|
const colProject = maxProjectLen + 2;
|
|
250
276
|
const colBaseagent = maxBaseagentLen + 2;
|
|
251
|
-
|
|
252
|
-
|
|
277
|
+
const colModel = maxModelLen + 2;
|
|
278
|
+
console.log(padEndDisplay(headers.name, colName) + padEndDisplay(headers.personal, colPersonal) +
|
|
279
|
+
padEndDisplay(headers.status, colStatus) + padEndDisplay(headers.channels, colChannels) +
|
|
280
|
+
padEndDisplay(headers.project, colProject) + padEndDisplay(headers.baseagent, colBaseagent) +
|
|
281
|
+
padEndDisplay(headers.model, colModel) + headers.lastActive);
|
|
253
282
|
for (const info of result.agents) {
|
|
254
283
|
const name = info.name;
|
|
255
284
|
const personal = info.personalName || '—';
|
|
256
|
-
const
|
|
285
|
+
const lcSuffix = info.lifecycle && info.lifecycle !== 'active' ? ` (${info.lifecycle})` : '';
|
|
286
|
+
const status = `${info.status || 'unknown'}${info.stale ? ' (stale)' : ''}${lcSuffix}`;
|
|
257
287
|
const channels = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
258
288
|
const project = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
259
289
|
const baseagent = info.baseagent || '—';
|
|
290
|
+
const model = info.model || '—';
|
|
260
291
|
const lastActive = info.lastActivity ? formatTimeAgo(Date.now() - info.lastActivity) : '—';
|
|
261
|
-
console.log(name
|
|
262
|
-
|
|
292
|
+
console.log(padEndDisplay(name, colName) + padEndDisplay(personal, colPersonal) +
|
|
293
|
+
padEndDisplay(status, colStatus) + padEndDisplay(channels, colChannels) +
|
|
294
|
+
padEndDisplay(project, colProject) + padEndDisplay(baseagent, colBaseagent) +
|
|
295
|
+
padEndDisplay(model, colModel) + lastActive);
|
|
263
296
|
}
|
|
264
297
|
return;
|
|
265
298
|
}
|
|
@@ -671,7 +704,7 @@ Options:
|
|
|
671
704
|
process.exitCode = 1;
|
|
672
705
|
return;
|
|
673
706
|
}
|
|
674
|
-
const result = await agentPersona(aid, { text, file });
|
|
707
|
+
const result = await agentPersona(aid, { text, file, argv: args });
|
|
675
708
|
if (!result.ok) {
|
|
676
709
|
if (formatJson)
|
|
677
710
|
console.log(JSON.stringify(result));
|
|
@@ -698,7 +731,7 @@ Options:
|
|
|
698
731
|
console.log(`用法: ${MAIN_CLI_BIN} agent welcome show [--format json]
|
|
699
732
|
${MAIN_CLI_BIN} agent welcome --text <text> [--format json]
|
|
700
733
|
|
|
701
|
-
读取或保存当前 Agent 在 Bootstrap
|
|
734
|
+
读取或保存当前 Agent 在 Bootstrap 中自主准备的 welcome 文案。文案固定保存到 personal/welcome.md。`);
|
|
702
735
|
return;
|
|
703
736
|
}
|
|
704
737
|
const result = { ok: false, code: 'INVALID_ARGS', error: error.message };
|
|
@@ -720,7 +753,7 @@ Options:
|
|
|
720
753
|
process.exitCode = 1;
|
|
721
754
|
return;
|
|
722
755
|
}
|
|
723
|
-
const result = await agentWelcome(aid, { text });
|
|
756
|
+
const result = await agentWelcome(aid, { text, argv: args });
|
|
724
757
|
if (!result.ok) {
|
|
725
758
|
if (formatJson)
|
|
726
759
|
console.log(JSON.stringify(result));
|
|
@@ -756,7 +789,7 @@ Options:
|
|
|
756
789
|
console.error('用法: ec agent get <aid> <key>');
|
|
757
790
|
process.exit(1);
|
|
758
791
|
}
|
|
759
|
-
const result = await agentGet(aid, key);
|
|
792
|
+
const result = await agentGet(aid, key, { argv: args });
|
|
760
793
|
if (!result.ok) {
|
|
761
794
|
if (formatJson) {
|
|
762
795
|
console.log(JSON.stringify(result));
|
|
@@ -797,7 +830,7 @@ Options:
|
|
|
797
830
|
console.error('用法: ec agent set <aid> <key> <value>');
|
|
798
831
|
process.exit(1);
|
|
799
832
|
}
|
|
800
|
-
const result = await agentSet(aid, key, val);
|
|
833
|
+
const result = await agentSet(aid, key, val, { argv: args });
|
|
801
834
|
if (!result.ok) {
|
|
802
835
|
if (formatJson) {
|
|
803
836
|
console.log(JSON.stringify(result));
|
|
@@ -828,7 +861,7 @@ Options:
|
|
|
828
861
|
console.error('用法: ec agent ready <aid>');
|
|
829
862
|
process.exit(1);
|
|
830
863
|
}
|
|
831
|
-
const result = await agentReady(aid);
|
|
864
|
+
const result = await agentReady(aid, args);
|
|
832
865
|
if (!result.ok) {
|
|
833
866
|
if (formatJson) {
|
|
834
867
|
console.log(JSON.stringify(result));
|
package/dist/cli/agent-create.js
CHANGED
|
@@ -20,6 +20,7 @@ import { atomicWriteJson } from '../utils/atomic-write.js';
|
|
|
20
20
|
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
|
|
21
21
|
import { MAIN_CLI_BIN } from '../product.js';
|
|
22
22
|
import { uploadAvatarFile, uploadAvatarUrl } from '../utils/avatar-upload.js';
|
|
23
|
+
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
23
24
|
/** Parse the current Agent's Bootstrap welcome subcommand. */
|
|
24
25
|
export function parseAgentWelcomeArgs(args) {
|
|
25
26
|
const tail = args.at(-2) === '--format' && args.at(-1) === 'json' ? args.slice(0, -2) : args;
|
|
@@ -190,9 +191,9 @@ function projectSlug(project) {
|
|
|
190
191
|
.replace(/-+$/g, '');
|
|
191
192
|
return slug || 'agent';
|
|
192
193
|
}
|
|
193
|
-
export function resolveAgentCreatePlan(options) {
|
|
194
|
+
export function resolveAgentCreatePlan(options, projectCwd = process.cwd()) {
|
|
194
195
|
const aid = options.aid.trim();
|
|
195
|
-
const project = path.resolve(options.project ||
|
|
196
|
+
const project = path.resolve(projectCwd, options.project || '.');
|
|
196
197
|
const metadata = readProjectMetadata(project);
|
|
197
198
|
const defaults = loadDefaults();
|
|
198
199
|
const daemon = loadDaemonConfig();
|
|
@@ -940,6 +941,20 @@ export class AgentInitializationService {
|
|
|
940
941
|
await this.running.get(aid);
|
|
941
942
|
}
|
|
942
943
|
async settle(aid) { await this.running.get(aid); }
|
|
944
|
+
async wait(aid, seconds) {
|
|
945
|
+
const work = this.running.get(aid);
|
|
946
|
+
if (work && seconds > 0) {
|
|
947
|
+
let timer;
|
|
948
|
+
try {
|
|
949
|
+
await Promise.race([work, new Promise(resolve => { timer = setTimeout(resolve, seconds * 1000); })]);
|
|
950
|
+
}
|
|
951
|
+
finally {
|
|
952
|
+
if (timer)
|
|
953
|
+
clearTimeout(timer);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return this.status(aid);
|
|
957
|
+
}
|
|
943
958
|
launch(r) {
|
|
944
959
|
const aid = r.plan.aid;
|
|
945
960
|
const work = Promise.resolve().then(() => this.run(r)).catch(error => {
|
|
@@ -1167,33 +1182,37 @@ export function parseInitializationArgs(args) {
|
|
|
1167
1182
|
throw new Error('--wait must be an integer from 0 to 300');
|
|
1168
1183
|
return { aid: args[1], resume, dryRun: seen.has('--dry-run'), wait, values };
|
|
1169
1184
|
}
|
|
1170
|
-
export function resolveInitializationPlan(args) {
|
|
1185
|
+
export function resolveInitializationPlan(args, context) {
|
|
1171
1186
|
const parsed = parseInitializationArgs(args);
|
|
1172
1187
|
if (parsed.resume)
|
|
1173
1188
|
throw new Error('A resume request cannot supply a new plan');
|
|
1174
1189
|
const v = parsed.values;
|
|
1175
1190
|
if (v['--avatar-file'] !== undefined && v['--avatar-url'] !== undefined)
|
|
1176
1191
|
throw new Error('--avatar-file and --avatar-url are mutually exclusive');
|
|
1177
|
-
if (v['--avatar-file']?.startsWith('attachment:') && !process.env.EVOLCORE_SESSION_ID) {
|
|
1192
|
+
if (v['--avatar-file']?.startsWith('attachment:') && !context && !process.env.EVOLCORE_SESSION_ID) {
|
|
1178
1193
|
throw new Error('attachment:<id> avatar sources require a managed Agent task');
|
|
1179
1194
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1195
|
+
const persona = v['--persona-file'] !== undefined
|
|
1196
|
+
? readInitializationPersona(v['--persona-file'], !!context, context)
|
|
1197
|
+
: v['--persona-text'];
|
|
1182
1198
|
const p = resolveAgentCreatePlan({
|
|
1183
1199
|
aid: parsed.aid, project: v['--project'], baseagent: v['--baseagent'], ...parseAgentOwnerArgs(args),
|
|
1184
1200
|
avatarFile: v['--avatar-file'], avatarUrl: v['--avatar-url'],
|
|
1185
1201
|
name: v['--name'], description: v['--description'], tags: parseAgentTags(v['--tags']), type: v['--type'],
|
|
1186
|
-
});
|
|
1202
|
+
}, context?.projectCwd);
|
|
1187
1203
|
return {
|
|
1188
1204
|
aid: p.aid, project: p.project, baseagent: p.baseagent ?? '', owners: p.owners ?? (p.owner ? [p.owner] : []),
|
|
1189
1205
|
name: p.name, description: p.description, tags: p.tags, type: p.type,
|
|
1190
|
-
...(
|
|
1206
|
+
...(persona !== undefined ? { persona } : {}),
|
|
1191
1207
|
...(v['--avatar-file'] !== undefined ? { avatarFile: v['--avatar-file'] } : {}),
|
|
1192
1208
|
...(v['--avatar-url'] !== undefined ? { avatarUrl: v['--avatar-url'] } : {}),
|
|
1193
1209
|
};
|
|
1194
1210
|
}
|
|
1195
|
-
export function readInitializationPersona(file, managed
|
|
1196
|
-
|
|
1211
|
+
export function readInitializationPersona(file, managed, context = {
|
|
1212
|
+
projectCwd: managed ? readTaskRuntimeContextFromEnv()?.projectPath ?? process.cwd() : process.cwd(),
|
|
1213
|
+
tempDir: process.env.TMPDIR,
|
|
1214
|
+
}) {
|
|
1215
|
+
const resolved = path.resolve(context.projectCwd, file);
|
|
1197
1216
|
if (!/\.(md|txt)$/i.test(resolved))
|
|
1198
1217
|
throw new Error('persona file must be .md or .txt');
|
|
1199
1218
|
const stat = fs.lstatSync(resolved);
|
|
@@ -1201,8 +1220,8 @@ export function readInitializationPersona(file, managed) {
|
|
|
1201
1220
|
throw new Error('persona must be a regular text file of at most 64 KiB');
|
|
1202
1221
|
if (managed) {
|
|
1203
1222
|
const real = fs.realpathSync(resolved);
|
|
1204
|
-
const roots = [
|
|
1205
|
-
const allowed = roots.some(root => isSameOrDescendant(real, fs.realpathSync(root)));
|
|
1223
|
+
const roots = [context.projectCwd, context.tempDir].filter((root) => !!root && path.isAbsolute(root));
|
|
1224
|
+
const allowed = roots.some(root => fs.existsSync(root) && isSameOrDescendant(real, fs.realpathSync(root)));
|
|
1206
1225
|
if (!allowed || isHClassPath(real) || isLClassPath(real))
|
|
1207
1226
|
throw new Error('persona file must be inside the project or current TMPDIR');
|
|
1208
1227
|
}
|
|
@@ -1216,27 +1235,24 @@ export async function runInitializationCommand(args) {
|
|
|
1216
1235
|
const parsed = parseInitializationArgs(args);
|
|
1217
1236
|
const managed = !!process.env.EVOLCORE_SESSION_ID;
|
|
1218
1237
|
const argv = [...args];
|
|
1219
|
-
if (parsed.values['--persona-file'] !== undefined) {
|
|
1238
|
+
if (parsed.values['--persona-file'] !== undefined && !managed) {
|
|
1239
|
+
const persona = readInitializationPersona(parsed.values['--persona-file'], managed);
|
|
1220
1240
|
const at = argv.indexOf('--persona-file');
|
|
1221
|
-
argv.splice(at, 2, '--persona-text',
|
|
1241
|
+
argv.splice(at, 2, '--persona-text', persona);
|
|
1222
1242
|
}
|
|
1223
|
-
if (!parsed.resume) {
|
|
1243
|
+
if (!managed && !parsed.resume) {
|
|
1224
1244
|
const at = argv.indexOf('--project');
|
|
1225
1245
|
if (at < 0)
|
|
1226
1246
|
argv.push('--project', process.cwd());
|
|
1227
1247
|
else
|
|
1228
1248
|
argv[at + 1] = path.resolve(argv[at + 1]);
|
|
1229
1249
|
}
|
|
1250
|
+
if (managed) {
|
|
1251
|
+
const { runManagedAgentOperation } = await import('./agent.js');
|
|
1252
|
+
const result = await runManagedAgentOperation(argv, (parsed.wait + 10) * 1000);
|
|
1253
|
+
return initializationCommandResult(result);
|
|
1254
|
+
}
|
|
1230
1255
|
const request = async (poll) => {
|
|
1231
|
-
if (managed) {
|
|
1232
|
-
const { runManagedAgentOperation } = await import('./agent.js');
|
|
1233
|
-
const r = await runManagedAgentOperation(poll ? ['show', parsed.aid, '--format', 'json'] : argv);
|
|
1234
|
-
if (!poll || !r.ok || !r.initialization)
|
|
1235
|
-
return r;
|
|
1236
|
-
const init = r.initialization;
|
|
1237
|
-
return { ...init, ok: !['failed', 'cancelled'].includes(init.status), aid: parsed.aid,
|
|
1238
|
-
accepted: true, lifecycle: r.lifecycle, usable: init.complete && r.connection?.status === 'connected' };
|
|
1239
|
-
}
|
|
1240
1256
|
return await ipcQuery(resolvePaths().socket, {
|
|
1241
1257
|
type: 'evolagent.initialize', argv, poll,
|
|
1242
1258
|
}, 10_000) ?? { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Initialization requires a running daemon' };
|
|
@@ -1249,6 +1265,9 @@ export async function runInitializationCommand(args) {
|
|
|
1249
1265
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1250
1266
|
result = await request(true);
|
|
1251
1267
|
}
|
|
1268
|
+
return initializationCommandResult(result);
|
|
1269
|
+
}
|
|
1270
|
+
function initializationCommandResult(result) {
|
|
1252
1271
|
if (result.ok && !result.complete && result.accepted)
|
|
1253
1272
|
return { ...result, ok: false, code: 'INITIALIZE_PENDING', error: 'Initialization is still running; inspect agent show' };
|
|
1254
1273
|
if (result.complete && !result.usable)
|