evolcore 0.0.25 → 0.0.27
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 +632 -565
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +203 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +40 -29
- package/bin/install-codex-managed-hooks.mjs +582 -555
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +172 -33
- package/dist/agents/codex/app-server-client.js +76 -31
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +434 -446
- package/dist/agents/ecagent-runner.js +174 -34
- package/dist/agents/gemini-runner.js +8 -7
- package/dist/agents/runner-types.js +75 -19
- package/dist/aun/aid/agentmd.js +151 -36
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +4 -4
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +162 -39
- package/dist/aun/{menu-client.js → menu/client.js} +85 -6
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +85 -19
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +19 -9
- package/dist/aun/msg/upload.js +43 -80
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +842 -318
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +212 -89
- package/dist/cli/agent-create.js +626 -11
- package/dist/cli/agent.js +125 -13
- package/dist/cli/aun-commands.js +487 -337
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +55 -33
- package/dist/cli/contact.js +72 -15
- package/dist/cli/ctl-command.js +24 -24
- package/dist/cli/daemon-commands.js +140 -63
- package/dist/cli/diag.js +31 -0
- package/dist/cli/fs-command.js +70 -70
- package/dist/cli/handoff-command.js +46 -14
- package/dist/cli/index.js +91 -79
- package/dist/cli/init.js +34 -1
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +5 -1
- package/dist/cli/menu-client.js +51 -32
- package/dist/cli/model.js +3 -3
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +31 -31
- package/dist/cli/response.js +32 -32
- package/dist/cli/restart-monitor.js +103 -42
- package/dist/cli/stats.js +51 -51
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +74 -64
- package/dist/config/builtin-roles.js +11 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +160 -0
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +97 -8
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +30 -7
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +7 -1
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +53 -49
- package/dist/core/auth/operation-authorizer.js +90 -7
- package/dist/core/auth/operation-catalog.js +145 -4
- package/dist/core/auth/remote-access-store.js +155 -10
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +111 -20
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +159 -65
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +46 -7
- package/dist/core/command/menu-handler.js +392 -100
- package/dist/core/command/menu-protocol.js +45 -6
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +123 -43
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +146 -34
- package/dist/core/message/message-bridge.js +163 -20
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +99 -41
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +755 -144
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +112 -41
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +108 -17
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +9 -8
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +34 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +588 -217
- package/dist/ipc.js +193 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +2 -0
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/price-resolver.js +28 -10
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +61 -2
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +26 -14
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/npm-ops.js +176 -35
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +196 -23
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +60 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +69 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +379 -367
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +76 -71
- package/kits/docs/evolcore/agent-create.md +202 -165
- package/kits/docs/evolcore/agent.md +99 -67
- package/kits/docs/evolcore/aid.md +68 -60
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
- package/kits/docs/evolcore/config.md +241 -195
- package/kits/docs/evolcore/contact.md +75 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +244 -244
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +267 -152
- package/kits/docs/evolcore/menu-client.md +58 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +169 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +53 -50
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +623 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +269 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +42 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +217 -190
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +166 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +36 -36
- package/kits/rules/04-relation.md +76 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +65 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +451 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +215 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +92 -92
- package/kits/templates/roles/member.json +101 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +90 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +53 -34
- package/kits/templates/system-fragments/channel.md +28 -64
- package/kits/templates/system-fragments/commands.md +36 -28
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/reply.md +56 -0
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +104 -98
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
|
@@ -25,13 +25,15 @@ import { sanitizeSessionTitle } from '../core/session/session-title.js';
|
|
|
25
25
|
import { resolveClaudeCapabilityRunOptionsForProject } from '../core/capability/capability-manager.js';
|
|
26
26
|
import { normalizeExecutionPermissionMode, normalizePermissionMode, resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
|
|
27
27
|
import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, isSameOrDescendant, resolveProtectedCandidate, } from '../core/permission/protected-paths.js';
|
|
28
|
-
import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createSandboxInitializationError, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, recordSandboxInitializationFailure, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
|
|
28
|
+
import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createSandboxInitializationError, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, isSandboxInitializationToolResult, recordSandboxInitializationFailure, getSandboxInitializationFailureCounters, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, shouldEnableClaudeWeakerNestedSandbox, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
|
|
29
29
|
import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
|
|
30
30
|
import { auditToolInfrastructureFailure, auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
31
31
|
import { getManagedTaskTempDir, isManagedSessionRuntimeDir } from '../cli/task-context.js';
|
|
32
|
-
import { hasTrustedFullAccessContext } from './runner-types.js';
|
|
33
|
-
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel } from './runner-types.js';
|
|
32
|
+
import { unsettledInterruptError, withInterruptDeadline, hasTrustedFullAccessContext } from './runner-types.js';
|
|
33
|
+
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel, sdkAutoCompactWindowForModel } from './runner-types.js';
|
|
34
|
+
import { hasContextWindowSuffix, stripContextWindowSuffix } from '../core/model/context-window-suffix.js';
|
|
34
35
|
const resolveModule = createRequire(import.meta.url);
|
|
36
|
+
const CLAUDE_INTERRUPT_TIMEOUT_MS = 5_000;
|
|
35
37
|
/**
|
|
36
38
|
* Resolve the platform package explicitly instead of relying on the SDK's
|
|
37
39
|
* internal optional-dependency lookup. The latter is sensitive to the module
|
|
@@ -281,16 +283,17 @@ function buildClaudeManagedLockdownSettings(capabilityOptions) {
|
|
|
281
283
|
}
|
|
282
284
|
const CLAUDE_SANDBOX_GLOB_MAGIC = /[*?[\]]/;
|
|
283
285
|
function trustedRuntimeWritePaths(runtimeEnv) {
|
|
284
|
-
const
|
|
285
|
-
if (!
|
|
286
|
+
const sessionDir = runtimeEnv?.EVOLCORE_SESSION_RUNTIME_DIR?.trim();
|
|
287
|
+
if (!sessionDir || !isManagedSessionRuntimeDir(sessionDir))
|
|
286
288
|
return [];
|
|
287
|
-
const root = resolveProtectedCandidate(
|
|
289
|
+
const root = resolveProtectedCandidate(sessionDir);
|
|
288
290
|
const candidates = [runtimeEnv?.EVOLCORE_SESSION_RUNTIME_DIR, runtimeEnv?.EVOLCORE_RUNTIME_LOCK_DIR]
|
|
289
291
|
.filter((value) => typeof value === 'string' && path.isAbsolute(value));
|
|
290
292
|
const paths = [];
|
|
291
293
|
for (const candidate of candidates) {
|
|
292
294
|
const resolved = resolveProtectedCandidate(candidate);
|
|
293
|
-
if (!isSameOrDescendant(resolved, root)
|
|
295
|
+
if (!isSameOrDescendant(resolved, root)
|
|
296
|
+
|| (resolved !== root && !(path.dirname(resolved) === root && path.basename(resolved) === 'locks')))
|
|
294
297
|
continue;
|
|
295
298
|
try {
|
|
296
299
|
const stat = fs.lstatSync(resolved);
|
|
@@ -409,11 +412,18 @@ async function refreshGatewayPricing(baseUrl, apiKey, requestHeaders) {
|
|
|
409
412
|
}
|
|
410
413
|
/**
|
|
411
414
|
* 为支持 1M 上下文的模型追加 `[1m]` 后缀——仅在交给 SDK query() 时调用。
|
|
412
|
-
*
|
|
415
|
+
*
|
|
416
|
+
* 若网关把窗口变体作为独立条目下发(目录里同时有 `claude-opus-5` 与
|
|
417
|
+
* `claude-opus-5[1m]`),裸 ID 就是用户明确选的非 1M 变体,不能擅自升级,
|
|
418
|
+
* 否则两条列表项的实际效果无法区分。只有目录未提供任何窗口变体时,才沿用
|
|
419
|
+
* 按本地名单补 `[1m]` 的旧行为。
|
|
413
420
|
*/
|
|
414
|
-
function applyContextWindow(modelId) {
|
|
415
|
-
if (
|
|
416
|
-
return modelId; //
|
|
421
|
+
function applyContextWindow(modelId, catalog) {
|
|
422
|
+
if (hasContextWindowSuffix(modelId))
|
|
423
|
+
return modelId; // 窗口已显式指定
|
|
424
|
+
const variantListed = catalog?.models.some(m => hasContextWindowSuffix(m.id) && stripContextWindowSuffix(m.id) === modelId);
|
|
425
|
+
if (variantListed)
|
|
426
|
+
return modelId;
|
|
417
427
|
if (isOneMillionContextModel(modelId))
|
|
418
428
|
return `${modelId}[1m]`;
|
|
419
429
|
return modelId;
|
|
@@ -472,7 +482,7 @@ class MessageStream {
|
|
|
472
482
|
}
|
|
473
483
|
export class AgentRunner {
|
|
474
484
|
name = 'claude';
|
|
475
|
-
capabilities = { fullaccess: true, clear: true, compact: true, fork: true, forkAtTurn: true, askUserQuestion: true, planApproval: true, fileRewind: 'checkpoint' };
|
|
485
|
+
capabilities = { taskRecovery: true, fullaccess: true, clear: true, compact: true, fork: true, forkAtTurn: true, askUserQuestion: true, planApproval: true, fileRewind: 'checkpoint' };
|
|
476
486
|
apiKey;
|
|
477
487
|
model;
|
|
478
488
|
effort;
|
|
@@ -491,6 +501,7 @@ export class AgentRunner {
|
|
|
491
501
|
sandboxSettingsFiles = new Map();
|
|
492
502
|
streamDone = new Map();
|
|
493
503
|
streamDoneResolvers = new Map();
|
|
504
|
+
unconfirmedLifecycleFences = new Map();
|
|
494
505
|
onSessionIdUpdate;
|
|
495
506
|
onCompactStart;
|
|
496
507
|
permissionGateway;
|
|
@@ -500,9 +511,9 @@ export class AgentRunner {
|
|
|
500
511
|
/** 每个 session 最近的子进程 stderr 行(环形缓冲),用于子进程崩溃时还原真正原因 */
|
|
501
512
|
recentStderr = new Map();
|
|
502
513
|
static STDERR_BUFFER_MAX = 80;
|
|
503
|
-
async recordSandboxInfrastructureFailure(sessionId, inputId, permissionMode, error, stderr = []) {
|
|
514
|
+
async recordSandboxInfrastructureFailure(sessionId, inputId, permissionMode, error, stderr = [], scope = 'process') {
|
|
504
515
|
const detail = createSandboxInitializationError(error, stderr);
|
|
505
|
-
const circuit = recordSandboxInitializationFailure(detail, stderr);
|
|
516
|
+
const circuit = recordSandboxInitializationFailure(detail, stderr, Date.now(), scope === 'session' ? sessionId : undefined);
|
|
506
517
|
const context = this.permissionContexts.get(sessionId);
|
|
507
518
|
auditToolInfrastructureFailure({
|
|
508
519
|
toolName: 'Bash',
|
|
@@ -516,6 +527,11 @@ export class AgentRunner {
|
|
|
516
527
|
callId: inputId,
|
|
517
528
|
taskId: context?.taskId,
|
|
518
529
|
occurrences: circuit.occurrences,
|
|
530
|
+
sandboxFailure: {
|
|
531
|
+
kind: circuit.kind,
|
|
532
|
+
observation: 'initialization_failed',
|
|
533
|
+
counters: getSandboxInitializationFailureCounters(),
|
|
534
|
+
},
|
|
519
535
|
});
|
|
520
536
|
await context?.recordExecutionAnomaly?.({
|
|
521
537
|
code: 'capability_unavailable',
|
|
@@ -1329,7 +1345,7 @@ export class AgentRunner {
|
|
|
1329
1345
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1330
1346
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1331
1347
|
*/
|
|
1332
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn, projectPath = process.cwd(), topicBinding = false) {
|
|
1348
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn, projectPath = process.cwd(), topicBinding = false, recoverySessionId) {
|
|
1333
1349
|
let lastSessionId;
|
|
1334
1350
|
let pendingSessionId;
|
|
1335
1351
|
let hasTurnActivity = false;
|
|
@@ -1343,6 +1359,7 @@ export class AgentRunner {
|
|
|
1343
1359
|
const taskMetadata = new Map();
|
|
1344
1360
|
// tool_use_id → tool_name 映射,用于从 SDKUserMessage 的 tool_result 块中还原工具名
|
|
1345
1361
|
const toolUseNames = new Map();
|
|
1362
|
+
const sandboxedBashCalls = new Set();
|
|
1346
1363
|
const emittedToolResultIds = new Set();
|
|
1347
1364
|
let turnCount = 0;
|
|
1348
1365
|
const seenMessageIds = new Set();
|
|
@@ -1355,6 +1372,9 @@ export class AgentRunner {
|
|
|
1355
1372
|
const candidate = pendingSessionId;
|
|
1356
1373
|
if (!candidate)
|
|
1357
1374
|
return undefined;
|
|
1375
|
+
if (recoverySessionId && candidate !== recoverySessionId) {
|
|
1376
|
+
throw new Error('Task recovery cannot replace the original Claude session');
|
|
1377
|
+
}
|
|
1358
1378
|
if (!isResuming && topicBinding) {
|
|
1359
1379
|
const ready = waitForFile
|
|
1360
1380
|
? await waitForClaudeSessionFile(projectPath, candidate)
|
|
@@ -1481,6 +1501,12 @@ export class AgentRunner {
|
|
|
1481
1501
|
if (event.type === 'stream_event') {
|
|
1482
1502
|
const streamEvent = event.event;
|
|
1483
1503
|
if (streamEvent?.type === 'content_block_start'
|
|
1504
|
+
&& streamEvent.content_block?.type === 'text') {
|
|
1505
|
+
hasTurnActivity = true;
|
|
1506
|
+
const contentIndex = typeof streamEvent.index === 'number' ? streamEvent.index : 0;
|
|
1507
|
+
yield { type: 'text_start', contentIndex, turn: turnCount || undefined };
|
|
1508
|
+
}
|
|
1509
|
+
else if (streamEvent?.type === 'content_block_start'
|
|
1484
1510
|
&& (streamEvent.content_block?.type === 'thinking' || streamEvent.content_block?.type === 'redacted_thinking')) {
|
|
1485
1511
|
hasTurnActivity = true;
|
|
1486
1512
|
yield { type: 'heartbeat', source: 'reasoning', turn: turnCount || undefined };
|
|
@@ -1617,6 +1643,10 @@ export class AgentRunner {
|
|
|
1617
1643
|
if (content.type === 'tool_use') {
|
|
1618
1644
|
if (content.id)
|
|
1619
1645
|
toolUseNames.set(content.id, content.name);
|
|
1646
|
+
if (content.id && content.name === 'Bash' && content.input?.dangerouslyDisableSandbox !== true
|
|
1647
|
+
&& resolvePhaseOneExecutionSandbox(permissionMode, this.permissionContexts.get(sessionId)?.role).state !== 'off') {
|
|
1648
|
+
sandboxedBashCalls.add(content.id);
|
|
1649
|
+
}
|
|
1620
1650
|
yield { type: 'tool_use', name: content.name, input: content.input, callId: content.id, turn: turnCount, outputTokens: turnOutputChars, assistantUuid: event.uuid };
|
|
1621
1651
|
}
|
|
1622
1652
|
else if (content.type === 'text' && content.text) {
|
|
@@ -1635,6 +1665,31 @@ export class AgentRunner {
|
|
|
1635
1665
|
: block.content != null ? JSON.stringify(block.content) : '';
|
|
1636
1666
|
if (block.tool_use_id)
|
|
1637
1667
|
emittedToolResultIds.add(block.tool_use_id);
|
|
1668
|
+
if (sandboxedBashCalls.delete(block.tool_use_id) && block.is_error === true
|
|
1669
|
+
&& isSandboxInitializationToolResult(block.content)) {
|
|
1670
|
+
const { detail } = await this.recordSandboxInfrastructureFailure(sessionId, block.tool_use_id, permissionMode, new Error(resultContent), [], 'session');
|
|
1671
|
+
// Stop this SDK query before another model/tool retry. Do not use
|
|
1672
|
+
// interrupt(), which waits for this very generator to finish.
|
|
1673
|
+
this.activeMessageStreams.get(sessionId)?.end();
|
|
1674
|
+
const query = sdkStream;
|
|
1675
|
+
try {
|
|
1676
|
+
await query.close?.();
|
|
1677
|
+
}
|
|
1678
|
+
catch (error) {
|
|
1679
|
+
logger.warn(`[ClaudeSandbox] failed to close sandbox-failed query: ${error}`);
|
|
1680
|
+
}
|
|
1681
|
+
yield {
|
|
1682
|
+
type: 'tool_result', name: toolName, result: resultContent, isError: true,
|
|
1683
|
+
error: detail.message, errorCode: 'CAPABILITY_UNAVAILABLE', callId: block.tool_use_id,
|
|
1684
|
+
correlationId: block.tool_use_id, decision: 'error', decisionSource: 'infrastructure',
|
|
1685
|
+
policyCode: SANDBOX_INITIALIZATION_FAILED, executed: false, executionState: 'blocked',
|
|
1686
|
+
};
|
|
1687
|
+
yield {
|
|
1688
|
+
type: 'complete', isError: true, subtype: 'infrastructure',
|
|
1689
|
+
terminalReason: SANDBOX_INITIALIZATION_FAILED, errors: [detail.message], queryFinal: true,
|
|
1690
|
+
};
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1638
1693
|
yield {
|
|
1639
1694
|
type: 'tool_result',
|
|
1640
1695
|
name: toolName,
|
|
@@ -1898,6 +1953,17 @@ export class AgentRunner {
|
|
|
1898
1953
|
// Validate the daemon-injected session namespace before the SDK inherits
|
|
1899
1954
|
// the process environment for any temporary state.
|
|
1900
1955
|
getManagedTaskTempDir(runtimeEnv);
|
|
1956
|
+
this.assertLifecycleQuiescent(sessionId);
|
|
1957
|
+
const staleLifecycle = this.activeStreams.has(sessionId)
|
|
1958
|
+
|| this.activeQueries.has(sessionId)
|
|
1959
|
+
|| this.activeMessageStreams.has(sessionId)
|
|
1960
|
+
|| this.interruptFns.has(sessionId);
|
|
1961
|
+
if (staleLifecycle) {
|
|
1962
|
+
logger.warn(`[AgentRunner] Stale query lifecycle before runQuery; forcing cleanup: session=${sessionId}`);
|
|
1963
|
+
await this.interrupt(sessionId).catch(error => {
|
|
1964
|
+
logger.warn(`[AgentRunner] Stale query interrupt failed before runQuery: session=${sessionId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1901
1967
|
// 记录当前 evolcore session ID,用于 Agent ctl 环境变量注入
|
|
1902
1968
|
// 同步用户级配置到内存
|
|
1903
1969
|
this.syncFromUserSettings();
|
|
@@ -1928,6 +1994,10 @@ export class AgentRunner {
|
|
|
1928
1994
|
if (topicBinding && topicAgentSessionId !== agentSessionId) {
|
|
1929
1995
|
agentSessionId = topicAgentSessionId || undefined;
|
|
1930
1996
|
}
|
|
1997
|
+
const recoverySessionId = modelOverride?.recovery?.agentSessionId;
|
|
1998
|
+
if (modelOverride?.recovery && (!recoverySessionId || agentSessionId !== recoverySessionId)) {
|
|
1999
|
+
throw new Error('Task recovery requires the original Claude session binding');
|
|
2000
|
+
}
|
|
1931
2001
|
// 验证会话文件是否存在且有效(仅在有 agentSessionId 时)
|
|
1932
2002
|
if (agentSessionId) {
|
|
1933
2003
|
const homeDir = os.homedir();
|
|
@@ -1942,7 +2012,7 @@ export class AgentRunner {
|
|
|
1942
2012
|
for (const line of lines) {
|
|
1943
2013
|
try {
|
|
1944
2014
|
const data = JSON.parse(line);
|
|
1945
|
-
if (data.sessionId && data.version) {
|
|
2015
|
+
if (data.sessionId && data.version && (!recoverySessionId || data.sessionId === recoverySessionId)) {
|
|
1946
2016
|
isValid = true;
|
|
1947
2017
|
break;
|
|
1948
2018
|
}
|
|
@@ -1958,6 +2028,9 @@ export class AgentRunner {
|
|
|
1958
2028
|
}
|
|
1959
2029
|
}
|
|
1960
2030
|
if (!isValid) {
|
|
2031
|
+
if (recoverySessionId) {
|
|
2032
|
+
throw new Error(`Task recovery backend is unavailable: ${recoverySessionId}`);
|
|
2033
|
+
}
|
|
1961
2034
|
if (topicBinding) {
|
|
1962
2035
|
const error = new Error(`bound topic backend is unavailable: ${agentSessionId}`);
|
|
1963
2036
|
error.code = 'TOPIC_BACKEND_UNAVAILABLE';
|
|
@@ -1992,7 +2065,7 @@ export class AgentRunner {
|
|
|
1992
2065
|
const executionSandbox = resolvePhaseOneExecutionSandbox(callPermissionMode, runPermissionContext?.role);
|
|
1993
2066
|
const inputId = modelOverride?.turn?.inputId ?? crypto.randomUUID();
|
|
1994
2067
|
if (executionSandbox.state !== 'off') {
|
|
1995
|
-
const activeSandboxFailure = getActiveSandboxInitializationFailure();
|
|
2068
|
+
const activeSandboxFailure = getActiveSandboxInitializationFailure(undefined, undefined, sessionId);
|
|
1996
2069
|
if (activeSandboxFailure) {
|
|
1997
2070
|
const detail = createSandboxInitializationError(new Error(`sandbox bootstrap circuit open; firstObservedAt=${activeSandboxFailure.openedAt} ` +
|
|
1998
2071
|
`occurrences=${activeSandboxFailure.occurrences}; ${activeSandboxFailure.detail}`));
|
|
@@ -2009,6 +2082,11 @@ export class AgentRunner {
|
|
|
2009
2082
|
callId: inputId,
|
|
2010
2083
|
taskId: context?.taskId,
|
|
2011
2084
|
occurrences: activeSandboxFailure.occurrences,
|
|
2085
|
+
sandboxFailure: {
|
|
2086
|
+
kind: activeSandboxFailure.kind,
|
|
2087
|
+
observation: 'circuit_open',
|
|
2088
|
+
counters: getSandboxInitializationFailureCounters(),
|
|
2089
|
+
},
|
|
2012
2090
|
});
|
|
2013
2091
|
await context?.recordExecutionAnomaly?.({
|
|
2014
2092
|
code: 'capability_unavailable',
|
|
@@ -2469,7 +2547,7 @@ export class AgentRunner {
|
|
|
2469
2547
|
else {
|
|
2470
2548
|
logger.info(`[AgentRunner] systemPromptAppend: none`);
|
|
2471
2549
|
}
|
|
2472
|
-
const sdkModel = applyContextWindow(resolveCatalogModel(runCatalog, callModel));
|
|
2550
|
+
const sdkModel = applyContextWindow(resolveCatalogModel(runCatalog, callModel), runCatalog);
|
|
2473
2551
|
const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
|
|
2474
2552
|
const managedSettings = callPermissionMode === 'fullaccess'
|
|
2475
2553
|
? undefined
|
|
@@ -2479,6 +2557,7 @@ export class AgentRunner {
|
|
|
2479
2557
|
: await (async () => {
|
|
2480
2558
|
const sandboxProjectionStartedAt = Date.now();
|
|
2481
2559
|
ensureClaudeGitWorktreeConfig(projectPath);
|
|
2560
|
+
const enableWeakerNestedSandbox = shouldEnableClaudeWeakerNestedSandbox();
|
|
2482
2561
|
const sandboxFilesystem = buildClaudeProtectedFilesystem(resolvePaths().root, {
|
|
2483
2562
|
denyLClassRead: callPermissionMode === 'readonly',
|
|
2484
2563
|
});
|
|
@@ -2499,10 +2578,12 @@ export class AgentRunner {
|
|
|
2499
2578
|
throw new Error(`[ClaudeSandbox] protected filesystem projection exceeded 2000ms: ${sandboxProjectionDurationMs}ms`);
|
|
2500
2579
|
}
|
|
2501
2580
|
logger.info(`[ClaudeSandbox] projection ready denyRead=${sandboxFilesystem.denyRead.length} ` +
|
|
2502
|
-
`denyWrite=${sandboxFilesystem.denyWrite.length} durationMs=${sandboxProjectionDurationMs}`
|
|
2581
|
+
`denyWrite=${sandboxFilesystem.denyWrite.length} durationMs=${sandboxProjectionDurationMs} ` +
|
|
2582
|
+
`weakerNestedSandbox=${enableWeakerNestedSandbox}`);
|
|
2503
2583
|
return {
|
|
2504
2584
|
enabled: true,
|
|
2505
2585
|
failIfUnavailable: shouldFailIfClaudeSandboxUnavailable(),
|
|
2586
|
+
enableWeakerNestedSandbox,
|
|
2506
2587
|
allowUnsandboxedCommands: true,
|
|
2507
2588
|
network: {
|
|
2508
2589
|
allowUnixSockets: sandboxUnixSockets,
|
|
@@ -2552,7 +2633,7 @@ export class AgentRunner {
|
|
|
2552
2633
|
...(executionSandbox.state === 'off' && callPermissionMode !== 'fullaccess' && process.platform === 'linux' ? {
|
|
2553
2634
|
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr),
|
|
2554
2635
|
} : {}),
|
|
2555
|
-
autoCompactWindow:
|
|
2636
|
+
autoCompactWindow: sdkAutoCompactWindowForModel(sdkModel),
|
|
2556
2637
|
advisorModel: 'haiku',
|
|
2557
2638
|
canUseTool: canUseToolCallback,
|
|
2558
2639
|
permissionMode: sdkPermissionMode,
|
|
@@ -2702,7 +2783,7 @@ export class AgentRunner {
|
|
|
2702
2783
|
// Topic bindings are authoritative for every input shape. Starting
|
|
2703
2784
|
// an unresumed Claude query for a bound topic would discover a second
|
|
2704
2785
|
// backend and then fail activation with a binding conflict.
|
|
2705
|
-
sdkStream = topicBinding
|
|
2786
|
+
sdkStream = topicBinding || recoverySessionId
|
|
2706
2787
|
? createQuery(msgStream, agentSessionId, resumeAt)
|
|
2707
2788
|
: createQuery(msgStream);
|
|
2708
2789
|
}
|
|
@@ -2747,7 +2828,7 @@ export class AgentRunner {
|
|
|
2747
2828
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
2748
2829
|
}
|
|
2749
2830
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
2750
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding);
|
|
2831
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn, projectPath, topicBinding, recoverySessionId);
|
|
2751
2832
|
const self = this;
|
|
2752
2833
|
return (async function* () {
|
|
2753
2834
|
try {
|
|
@@ -2770,43 +2851,92 @@ export class AgentRunner {
|
|
|
2770
2851
|
async interrupt(sessionId) {
|
|
2771
2852
|
this.permissionContexts.get(sessionId)?.pauseController?.cancel();
|
|
2772
2853
|
const streamRegistered = this.activeStreams.has(sessionId);
|
|
2854
|
+
const registeredStream = this.activeStreams.get(sessionId);
|
|
2773
2855
|
const fn = this.interruptFns.get(sessionId);
|
|
2774
2856
|
const query = this.activeQueries.get(sessionId);
|
|
2857
|
+
const messageStream = this.activeMessageStreams.get(sessionId);
|
|
2775
2858
|
const cancelledInputIds = [];
|
|
2776
2859
|
let stillQueued;
|
|
2860
|
+
let interruptConfirmed = false;
|
|
2861
|
+
let queuedCancellationComplete = true;
|
|
2777
2862
|
if (fn) {
|
|
2863
|
+
let interruptTimer;
|
|
2778
2864
|
try {
|
|
2779
|
-
const receipt = await
|
|
2865
|
+
const receipt = await Promise.race([
|
|
2866
|
+
fn(),
|
|
2867
|
+
new Promise((_, reject) => {
|
|
2868
|
+
interruptTimer = setTimeout(() => reject(new Error('CLAUDE_INTERRUPT_TIMEOUT')), CLAUDE_INTERRUPT_TIMEOUT_MS);
|
|
2869
|
+
interruptTimer.unref?.();
|
|
2870
|
+
}),
|
|
2871
|
+
]);
|
|
2872
|
+
interruptConfirmed = true;
|
|
2780
2873
|
stillQueued = Array.isArray(receipt?.still_queued) ? receipt.still_queued.filter((id) => typeof id === 'string') : undefined;
|
|
2781
2874
|
if (stillQueued && query && typeof query.cancelAsyncMessage === 'function') {
|
|
2782
|
-
|
|
2875
|
+
const cancellations = await Promise.all(stillQueued.map(async (inputId) => {
|
|
2783
2876
|
try {
|
|
2784
|
-
|
|
2877
|
+
const cancelled = await withInterruptDeadline(query.cancelAsyncMessage(inputId), 'Claude cancel queued input', CLAUDE_INTERRUPT_TIMEOUT_MS);
|
|
2878
|
+
if (cancelled)
|
|
2785
2879
|
cancelledInputIds.push(inputId);
|
|
2880
|
+
return cancelled;
|
|
2786
2881
|
}
|
|
2787
2882
|
catch (error) {
|
|
2788
2883
|
logger.debug(`[AgentRunner] Failed to cancel queued input ${inputId}: ${error}`);
|
|
2884
|
+
return false;
|
|
2789
2885
|
}
|
|
2790
|
-
}
|
|
2886
|
+
}));
|
|
2887
|
+
queuedCancellationComplete = cancellations.every(Boolean);
|
|
2791
2888
|
}
|
|
2792
2889
|
logger.info(`[AgentRunner] Interrupted session: ${sessionId}`);
|
|
2793
2890
|
}
|
|
2794
2891
|
catch (error) {
|
|
2795
|
-
logger.warn(`[AgentRunner] Interrupt failed
|
|
2892
|
+
logger.warn(`[AgentRunner] Interrupt failed: ${sessionId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
2893
|
+
}
|
|
2894
|
+
finally {
|
|
2895
|
+
if (interruptTimer)
|
|
2896
|
+
clearTimeout(interruptTimer);
|
|
2796
2897
|
}
|
|
2797
2898
|
}
|
|
2798
2899
|
this.activeMessageStreams.get(sessionId)?.end();
|
|
2799
2900
|
if (query && typeof query.close === 'function')
|
|
2800
2901
|
query.close();
|
|
2801
2902
|
const done = this.streamDone.get(sessionId);
|
|
2903
|
+
let streamDoneSettled = false;
|
|
2802
2904
|
// A stream interrupted after runQuery resolves but before registerStream has
|
|
2803
2905
|
// no consumer that can enter the wrapper generator's finally block. Waiting
|
|
2804
2906
|
// for streamDone here would always burn the full timeout.
|
|
2805
2907
|
if (done && streamRegistered) {
|
|
2806
|
-
await Promise.race([
|
|
2908
|
+
await Promise.race([
|
|
2909
|
+
done.then(() => { streamDoneSettled = queuedCancellationComplete; }),
|
|
2910
|
+
new Promise(resolve => setTimeout(resolve, CLAUDE_INTERRUPT_TIMEOUT_MS)),
|
|
2911
|
+
]);
|
|
2912
|
+
}
|
|
2913
|
+
else if (done) {
|
|
2914
|
+
// A runQuery result can exist before ResponseEngine registers a consumer.
|
|
2915
|
+
// In that state there is no wrapper-finally callback to observe. Trust an
|
|
2916
|
+
// acknowledged SDK interrupt; otherwise keep the session fenced.
|
|
2917
|
+
streamDoneSettled = (!query || interruptConfirmed) && queuedCancellationComplete;
|
|
2918
|
+
}
|
|
2919
|
+
// Local queue cleanup cannot authorize a second SDK writer while the old
|
|
2920
|
+
// query remains unconfirmed.
|
|
2921
|
+
// Do not let a late interrupt from an older turn delete a lifecycle that
|
|
2922
|
+
// a newer turn installed while the old SDK call was unwinding.
|
|
2923
|
+
if (this.activeStreams.get(sessionId) === registeredStream
|
|
2924
|
+
&& this.interruptFns.get(sessionId) === fn
|
|
2925
|
+
&& this.activeQueries.get(sessionId) === query
|
|
2926
|
+
&& this.activeMessageStreams.get(sessionId) === messageStream) {
|
|
2927
|
+
if ((done && !streamDoneSettled) || !queuedCancellationComplete) {
|
|
2928
|
+
const fence = done ?? new Promise(() => { });
|
|
2929
|
+
this.unconfirmedLifecycleFences.set(sessionId, fence);
|
|
2930
|
+
if (queuedCancellationComplete) {
|
|
2931
|
+
void fence.then(() => {
|
|
2932
|
+
if (this.unconfirmedLifecycleFences.get(sessionId) === fence) {
|
|
2933
|
+
this.unconfirmedLifecycleFences.delete(sessionId);
|
|
2934
|
+
}
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
this.cleanupStreamLifecycle(sessionId, false, streamDoneSettled);
|
|
2807
2939
|
}
|
|
2808
|
-
this.interruptFns.delete(sessionId);
|
|
2809
|
-
this.activeStreams.delete(sessionId);
|
|
2810
2940
|
this.cleanupSandboxSettingsFiles(sessionId);
|
|
2811
2941
|
return { stillQueued, cancelledInputIds, closed: true };
|
|
2812
2942
|
}
|
|
@@ -2817,9 +2947,18 @@ export class AgentRunner {
|
|
|
2817
2947
|
this.activeStreams.set(key, stream);
|
|
2818
2948
|
}
|
|
2819
2949
|
cleanupStream(sessionId) {
|
|
2820
|
-
this.
|
|
2950
|
+
this.cleanupStreamLifecycle(sessionId, true, true);
|
|
2951
|
+
}
|
|
2952
|
+
assertLifecycleQuiescent(sessionId) {
|
|
2953
|
+
if (this.unconfirmedLifecycleFences.has(sessionId))
|
|
2954
|
+
throw unsettledInterruptError('Claude');
|
|
2955
|
+
}
|
|
2956
|
+
cleanupStreamLifecycle(sessionId, endMessageStream, resolveDone) {
|
|
2957
|
+
if (endMessageStream)
|
|
2958
|
+
this.activeMessageStreams.get(sessionId)?.end();
|
|
2821
2959
|
this.activeMessageStreams.delete(sessionId);
|
|
2822
|
-
|
|
2960
|
+
if (resolveDone)
|
|
2961
|
+
this.streamDoneResolvers.get(sessionId)?.();
|
|
2823
2962
|
this.streamDoneResolvers.delete(sessionId);
|
|
2824
2963
|
this.streamDone.delete(sessionId);
|
|
2825
2964
|
this.activeStreams.delete(sessionId);
|
|
@@ -2891,7 +3030,7 @@ export class AgentRunner {
|
|
|
2891
3030
|
prompt,
|
|
2892
3031
|
options: {
|
|
2893
3032
|
cwd: projectPath,
|
|
2894
|
-
...(catalog && callModel ? { model: applyContextWindow(resolveCatalogModel(catalog, callModel)) } : {}),
|
|
3033
|
+
...(catalog && callModel ? { model: applyContextWindow(resolveCatalogModel(catalog, callModel), catalog) } : {}),
|
|
2895
3034
|
resume: agentSessionId,
|
|
2896
3035
|
maxTurns: 1,
|
|
2897
3036
|
tools: [],
|
|
@@ -210,8 +210,8 @@ export class CodexAppServerClient {
|
|
|
210
210
|
}
|
|
211
211
|
return undefined;
|
|
212
212
|
}
|
|
213
|
-
async threadRead(threadId, includeTurns = true) {
|
|
214
|
-
return this.request('thread/read', { threadId, includeTurns });
|
|
213
|
+
async threadRead(threadId, includeTurns = true, timeoutMs) {
|
|
214
|
+
return this.request('thread/read', { threadId, includeTurns }, timeoutMs);
|
|
215
215
|
}
|
|
216
216
|
async threadRollback(threadId, numTurns) {
|
|
217
217
|
return this.request('thread/rollback', { threadId, numTurns });
|
|
@@ -232,7 +232,7 @@ export class CodexAppServerClient {
|
|
|
232
232
|
: { sandbox: options?.sandbox ?? null }),
|
|
233
233
|
config: this.buildThreadConfig(effort, options?.config),
|
|
234
234
|
excludeTurns: false,
|
|
235
|
-
|
|
235
|
+
...(options?.lastTurnId ? { lastTurnId: options.lastTurnId } : {}),
|
|
236
236
|
});
|
|
237
237
|
const forkedThreadId = response.thread?.id;
|
|
238
238
|
if (title && forkedThreadId) {
|
|
@@ -252,16 +252,19 @@ export class CodexAppServerClient {
|
|
|
252
252
|
await this.request('thread/compact/start', { threadId });
|
|
253
253
|
return true;
|
|
254
254
|
}
|
|
255
|
-
async threadTurnsList(threadId, limit) {
|
|
255
|
+
async threadTurnsList(threadId, limit, cursor) {
|
|
256
256
|
return this.request('thread/turns/list', {
|
|
257
257
|
threadId,
|
|
258
258
|
...(limit ? { limit } : {}),
|
|
259
|
-
|
|
259
|
+
...(cursor ? { cursor } : {}),
|
|
260
|
+
// The app-server defaults to newest-first. `nextCursor` then walks
|
|
261
|
+
// toward older turns, which is the direction needed to hydrate history.
|
|
262
|
+
sortDirection: 'desc',
|
|
260
263
|
itemsView: 'full',
|
|
261
264
|
});
|
|
262
265
|
}
|
|
263
266
|
async threadTurnsItemsList(threadId, turnId, limit) {
|
|
264
|
-
return this.request('thread/
|
|
267
|
+
return this.request('thread/items/list', {
|
|
265
268
|
threadId,
|
|
266
269
|
turnId,
|
|
267
270
|
...(limit ? { limit } : {}),
|
|
@@ -619,7 +622,7 @@ export class CodexAppServerClient {
|
|
|
619
622
|
env.TMPDIR = managedTmpDir;
|
|
620
623
|
delete env.EVOLCORE_RUNTIME_LOCK_DIR;
|
|
621
624
|
if (managedTmpDir) {
|
|
622
|
-
const runtimeLockDir = path.join(managedTmpDir, '
|
|
625
|
+
const runtimeLockDir = path.join(managedTmpDir, 'locks');
|
|
623
626
|
try {
|
|
624
627
|
// The parent is the already-validated process root, so only create
|
|
625
628
|
// and validate the final component. Never follow a pre-existing
|
|
@@ -643,7 +646,12 @@ export class CodexAppServerClient {
|
|
|
643
646
|
logger.warn(`[CodexAppServer] managed runtime lock directory unavailable: ${runtimeLockDir}`);
|
|
644
647
|
}
|
|
645
648
|
}
|
|
646
|
-
|
|
649
|
+
if (this.options.authType === 'bearerToken') {
|
|
650
|
+
delete env.CODEX_API_KEY;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
env.CODEX_API_KEY = this.options.apiKey;
|
|
654
|
+
}
|
|
647
655
|
env.EVOLCORE_INSTANCE_SOCKET = resolvePaths().instanceSocket;
|
|
648
656
|
for (const [name, value] of Object.entries(this.options.headers ?? {})) {
|
|
649
657
|
env[codexHeaderEnvName(name)] = value;
|
|
@@ -760,30 +768,63 @@ export class CodexAppServerClient {
|
|
|
760
768
|
}
|
|
761
769
|
return args;
|
|
762
770
|
}
|
|
763
|
-
async request(method, params) {
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
this.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
771
|
+
async request(method, params, timeoutMs) {
|
|
772
|
+
// Only lifecycle RPCs get short deadlines. Model work has its own policy.
|
|
773
|
+
const deadline = timeoutMs ?? (method === 'turn/interrupt' ? 5_000 : undefined);
|
|
774
|
+
let timer;
|
|
775
|
+
let requestId;
|
|
776
|
+
let expired = false;
|
|
777
|
+
const operation = (async () => {
|
|
778
|
+
await this.ensureReadyFor(method);
|
|
779
|
+
if (expired)
|
|
780
|
+
throw new Error(`Codex ${method} expired before dispatch`);
|
|
781
|
+
const proc = this.proc;
|
|
782
|
+
if (!proc)
|
|
783
|
+
throw new Error('Codex app-server is not running');
|
|
784
|
+
const id = String(this.nextId++);
|
|
785
|
+
requestId = id;
|
|
786
|
+
const payload = { id, method };
|
|
787
|
+
if (params !== undefined && params !== null)
|
|
788
|
+
payload.params = params;
|
|
789
|
+
return new Promise((resolve, reject) => {
|
|
790
|
+
this.pending.set(id, {
|
|
791
|
+
resolve,
|
|
792
|
+
reject,
|
|
793
|
+
method,
|
|
794
|
+
params,
|
|
795
|
+
stderrStartSequence: this.stderrSequence,
|
|
796
|
+
});
|
|
797
|
+
proc.stdin.write(`${JSON.stringify(payload)}\n`, error => {
|
|
798
|
+
if (!error)
|
|
799
|
+
return;
|
|
800
|
+
this.pending.delete(id);
|
|
801
|
+
reject(error);
|
|
802
|
+
});
|
|
785
803
|
});
|
|
786
|
-
});
|
|
804
|
+
})();
|
|
805
|
+
if (deadline === undefined)
|
|
806
|
+
return operation;
|
|
807
|
+
try {
|
|
808
|
+
return await Promise.race([
|
|
809
|
+
operation,
|
|
810
|
+
new Promise((_, reject) => {
|
|
811
|
+
timer = setTimeout(() => {
|
|
812
|
+
expired = true;
|
|
813
|
+
reject(new Error(`Codex ${method} deadline exceeded`));
|
|
814
|
+
}, deadline);
|
|
815
|
+
timer.unref?.();
|
|
816
|
+
}),
|
|
817
|
+
]);
|
|
818
|
+
}
|
|
819
|
+
finally {
|
|
820
|
+
if (timer)
|
|
821
|
+
clearTimeout(timer);
|
|
822
|
+
if (requestId && expired) {
|
|
823
|
+
const pending = this.pending.get(requestId);
|
|
824
|
+
this.pending.delete(requestId);
|
|
825
|
+
pending?.reject(new Error(`Codex ${method} deadline exceeded`));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
787
828
|
}
|
|
788
829
|
respond(id, result) {
|
|
789
830
|
if (!this.proc)
|
|
@@ -862,6 +903,10 @@ export class CodexAppServerClient {
|
|
|
862
903
|
else if (/already has an active writer/i.test(detail)) {
|
|
863
904
|
error.code = CODEX_THREAD_ACTIVE_WRITER;
|
|
864
905
|
}
|
|
906
|
+
else if (pending.method === 'turn/steer' && /\bno active turn\b/i.test(detail)) {
|
|
907
|
+
// Explicit server rejection proves the input was not accepted. Transport errors do not.
|
|
908
|
+
error.code = 'CODEX_NO_ACTIVE_TURN';
|
|
909
|
+
}
|
|
865
910
|
pending.reject(error);
|
|
866
911
|
return;
|
|
867
912
|
}
|
|
@@ -28,7 +28,7 @@ export async function codexNativeCatalog(context) {
|
|
|
28
28
|
if (context.baseagent !== 'codex')
|
|
29
29
|
return null;
|
|
30
30
|
const client = new CodexAppServerClient({
|
|
31
|
-
apiKey: context.apiKey ?? '', baseUrl: context.baseUrl, provider: context.provider,
|
|
31
|
+
apiKey: context.apiKey ?? '', authType: context.authType, baseUrl: context.baseUrl, provider: context.provider,
|
|
32
32
|
headers: context.headers, queryParams: context.queryParams,
|
|
33
33
|
});
|
|
34
34
|
let timer;
|