evolcore 0.0.2 → 0.0.4
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 +93 -778
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +198 -48
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +203 -8
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/aun/outbox.js +8 -0
- package/dist/channels/aun.js +241 -116
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +1005 -260
- package/dist/channels/feishu.js +184 -56
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +1132 -164
- package/dist/cli/agent-command.js +2 -1
- package/dist/cli/aun-commands.js +98 -43
- package/dist/cli/bench.js +2 -2
- package/dist/cli/contact.js +71 -0
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +77 -214
- package/dist/cli/handoff-command.js +4 -3
- package/dist/cli/help.js +9 -5
- package/dist/cli/index.js +132 -116
- package/dist/cli/init-channel.js +242 -129
- package/dist/cli/init.js +70 -31
- package/dist/cli/model.js +2 -1
- package/dist/cli/net-check.js +2 -2
- package/dist/cli/queue-command.js +30 -6
- package/dist/cli/raw-key-input.js +25 -0
- package/dist/cli/response.js +5 -6
- package/dist/cli/restart-monitor.js +25 -1
- package/dist/cli/stats.js +6 -4
- package/dist/cli/trigger-command.js +146 -25
- package/dist/cli/version.js +6 -1
- package/dist/config/access-policy-domain.js +38 -0
- package/dist/config/access-policy.js +134 -0
- package/dist/config/builtin-role-templates.js +42 -17
- package/dist/config/builtin-roles.js +31 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +409 -29
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-alias.js +68 -0
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +622 -0
- package/dist/config/contact-book-v2-startup.js +35 -0
- package/dist/config/contact-book.js +185 -300
- package/dist/config/contact-operation-service.js +116 -0
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +136 -55
- package/dist/config/role-ranks.js +18 -0
- package/dist/config/role-service.js +16 -19
- package/dist/config/role-store.js +16 -5
- package/dist/config/roles.js +10 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -4
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +16 -1
- package/dist/core/auth/operation-authorizer.js +10 -0
- package/dist/core/auth/operation-catalog.js +105 -1
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +113 -21
- package/dist/core/command/connect-menu.js +599 -0
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +382 -100
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/role-menu.js +128 -29
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +186 -49
- package/dist/core/daemon-file-cache.js +40 -6
- package/dist/core/event-catalog.js +99 -0
- package/dist/core/evolagent-registry.js +4 -40
- package/dist/core/evolagent.js +20 -9
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +20 -14
- package/dist/core/message/inbound-admission.js +124 -0
- package/dist/core/message/message-bridge.js +670 -57
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +195 -10
- package/dist/core/message/message-utils.js +8 -2
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +642 -96
- package/dist/core/message/send-receipt.js +24 -0
- package/dist/core/message/stream-debouncer.js +11 -2
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +115 -38
- package/dist/core/protected-paths.js +38 -11
- package/dist/core/session/session-fs-store.js +44 -3
- package/dist/core/session/session-manager.js +86 -8
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +285 -94
- package/dist/ipc.js +114 -8
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/manager.js +3 -0
- package/dist/trigger/parser.js +126 -11
- package/dist/trigger/patch.js +12 -2
- package/dist/trigger/scheduler.js +444 -40
- package/dist/trigger/validation.js +13 -0
- package/dist/utils/aid-bind.js +43 -29
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/instance-registry.js +14 -7
- package/dist/utils/log-writer.js +46 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/media-cache.js +4 -1
- package/dist/utils/model-prices.jsonl +6 -3
- package/dist/utils/restart-safety.js +31 -0
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/dist/utils/system-memory.js +62 -0
- package/kits/docs/INDEX.md +2 -1
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +5 -3
- package/kits/docs/evolcore/agent.md +9 -1
- package/kits/docs/evolcore/aid.md +5 -2
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +61 -0
- package/kits/docs/evolcore/fs.md +9 -0
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +15 -6
- package/kits/docs/evolcore/model.md +4 -1
- package/kits/docs/evolcore/msg.md +12 -6
- package/kits/docs/evolcore/response.md +16 -21
- package/kits/docs/evolcore/rpc.md +2 -0
- package/kits/docs/evolcore/stats.md +15 -2
- package/kits/docs/evolcore/storage.md +1 -0
- package/kits/docs/evolcore/trigger.md +42 -10
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_manifest.json +12 -0
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +754 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +21 -7
- package/kits/schemas/agent-config.schema.6.json +322 -0
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +374 -0
- package/kits/schemas/contact-book.schema.2.json +43 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.5.json +47 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/role-config.schema.1.json +1 -0
- package/kits/schemas/role-registry.schema.1.json +2 -2
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +47 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/owner.json +1 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/commands.md +3 -1
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +7 -5
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/assets/brand/evolcore/README.md +0 -19
- package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
- package/assets/brand/evolcore/evolcore-app-icon.svg +0 -13
- package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
- package/assets/brand/evolcore/evolcore-brand-board.svg +0 -126
- package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.svg +0 -14
- package/assets/brand/evolcore/evolcore-logo.png +0 -0
- package/assets/brand/evolcore/evolcore-logo.svg +0 -14
- package/assets/brand/evolcore/evolcore-mark.png +0 -0
- package/assets/brand/evolcore/evolcore-mark.svg +0 -10
|
@@ -719,7 +719,7 @@ export class AgentRunner {
|
|
|
719
719
|
if (!permCtx?.adapter || !permCtx?.channelId) {
|
|
720
720
|
return this.handleAskUserQuestionFallback(sessionId, input, questions, options);
|
|
721
721
|
}
|
|
722
|
-
const adapterHasInteractionPath =
|
|
722
|
+
const adapterHasInteractionPath = permCtx.adapter.capabilities?.interaction === true;
|
|
723
723
|
if (!adapterHasInteractionPath || !permCtx.interactionRouter) {
|
|
724
724
|
return this.handleAskUserQuestionFallback(sessionId, input, questions, options);
|
|
725
725
|
}
|
|
@@ -756,6 +756,7 @@ export class AgentRunner {
|
|
|
756
756
|
},
|
|
757
757
|
channelId: permCtx.channelId,
|
|
758
758
|
sessionId,
|
|
759
|
+
initiatorId: permCtx.userId,
|
|
759
760
|
};
|
|
760
761
|
}
|
|
761
762
|
else {
|
|
@@ -783,6 +784,7 @@ export class AgentRunner {
|
|
|
783
784
|
},
|
|
784
785
|
channelId: permCtx.channelId,
|
|
785
786
|
sessionId,
|
|
787
|
+
initiatorId: permCtx.userId,
|
|
786
788
|
};
|
|
787
789
|
}
|
|
788
790
|
let cardSent = false;
|
|
@@ -797,7 +799,10 @@ export class AgentRunner {
|
|
|
797
799
|
replyContext: permCtx.replyContext,
|
|
798
800
|
});
|
|
799
801
|
const optionLines = q.options.map((o, idx) => ` ${idx + 1}. ${o.label}${o.description ? ` — ${o.description}` : ''}`).join('\n');
|
|
800
|
-
const
|
|
802
|
+
const answerHint = q.multiSelect
|
|
803
|
+
? '回复 /ask 1,2(多选用逗号分隔),或 /ask <自定义内容>'
|
|
804
|
+
: '回复 /ask 1,或 /ask <自定义内容>';
|
|
805
|
+
const fallbackText = `💬 ${q.header || q.question}\n${q.header ? q.question + '\n' : ''}${optionLines}\n\n${answerHint}`;
|
|
801
806
|
const result = await sendInteractionPayload(permCtx.adapter, envelope, interaction, fallbackText, permCtx.replyContext);
|
|
802
807
|
cardSent = !!result;
|
|
803
808
|
}
|
|
@@ -850,20 +855,6 @@ export class AgentRunner {
|
|
|
850
855
|
const customText = values?.custom_text;
|
|
851
856
|
finish({ kind: 'answered', value: typeof customText === 'string' && customText.trim() ? customText.trim() : null });
|
|
852
857
|
}
|
|
853
|
-
else if (action === '_show_input') {
|
|
854
|
-
if (permCtx?.interceptNextMessage) {
|
|
855
|
-
permCtx.interactionRouter?.markWaiting(sessionId);
|
|
856
|
-
sendPrompt?.('✏️ 请直接发送你的自定义回复').catch(() => { });
|
|
857
|
-
permCtx.interceptNextMessage(sessionId, (msg) => {
|
|
858
|
-
permCtx.interactionRouter?.unmarkWaiting(sessionId);
|
|
859
|
-
const text = (msg.content || '').trim();
|
|
860
|
-
finish({ kind: 'answered', value: text || null });
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
finish({ kind: 'cancelled', reason: 'explicit_cancel' });
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
858
|
else if (action === 'submit' && q.multiSelect && values) {
|
|
868
859
|
const selected = [];
|
|
869
860
|
q.options.forEach((opt, idx) => {
|
|
@@ -895,7 +886,6 @@ export class AgentRunner {
|
|
|
895
886
|
});
|
|
896
887
|
if (settlement.kind === 'cancelled') {
|
|
897
888
|
try {
|
|
898
|
-
permCtx.cancelIntercept?.(sessionId);
|
|
899
889
|
try {
|
|
900
890
|
await permCtx.turn?.onInteractionSettled?.(requestId, settlement.reason === 'timeout' ? 'timed_out' : 'cancelled');
|
|
901
891
|
}
|
|
@@ -958,7 +948,9 @@ export class AgentRunner {
|
|
|
958
948
|
command: 'ask',
|
|
959
949
|
buttonArgMap: Object.fromEntries(q.options.map((_, i) => [`opt-${i}`, String(i + 1)])),
|
|
960
950
|
acceptFreeText: true,
|
|
961
|
-
freeTextHint:
|
|
951
|
+
freeTextHint: q.multiSelect
|
|
952
|
+
? '多选请回复 /ask 1,2;也可回复 /ask <自定义内容>'
|
|
953
|
+
: '或回复 /ask <自定义内容>',
|
|
962
954
|
},
|
|
963
955
|
};
|
|
964
956
|
await sendPrompt(renderActionAsText(interaction));
|
|
@@ -990,12 +982,25 @@ export class AgentRunner {
|
|
|
990
982
|
finish({ kind: 'cancelled', reason: 'aborted' });
|
|
991
983
|
};
|
|
992
984
|
permCtx.interactionRouter.register(requestId, sessionId, (action) => {
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
985
|
+
const answer = action.trim();
|
|
986
|
+
const selectionParts = answer.split(/[,,]/).map(part => part.trim());
|
|
987
|
+
const selectedIndexes = selectionParts.map(part => Number(part));
|
|
988
|
+
const hasValidNumericSelection = selectionParts.length > 0
|
|
989
|
+
&& selectionParts.every((part, index) => (/^\d+$/.test(part)
|
|
990
|
+
&& Number.isInteger(selectedIndexes[index])
|
|
991
|
+
&& selectedIndexes[index] >= 1
|
|
992
|
+
&& selectedIndexes[index] <= q.options.length));
|
|
993
|
+
if (q.multiSelect && hasValidNumericSelection) {
|
|
994
|
+
finish({
|
|
995
|
+
kind: 'answered',
|
|
996
|
+
value: [...new Set(selectedIndexes)].map(num => q.options[num - 1].label),
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
else if (!q.multiSelect && hasValidNumericSelection && selectedIndexes.length === 1) {
|
|
1000
|
+
finish({ kind: 'answered', value: q.options[selectedIndexes[0] - 1].label });
|
|
996
1001
|
}
|
|
997
1002
|
else {
|
|
998
|
-
finish({ kind: 'answered', value:
|
|
1003
|
+
finish({ kind: 'answered', value: answer });
|
|
999
1004
|
}
|
|
1000
1005
|
}, {
|
|
1001
1006
|
initiatorId: permCtx.userId,
|
|
@@ -1113,20 +1118,6 @@ export class AgentRunner {
|
|
|
1113
1118
|
const feedback = typeof values?.custom_text === 'string' ? values.custom_text.trim() : '';
|
|
1114
1119
|
deny(feedback || '用户提交了反馈');
|
|
1115
1120
|
}
|
|
1116
|
-
else if (trimmed === '_show_input') {
|
|
1117
|
-
if (permCtx.interceptNextMessage) {
|
|
1118
|
-
router.markWaiting(sessionId);
|
|
1119
|
-
sendPrompt?.('✏️ 请直接发送你的反馈意见').catch(() => { });
|
|
1120
|
-
permCtx.interceptNextMessage(sessionId, msg => {
|
|
1121
|
-
router.unmarkWaiting(sessionId);
|
|
1122
|
-
const feedback = (msg.content || '').trim();
|
|
1123
|
-
deny(feedback || '用户提交了反馈');
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
else {
|
|
1127
|
-
deny('用户提交了反馈');
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
1121
|
else if (trimmed === '2' || trimmed.toLowerCase() === 'reject' || trimmed === '拒绝') {
|
|
1131
1122
|
deny('用户拒绝了计划');
|
|
1132
1123
|
}
|
|
@@ -1153,7 +1144,6 @@ export class AgentRunner {
|
|
|
1153
1144
|
});
|
|
1154
1145
|
try {
|
|
1155
1146
|
if (settlement.kind === 'cancelled') {
|
|
1156
|
-
permCtx.cancelIntercept?.(sessionId);
|
|
1157
1147
|
try {
|
|
1158
1148
|
await permCtx.turn?.onInteractionSettled?.(requestId, settlement.reason === 'timeout' ? 'timed_out' : 'cancelled');
|
|
1159
1149
|
}
|
|
@@ -1181,7 +1171,7 @@ export class AgentRunner {
|
|
|
1181
1171
|
};
|
|
1182
1172
|
// 尝试发送交互卡片
|
|
1183
1173
|
let cardSent = false;
|
|
1184
|
-
if (permCtx.adapter?.
|
|
1174
|
+
if (permCtx.adapter?.capabilities?.interaction === true) {
|
|
1185
1175
|
// 发送计划内容:找 plans 目录中最新修改的 .md 文件
|
|
1186
1176
|
if (sendPrompt) {
|
|
1187
1177
|
try {
|
|
@@ -1283,10 +1273,20 @@ export class AgentRunner {
|
|
|
1283
1273
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1284
1274
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1285
1275
|
*/
|
|
1286
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel) {
|
|
1276
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false) {
|
|
1287
1277
|
let lastSessionId;
|
|
1278
|
+
let hasTurnActivity = false;
|
|
1279
|
+
let ignoredPreTurnResult = false;
|
|
1280
|
+
let waitingForBackgroundCompletion = false;
|
|
1281
|
+
let liveBackgroundTasks = new Set();
|
|
1282
|
+
const unsettledTasks = new Set();
|
|
1283
|
+
const terminalTasks = new Set();
|
|
1284
|
+
const ambientTasks = new Set();
|
|
1285
|
+
const awaitedTaskNotifications = new Set();
|
|
1286
|
+
const taskMetadata = new Map();
|
|
1288
1287
|
// tool_use_id → tool_name 映射,用于从 SDKUserMessage 的 tool_result 块中还原工具名
|
|
1289
1288
|
const toolUseNames = new Map();
|
|
1289
|
+
const emittedToolResultIds = new Set();
|
|
1290
1290
|
let turnCount = 0;
|
|
1291
1291
|
const seenMessageIds = new Set();
|
|
1292
1292
|
let lastModelCall;
|
|
@@ -1302,11 +1302,99 @@ export class AgentRunner {
|
|
|
1302
1302
|
yield { type: 'session_id', sessionId: event.session_id };
|
|
1303
1303
|
}
|
|
1304
1304
|
if (event.type === 'user' && event.uuid === inputId) {
|
|
1305
|
+
hasTurnActivity = true;
|
|
1305
1306
|
yield { type: 'input_accepted', inputId };
|
|
1306
1307
|
}
|
|
1308
|
+
if (event.type === 'system' && event.subtype === 'task_started' && typeof event.task_id === 'string') {
|
|
1309
|
+
const taskKind = event.subagent_type || event.task_type === 'remote_agent'
|
|
1310
|
+
? 'agent'
|
|
1311
|
+
: event.workflow_name || event.task_type === 'local_workflow'
|
|
1312
|
+
? 'workflow'
|
|
1313
|
+
: 'background';
|
|
1314
|
+
taskMetadata.set(event.task_id, {
|
|
1315
|
+
taskKind,
|
|
1316
|
+
description: event.description,
|
|
1317
|
+
subagentType: event.subagent_type,
|
|
1318
|
+
taskType: event.task_type,
|
|
1319
|
+
});
|
|
1320
|
+
if (event.skip_transcript === true) {
|
|
1321
|
+
ambientTasks.add(event.task_id);
|
|
1322
|
+
unsettledTasks.delete(event.task_id);
|
|
1323
|
+
liveBackgroundTasks.delete(event.task_id);
|
|
1324
|
+
}
|
|
1325
|
+
else if (!terminalTasks.has(event.task_id)) {
|
|
1326
|
+
unsettledTasks.add(event.task_id);
|
|
1327
|
+
hasTurnActivity = true;
|
|
1328
|
+
}
|
|
1329
|
+
yield {
|
|
1330
|
+
type: 'task_started',
|
|
1331
|
+
taskId: event.task_id,
|
|
1332
|
+
taskKind,
|
|
1333
|
+
toolUseId: event.tool_use_id,
|
|
1334
|
+
description: event.description || event.task_id,
|
|
1335
|
+
subagentType: event.subagent_type,
|
|
1336
|
+
taskType: event.task_type,
|
|
1337
|
+
workflowName: event.workflow_name,
|
|
1338
|
+
prompt: event.prompt,
|
|
1339
|
+
skipTranscript: event.skip_transcript,
|
|
1340
|
+
};
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
if (event.type === 'system' && event.subtype === 'background_tasks_changed') {
|
|
1344
|
+
const tasks = Array.isArray(event.tasks) ? event.tasks : [];
|
|
1345
|
+
liveBackgroundTasks = new Set(tasks
|
|
1346
|
+
.map((task) => task?.task_id)
|
|
1347
|
+
.filter((taskId) => (typeof taskId === 'string'
|
|
1348
|
+
&& !terminalTasks.has(taskId)
|
|
1349
|
+
&& !ambientTasks.has(taskId))));
|
|
1350
|
+
yield {
|
|
1351
|
+
type: 'background_tasks_changed',
|
|
1352
|
+
tasks: tasks.map((task) => ({
|
|
1353
|
+
taskId: task.task_id,
|
|
1354
|
+
taskKind: task.task_type === 'remote_agent'
|
|
1355
|
+
? 'agent'
|
|
1356
|
+
: task.task_type === 'local_workflow'
|
|
1357
|
+
? 'workflow'
|
|
1358
|
+
: taskMetadata.get(task.task_id)?.taskKind || 'background',
|
|
1359
|
+
taskType: task.task_type,
|
|
1360
|
+
description: task.description,
|
|
1361
|
+
})),
|
|
1362
|
+
};
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
if (event.type === 'system' && event.subtype === 'task_notification' && typeof event.task_id === 'string') {
|
|
1366
|
+
const metadata = taskMetadata.get(event.task_id);
|
|
1367
|
+
const taskKind = metadata?.taskKind
|
|
1368
|
+
|| (event.tool_use_id && toolUseNames.get(event.tool_use_id) === 'Agent' ? 'agent' : undefined)
|
|
1369
|
+
|| (typeof event.summary === 'string' && /^Agent\b/.test(event.summary) ? 'agent' : 'background');
|
|
1370
|
+
terminalTasks.add(event.task_id);
|
|
1371
|
+
ambientTasks.delete(event.task_id);
|
|
1372
|
+
unsettledTasks.delete(event.task_id);
|
|
1373
|
+
liveBackgroundTasks.delete(event.task_id);
|
|
1374
|
+
awaitedTaskNotifications.delete(event.task_id);
|
|
1375
|
+
yield {
|
|
1376
|
+
type: 'task_notification',
|
|
1377
|
+
taskId: event.task_id,
|
|
1378
|
+
taskKind,
|
|
1379
|
+
toolUseId: event.tool_use_id,
|
|
1380
|
+
description: metadata?.description,
|
|
1381
|
+
subagentType: metadata?.subagentType,
|
|
1382
|
+
status: event.status || 'completed',
|
|
1383
|
+
outputFile: event.output_file,
|
|
1384
|
+
summary: event.summary,
|
|
1385
|
+
usage: event.usage ? {
|
|
1386
|
+
totalTokens: event.usage.total_tokens,
|
|
1387
|
+
toolUses: event.usage.tool_uses,
|
|
1388
|
+
durationMs: event.usage.duration_ms,
|
|
1389
|
+
} : undefined,
|
|
1390
|
+
skipTranscript: event.skip_transcript,
|
|
1391
|
+
};
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1307
1394
|
if (event.type === 'stream_event') {
|
|
1308
1395
|
const streamEvent = event.event;
|
|
1309
1396
|
if (streamEvent?.type === 'message_start' && streamEvent.message?.usage) {
|
|
1397
|
+
hasTurnActivity = true;
|
|
1310
1398
|
lastModelCall = {
|
|
1311
1399
|
uuid: event.uuid,
|
|
1312
1400
|
model: streamEvent.message.model,
|
|
@@ -1347,11 +1435,21 @@ export class AgentRunner {
|
|
|
1347
1435
|
}
|
|
1348
1436
|
// system: task_progress → task_progress
|
|
1349
1437
|
if (event.type === 'system' && event.subtype === 'task_progress') {
|
|
1438
|
+
const metadata = typeof event.task_id === 'string' ? taskMetadata.get(event.task_id) : undefined;
|
|
1439
|
+
if (!event.task_id || !ambientTasks.has(event.task_id))
|
|
1440
|
+
hasTurnActivity = true;
|
|
1350
1441
|
yield {
|
|
1351
1442
|
type: 'task_progress',
|
|
1443
|
+
taskId: event.task_id,
|
|
1444
|
+
taskKind: metadata?.taskKind || (event.subagent_type ? 'agent' : undefined),
|
|
1445
|
+
toolUseId: event.tool_use_id,
|
|
1446
|
+
description: event.description,
|
|
1447
|
+
subagentType: event.subagent_type || metadata?.subagentType,
|
|
1352
1448
|
summary: event.summary,
|
|
1353
|
-
|
|
1354
|
-
|
|
1449
|
+
lastToolName: event.last_tool_name,
|
|
1450
|
+
totalTokens: event.usage?.total_tokens,
|
|
1451
|
+
toolUses: event.usage?.tool_uses ?? event.tool_uses,
|
|
1452
|
+
durationMs: event.usage?.duration_ms ?? event.duration_ms,
|
|
1355
1453
|
};
|
|
1356
1454
|
}
|
|
1357
1455
|
// system: session_state_changed → state_changed
|
|
@@ -1360,6 +1458,7 @@ export class AgentRunner {
|
|
|
1360
1458
|
}
|
|
1361
1459
|
// assistant: 提取 tool_use 和文本(仅无 text_delta 时提取文本)
|
|
1362
1460
|
if (event.type === 'assistant' && event.message?.content) {
|
|
1461
|
+
hasTurnActivity = true;
|
|
1363
1462
|
lastAssistantUuid = event.uuid ?? lastAssistantUuid;
|
|
1364
1463
|
const msgId = event.message.id;
|
|
1365
1464
|
if (!msgId || !seenMessageIds.has(msgId)) {
|
|
@@ -1410,6 +1509,8 @@ export class AgentRunner {
|
|
|
1410
1509
|
const resultContent = typeof block.content === 'string'
|
|
1411
1510
|
? block.content
|
|
1412
1511
|
: block.content != null ? JSON.stringify(block.content) : '';
|
|
1512
|
+
if (block.tool_use_id)
|
|
1513
|
+
emittedToolResultIds.add(block.tool_use_id);
|
|
1413
1514
|
yield {
|
|
1414
1515
|
type: 'tool_result',
|
|
1415
1516
|
name: toolName,
|
|
@@ -1423,15 +1524,22 @@ export class AgentRunner {
|
|
|
1423
1524
|
}
|
|
1424
1525
|
// result → complete(含 permission_denials 提取)
|
|
1425
1526
|
if (event.type === 'result') {
|
|
1426
|
-
//
|
|
1527
|
+
// Most SDK streams surface denials as user-message tool_result blocks.
|
|
1528
|
+
// The terminal summary is only a fallback for incomplete streams.
|
|
1427
1529
|
if (Array.isArray(event.permission_denials)) {
|
|
1428
1530
|
for (const denial of event.permission_denials) {
|
|
1531
|
+
const callId = typeof denial.tool_use_id === 'string' ? denial.tool_use_id : undefined;
|
|
1532
|
+
if (callId && emittedToolResultIds.has(callId))
|
|
1533
|
+
continue;
|
|
1534
|
+
if (callId)
|
|
1535
|
+
emittedToolResultIds.add(callId);
|
|
1429
1536
|
yield {
|
|
1430
1537
|
type: 'tool_result',
|
|
1431
1538
|
name: denial.tool_name || '',
|
|
1432
1539
|
result: '',
|
|
1433
1540
|
isError: true,
|
|
1434
|
-
error: `权限被拒绝: ${denial.tool_name}`,
|
|
1541
|
+
error: `权限被拒绝: ${denial.tool_name || 'unknown tool'}`,
|
|
1542
|
+
callId,
|
|
1435
1543
|
};
|
|
1436
1544
|
}
|
|
1437
1545
|
}
|
|
@@ -1502,7 +1610,7 @@ export class AgentRunner {
|
|
|
1502
1610
|
// 降级:无逐次数据,写一条累计行
|
|
1503
1611
|
modelCalls = [{ call_index: 0, model: callModel_, tokenUsage: u, contextUsage: contextUsageForCall(u), degraded: true }];
|
|
1504
1612
|
}
|
|
1505
|
-
|
|
1613
|
+
const completeEvent = {
|
|
1506
1614
|
type: 'complete',
|
|
1507
1615
|
result: cleanResult,
|
|
1508
1616
|
subtype: event.subtype,
|
|
@@ -1520,10 +1628,53 @@ export class AgentRunner {
|
|
|
1520
1628
|
modelCalls,
|
|
1521
1629
|
assistantUuid: lastAssistantUuid,
|
|
1522
1630
|
};
|
|
1523
|
-
//
|
|
1631
|
+
// Fatal results belong to the query transport itself and must terminate
|
|
1632
|
+
// even when the current input was not echoed back by the CLI.
|
|
1633
|
+
if (event.is_error) {
|
|
1634
|
+
yield { ...completeEvent, queryFinal: true };
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
if (isResuming && !hasTurnActivity && !ignoredPreTurnResult) {
|
|
1638
|
+
ignoredPreTurnResult = true;
|
|
1639
|
+
logger.info(`[AgentRunner] Ignoring pre-turn result while resuming session=${sessionId} input=${inputId}`);
|
|
1640
|
+
continue;
|
|
1641
|
+
}
|
|
1642
|
+
const hasLiveTasks = liveBackgroundTasks.size > 0 || unsettledTasks.size > 0;
|
|
1643
|
+
if (hasLiveTasks) {
|
|
1644
|
+
waitingForBackgroundCompletion = true;
|
|
1645
|
+
for (const taskId of liveBackgroundTasks)
|
|
1646
|
+
awaitedTaskNotifications.add(taskId);
|
|
1647
|
+
for (const taskId of unsettledTasks)
|
|
1648
|
+
awaitedTaskNotifications.add(taskId);
|
|
1649
|
+
yield { ...completeEvent, queryFinal: false };
|
|
1650
|
+
hasTurnActivity = false;
|
|
1651
|
+
ignoredPreTurnResult = false;
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
// background_tasks_changed may report an empty set before the matching
|
|
1655
|
+
// task_notification. Do not close in that gap: the notification wakes the
|
|
1656
|
+
// parent agent for the final synthesis turn.
|
|
1657
|
+
if (waitingForBackgroundCompletion && awaitedTaskNotifications.size > 0) {
|
|
1658
|
+
yield { ...completeEvent, queryFinal: false };
|
|
1659
|
+
hasTurnActivity = false;
|
|
1660
|
+
ignoredPreTurnResult = false;
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
yield { ...completeEvent, queryFinal: true };
|
|
1524
1664
|
return;
|
|
1525
1665
|
}
|
|
1526
1666
|
}
|
|
1667
|
+
if (waitingForBackgroundCompletion) {
|
|
1668
|
+
yield {
|
|
1669
|
+
type: 'complete',
|
|
1670
|
+
isError: true,
|
|
1671
|
+
subtype: 'protocol_incomplete',
|
|
1672
|
+
terminalReason: 'background_tasks_incomplete',
|
|
1673
|
+
protocolIncompleteReason: 'background_tasks_incomplete',
|
|
1674
|
+
errors: ['Claude SDK stream ended before background tasks produced a final parent result'],
|
|
1675
|
+
queryFinal: true,
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1527
1678
|
}
|
|
1528
1679
|
catch (err) {
|
|
1529
1680
|
// 子进程崩溃(如 exited with code 1)时,把缓冲的 stderr 打出来还原真实原因。
|
|
@@ -1705,7 +1856,7 @@ export class AgentRunner {
|
|
|
1705
1856
|
}
|
|
1706
1857
|
// These tools need the dedicated canUseTool interaction handlers below.
|
|
1707
1858
|
if (toolName === 'AskUserQuestion' || toolName === 'ExitPlanMode') {
|
|
1708
|
-
return hookDecision('
|
|
1859
|
+
return hookDecision('ask', undefined, updatedInput);
|
|
1709
1860
|
}
|
|
1710
1861
|
if (callPermissionMode === 'readonly') {
|
|
1711
1862
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
@@ -2107,7 +2258,6 @@ export class AgentRunner {
|
|
|
2107
2258
|
logger.info('[AgentRunner] Creating query with images:', images.length, 'first image size:', images[0]?.data?.length ?? 0);
|
|
2108
2259
|
logger.debug('[AgentRunner] Skipping resume for image message to avoid history conflict');
|
|
2109
2260
|
msgStream.push(prompt, images, inputId);
|
|
2110
|
-
msgStream.end();
|
|
2111
2261
|
sdkStream = createQuery(msgStream);
|
|
2112
2262
|
}
|
|
2113
2263
|
else {
|
|
@@ -2124,7 +2274,7 @@ export class AgentRunner {
|
|
|
2124
2274
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
2125
2275
|
}
|
|
2126
2276
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
2127
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel);
|
|
2277
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId);
|
|
2128
2278
|
const self = this;
|
|
2129
2279
|
return (async function* () {
|
|
2130
2280
|
try {
|
|
@@ -167,6 +167,74 @@ export class CodexAppServerClient {
|
|
|
167
167
|
await this.request('thread/metadata/update', params);
|
|
168
168
|
return true;
|
|
169
169
|
}
|
|
170
|
+
async threadShellCommand(threadId, command, timeoutMs = 10_000) {
|
|
171
|
+
let commandItemId;
|
|
172
|
+
let output = '';
|
|
173
|
+
let settle;
|
|
174
|
+
let fail;
|
|
175
|
+
const completion = new Promise((resolve, reject) => {
|
|
176
|
+
settle = resolve;
|
|
177
|
+
fail = reject;
|
|
178
|
+
});
|
|
179
|
+
const unsubscribe = this.onNotification(notification => {
|
|
180
|
+
const params = (notification.params || {});
|
|
181
|
+
const notificationThreadId = params.threadId ?? params.thread_id;
|
|
182
|
+
if (notificationThreadId !== threadId)
|
|
183
|
+
return;
|
|
184
|
+
const item = params.item && typeof params.item === 'object'
|
|
185
|
+
? params.item
|
|
186
|
+
: undefined;
|
|
187
|
+
const itemId = typeof item?.id === 'string'
|
|
188
|
+
? item.id
|
|
189
|
+
: typeof params.itemId === 'string'
|
|
190
|
+
? params.itemId
|
|
191
|
+
: undefined;
|
|
192
|
+
const source = item?.source;
|
|
193
|
+
const isUserShell = source === 'userShell' || source === 'user_shell';
|
|
194
|
+
if (notification.method === 'item/started'
|
|
195
|
+
&& item?.type === 'commandExecution'
|
|
196
|
+
&& isUserShell
|
|
197
|
+
&& (item.command === command || commandItemId === undefined)) {
|
|
198
|
+
commandItemId = itemId;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (notification.method === 'item/commandExecution/outputDelta'
|
|
202
|
+
&& commandItemId
|
|
203
|
+
&& itemId === commandItemId
|
|
204
|
+
&& typeof params.delta === 'string') {
|
|
205
|
+
output += params.delta;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (notification.method === 'item/completed'
|
|
209
|
+
&& item?.type === 'commandExecution'
|
|
210
|
+
&& isUserShell
|
|
211
|
+
&& (!commandItemId || itemId === commandItemId)) {
|
|
212
|
+
const aggregated = typeof item.aggregatedOutput === 'string'
|
|
213
|
+
? item.aggregatedOutput
|
|
214
|
+
: typeof item.aggregated_output === 'string'
|
|
215
|
+
? item.aggregated_output
|
|
216
|
+
: output;
|
|
217
|
+
const exitCode = typeof item.exitCode === 'number'
|
|
218
|
+
? item.exitCode
|
|
219
|
+
: typeof item.exit_code === 'number'
|
|
220
|
+
? item.exit_code
|
|
221
|
+
: undefined;
|
|
222
|
+
settle({ output: aggregated, ...(exitCode !== undefined ? { exitCode } : {}) });
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
const timer = setTimeout(() => {
|
|
226
|
+
fail(new Error(`thread/shellCommand timed out for thread ${threadId}`));
|
|
227
|
+
}, timeoutMs);
|
|
228
|
+
timer.unref?.();
|
|
229
|
+
try {
|
|
230
|
+
await this.request('thread/shellCommand', { threadId, command });
|
|
231
|
+
return await completion;
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
clearTimeout(timer);
|
|
235
|
+
unsubscribe();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
170
238
|
async turnInterrupt(threadId, turnId) {
|
|
171
239
|
await this.request('turn/interrupt', { threadId, turnId });
|
|
172
240
|
return true;
|