oysterun 1.6.0-beta.2 → 1.6.0-beta.21
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/bin/oysterun.mjs +64 -17
- package/dev/client/connectivity/completed-message-neighbor-click.mjs +78 -0
- package/dev/client/connectivity/completed-message-neighbor-contract.mjs +205 -0
- package/dev/client/connectivity/completed-message-visibility.d.mts +4 -4
- package/dev/client/connectivity/completed-message-visibility.mjs +14 -11
- package/dev/client/connectivity/desktop-chat-sidebar-preference.mjs +57 -0
- package/dev/client/connectivity/host-connection-feedback.mjs +12 -1
- package/dev/client/connectivity/managed-host-browser.mjs +10 -0
- package/dev/client/connectivity/session-navigation-intent.mjs +189 -0
- package/dev/client/connectivity/session-transition-state.mjs +18 -6
- package/dev/client/connectivity/test-completed-message-neighbor-click.mjs +180 -0
- package/dev/client/web/index.html +5367 -609
- package/dev/client/web-chat/dist/assets/{ReactPrism-BwJhidYf.js → ReactPrism-C6YiTZTC.js} +1 -1
- package/dev/client/web-chat/dist/assets/{ReactPrism-BwJhidYf.js.map → ReactPrism-C6YiTZTC.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-CKJOSPFP.js +6442 -0
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-CKJOSPFP.js.map +1 -0
- package/dev/client/web-chat/dist/assets/SessionsSetupApp-Dx09Uz-6.css +1 -0
- package/dev/client/web-chat/dist/assets/index-Ae5I4LHp.js +9 -0
- package/dev/client/web-chat/dist/assets/index-Ae5I4LHp.js.map +1 -0
- package/dev/client/web-chat/dist/assets/index-CN_FoTB1.js +2 -0
- package/dev/client/web-chat/dist/assets/index-CN_FoTB1.js.map +1 -0
- package/dev/client/web-chat/dist/assets/{index--1xmQ99x.js → index-DCQFwgGf.js} +2 -2
- package/dev/client/web-chat/dist/assets/{index--1xmQ99x.js.map → index-DCQFwgGf.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/matrix-entry-BV7G38kH.css +10 -0
- package/dev/client/web-chat/dist/assets/matrix-entry-abAy3aOo.js +594 -0
- package/dev/client/web-chat/dist/assets/matrix-entry-abAy3aOo.js.map +1 -0
- package/dev/client/web-chat/dist/assets/{pdf-ZjtalfgI.js → pdf-DN7oVvM6.js} +5 -5
- package/dev/client/web-chat/dist/assets/pdf-DN7oVvM6.js.map +1 -0
- package/dev/client/web-chat/dist/assets/session-notice-DlyODy1z.css +1 -0
- package/dev/client/web-chat/dist/assets/web-BlYuDlOV.js +1 -0
- package/dev/client/web-chat/dist/assets/{web-c70YaskK.js.map → web-BlYuDlOV.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/web-Dabw6cJi.js +1 -0
- package/dev/client/web-chat/dist/assets/{web-BzHcZK2M.js.map → web-Dabw6cJi.js.map} +1 -1
- package/dev/client/web-chat/dist/index.html +1 -2
- package/docs/routec/dev/cloud_deplyments.md +5 -0
- package/docs/routec/dev/npm_native_dependency_install.md +6 -2
- package/docs/routec/dev/npmjs_publish.md +55 -19
- package/docs/routec/dev/p262_setup_input_file.md +117 -0
- package/docs/routec/host2_turn_started_idempotency_diagnostics.md +8 -7
- package/docs/routec/host_setup.md +39 -10
- package/docs/routec/oysterun_operation_interface_spec.md +78 -2
- package/docs/routec/provider_contract.md +35 -5
- package/host-service/README.md +36 -0
- package/host-service/adapters/claude-code-adapter.mjs +48 -5
- package/host-service/adapters/codex-app-server-adapter.mjs +320 -18
- package/host-service/adapters/debug-fixture-provider-adapter.mjs +102 -1
- package/host-service/adapters/debug-large-tool-spillover-provider-adapter.mjs +2 -0
- package/host-service/adapters/debug-p135-codex-replay-provider-adapter.mjs +2 -0
- package/host-service/adapters/debug-routec-structural-replay-provider-adapter.mjs +2 -0
- package/host-service/agent-config.mjs +16 -5
- package/host-service/agent-folder-availability.mjs +41 -0
- package/host-service/apns-complete-message-dispatcher.mjs +1 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-find-context/SKILL.md +24 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-session-chat/SKILL.md +94 -5
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-sessions/SKILL.md +103 -0
- package/host-service/assets/product-skills/Oysterun/modules/oysterun-website/SKILL.md +7 -0
- package/host-service/assets/product-skills/oysterun-find-context/SKILL.md +24 -0
- package/host-service/assets/product-skills/oysterun-session-chat/SKILL.md +94 -5
- package/host-service/assets/product-skills/oysterun-sessions/SKILL.md +103 -0
- package/host-service/assets/product-skills/oysterun-website/SKILL.md +7 -0
- package/host-service/callback-durable-store.mjs +354 -47
- package/host-service/callback-foundation.mjs +21 -1
- package/host-service/callback-host-integration.mjs +345 -30
- package/host-service/claude-acp-model-discovery.mjs +23 -18
- package/host-service/cli/assert-installer-reinstall-target.mjs +9 -0
- package/host-service/cli/installer-recovery-guidance.mjs +20 -0
- package/host-service/cli/prepare-restart-restore.mjs +2 -0
- package/host-service/cli/product-cli.mjs +34 -33
- package/host-service/cli/reconcile-initial-preflight.mjs +19 -0
- package/host-service/cli/recover-installer-lifecycle.mjs +24 -0
- package/host-service/cli/run-host-lifecycle.mjs +2 -0
- package/host-service/cli/run-staged-lifecycle-recovery.mjs +161 -0
- package/host-service/codex-model-discovery.mjs +23 -18
- package/host-service/complete-cli-uninstall.mjs +176 -0
- package/host-service/complete-message-notification-predicate.mjs +8 -6
- package/host-service/complete-message-notification-title.mjs +4 -0
- package/host-service/completed-session-unread.mjs +425 -42
- package/host-service/completed-turn-navigation.mjs +444 -144
- package/host-service/completed-turn-notification-observer.mjs +116 -0
- package/host-service/completed-turn-service.mjs +445 -0
- package/host-service/config.mjs +115 -9
- package/host-service/desired-open-session-state.mjs +331 -0
- package/host-service/fixtures/p218-completed-turn-notification.json +11 -0
- package/host-service/frp-agent.mjs +9 -9
- package/host-service/grok-acp-model-discovery.mjs +31 -33
- package/host-service/host-lifecycle-recovery.mjs +37 -0
- package/host-service/host-port-resolution.mjs +98 -0
- package/host-service/host-runtime-diagnostics.mjs +18 -1
- package/host-service/initial-preflight-recovery.mjs +71 -0
- package/host-service/installer-recovery-guidance.mjs +85 -0
- package/host-service/installer-reinstall-bindings.mjs +49 -0
- package/host-service/installer-reinstall-target.mjs +49 -0
- package/host-service/legacy-continuity-observation.mjs +33 -0
- package/host-service/lifecycle-compat-cli.mjs +13 -0
- package/host-service/lifecycle-compat-preload.mjs +89 -0
- package/host-service/lifecycle-compatibility.mjs +164 -0
- package/host-service/lifecycle-continuity-assets.mjs +222 -0
- package/host-service/lifecycle-continuity-probe.mjs +97 -0
- package/host-service/lifecycle-continuity-projection.mjs +29 -0
- package/host-service/lifecycle-continuity-runner.mjs +192 -0
- package/host-service/lifecycle-continuity.mjs +493 -0
- package/host-service/lifecycle-service-render-bundle.mjs +120 -0
- package/host-service/lifecycle-user-path.mjs +34 -0
- package/host-service/link-annotations.mjs +10 -0
- package/host-service/matrix-credential-issuer.mjs +32 -1
- package/host-service/matrix-event-correlator.mjs +166 -27
- package/host-service/matrix-facade.mjs +96 -19
- package/host-service/matrix-room-binding.mjs +24 -0
- package/host-service/matrix-transcript-read-adapter.mjs +17 -3
- package/host-service/model-discovery-guardian.mjs +40 -0
- package/host-service/model-discovery-process.mjs +141 -0
- package/host-service/onboard-lifecycle-status.mjs +28 -0
- package/host-service/onboard.mjs +479 -0
- package/host-service/package.json +2 -1
- package/host-service/portable-scheduler-definitions.mjs +28 -0
- package/host-service/product-runtime-host-origin.mjs +22 -0
- package/host-service/provider-model-params.mjs +14 -17
- package/host-service/provider-model-refresh-runner.mjs +124 -169
- package/host-service/provider-model-refresh-schedule.mjs +11 -0
- package/host-service/provider-resume-contract.mjs +9 -3
- package/host-service/restart-restore-state.mjs +25 -0
- package/host-service/retained-host-state-holder.mjs +57 -0
- package/host-service/routec-matrix-sqlite-store.mjs +155 -10
- package/host-service/routec-matrix-storage-adapter.mjs +208 -25
- package/host-service/routec-matrix-storage-paths.mjs +12 -0
- package/host-service/routec-request-delay-window.mjs +55 -0
- package/host-service/scheduler-acp-provider-runner.mjs +2 -1
- package/host-service/scheduler-direct-provider-runner.mjs +71 -9
- package/host-service/scheduler-run-timeout-policy.mjs +85 -0
- package/host-service/scheduler-service.mjs +96 -9
- package/host-service/scheduler-store.mjs +9 -2
- package/host-service/server.mjs +2202 -552
- package/host-service/service-definition-migration.mjs +10 -3
- package/host-service/session-collection-service.mjs +320 -0
- package/host-service/session-history.mjs +99 -15
- package/host-service/session-list-preview-service.mjs +87 -12
- package/host-service/session-list-projection-service.mjs +182 -0
- package/host-service/session-loop-definitions.mjs +702 -75
- package/host-service/session-manager.mjs +473 -38
- package/host-service/session-switcher-projection.mjs +11 -3
- package/host-service/session-tags.mjs +451 -0
- package/host-service/session-transcript-target.mjs +103 -0
- package/host-service/setup-input-file.mjs +91 -0
- package/host-service/setup-provider-model-defaults.mjs +53 -0
- package/host-service/setup.mjs +133 -101
- package/host-service/stopped-user-host-package.mjs +60 -0
- package/host-service/test-fixtures/p236-navigation-original.mjs +500 -0
- package/host-service/update-reminder-state.mjs +7 -0
- package/host-service/voice-input-receipt-store.mjs +60 -0
- package/package.json +1 -1
- package/static/oysterun-social-preview.png +0 -0
- package/static/site/assets/oysterun-social-preview.png +0 -0
- package/static/site/blog/en/index.html +17 -0
- package/static/site/blog/generated_manifest.json +9 -9
- package/static/site/blog/index.html +18 -0
- package/static/site/blog/voice-input-model-experiment/en/index.html +17 -0
- package/static/site/blog/voice-input-model-experiment/zh/index.html +17 -0
- package/static/site/blog/zh/index.html +17 -0
- package/static/site/docs/choose-and-recover-a-provider/en/index.html +17 -0
- package/static/site/docs/choose-and-recover-a-provider/zh/index.html +17 -0
- package/static/site/docs/configure-session-files-and-settings/en/index.html +17 -0
- package/static/site/docs/configure-session-files-and-settings/zh/index.html +17 -0
- package/static/site/docs/en/index.html +17 -0
- package/static/site/docs/generated_manifest.json +60 -60
- package/static/site/docs/host-authentication-and-connection/en/index.html +17 -0
- package/static/site/docs/host-authentication-and-connection/zh/index.html +17 -0
- package/static/site/docs/index.html +18 -0
- package/static/site/docs/install-and-use-a-skill/en/index.html +17 -0
- package/static/site/docs/install-and-use-a-skill/zh/index.html +17 -0
- package/static/site/docs/read-large-tool-results/en/index.html +17 -0
- package/static/site/docs/read-large-tool-results/zh/index.html +17 -0
- package/static/site/docs/recover-from-a-host-runtime-error/en/index.html +17 -0
- package/static/site/docs/recover-from-a-host-runtime-error/zh/index.html +17 -0
- package/static/site/docs/restore-work-after-a-host-restart/en/index.html +17 -0
- package/static/site/docs/restore-work-after-a-host-restart/zh/index.html +17 -0
- package/static/site/docs/send-images-and-media/en/index.html +17 -0
- package/static/site/docs/send-images-and-media/zh/index.html +17 -0
- package/static/site/docs/set-up-and-understand-a-host/en/index.html +17 -0
- package/static/site/docs/set-up-and-understand-a-host/zh/index.html +17 -0
- package/static/site/docs/understand-agent-activity/en/index.html +19 -2
- package/static/site/docs/understand-agent-activity/en/index.md +2 -2
- package/static/site/docs/understand-agent-activity/zh/index.html +19 -2
- package/static/site/docs/understand-agent-activity/zh/index.md +2 -2
- package/static/site/docs/use-the-oysterun-cli-safely/en/index.html +17 -0
- package/static/site/docs/use-the-oysterun-cli-safely/zh/index.html +17 -0
- package/static/site/docs/zh/index.html +17 -0
- package/static/site/en/index.html +18 -1
- package/static/site/en/index.md +1 -1
- package/static/site/faq/en/index.html +17 -0
- package/static/site/faq/index.html +18 -0
- package/static/site/faq/zh/index.html +17 -0
- package/static/site/getting-started/en/index.html +23 -1
- package/static/site/getting-started/en/index.md +7 -1
- package/static/site/getting-started/index.html +18 -0
- package/static/site/getting-started/zh/index.html +23 -1
- package/static/site/getting-started/zh/index.md +7 -1
- package/static/site/index.html +18 -0
- package/static/site/install.sh +676 -127
- package/static/site/overview/en/index.html +18 -1
- package/static/site/overview/en/index.md +2 -2
- package/static/site/overview/index.html +18 -0
- package/static/site/overview/zh/index.html +18 -1
- package/static/site/overview/zh/index.md +2 -2
- package/static/site/privacy/en/index.html +17 -0
- package/static/site/privacy/index.html +18 -0
- package/static/site/privacy/zh/index.html +17 -0
- package/static/site/zh/index.html +18 -1
- package/static/site/zh/index.md +1 -1
- package/static/site_source/content/tutorials/published/understand-agent-activity/en/article.html +2 -2
- package/static/site_source/content/tutorials/published/understand-agent-activity/zh/article.html +2 -2
- package/static/site_source/downloads/install.sh +676 -127
- package/static/site_source/manifests/host_install.json +2 -10
- package/static/site_source/pages/getting-started/en.html +6 -1
- package/static/site_source/pages/getting-started/zh.html +6 -1
- package/static/site_source/pages/home/en.html +1 -1
- package/static/site_source/pages/home/zh.html +1 -1
- package/static/site_source/pages/home_main.html +3 -2
- package/static/site_source/pages/overview/en.html +1 -1
- package/static/site_source/pages/overview/zh.html +1 -1
- package/tool_scripts/install_oysterun_update.mjs +78 -13
- package/tool_scripts/oysterun_continuity_transaction.mjs +438 -0
- package/tool_scripts/oysterun_npm_package_manifest.mjs +32 -0
- package/tool_scripts/oysterun_release_service.sh +5 -0
- package/tool_scripts/oysterun_service_common.sh +59 -24
- package/tool_scripts/oysterun_setup_input.sh +97 -0
- package/tool_scripts/oysterun_update_restart_runner.mjs +19 -3
- package/tool_scripts/p233_legacy_bootstrap.mjs +254 -0
- package/tool_scripts/resolve_user_host_port.mjs +15 -0
- package/tool_scripts/restart_oysterun.sh +8 -2
- package/tool_scripts/stack_dashboard_credentials.mjs +2 -1
- package/tool_scripts/status_oysterun_launch_agents.sh +13 -1
- package/tool_scripts/sync_host_stack_config.mjs +9 -6
- package/tool_scripts/test_environment_remote_cloud_deprovision.mjs +419 -0
- package/tool_scripts/uninstall_oysterun_launch_agents.sh +3 -5
- package/dev/client/web-chat/dist/assets/index-Ce9NZBZf.js +0 -129
- package/dev/client/web-chat/dist/assets/index-Ce9NZBZf.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index-DzIClZ3T.js +0 -2
- package/dev/client/web-chat/dist/assets/index-DzIClZ3T.js.map +0 -1
- package/dev/client/web-chat/dist/assets/index-NYnaSGBb.css +0 -1
- package/dev/client/web-chat/dist/assets/pdf-ZjtalfgI.js.map +0 -1
- package/dev/client/web-chat/dist/assets/web-BzHcZK2M.js +0 -1
- package/dev/client/web-chat/dist/assets/web-c70YaskK.js +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "crypto";
|
|
2
2
|
import { EventEmitter } from "events";
|
|
3
|
+
import { appendTaskProviderLifecycleDiagnostic } from "./host-runtime-diagnostics.mjs";
|
|
3
4
|
import {
|
|
4
5
|
chmodSync,
|
|
5
6
|
copyFileSync,
|
|
@@ -19,7 +20,8 @@ import { DebugFixtureProviderAdapter } from "./adapters/debug-fixture-provider-a
|
|
|
19
20
|
import { DebugLargeToolSpilloverProviderAdapter } from "./adapters/debug-large-tool-spillover-provider-adapter.mjs";
|
|
20
21
|
import { DebugP135CodexReplayProviderAdapter } from "./adapters/debug-p135-codex-replay-provider-adapter.mjs";
|
|
21
22
|
import { DebugRouteCStructuralReplayProviderAdapter } from "./adapters/debug-routec-structural-replay-provider-adapter.mjs";
|
|
22
|
-
import { readConfig } from "./config.mjs";
|
|
23
|
+
import { getConfigDir, readConfig } from "./config.mjs";
|
|
24
|
+
import { DesiredOpenSessionStateOwner } from "./desired-open-session-state.mjs";
|
|
23
25
|
import {
|
|
24
26
|
createProviderUnavailableError,
|
|
25
27
|
getConfiguredProviderCommand,
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
saveSessionRecord,
|
|
31
33
|
updateSessionRecord,
|
|
32
34
|
} from "./session-history.mjs";
|
|
35
|
+
import { canonicalSessionTags, normalizeSessionTags } from "./session-tags.mjs";
|
|
33
36
|
import {
|
|
34
37
|
HOST_SESSION_RESUME_IDENTITY_KIND,
|
|
35
38
|
getAdapterResumeIdentityKind,
|
|
@@ -53,6 +56,7 @@ import {
|
|
|
53
56
|
import { normalizeAssetReadablePaths } from "./workspace-policy.mjs";
|
|
54
57
|
import {
|
|
55
58
|
blockRouteCHost2IntakeProviderDelivery,
|
|
59
|
+
cancelRouteCHost2IntakeForMatrixEvent,
|
|
56
60
|
claimRouteCHost2IntakeForProviderDelivery,
|
|
57
61
|
getRouteCHost2IntakeProof,
|
|
58
62
|
markRouteCHost2IntakeAgentTurnStarted,
|
|
@@ -846,6 +850,19 @@ export class SessionManager extends EventEmitter {
|
|
|
846
850
|
DEFAULT_CLAUDE_CLAIMED_TURN_NO_OUTPUT_TIMEOUT_MS,
|
|
847
851
|
"claudeClaimedTurnNoOutputTimeoutMs"
|
|
848
852
|
);
|
|
853
|
+
const desiredOpenStackName =
|
|
854
|
+
options.desiredOpenStackName ||
|
|
855
|
+
process.env.OYSTERUN_ROUTEC_STACK_NAME ||
|
|
856
|
+
process.env.OYSTERUN_STACK_NAME ||
|
|
857
|
+
process.env.OYSTERUN_STACK ||
|
|
858
|
+
"default";
|
|
859
|
+
this.desiredOpenStateOwner =
|
|
860
|
+
options.desiredOpenStateOwner ||
|
|
861
|
+
new DesiredOpenSessionStateOwner({
|
|
862
|
+
configDir: options.configDir || getConfigDir(),
|
|
863
|
+
stackName: desiredOpenStackName,
|
|
864
|
+
});
|
|
865
|
+
this.desiredOpenRestoreOutcomes = new Map();
|
|
849
866
|
/** @type {Map<string, import("./adapters/claude-code-adapter.mjs").ClaudeCodeSession>} sessionId → Session */
|
|
850
867
|
this.sessions = new Map();
|
|
851
868
|
/** @type {Map<string, Set<string>>} agentId → sessionIds */
|
|
@@ -877,6 +894,168 @@ export class SessionManager extends EventEmitter {
|
|
|
877
894
|
return session.providerResumeId || session.resumeSessionId || null;
|
|
878
895
|
}
|
|
879
896
|
|
|
897
|
+
buildDesiredOpenSessionRecord(sessionId, session) {
|
|
898
|
+
return {
|
|
899
|
+
session_id: sessionId,
|
|
900
|
+
session_name: session.sessionName || sessionId,
|
|
901
|
+
tags: canonicalSessionTags(session),
|
|
902
|
+
agent_id: session.agentId,
|
|
903
|
+
agent_folder: session.cwd,
|
|
904
|
+
provider: session.provider || session.adapterId || "claude",
|
|
905
|
+
model: session.model ?? null,
|
|
906
|
+
fixture_resume_generation:
|
|
907
|
+
session.resumeIdentityKind === HOST_SESSION_RESUME_IDENTITY_KIND
|
|
908
|
+
? normalizeFixtureResumeGeneration(session.fixtureResumeGeneration)
|
|
909
|
+
: null,
|
|
910
|
+
reasoning_effort: session.reasoningEffort ?? null,
|
|
911
|
+
reasoning_effort_source: session.reasoningEffortSource ?? null,
|
|
912
|
+
permission_mode: session.permissionMode ?? null,
|
|
913
|
+
approval_policy: session.approvalPolicy ?? null,
|
|
914
|
+
sandbox_mode: session.sandboxMode ?? null,
|
|
915
|
+
dangerous_mode: session.dangerousMode === true,
|
|
916
|
+
allow_dangerously_skip_permissions:
|
|
917
|
+
session.allowDangerouslySkipPermissions === true,
|
|
918
|
+
search_enabled: session.searchEnabled === true,
|
|
919
|
+
image_input_enabled: session.imageInputEnabled === true,
|
|
920
|
+
provider_resume_id: this.getEffectiveProviderResumeId(session),
|
|
921
|
+
provider_thread_id: session.threadId || null,
|
|
922
|
+
provider_transport: session.transport || null,
|
|
923
|
+
resume_identity_kind: session.resumeIdentityKind || null,
|
|
924
|
+
runtime_capabilities: cloneJsonObject(session.runtimeCapabilities) || {},
|
|
925
|
+
workspace_policy: cloneJsonObject(session.workspacePolicy),
|
|
926
|
+
asset_readable_paths: Array.isArray(session.assetReadablePaths)
|
|
927
|
+
? [...session.assetReadablePaths]
|
|
928
|
+
: [],
|
|
929
|
+
started_at: session.historyCreatedAt || session.startedAt || null,
|
|
930
|
+
last_active_at:
|
|
931
|
+
session.lastActiveAt ||
|
|
932
|
+
session.historyCreatedAt ||
|
|
933
|
+
session.startedAt ||
|
|
934
|
+
null,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
syncDesiredOpenSession(session) {
|
|
939
|
+
const state = this.desiredOpenStateOwner.upsert(
|
|
940
|
+
this.buildDesiredOpenSessionRecord(session.id, session)
|
|
941
|
+
);
|
|
942
|
+
const existing = this.desiredOpenRestoreOutcomes.get(session.id);
|
|
943
|
+
if (!existing || existing.state !== "failed") {
|
|
944
|
+
this.desiredOpenRestoreOutcomes.set(session.id, {
|
|
945
|
+
session_id: session.id,
|
|
946
|
+
state: session._ready === true ? "ready" : "restoring",
|
|
947
|
+
reason: null,
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
return state;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
getDesiredOpenRestoreStatus() {
|
|
954
|
+
const desired = this.desiredOpenStateOwner.read();
|
|
955
|
+
if (!desired.valid) {
|
|
956
|
+
return {
|
|
957
|
+
status: desired.status,
|
|
958
|
+
valid: false,
|
|
959
|
+
sessions: [],
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const sessions = desired.state.sessions.map((record) => ({
|
|
963
|
+
session_id: record.session_id,
|
|
964
|
+
session_name: record.session_name,
|
|
965
|
+
agent_id: record.agent_id,
|
|
966
|
+
agent_folder: record.agent_folder,
|
|
967
|
+
provider: record.provider,
|
|
968
|
+
...(this.desiredOpenRestoreOutcomes.get(record.session_id) || {
|
|
969
|
+
state: record.restore_failed_reason ? "failed" : "pending",
|
|
970
|
+
reason: record.restore_failed_reason || null,
|
|
971
|
+
...(record.restore_failed_at
|
|
972
|
+
? { restore_failed_at: record.restore_failed_at }
|
|
973
|
+
: {}),
|
|
974
|
+
}),
|
|
975
|
+
}));
|
|
976
|
+
const desiredIds = new Set(sessions.map((entry) => entry.session_id));
|
|
977
|
+
for (const outcome of this.desiredOpenRestoreOutcomes.values()) {
|
|
978
|
+
if (
|
|
979
|
+
outcome.state === "failed" &&
|
|
980
|
+
outcome.session_id &&
|
|
981
|
+
!desiredIds.has(outcome.session_id)
|
|
982
|
+
) {
|
|
983
|
+
sessions.push({ ...outcome });
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
status: "available",
|
|
988
|
+
valid: true,
|
|
989
|
+
sessions,
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
getDesiredOpenRestoreOutcome(sessionId) {
|
|
994
|
+
return this.desiredOpenRestoreOutcomes.get(sessionId) || null;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
markDesiredOpenRestoreReady(sessionId) {
|
|
998
|
+
if (!this.desiredOpenRestoreOutcomes.has(sessionId)) return null;
|
|
999
|
+
const outcome = {
|
|
1000
|
+
session_id: sessionId,
|
|
1001
|
+
state: "ready",
|
|
1002
|
+
reason: null,
|
|
1003
|
+
};
|
|
1004
|
+
this.desiredOpenRestoreOutcomes.set(sessionId, outcome);
|
|
1005
|
+
return outcome;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
finalizeDesiredOpenRestoreFailure(sessionId, details = {}) {
|
|
1009
|
+
const desired = this.desiredOpenStateOwner.read();
|
|
1010
|
+
const record = desired.valid
|
|
1011
|
+
? desired.state.sessions.find((entry) => entry.session_id === sessionId)
|
|
1012
|
+
: null;
|
|
1013
|
+
const existing = this.desiredOpenRestoreOutcomes.get(sessionId) || {};
|
|
1014
|
+
const outcome = {
|
|
1015
|
+
...(record || {}),
|
|
1016
|
+
...existing,
|
|
1017
|
+
...details,
|
|
1018
|
+
session_id: sessionId,
|
|
1019
|
+
state: "failed",
|
|
1020
|
+
reason:
|
|
1021
|
+
details.reason || existing.reason || "provider_runtime_restore_failed",
|
|
1022
|
+
};
|
|
1023
|
+
if (record) {
|
|
1024
|
+
this.desiredOpenStateOwner.upsert({
|
|
1025
|
+
...record,
|
|
1026
|
+
restore_failed_reason: outcome.reason,
|
|
1027
|
+
restore_failed_at: new Date().toISOString(),
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
this.desiredOpenRestoreOutcomes.set(sessionId, outcome);
|
|
1031
|
+
return outcome;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
markDesiredOpenRestoreFailure(
|
|
1035
|
+
sessionId,
|
|
1036
|
+
{ reason, reconciliationId = null, ...details } = {}
|
|
1037
|
+
) {
|
|
1038
|
+
const existing = this.desiredOpenRestoreOutcomes.get(sessionId);
|
|
1039
|
+
if (
|
|
1040
|
+
existing?.state === "failed" &&
|
|
1041
|
+
existing.reason === "restore_timeout" &&
|
|
1042
|
+
reason !== "restore_timeout" &&
|
|
1043
|
+
!reconciliationId
|
|
1044
|
+
) {
|
|
1045
|
+
return existing;
|
|
1046
|
+
}
|
|
1047
|
+
const outcome = {
|
|
1048
|
+
session_id: sessionId,
|
|
1049
|
+
...details,
|
|
1050
|
+
state: "failed",
|
|
1051
|
+
reason: reason || "provider_runtime_restore_failed",
|
|
1052
|
+
reconciliation_id:
|
|
1053
|
+
reconciliationId || existing?.reconciliation_id || null,
|
|
1054
|
+
};
|
|
1055
|
+
this.desiredOpenRestoreOutcomes.set(sessionId, outcome);
|
|
1056
|
+
return outcome;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
880
1059
|
getProviderTrustedFolderStatus({ cwd, provider, env = process.env }) {
|
|
881
1060
|
return this.providerTrust.getStatus({ cwd, provider, env });
|
|
882
1061
|
}
|
|
@@ -1531,10 +1710,23 @@ export class SessionManager extends EventEmitter {
|
|
|
1531
1710
|
if (session._claimedProviderTurnNoOutputTimer) {
|
|
1532
1711
|
clearTimeout(session._claimedProviderTurnNoOutputTimer);
|
|
1533
1712
|
}
|
|
1713
|
+
const message = messageId ? this.getOutboxMessage(session, messageId) : null;
|
|
1714
|
+
// A primary-timer reset on a still-claimed silent prompt keeps the existing
|
|
1715
|
+
// failsafe. Terminal/visible completion and re-arm clear both handles.
|
|
1716
|
+
const clearFailsafe = resetState || !messageId ||
|
|
1717
|
+
message?.claimedClaudeFinalEventSeen === true ||
|
|
1718
|
+
["failed", "canceled", "interrupted", "completed"].includes(message?.state);
|
|
1719
|
+
if (clearFailsafe && session._claimedProviderTurnNoOutputFailsafeTimer) {
|
|
1720
|
+
clearTimeout(session._claimedProviderTurnNoOutputFailsafeTimer);
|
|
1721
|
+
}
|
|
1722
|
+
appendTaskProviderLifecycleDiagnostic("claude_no_output_timer_clear", {
|
|
1723
|
+
session_id: session.id, message_id: session._claimedProviderTurnNoOutputMessageId,
|
|
1724
|
+
state: messageId ? this.getOutboxMessage(session, messageId)?.state : null,
|
|
1725
|
+
});
|
|
1534
1726
|
session._claimedProviderTurnNoOutputTimer = null;
|
|
1727
|
+
if (clearFailsafe) session._claimedProviderTurnNoOutputFailsafeTimer = null;
|
|
1535
1728
|
session._claimedProviderTurnNoOutputMessageId = null;
|
|
1536
1729
|
session._claimedProviderTurnNoOutputDeadlineAt = null;
|
|
1537
|
-
const message = messageId ? this.getOutboxMessage(session, messageId) : null;
|
|
1538
1730
|
if (message) {
|
|
1539
1731
|
message.claimedClaudeNoOutputDeadlineAt = null;
|
|
1540
1732
|
}
|
|
@@ -2001,7 +2193,9 @@ export class SessionManager extends EventEmitter {
|
|
|
2001
2193
|
if (
|
|
2002
2194
|
message.state === "queued" ||
|
|
2003
2195
|
message.state === "failed" ||
|
|
2004
|
-
message.state === "canceled"
|
|
2196
|
+
message.state === "canceled" ||
|
|
2197
|
+
message.state === "interrupted" ||
|
|
2198
|
+
message.state === "completed"
|
|
2005
2199
|
) {
|
|
2006
2200
|
return false;
|
|
2007
2201
|
}
|
|
@@ -2530,6 +2724,10 @@ export class SessionManager extends EventEmitter {
|
|
|
2530
2724
|
) {
|
|
2531
2725
|
return;
|
|
2532
2726
|
}
|
|
2727
|
+
appendTaskProviderLifecycleDiagnostic("claude_no_output_timer_fire", {
|
|
2728
|
+
session_id: liveSession.id, message_id: messageId,
|
|
2729
|
+
state: targetMessage.state, generation,
|
|
2730
|
+
});
|
|
2533
2731
|
this.enforceClaimedClaudeNoOutputMessage(liveSession, targetMessage, {
|
|
2534
2732
|
nowMs: Date.now(),
|
|
2535
2733
|
phase: "claimed_provider_turn_timeout",
|
|
@@ -2548,6 +2746,10 @@ export class SessionManager extends EventEmitter {
|
|
|
2548
2746
|
Math.min(50, Math.max(1, this.claudeClaimedTurnNoOutputTimeoutMs))
|
|
2549
2747
|
);
|
|
2550
2748
|
failsafeTimer.unref?.();
|
|
2749
|
+
session._claimedProviderTurnNoOutputFailsafeTimer = failsafeTimer;
|
|
2750
|
+
appendTaskProviderLifecycleDiagnostic("claude_no_output_timer_arm", {
|
|
2751
|
+
session_id: session.id, message_id: messageId, state: message.state, generation,
|
|
2752
|
+
});
|
|
2551
2753
|
}
|
|
2552
2754
|
|
|
2553
2755
|
providerControlKindFromPendingRequest(pendingRequest) {
|
|
@@ -2727,6 +2929,7 @@ export class SessionManager extends EventEmitter {
|
|
|
2727
2929
|
semantic_body: `Cancel ${outcome} for queued message ${message.id}.`,
|
|
2728
2930
|
control_outcome_id: controlOutcomeId,
|
|
2729
2931
|
control_outcome: outcome,
|
|
2932
|
+
cancel_outcome: outcome === "accepted" ? "accepted" : outcome,
|
|
2730
2933
|
outcome,
|
|
2731
2934
|
replay_policy: "always",
|
|
2732
2935
|
})
|
|
@@ -2842,17 +3045,69 @@ export class SessionManager extends EventEmitter {
|
|
|
2842
3045
|
return message;
|
|
2843
3046
|
}
|
|
2844
3047
|
|
|
2845
|
-
cancelOutboxMessage(sessionId, messageId) {
|
|
3048
|
+
async cancelOutboxMessage(sessionId, messageId) {
|
|
2846
3049
|
const session = this.requireSession(sessionId);
|
|
2847
3050
|
const message = this.getOutboxMessage(session, messageId);
|
|
2848
3051
|
if (!message) {
|
|
2849
3052
|
throw new Error(`Outbox message ${messageId} not found`);
|
|
2850
3053
|
}
|
|
3054
|
+
const delivery = message.routeCMatrixDelivery || null;
|
|
3055
|
+
const providerStarted =
|
|
3056
|
+
delivery?.host2_intake_proof?.agent_turn_started === true ||
|
|
3057
|
+
delivery?.agent_turn_started === true ||
|
|
3058
|
+
message.state === "running";
|
|
3059
|
+
if (providerStarted || !["queued", "dispatching"].includes(message.state)) {
|
|
3060
|
+
throw new Error(`Outbox message ${messageId} is not cancelable`);
|
|
3061
|
+
}
|
|
3062
|
+
const dispatchAdapter = message.state === "dispatching"
|
|
3063
|
+
? this.getAdapter(session.provider || session.adapterId || "claude")
|
|
3064
|
+
: null;
|
|
2851
3065
|
if (
|
|
2852
|
-
message.
|
|
2853
|
-
|
|
3066
|
+
message.state === "dispatching" &&
|
|
3067
|
+
typeof dispatchAdapter?.cancelPendingMessage !== "function"
|
|
2854
3068
|
) {
|
|
2855
|
-
throw new Error(
|
|
3069
|
+
throw new Error(
|
|
3070
|
+
`Provider ${session.provider || session.adapterId || "unknown"} cannot prove pre-start cancellation`
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
if (message.state === "dispatching") {
|
|
3074
|
+
const adapterCancellation = await dispatchAdapter.cancelPendingMessage(
|
|
3075
|
+
session,
|
|
3076
|
+
message.id
|
|
3077
|
+
);
|
|
3078
|
+
if (adapterCancellation?.status === "provider_turn_started") {
|
|
3079
|
+
throw new Error(`Outbox message ${messageId} is too late to cancel`);
|
|
3080
|
+
}
|
|
3081
|
+
if (adapterCancellation?.status === "provider_acceptance_unknown") {
|
|
3082
|
+
const error = new Error(
|
|
3083
|
+
`Outbox message ${messageId} provider acceptance is unknown`,
|
|
3084
|
+
);
|
|
3085
|
+
error.code = "PROVIDER_ACCEPTANCE_UNKNOWN";
|
|
3086
|
+
error.providerAcceptance = "unknown";
|
|
3087
|
+
throw error;
|
|
3088
|
+
}
|
|
3089
|
+
if (adapterCancellation?.status !== "pending_message_cancelled") {
|
|
3090
|
+
throw new Error(
|
|
3091
|
+
`Provider ${session.provider || session.adapterId || "unknown"} did not prove pre-start cancellation`
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
if (delivery?.source_user_event_id) {
|
|
3096
|
+
const cancellation = cancelRouteCHost2IntakeForMatrixEvent({
|
|
3097
|
+
hostSessionId: session.id,
|
|
3098
|
+
matrixRoomId: delivery.matrix_room_id,
|
|
3099
|
+
serverEventId: delivery.source_user_event_id,
|
|
3100
|
+
});
|
|
3101
|
+
if (cancellation.status !== 200) {
|
|
3102
|
+
throw new Error(`Outbox message ${messageId} is too late to cancel`);
|
|
3103
|
+
}
|
|
3104
|
+
delivery.host2_intake_proof = cancellation.body?.proof || delivery.host2_intake_proof;
|
|
3105
|
+
}
|
|
3106
|
+
if (message.state === "dispatching") {
|
|
3107
|
+
session._activeOutboxMessageId = null;
|
|
3108
|
+
session._activeOutboxProgressSeen = false;
|
|
3109
|
+
session._activeOutboxProviderVisibleEventSeen = false;
|
|
3110
|
+
session._activeOutboxToolProgressSeen = false;
|
|
2856
3111
|
}
|
|
2857
3112
|
this.emitOutboxCancelControlEvent(session, message, {
|
|
2858
3113
|
semanticType: "control.request",
|
|
@@ -2865,9 +3120,23 @@ export class SessionManager extends EventEmitter {
|
|
|
2865
3120
|
outcome: "accepted",
|
|
2866
3121
|
});
|
|
2867
3122
|
this.emitDeliveryStateNotice(session);
|
|
3123
|
+
this.drainOutbox(session);
|
|
2868
3124
|
return message;
|
|
2869
3125
|
}
|
|
2870
3126
|
|
|
3127
|
+
async reconcileProviderMessageCancellation(sessionId, payload) {
|
|
3128
|
+
const session = this.requireSession(sessionId);
|
|
3129
|
+
const adapter = this.getAdapter(session.provider || session.adapterId || "claude");
|
|
3130
|
+
if (typeof adapter?.reconcilePendingMessageCancellation !== "function") {
|
|
3131
|
+
return {
|
|
3132
|
+
status: "provider_acceptance_unknown",
|
|
3133
|
+
message_id: payload?.messageId || null,
|
|
3134
|
+
reason: "provider_cancellation_reconciliation_unsupported",
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
return adapter.reconcilePendingMessageCancellation(session, payload);
|
|
3138
|
+
}
|
|
3139
|
+
|
|
2871
3140
|
handleOysterunProviderSkillInstallSlashCommand(
|
|
2872
3141
|
session,
|
|
2873
3142
|
userId,
|
|
@@ -3085,6 +3354,7 @@ export class SessionManager extends EventEmitter {
|
|
|
3085
3354
|
}
|
|
3086
3355
|
|
|
3087
3356
|
canDrainOutbox(session) {
|
|
3357
|
+
if (session._stopping === true) return false;
|
|
3088
3358
|
const providerId = session.provider || session.adapterId || "claude";
|
|
3089
3359
|
const allowFreshClaudeStartupDispatch =
|
|
3090
3360
|
providerId === "claude" &&
|
|
@@ -3192,12 +3462,17 @@ export class SessionManager extends EventEmitter {
|
|
|
3192
3462
|
}
|
|
3193
3463
|
this.armClaimedClaudeNoOutputTimer(session, message);
|
|
3194
3464
|
adapter.sendMessage(session, {
|
|
3465
|
+
messageId: message.id,
|
|
3195
3466
|
userId: message.userId,
|
|
3196
3467
|
nickname: message.nickname,
|
|
3197
3468
|
text: message.providerText,
|
|
3198
3469
|
rawText: message.rawText,
|
|
3199
3470
|
isSlashCommand: message.isSlashCommand,
|
|
3200
3471
|
slashCommandName: message.slashCommandName,
|
|
3472
|
+
reconcileProviderAcceptance:
|
|
3473
|
+
message.routeCMatrixDelivery?.delivery_kind === "callback_result" &&
|
|
3474
|
+
message.routeCMatrixDelivery?.session_correlation?.reconcile_provider_acceptance === true,
|
|
3475
|
+
providerTurnId: message.providerTurnId,
|
|
3201
3476
|
});
|
|
3202
3477
|
this.emit(
|
|
3203
3478
|
"runtimeEvent",
|
|
@@ -3887,6 +4162,8 @@ export class SessionManager extends EventEmitter {
|
|
|
3887
4162
|
provider = "claude",
|
|
3888
4163
|
sessionId,
|
|
3889
4164
|
sessionName,
|
|
4165
|
+
tags = [],
|
|
4166
|
+
deferTagsPersistence = false,
|
|
3890
4167
|
parentSessionId = null,
|
|
3891
4168
|
resumeSessionId,
|
|
3892
4169
|
fork = false,
|
|
@@ -4071,6 +4348,8 @@ export class SessionManager extends EventEmitter {
|
|
|
4071
4348
|
session.sandboxMode = session.sandboxMode ?? sandboxMode ?? null;
|
|
4072
4349
|
session.dangerousMode = dangerousMode === true;
|
|
4073
4350
|
session.sessionName = resolvedSessionName;
|
|
4351
|
+
session.tags = normalizeSessionTags(tags);
|
|
4352
|
+
session.tagsPersistenceDeferred = deferTagsPersistence === true;
|
|
4074
4353
|
session.startRequestedAt = new Date().toISOString();
|
|
4075
4354
|
session.historyCreatedAt =
|
|
4076
4355
|
session.providerReadinessDeferred === true
|
|
@@ -4123,6 +4402,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4123
4402
|
session_id: session.id,
|
|
4124
4403
|
session_name: session.sessionName,
|
|
4125
4404
|
parent_session_id: session.parentSessionId,
|
|
4405
|
+
...(session.tagsPersistenceDeferred ? {} : { tags: session.tags }),
|
|
4126
4406
|
agent_id: agentId,
|
|
4127
4407
|
agent_folder: cwd,
|
|
4128
4408
|
runtime: session.provider,
|
|
@@ -4138,6 +4418,8 @@ export class SessionManager extends EventEmitter {
|
|
|
4138
4418
|
fixture_resume_generation: session.fixtureResumeGeneration,
|
|
4139
4419
|
created_at: session.historyCreatedAt,
|
|
4140
4420
|
last_active_at: lastActiveAt,
|
|
4421
|
+
restore_failed_reason: null,
|
|
4422
|
+
restore_failed_at: null,
|
|
4141
4423
|
});
|
|
4142
4424
|
|
|
4143
4425
|
const buildHistoryMetadataSnapshot = () =>
|
|
@@ -4164,6 +4446,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4164
4446
|
session.historyCreatedAt ||= new Date().toISOString();
|
|
4165
4447
|
session.lastActiveAt ||= session.historyCreatedAt;
|
|
4166
4448
|
saveSessionRecord(buildHistoryRecord());
|
|
4449
|
+
this.syncDesiredOpenSession(session);
|
|
4167
4450
|
session.historyRecordId = session.id;
|
|
4168
4451
|
session.historyCommitted = true;
|
|
4169
4452
|
session.historyMetadataSnapshot = buildHistoryMetadataSnapshot();
|
|
@@ -4190,6 +4473,16 @@ export class SessionManager extends EventEmitter {
|
|
|
4190
4473
|
}
|
|
4191
4474
|
this.handleOutboxRuntimeEvent(session, event);
|
|
4192
4475
|
this.syncSessionRuntimeState(session, event);
|
|
4476
|
+
if (
|
|
4477
|
+
event.type === "session.ready" &&
|
|
4478
|
+
this.desiredOpenRestoreOutcomes.get(session.id)?.state !== "failed"
|
|
4479
|
+
) {
|
|
4480
|
+
this.desiredOpenRestoreOutcomes.set(session.id, {
|
|
4481
|
+
session_id: session.id,
|
|
4482
|
+
state: "ready",
|
|
4483
|
+
reason: null,
|
|
4484
|
+
});
|
|
4485
|
+
}
|
|
4193
4486
|
if (event.type === "session.ready" && !session.historyCommitted) {
|
|
4194
4487
|
try {
|
|
4195
4488
|
commitHistoryRecord();
|
|
@@ -4225,6 +4518,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4225
4518
|
buildHistoryRecord(new Date().toISOString())
|
|
4226
4519
|
);
|
|
4227
4520
|
session.historyMetadataSnapshot = nextHistoryMetadata;
|
|
4521
|
+
this.syncDesiredOpenSession(session);
|
|
4228
4522
|
} catch (err) {
|
|
4229
4523
|
recordBookkeepingWarning("history.runtime_sync_failed", err);
|
|
4230
4524
|
}
|
|
@@ -4235,6 +4529,19 @@ export class SessionManager extends EventEmitter {
|
|
|
4235
4529
|
}
|
|
4236
4530
|
});
|
|
4237
4531
|
session.on("error", (err) => {
|
|
4532
|
+
if (this.desiredOpenRestoreOutcomes.has(session.id) &&
|
|
4533
|
+
err.codexRequestOutcome !== "pre_acceptance_rejected") {
|
|
4534
|
+
const authFailure = this.classifyProviderAuthFailure(session, null, {
|
|
4535
|
+
type: "runtime.error",
|
|
4536
|
+
error: err.message || String(err),
|
|
4537
|
+
});
|
|
4538
|
+
this.markDesiredOpenRestoreFailure(session.id, {
|
|
4539
|
+
reason: authFailure
|
|
4540
|
+
? "provider_login_required"
|
|
4541
|
+
: "provider_runtime_restore_failed",
|
|
4542
|
+
error: err.message || String(err),
|
|
4543
|
+
});
|
|
4544
|
+
}
|
|
4238
4545
|
if (this.shouldSuppressExitDerivedRuntimeError(session, err)) {
|
|
4239
4546
|
return;
|
|
4240
4547
|
}
|
|
@@ -4280,6 +4587,12 @@ export class SessionManager extends EventEmitter {
|
|
|
4280
4587
|
this.handleOutboxExit(session);
|
|
4281
4588
|
this.sessions.delete(id);
|
|
4282
4589
|
this.removeAgentSession(agentId, id);
|
|
4590
|
+
if (this.desiredOpenRestoreOutcomes.has(id)) {
|
|
4591
|
+
this.markDesiredOpenRestoreFailure(id, {
|
|
4592
|
+
reason: "provider_runtime_exited",
|
|
4593
|
+
exit_code: code ?? null,
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4283
4596
|
const providerExitEvent = session._lastProviderExitEvent || null;
|
|
4284
4597
|
try {
|
|
4285
4598
|
if (session.historyCommitted) {
|
|
@@ -4620,6 +4933,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4620
4933
|
session.runtimeCapabilities = JSON.parse(
|
|
4621
4934
|
JSON.stringify(runtimeCapabilities)
|
|
4622
4935
|
);
|
|
4936
|
+
this.syncDesiredOpenSession(session);
|
|
4623
4937
|
this.emit(
|
|
4624
4938
|
"runtimeEvent",
|
|
4625
4939
|
session.agentId,
|
|
@@ -4638,6 +4952,13 @@ export class SessionManager extends EventEmitter {
|
|
|
4638
4952
|
return session.runtimeCapabilities;
|
|
4639
4953
|
}
|
|
4640
4954
|
|
|
4955
|
+
updateWorkspacePolicy(sessionId, workspacePolicy) {
|
|
4956
|
+
const session = this.requireSession(sessionId);
|
|
4957
|
+
session.workspacePolicy = cloneJsonObject(workspacePolicy);
|
|
4958
|
+
this.syncDesiredOpenSession(session);
|
|
4959
|
+
return session.workspacePolicy;
|
|
4960
|
+
}
|
|
4961
|
+
|
|
4641
4962
|
renameSession(sessionId, sessionName) {
|
|
4642
4963
|
const session = this.requireSession(sessionId);
|
|
4643
4964
|
const resolvedSessionName = normalizeSessionName(sessionName);
|
|
@@ -4661,6 +4982,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4661
4982
|
last_active_at: new Date().toISOString(),
|
|
4662
4983
|
});
|
|
4663
4984
|
session.sessionName = resolvedSessionName;
|
|
4985
|
+
this.syncDesiredOpenSession(session);
|
|
4664
4986
|
this.emit(
|
|
4665
4987
|
"runtimeEvent",
|
|
4666
4988
|
session.agentId,
|
|
@@ -4830,6 +5152,19 @@ export class SessionManager extends EventEmitter {
|
|
|
4830
5152
|
|
|
4831
5153
|
async stopSession(sessionId) {
|
|
4832
5154
|
const session = this.requireSession(sessionId);
|
|
5155
|
+
// Stop must leave a durable visible cancellation for never-started user
|
|
5156
|
+
// prompts, while callback results retain their own recovery contract.
|
|
5157
|
+
session._stopping = true;
|
|
5158
|
+
const discarded = [];
|
|
5159
|
+
for (const message of session._outbox || []) {
|
|
5160
|
+
if (message.state !== "queued" ||
|
|
5161
|
+
message.routeCMatrixDelivery?.delivery_kind === "callback_result") continue;
|
|
5162
|
+
await this.cancelOutboxMessage(session.id, message.id);
|
|
5163
|
+
discarded.push(message.id);
|
|
5164
|
+
}
|
|
5165
|
+
appendTaskProviderLifecycleDiagnostic("session_stop_queued_prompts", {
|
|
5166
|
+
session_id: session.id, discarded_message_ids: discarded,
|
|
5167
|
+
});
|
|
4833
5168
|
const agentId = session.agentId;
|
|
4834
5169
|
const adapter = this.getAdapter(
|
|
4835
5170
|
session.provider || session.adapterId || "claude"
|
|
@@ -4851,6 +5186,14 @@ export class SessionManager extends EventEmitter {
|
|
|
4851
5186
|
adapter.killSession(session);
|
|
4852
5187
|
await this.waitForSessionExit(session, 5000);
|
|
4853
5188
|
}
|
|
5189
|
+
this.desiredOpenStateOwner.remove(sessionId);
|
|
5190
|
+
this.desiredOpenRestoreOutcomes.delete(sessionId);
|
|
5191
|
+
}
|
|
5192
|
+
|
|
5193
|
+
removeDesiredOpenSession(sessionId) {
|
|
5194
|
+
const state = this.desiredOpenStateOwner.remove(sessionId);
|
|
5195
|
+
this.desiredOpenRestoreOutcomes.delete(sessionId);
|
|
5196
|
+
return state;
|
|
4854
5197
|
}
|
|
4855
5198
|
|
|
4856
5199
|
/**
|
|
@@ -4934,6 +5277,7 @@ export class SessionManager extends EventEmitter {
|
|
|
4934
5277
|
provider: nextProvider,
|
|
4935
5278
|
sessionId: nextSessionId,
|
|
4936
5279
|
sessionName: overrides.sessionName ?? session.sessionName,
|
|
5280
|
+
tags: overrides.tags ?? session.tags,
|
|
4937
5281
|
resumeSessionId: providerResumeId,
|
|
4938
5282
|
resume: providerResumeId !== undefined,
|
|
4939
5283
|
model:
|
|
@@ -4976,52 +5320,120 @@ export class SessionManager extends EventEmitter {
|
|
|
4976
5320
|
|
|
4977
5321
|
snapshotHostRestartState() {
|
|
4978
5322
|
return [...this.sessions.entries()].map(([sessionId, session]) => ({
|
|
4979
|
-
|
|
4980
|
-
session_name: session.sessionName || null,
|
|
4981
|
-
agent_id: session.agentId,
|
|
4982
|
-
agent_folder: session.cwd,
|
|
4983
|
-
provider: session.provider || "claude",
|
|
4984
|
-
model: session.model ?? null,
|
|
4985
|
-
fixture_resume_generation:
|
|
4986
|
-
session.resumeIdentityKind === HOST_SESSION_RESUME_IDENTITY_KIND
|
|
4987
|
-
? normalizeFixtureResumeGeneration(session.fixtureResumeGeneration)
|
|
4988
|
-
: null,
|
|
4989
|
-
reasoning_effort: session.reasoningEffort ?? null,
|
|
4990
|
-
permission_mode: session.permissionMode ?? null,
|
|
4991
|
-
approval_policy: session.approvalPolicy ?? null,
|
|
4992
|
-
sandbox_mode: session.sandboxMode ?? null,
|
|
4993
|
-
dangerous_mode: session.dangerousMode === true,
|
|
4994
|
-
provider_resume_id: this.getEffectiveProviderResumeId(session),
|
|
4995
|
-
provider_thread_id: session.threadId || null,
|
|
4996
|
-
provider_transport: session.transport || null,
|
|
4997
|
-
runtime_capabilities: cloneJsonObject(session.runtimeCapabilities) || {},
|
|
5323
|
+
...this.buildDesiredOpenSessionRecord(sessionId, session),
|
|
4998
5324
|
runtime_capability_grant: cloneJsonObject(session.runtimeCapabilityGrant),
|
|
4999
|
-
reasoning_effort_source: session.reasoningEffortSource || null,
|
|
5000
5325
|
native: cloneJsonObject(session.native) || session.native || null,
|
|
5001
|
-
workspace_policy: cloneJsonObject(session.workspacePolicy),
|
|
5002
|
-
asset_readable_paths: Array.isArray(session.assetReadablePaths)
|
|
5003
|
-
? [...session.assetReadablePaths]
|
|
5004
|
-
: [],
|
|
5005
5326
|
notification_config: serializeHostRestartNotificationConfig(
|
|
5006
5327
|
session.notificationConfig
|
|
5007
5328
|
),
|
|
5008
5329
|
notification_config_updated_at: normalizeHostRestartString(
|
|
5009
5330
|
session.notificationConfigUpdatedAt
|
|
5010
5331
|
),
|
|
5011
|
-
search_enabled: session.searchEnabled === true,
|
|
5012
|
-
image_input_enabled: session.imageInputEnabled === true,
|
|
5013
|
-
allow_dangerously_skip_permissions:
|
|
5014
|
-
session.allowDangerouslySkipPermissions === true,
|
|
5015
5332
|
alive_before_restart: session.alive === true,
|
|
5016
5333
|
ready_before_restart: session._ready === true,
|
|
5017
|
-
started_at: session.historyCreatedAt || session.startedAt || null,
|
|
5018
|
-
last_active_at:
|
|
5019
|
-
session.lastActiveAt || session.historyCreatedAt || session.startedAt || null,
|
|
5020
5334
|
restore_policy: "provider_runtime_resume_on_boot",
|
|
5021
5335
|
prompt_replay: false,
|
|
5022
5336
|
}));
|
|
5023
5337
|
}
|
|
5024
5338
|
|
|
5339
|
+
restoreDesiredOpenSessions({
|
|
5340
|
+
buildRuntimeCapabilityGrant = null,
|
|
5341
|
+
reconciliationId = null,
|
|
5342
|
+
sessionIds = null,
|
|
5343
|
+
} = {}) {
|
|
5344
|
+
const desired = this.desiredOpenStateOwner.read();
|
|
5345
|
+
if (!desired.valid) {
|
|
5346
|
+
return {
|
|
5347
|
+
restored: [],
|
|
5348
|
+
skipped: [],
|
|
5349
|
+
failed: [{ session_id: null, reason: desired.status }],
|
|
5350
|
+
};
|
|
5351
|
+
}
|
|
5352
|
+
const selectedIds = Array.isArray(sessionIds) ? new Set(sessionIds) : null;
|
|
5353
|
+
const eligible = [];
|
|
5354
|
+
const failed = [];
|
|
5355
|
+
for (const record of desired.state.sessions) {
|
|
5356
|
+
if (selectedIds && !selectedIds.has(record.session_id)) continue;
|
|
5357
|
+
if (record.restore_failed_reason) {
|
|
5358
|
+
this.desiredOpenRestoreOutcomes.set(record.session_id, {
|
|
5359
|
+
...record,
|
|
5360
|
+
session_id: record.session_id,
|
|
5361
|
+
state: "failed",
|
|
5362
|
+
reason: record.restore_failed_reason,
|
|
5363
|
+
});
|
|
5364
|
+
continue;
|
|
5365
|
+
}
|
|
5366
|
+
this.desiredOpenRestoreOutcomes.set(record.session_id, {
|
|
5367
|
+
session_id: record.session_id,
|
|
5368
|
+
state: "restoring",
|
|
5369
|
+
reason: null,
|
|
5370
|
+
reconciliation_id: reconciliationId,
|
|
5371
|
+
});
|
|
5372
|
+
if (!existsSync(record.agent_folder)) {
|
|
5373
|
+
const failure = {
|
|
5374
|
+
session_id: record.session_id,
|
|
5375
|
+
agent_id: record.agent_id,
|
|
5376
|
+
reason: "agent_folder_missing",
|
|
5377
|
+
};
|
|
5378
|
+
failed.push(failure);
|
|
5379
|
+
this.desiredOpenRestoreOutcomes.set(record.session_id, {
|
|
5380
|
+
...record,
|
|
5381
|
+
...failure,
|
|
5382
|
+
state: "failed",
|
|
5383
|
+
reconciliation_id: reconciliationId,
|
|
5384
|
+
});
|
|
5385
|
+
continue;
|
|
5386
|
+
}
|
|
5387
|
+
eligible.push({
|
|
5388
|
+
...record,
|
|
5389
|
+
alive_before_restart: true,
|
|
5390
|
+
ready_before_restart: true,
|
|
5391
|
+
prompt_replay: false,
|
|
5392
|
+
});
|
|
5393
|
+
}
|
|
5394
|
+
const result = this.restoreHostRestartState(eligible, {
|
|
5395
|
+
restartId: reconciliationId || `desired-open-${randomUUID()}`,
|
|
5396
|
+
buildRuntimeCapabilityGrant,
|
|
5397
|
+
});
|
|
5398
|
+
for (const entry of result.restored) {
|
|
5399
|
+
const session = this.getSession(entry.session_id);
|
|
5400
|
+
this.desiredOpenRestoreOutcomes.set(entry.session_id, {
|
|
5401
|
+
...desired.state.sessions.find(
|
|
5402
|
+
(record) => record.session_id === entry.session_id
|
|
5403
|
+
),
|
|
5404
|
+
session_id: entry.session_id,
|
|
5405
|
+
state: session?._ready === true ? "ready" : "restoring",
|
|
5406
|
+
reason: null,
|
|
5407
|
+
reconciliation_id: reconciliationId,
|
|
5408
|
+
});
|
|
5409
|
+
}
|
|
5410
|
+
for (const entry of result.skipped) {
|
|
5411
|
+
const session = this.getSession(entry.session_id);
|
|
5412
|
+
this.desiredOpenRestoreOutcomes.set(entry.session_id, {
|
|
5413
|
+
...desired.state.sessions.find(
|
|
5414
|
+
(record) => record.session_id === entry.session_id
|
|
5415
|
+
),
|
|
5416
|
+
...entry,
|
|
5417
|
+
state:
|
|
5418
|
+
entry.reason === "already_live" && session?._ready !== true
|
|
5419
|
+
? "restoring"
|
|
5420
|
+
: "ready",
|
|
5421
|
+
reconciliation_id: reconciliationId,
|
|
5422
|
+
});
|
|
5423
|
+
}
|
|
5424
|
+
for (const entry of result.failed) {
|
|
5425
|
+
this.desiredOpenRestoreOutcomes.set(entry.session_id, {
|
|
5426
|
+
...desired.state.sessions.find(
|
|
5427
|
+
(record) => record.session_id === entry.session_id
|
|
5428
|
+
),
|
|
5429
|
+
...entry,
|
|
5430
|
+
state: "failed",
|
|
5431
|
+
reconciliation_id: reconciliationId,
|
|
5432
|
+
});
|
|
5433
|
+
}
|
|
5434
|
+
return { ...result, failed: [...failed, ...result.failed] };
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5025
5437
|
restoreHostRestartState(
|
|
5026
5438
|
sessions = [],
|
|
5027
5439
|
{ restartId, restoredAt, buildRuntimeCapabilityGrant = null } = {}
|
|
@@ -5051,6 +5463,14 @@ export class SessionManager extends EventEmitter {
|
|
|
5051
5463
|
});
|
|
5052
5464
|
continue;
|
|
5053
5465
|
}
|
|
5466
|
+
if (!existsSync(agentFolder)) {
|
|
5467
|
+
failed.push({
|
|
5468
|
+
session_id: id,
|
|
5469
|
+
agent_id: agentId,
|
|
5470
|
+
reason: "agent_folder_missing",
|
|
5471
|
+
});
|
|
5472
|
+
continue;
|
|
5473
|
+
}
|
|
5054
5474
|
const wasLiveOrReady =
|
|
5055
5475
|
record.alive_before_restart === true ||
|
|
5056
5476
|
record.ready_before_restart === true;
|
|
@@ -5143,6 +5563,7 @@ export class SessionManager extends EventEmitter {
|
|
|
5143
5563
|
provider,
|
|
5144
5564
|
sessionId: id,
|
|
5145
5565
|
sessionName: record.session_name || id,
|
|
5566
|
+
tags: canonicalSessionTags(record),
|
|
5146
5567
|
resumeSessionId: providerResumeId,
|
|
5147
5568
|
resume: true,
|
|
5148
5569
|
fork: false,
|
|
@@ -5247,6 +5668,7 @@ export class SessionManager extends EventEmitter {
|
|
|
5247
5668
|
session_id: id,
|
|
5248
5669
|
session_name: session.sessionName,
|
|
5249
5670
|
parent_session_id: session.parentSessionId || null,
|
|
5671
|
+
tags: canonicalSessionTags(session),
|
|
5250
5672
|
agent_id: agentId,
|
|
5251
5673
|
agent_folder: session.cwd,
|
|
5252
5674
|
runtime: session.provider,
|
|
@@ -5273,6 +5695,18 @@ export class SessionManager extends EventEmitter {
|
|
|
5273
5695
|
prompt_replay: false,
|
|
5274
5696
|
});
|
|
5275
5697
|
}
|
|
5698
|
+
for (const failure of failed) {
|
|
5699
|
+
if (!failure.session_id) continue;
|
|
5700
|
+
const record = sessions.find(
|
|
5701
|
+
(entry) => entry?.session_id === failure.session_id
|
|
5702
|
+
);
|
|
5703
|
+
this.finalizeDesiredOpenRestoreFailure(failure.session_id, {
|
|
5704
|
+
...(record || {}),
|
|
5705
|
+
...failure,
|
|
5706
|
+
reason: failure.reason,
|
|
5707
|
+
reconciliation_id: restartId || null,
|
|
5708
|
+
});
|
|
5709
|
+
}
|
|
5276
5710
|
return { restored, skipped, failed };
|
|
5277
5711
|
}
|
|
5278
5712
|
|
|
@@ -5303,6 +5737,7 @@ export class SessionManager extends EventEmitter {
|
|
|
5303
5737
|
return [...this.sessions.entries()].map(([id, s]) => ({
|
|
5304
5738
|
sessionId: id,
|
|
5305
5739
|
sessionName: s.sessionName || null,
|
|
5740
|
+
tags: canonicalSessionTags(s),
|
|
5306
5741
|
agentId: s.agentId,
|
|
5307
5742
|
provider: s.provider || "claude",
|
|
5308
5743
|
cwd: s.cwd,
|