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
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "fs";
|
|
14
14
|
import { basename, dirname, join } from "path";
|
|
15
15
|
import { homedir } from "os";
|
|
16
|
+
import { routeCMatrixJsonStoragePath } from "./routec-matrix-storage-paths.mjs";
|
|
16
17
|
import {
|
|
17
18
|
getRouteCMatrixActorByKey,
|
|
18
19
|
getRouteCMatrixActorByUserId,
|
|
@@ -26,14 +27,20 @@ import {
|
|
|
26
27
|
} from "./tool-event-transfer-projection.mjs";
|
|
27
28
|
import { ROUTEC_TOOL_EVENT_DETAIL_SELECTED_DETAIL_LIMIT_BYTES } from "./tool-event-detail-store.mjs";
|
|
28
29
|
import { readCompletedTurnFinalMessageNeighborsFromSQLite } from "./completed-turn-navigation.mjs";
|
|
30
|
+
import {
|
|
31
|
+
decorateMatrixEventsWithCompletedTurnProjection,
|
|
32
|
+
MATRIX_COMPLETED_TURN_PROJECTION_KEY,
|
|
33
|
+
} from "./completed-turn-service.mjs";
|
|
29
34
|
import {
|
|
30
35
|
MATRIX_SQLITE_SCHEMA_VERSION,
|
|
36
|
+
routeCMatrixSQLiteRoomReadContextDB,
|
|
31
37
|
exportSQLiteStoreObject,
|
|
32
38
|
getLastRouteCMatrixSQLiteMigrationProof,
|
|
33
39
|
getRouteCMatrixSQLiteStorageHealth,
|
|
34
40
|
getRouteCMatrixSQLiteStoragePath,
|
|
35
41
|
insertRouteCMatrixSQLiteTxnMapping,
|
|
36
42
|
openRouteCMatrixSQLiteStore,
|
|
43
|
+
openRouteCMatrixSQLiteReadOnlyStore,
|
|
37
44
|
readRouteCMatrixSQLiteContiguousSemanticEvents,
|
|
38
45
|
readRouteCMatrixSQLiteContextWindow,
|
|
39
46
|
readRouteCMatrixSQLiteCurrentStateSince,
|
|
@@ -45,6 +52,7 @@ import {
|
|
|
45
52
|
readRouteCMatrixSQLiteMessagesWindow,
|
|
46
53
|
readRouteCMatrixSQLiteLatestPreviewRows,
|
|
47
54
|
readRouteCMatrixSQLiteLatestTimelineSeq,
|
|
55
|
+
readRouteCMatrixSQLiteLatestTimelineSeqs,
|
|
48
56
|
readRouteCMatrixSQLiteNextStreamSeq,
|
|
49
57
|
readRouteCMatrixSQLiteRoom,
|
|
50
58
|
readRouteCMatrixSQLiteStateEvent,
|
|
@@ -904,16 +912,8 @@ function isObject(value) {
|
|
|
904
912
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
905
913
|
}
|
|
906
914
|
|
|
907
|
-
function stackRootFromConfigDir() {
|
|
908
|
-
return basename(CONFIG_DIR) === "host" ? dirname(CONFIG_DIR) : CONFIG_DIR;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
915
|
export function getRouteCMatrixStoragePath() {
|
|
912
|
-
|
|
913
|
-
if (typeof configured === "string" && configured.trim()) {
|
|
914
|
-
return configured.trim();
|
|
915
|
-
}
|
|
916
|
-
return join(stackRootFromConfigDir(), "matrix", "homeserver.db");
|
|
916
|
+
return routeCMatrixJsonStoragePath(CONFIG_DIR, process.env.OYSTERUN_ROUTEC_MATRIX_STORAGE_PATH);
|
|
917
917
|
}
|
|
918
918
|
|
|
919
919
|
export function getRouteCMatrixStorageDeltaPath(
|
|
@@ -2292,7 +2292,37 @@ function ensureRoomInStore(store, binding) {
|
|
|
2292
2292
|
return room;
|
|
2293
2293
|
}
|
|
2294
2294
|
|
|
2295
|
+
const transcriptReadOnlyScope = new AsyncLocalStorage();
|
|
2296
|
+
|
|
2297
|
+
// Synchronous adapter consumers share one read-only DB and one bound room.
|
|
2298
|
+
// No cached writable opener, schema initializer, or room creator is used.
|
|
2299
|
+
export function withRouteCMatrixTranscriptReadOnly({ binding }, read) {
|
|
2300
|
+
requireBinding(binding);
|
|
2301
|
+
const db = openRouteCMatrixSQLiteReadOnlyStore({ jsonStoragePath: getRouteCMatrixStoragePath() });
|
|
2302
|
+
try {
|
|
2303
|
+
const room = readRouteCMatrixSQLiteRoom(db, { roomId: binding.matrix_room_id });
|
|
2304
|
+
if (!room) {
|
|
2305
|
+
const error = new Error("Durable Matrix transcript room is unavailable");
|
|
2306
|
+
error.code = "MATRIX_TRANSCRIPT_ROOM_UNAVAILABLE";
|
|
2307
|
+
throw error;
|
|
2308
|
+
}
|
|
2309
|
+
if (room.host_session_id !== binding.host_session_id || room.host_agent_id !== binding.host_agent_id) {
|
|
2310
|
+
const error = new Error("Durable Matrix transcript room ownership does not match binding");
|
|
2311
|
+
error.code = "MATRIX_TRANSCRIPT_BINDING_MISMATCH";
|
|
2312
|
+
throw error;
|
|
2313
|
+
}
|
|
2314
|
+
validateBoundRoomRecord(room, binding);
|
|
2315
|
+
return transcriptReadOnlyScope.run({ db, roomId: binding.matrix_room_id }, () => {
|
|
2316
|
+
const result = read();
|
|
2317
|
+
if (result?.then) throw new Error("Transcript read-only scope requires synchronous reads");
|
|
2318
|
+
return result;
|
|
2319
|
+
});
|
|
2320
|
+
} finally { db.close(); }
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2295
2323
|
function openRouteCMatrixAdapterSQLiteStore() {
|
|
2324
|
+
const scope = transcriptReadOnlyScope.getStore();
|
|
2325
|
+
if (scope) return scope.db;
|
|
2296
2326
|
return openRouteCMatrixSQLiteStore({
|
|
2297
2327
|
jsonStoragePath: getRouteCMatrixStoragePath(),
|
|
2298
2328
|
});
|
|
@@ -2426,12 +2456,14 @@ export function getRouteCMatrixRoomTimelineReplaySourceProof({
|
|
|
2426
2456
|
};
|
|
2427
2457
|
}
|
|
2428
2458
|
|
|
2429
|
-
export function readRouteCMatrixRoomLatestPreviewCheckpoint({ binding }) {
|
|
2459
|
+
export function readRouteCMatrixRoomLatestPreviewCheckpoint({ binding, readContext = null }) {
|
|
2430
2460
|
requireBinding(binding);
|
|
2431
|
-
const db = openRouteCMatrixAdapterSQLiteStore()
|
|
2461
|
+
const db = readContext === null ? openRouteCMatrixAdapterSQLiteStore()
|
|
2462
|
+
: routeCMatrixSQLiteRoomReadContextDB(readContext, binding.matrix_room_id);
|
|
2432
2463
|
return {
|
|
2433
2464
|
latest_committed_seq: readRouteCMatrixSQLiteLatestTimelineSeq(db, {
|
|
2434
2465
|
roomId: binding.matrix_room_id,
|
|
2466
|
+
readContext,
|
|
2435
2467
|
}),
|
|
2436
2468
|
dedicated_preview_query: true,
|
|
2437
2469
|
full_transcript_pagination_used: false,
|
|
@@ -2440,11 +2472,29 @@ export function readRouteCMatrixRoomLatestPreviewCheckpoint({ binding }) {
|
|
|
2440
2472
|
};
|
|
2441
2473
|
}
|
|
2442
2474
|
|
|
2443
|
-
export function
|
|
2444
|
-
|
|
2475
|
+
export function readRouteCMatrixRoomLatestPreviewCheckpoints({ bindings }) {
|
|
2476
|
+
const selected = [];
|
|
2477
|
+
const seen = new Set();
|
|
2478
|
+
for (const binding of Array.isArray(bindings) ? bindings : []) {
|
|
2479
|
+
requireBinding(binding);
|
|
2480
|
+
if (!seen.has(binding.matrix_room_id)) {
|
|
2481
|
+
seen.add(binding.matrix_room_id);
|
|
2482
|
+
selected.push(binding);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2445
2485
|
const db = openRouteCMatrixAdapterSQLiteStore();
|
|
2486
|
+
return readRouteCMatrixSQLiteLatestTimelineSeqs(db, {
|
|
2487
|
+
roomIds: selected.map((binding) => binding.matrix_room_id),
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
export function readRouteCMatrixRoomLatestPreviewRows({ binding, limit = 20, readContext = null }) {
|
|
2492
|
+
requireBinding(binding);
|
|
2493
|
+
const db = readContext === null ? openRouteCMatrixAdapterSQLiteStore()
|
|
2494
|
+
: routeCMatrixSQLiteRoomReadContextDB(readContext, binding.matrix_room_id);
|
|
2446
2495
|
const rows = readRouteCMatrixSQLiteLatestPreviewRows(db, {
|
|
2447
2496
|
roomId: binding.matrix_room_id,
|
|
2497
|
+
readContext,
|
|
2448
2498
|
limit,
|
|
2449
2499
|
});
|
|
2450
2500
|
return {
|
|
@@ -2477,7 +2527,11 @@ export function readRouteCMatrixRoomTimelinePreview({
|
|
|
2477
2527
|
});
|
|
2478
2528
|
const countLimited = candidates.length > normalizedLimit;
|
|
2479
2529
|
const preBudgetChunk = candidates.slice(0, normalizedLimit).reverse();
|
|
2480
|
-
const preBudgetClientEvents =
|
|
2530
|
+
const preBudgetClientEvents = eventsForClientMatrixRead(
|
|
2531
|
+
db,
|
|
2532
|
+
preBudgetChunk,
|
|
2533
|
+
binding
|
|
2534
|
+
);
|
|
2481
2535
|
const budgeted = selectNewestClientEventsWithinTransferBudget({
|
|
2482
2536
|
rawEvents: preBudgetChunk,
|
|
2483
2537
|
clientEvents: preBudgetClientEvents,
|
|
@@ -3335,6 +3389,100 @@ function eventForClientMatrixRead(event) {
|
|
|
3335
3389
|
return clientEvent;
|
|
3336
3390
|
}
|
|
3337
3391
|
|
|
3392
|
+
function eventsForClientMatrixRead(db, events, binding) {
|
|
3393
|
+
const clientEvents = events.map(eventForClientMatrixRead);
|
|
3394
|
+
return decorateMatrixEventsWithCompletedTurnProjection(db, clientEvents, {
|
|
3395
|
+
roomId: binding.matrix_room_id,
|
|
3396
|
+
hostSessionId: binding.host_session_id,
|
|
3397
|
+
getSession: (sessionId) =>
|
|
3398
|
+
sessionId === binding.host_session_id
|
|
3399
|
+
? {
|
|
3400
|
+
id: binding.host_session_id,
|
|
3401
|
+
sessionName: binding.host_session_name,
|
|
3402
|
+
}
|
|
3403
|
+
: null,
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
function parseMatrixObject(value) {
|
|
3408
|
+
if (isObject(value)) return value;
|
|
3409
|
+
if (typeof value !== "string") return {};
|
|
3410
|
+
try {
|
|
3411
|
+
const parsed = JSON.parse(value);
|
|
3412
|
+
return isObject(parsed) ? parsed : {};
|
|
3413
|
+
} catch {
|
|
3414
|
+
return {};
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
function completedAssistantRecoveryEventIds(clientEvents) {
|
|
3419
|
+
const loadedEventIds = new Set(
|
|
3420
|
+
clientEvents
|
|
3421
|
+
.map((event) => normalizeNonEmptyString(event?.event_id))
|
|
3422
|
+
.filter(Boolean)
|
|
3423
|
+
);
|
|
3424
|
+
const recoveryEventIds = [];
|
|
3425
|
+
for (const event of clientEvents) {
|
|
3426
|
+
const unsigned = parseMatrixObject(event?.unsigned);
|
|
3427
|
+
const projection = unsigned[MATRIX_COMPLETED_TURN_PROJECTION_KEY];
|
|
3428
|
+
if (
|
|
3429
|
+
!isObject(projection) ||
|
|
3430
|
+
projection.schema !== "local_turn_completion.v1" ||
|
|
3431
|
+
projection.successful !== true ||
|
|
3432
|
+
projection.conflict === true
|
|
3433
|
+
) {
|
|
3434
|
+
continue;
|
|
3435
|
+
}
|
|
3436
|
+
const eventId = normalizeNonEmptyString(projection.final_assistant_event_id);
|
|
3437
|
+
if (!eventId || loadedEventIds.has(eventId)) continue;
|
|
3438
|
+
loadedEventIds.add(eventId);
|
|
3439
|
+
recoveryEventIds.push(eventId);
|
|
3440
|
+
}
|
|
3441
|
+
return recoveryEventIds;
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
function recoverCompletedAssistantSyncCompanions({
|
|
3445
|
+
db,
|
|
3446
|
+
binding,
|
|
3447
|
+
rawEvents,
|
|
3448
|
+
clientEvents,
|
|
3449
|
+
}) {
|
|
3450
|
+
const recoveredRawEvents = [];
|
|
3451
|
+
for (const eventId of completedAssistantRecoveryEventIds(clientEvents)) {
|
|
3452
|
+
const event = readRouteCMatrixSQLiteEvent(db, {
|
|
3453
|
+
roomId: binding.matrix_room_id,
|
|
3454
|
+
eventId,
|
|
3455
|
+
});
|
|
3456
|
+
const content = parseMatrixObject(event?.content_json ?? event?.content);
|
|
3457
|
+
const semantic = content[OYSTERUN_SEMANTIC_NAMESPACE];
|
|
3458
|
+
if (
|
|
3459
|
+
!event ||
|
|
3460
|
+
event.type !== "m.room.message" ||
|
|
3461
|
+
!isObject(semantic) ||
|
|
3462
|
+
semantic.semantic_type !== "message.assistant" ||
|
|
3463
|
+
content.delta === true
|
|
3464
|
+
) {
|
|
3465
|
+
continue;
|
|
3466
|
+
}
|
|
3467
|
+
recoveredRawEvents.push(event);
|
|
3468
|
+
}
|
|
3469
|
+
if (recoveredRawEvents.length === 0) {
|
|
3470
|
+
return { rawEvents, clientEvents, recoveredCount: 0 };
|
|
3471
|
+
}
|
|
3472
|
+
const byEventId = new Map();
|
|
3473
|
+
for (const event of [...rawEvents, ...recoveredRawEvents]) {
|
|
3474
|
+
byEventId.set(event.event_id, event);
|
|
3475
|
+
}
|
|
3476
|
+
const mergedRawEvents = [...byEventId.values()].sort(
|
|
3477
|
+
(left, right) => eventStreamSeq(left) - eventStreamSeq(right)
|
|
3478
|
+
);
|
|
3479
|
+
return {
|
|
3480
|
+
rawEvents: mergedRawEvents,
|
|
3481
|
+
clientEvents: eventsForClientMatrixRead(db, mergedRawEvents, binding),
|
|
3482
|
+
recoveredCount: mergedRawEvents.length - rawEvents.length,
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3338
3486
|
function summarizeClientTransferBytes(clientEvents) {
|
|
3339
3487
|
const eventByteCounts = clientEvents
|
|
3340
3488
|
.map((event) => safeJsonByteLength(event))
|
|
@@ -4199,6 +4347,8 @@ function selectMessagesChunk({ events, startSeq, toSeq, direction, limit }) {
|
|
|
4199
4347
|
}
|
|
4200
4348
|
|
|
4201
4349
|
function applyMessagesTransferBudgetToWindow({
|
|
4350
|
+
db,
|
|
4351
|
+
binding,
|
|
4202
4352
|
chunk,
|
|
4203
4353
|
countLimited,
|
|
4204
4354
|
startSeq,
|
|
@@ -4206,7 +4356,7 @@ function applyMessagesTransferBudgetToWindow({
|
|
|
4206
4356
|
direction,
|
|
4207
4357
|
budgetBytes = P135_MATRIX_CLIENT_TRANSFER_BUDGET_BYTES,
|
|
4208
4358
|
}) {
|
|
4209
|
-
const clientEvents = chunk
|
|
4359
|
+
const clientEvents = eventsForClientMatrixRead(db, chunk, binding);
|
|
4210
4360
|
const selectedRaw = [];
|
|
4211
4361
|
const selectedClient = [];
|
|
4212
4362
|
const selectedByteCounts = [];
|
|
@@ -4392,6 +4542,10 @@ function clientTransferBudgetProof({
|
|
|
4392
4542
|
messages_gap_fill_direction: timeline.limited ? "b" : null,
|
|
4393
4543
|
p117_current_since_long_poll_preserved: true,
|
|
4394
4544
|
p131_projection_boundary_preserved: true,
|
|
4545
|
+
completed_assistant_recovery_count:
|
|
4546
|
+
transferBudget?.completedAssistantRecoveryCount ?? 0,
|
|
4547
|
+
completed_assistant_recovery_contract:
|
|
4548
|
+
"successful_completed_turn_final_assistant_companion_v1",
|
|
4395
4549
|
p82_large_tool_spillover_boundary_preserved: true,
|
|
4396
4550
|
foundation_pass_claimed: false,
|
|
4397
4551
|
};
|
|
@@ -4461,15 +4615,41 @@ function buildSyncBody({
|
|
|
4461
4615
|
sinceSeq,
|
|
4462
4616
|
limited: sqliteTimeline.limited,
|
|
4463
4617
|
});
|
|
4464
|
-
const initialClientTimelineEvents =
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4618
|
+
const initialClientTimelineEvents = eventsForClientMatrixRead(
|
|
4619
|
+
db,
|
|
4620
|
+
initialTimeline.events,
|
|
4621
|
+
binding
|
|
4622
|
+
);
|
|
4623
|
+
const budgetedTimeline = applyTransferBudgetToSyncTimeline({
|
|
4470
4624
|
timeline: initialTimeline,
|
|
4471
4625
|
clientEvents: initialClientTimelineEvents,
|
|
4472
4626
|
});
|
|
4627
|
+
const completionRecovery = recoverCompletedAssistantSyncCompanions({
|
|
4628
|
+
db,
|
|
4629
|
+
binding,
|
|
4630
|
+
rawEvents: budgetedTimeline.timeline.events,
|
|
4631
|
+
clientEvents: budgetedTimeline.clientEvents,
|
|
4632
|
+
});
|
|
4633
|
+
const timeline =
|
|
4634
|
+
completionRecovery.recoveredCount > 0
|
|
4635
|
+
? {
|
|
4636
|
+
...budgetedTimeline.timeline,
|
|
4637
|
+
events: completionRecovery.rawEvents,
|
|
4638
|
+
oldestReturnedSeq: eventSeqOrNull(completionRecovery.rawEvents[0]),
|
|
4639
|
+
newestReturnedSeq: eventSeqOrNull(
|
|
4640
|
+
completionRecovery.rawEvents.at(-1)
|
|
4641
|
+
),
|
|
4642
|
+
prevBatchSeq:
|
|
4643
|
+
eventSeqOrNull(completionRecovery.rawEvents[0]) ??
|
|
4644
|
+
budgetedTimeline.timeline.prevBatchSeq,
|
|
4645
|
+
budgetMode: `${budgetedTimeline.timeline.budgetMode}_completed_assistant_recovery`,
|
|
4646
|
+
}
|
|
4647
|
+
: budgetedTimeline.timeline;
|
|
4648
|
+
const clientTimelineEvents = completionRecovery.clientEvents;
|
|
4649
|
+
const transferBudget = {
|
|
4650
|
+
...budgetedTimeline.transferBudget,
|
|
4651
|
+
completedAssistantRecoveryCount: completionRecovery.recoveredCount,
|
|
4652
|
+
};
|
|
4473
4653
|
const memberships = roomMembershipEvents(binding, room);
|
|
4474
4654
|
const stateEvents = sinceSeq === null
|
|
4475
4655
|
? roomStateEvents(binding, room)
|
|
@@ -4595,7 +4775,7 @@ function readEvent({ binding, eventId }) {
|
|
|
4595
4775
|
if (!event) {
|
|
4596
4776
|
return null;
|
|
4597
4777
|
}
|
|
4598
|
-
return
|
|
4778
|
+
return eventsForClientMatrixRead(db, [event], binding)[0];
|
|
4599
4779
|
}
|
|
4600
4780
|
|
|
4601
4781
|
export function readRouteCMatrixEvent({ binding, eventId }) {
|
|
@@ -4630,6 +4810,8 @@ export function readRouteCMatrixRoomMessages({
|
|
|
4630
4810
|
limit: normalizedLimit,
|
|
4631
4811
|
});
|
|
4632
4812
|
const selected = applyMessagesTransferBudgetToWindow({
|
|
4813
|
+
db,
|
|
4814
|
+
binding,
|
|
4633
4815
|
chunk: window.chunk,
|
|
4634
4816
|
countLimited: window.countLimited,
|
|
4635
4817
|
startSeq,
|
|
@@ -4771,9 +4953,9 @@ export function readRouteCMatrixRoomContext({
|
|
|
4771
4953
|
start,
|
|
4772
4954
|
end,
|
|
4773
4955
|
state: roomStateEvents(binding, room),
|
|
4774
|
-
events_before: beforeRaw
|
|
4775
|
-
events_after: afterRaw
|
|
4776
|
-
event:
|
|
4956
|
+
events_before: eventsForClientMatrixRead(db, beforeRaw, binding),
|
|
4957
|
+
events_after: eventsForClientMatrixRead(db, afterRaw, binding),
|
|
4958
|
+
event: eventsForClientMatrixRead(db, [targetEvent], binding)[0],
|
|
4777
4959
|
...(diagnosticsEnabled
|
|
4778
4960
|
? {
|
|
4779
4961
|
routec_context_focus_proof: {
|
|
@@ -4914,6 +5096,7 @@ export async function handleRouteCMatrixStorageRequest({
|
|
|
4914
5096
|
}) {
|
|
4915
5097
|
const effectiveBinding = binding || {
|
|
4916
5098
|
host_session_id: tokenRecord?.host_session_id,
|
|
5099
|
+
host_session_name: tokenRecord?.host_session_name,
|
|
4917
5100
|
host_agent_id: tokenRecord?.host_agent_id,
|
|
4918
5101
|
matrix_room_id: tokenRecord?.matrix_room_id,
|
|
4919
5102
|
matrix_user_id: tokenRecord?.matrix_user_id,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { basename, dirname, join } from "node:path";
|
|
2
|
+
|
|
3
|
+
// Pure path contract shared by storage and lifecycle ownership inspection.
|
|
4
|
+
export function routeCMatrixJsonStoragePath(configDir, configuredPath) {
|
|
5
|
+
if (typeof configuredPath === "string" && configuredPath.trim()) return configuredPath.trim();
|
|
6
|
+
const stackRoot = basename(configDir) === "host" ? dirname(configDir) : configDir;
|
|
7
|
+
return join(stackRoot, "matrix", "homeserver.db");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function routeCMatrixSQLiteStoragePath(jsonStoragePath) {
|
|
11
|
+
return join(dirname(jsonStoragePath), "homeserver.sqlite");
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { monitorEventLoopDelay, performance } from "node:perf_hooks";
|
|
2
|
+
|
|
3
|
+
export function createRouteCRequestDelayWindowController({
|
|
4
|
+
createMonitor = () => monitorEventLoopDelay({ resolution: 10 }),
|
|
5
|
+
now = () => new Date(),
|
|
6
|
+
performanceNow = () => performance.now(),
|
|
7
|
+
} = {}) {
|
|
8
|
+
let activeWindow = null;
|
|
9
|
+
|
|
10
|
+
function sync(logPath) {
|
|
11
|
+
if (!logPath) {
|
|
12
|
+
activeWindow?.monitor.disable();
|
|
13
|
+
activeWindow = null;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (activeWindow?.logPath === logPath) return activeWindow;
|
|
17
|
+
|
|
18
|
+
activeWindow?.monitor.disable();
|
|
19
|
+
const monitor = createMonitor();
|
|
20
|
+
monitor.enable();
|
|
21
|
+
activeWindow = {
|
|
22
|
+
logPath,
|
|
23
|
+
monitor,
|
|
24
|
+
startedAt: now(),
|
|
25
|
+
startedPerformanceMs: performanceNow(),
|
|
26
|
+
};
|
|
27
|
+
return activeWindow;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function snapshot(window = activeWindow) {
|
|
31
|
+
if (!window) return null;
|
|
32
|
+
const count = window.monitor.count;
|
|
33
|
+
const milliseconds = (nanoseconds) =>
|
|
34
|
+
count > 0 && Number.isFinite(nanoseconds)
|
|
35
|
+
? Number((nanoseconds / 1_000_000).toFixed(3))
|
|
36
|
+
: null;
|
|
37
|
+
return {
|
|
38
|
+
monitor: "node_perf_hooks_monitor_event_loop_delay",
|
|
39
|
+
resolution_ms: 10,
|
|
40
|
+
window_started_at: window.startedAt.toISOString(),
|
|
41
|
+
window_elapsed_ms: Number(
|
|
42
|
+
(performanceNow() - window.startedPerformanceMs).toFixed(3)
|
|
43
|
+
),
|
|
44
|
+
samples: count,
|
|
45
|
+
min_ms: milliseconds(window.monitor.min),
|
|
46
|
+
max_ms: milliseconds(window.monitor.max),
|
|
47
|
+
mean_ms: milliseconds(window.monitor.mean),
|
|
48
|
+
p50_ms: milliseconds(window.monitor.percentile(50)),
|
|
49
|
+
p95_ms: milliseconds(window.monitor.percentile(95)),
|
|
50
|
+
p99_ms: milliseconds(window.monitor.percentile(99)),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { sync, snapshot };
|
|
55
|
+
}
|
|
@@ -2,8 +2,9 @@ import { randomUUID } from "crypto";
|
|
|
2
2
|
import { ClaudeCodeAdapter } from "./adapters/claude-code-adapter.mjs";
|
|
3
3
|
import { readConfig } from "./config.mjs";
|
|
4
4
|
import { getConfiguredProviderCommand } from "./provider-registry.mjs";
|
|
5
|
+
import { DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS } from "./scheduler-run-timeout-policy.mjs";
|
|
5
6
|
|
|
6
|
-
const DEFAULT_TIMEOUT_MS =
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS * 1000;
|
|
7
8
|
const DEFAULT_GRACEFUL_EXIT_MS = 5_000;
|
|
8
9
|
const DEFAULT_FORCED_EXIT_MS = 2_000;
|
|
9
10
|
const MAX_CAPTURE_CHARS = 24_000;
|
|
@@ -8,8 +8,12 @@ import {
|
|
|
8
8
|
getConfiguredProviderCommand,
|
|
9
9
|
isConfiguredCommandAvailable,
|
|
10
10
|
} from "./provider-registry.mjs";
|
|
11
|
+
import { DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS } from "./scheduler-run-timeout-policy.mjs";
|
|
11
12
|
|
|
12
|
-
const DEFAULT_SCHEDULER_DIRECT_RUN_TIMEOUT_MS =
|
|
13
|
+
const DEFAULT_SCHEDULER_DIRECT_RUN_TIMEOUT_MS =
|
|
14
|
+
DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS * 1000;
|
|
15
|
+
const DEFAULT_GRACEFUL_EXIT_MS = 5_000;
|
|
16
|
+
const DEFAULT_FORCED_EXIT_MS = 2_000;
|
|
13
17
|
const MAX_CAPTURE_CHARS = 24_000;
|
|
14
18
|
const MAX_SUMMARY_CHARS = 700;
|
|
15
19
|
|
|
@@ -208,6 +212,9 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
208
212
|
redactionValues = [],
|
|
209
213
|
spawnFn = spawn,
|
|
210
214
|
timeoutMs = DEFAULT_SCHEDULER_DIRECT_RUN_TIMEOUT_MS,
|
|
215
|
+
gracefulExitMs = DEFAULT_GRACEFUL_EXIT_MS,
|
|
216
|
+
forcedExitMs = DEFAULT_FORCED_EXIT_MS,
|
|
217
|
+
signal: abortSignal = null,
|
|
211
218
|
} = {}) {
|
|
212
219
|
const provider = requireString(runtime?.provider, "provider");
|
|
213
220
|
const resolvedCwd = requireString(cwd, "cwd");
|
|
@@ -227,12 +234,15 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
227
234
|
let stdoutTruncated = false;
|
|
228
235
|
let stderrTruncated = false;
|
|
229
236
|
let settled = false;
|
|
237
|
+
let terminalReason = null;
|
|
238
|
+
let childExited = false;
|
|
230
239
|
let timeout = null;
|
|
231
240
|
let child;
|
|
232
241
|
const finish = (result) => {
|
|
233
242
|
if (settled) return;
|
|
234
243
|
settled = true;
|
|
235
244
|
if (timeout) clearTimeout(timeout);
|
|
245
|
+
abortSignal?.removeEventListener?.("abort", onAbort);
|
|
236
246
|
const safeStdout = redactText(stdout, redactionTokens);
|
|
237
247
|
const safeStderr = redactText(stderr, redactionTokens);
|
|
238
248
|
resolve({
|
|
@@ -248,6 +258,11 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
248
258
|
stdout_truncated: stdoutTruncated,
|
|
249
259
|
stderr_truncated: stderrTruncated,
|
|
250
260
|
redacted_sensitive_env: redactionTokens.length > 0,
|
|
261
|
+
lifecycle_state: result.ok
|
|
262
|
+
? "succeeded"
|
|
263
|
+
: result.error_summary || result.signal || "failed",
|
|
264
|
+
provider_process_exited: childExited,
|
|
265
|
+
provider_force_killed: result.provider_force_killed === true,
|
|
251
266
|
log_summary: buildSummary({
|
|
252
267
|
commandLabel,
|
|
253
268
|
exitCode: result.exit_code,
|
|
@@ -257,6 +272,50 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
257
272
|
}),
|
|
258
273
|
});
|
|
259
274
|
};
|
|
275
|
+
const waitForExit = (waitMs) => {
|
|
276
|
+
if (childExited) return Promise.resolve(true);
|
|
277
|
+
return new Promise((resolve) => {
|
|
278
|
+
let done = false;
|
|
279
|
+
const complete = (value) => {
|
|
280
|
+
if (done) return;
|
|
281
|
+
done = true;
|
|
282
|
+
clearTimeout(waitTimer);
|
|
283
|
+
child?.off?.("close", onExit);
|
|
284
|
+
child?.off?.("exit", onExit);
|
|
285
|
+
resolve(value);
|
|
286
|
+
};
|
|
287
|
+
const onExit = () => complete(true);
|
|
288
|
+
const waitTimer = setTimeout(() => complete(childExited), waitMs);
|
|
289
|
+
child?.on?.("close", onExit);
|
|
290
|
+
child?.on?.("exit", onExit);
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
const terminate = async (reason) => {
|
|
294
|
+
if (settled || terminalReason) return;
|
|
295
|
+
terminalReason = reason;
|
|
296
|
+
child?.kill?.("SIGTERM");
|
|
297
|
+
let forceKilled = false;
|
|
298
|
+
let exited = await waitForExit(gracefulExitMs);
|
|
299
|
+
if (!exited) {
|
|
300
|
+
forceKilled = true;
|
|
301
|
+
child?.kill?.("SIGKILL");
|
|
302
|
+
exited = await waitForExit(forcedExitMs);
|
|
303
|
+
}
|
|
304
|
+
finish({
|
|
305
|
+
ok: false,
|
|
306
|
+
exit_code: null,
|
|
307
|
+
signal: reason === "timeout" ? "timeout" : "cancelled",
|
|
308
|
+
error_summary: exited
|
|
309
|
+
? reason === "timeout"
|
|
310
|
+
? "scheduler_provider_run_timed_out"
|
|
311
|
+
: "scheduler_provider_run_cancelled"
|
|
312
|
+
: "scheduler_provider_cleanup_failed",
|
|
313
|
+
provider_force_killed: forceKilled,
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
const onAbort = () => {
|
|
317
|
+
void terminate("cancelled");
|
|
318
|
+
};
|
|
260
319
|
try {
|
|
261
320
|
child = spawnFn(command, args, {
|
|
262
321
|
cwd: resolvedCwd,
|
|
@@ -290,6 +349,8 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
290
349
|
reject(err);
|
|
291
350
|
});
|
|
292
351
|
child.on("close", (code, signal) => {
|
|
352
|
+
childExited = true;
|
|
353
|
+
if (terminalReason) return;
|
|
293
354
|
finish({
|
|
294
355
|
ok: code === 0,
|
|
295
356
|
exit_code: typeof code === "number" ? code : null,
|
|
@@ -297,21 +358,22 @@ export async function runSchedulerDirectProviderCommand({
|
|
|
297
358
|
});
|
|
298
359
|
});
|
|
299
360
|
child.on("exit", (code, signal) => {
|
|
361
|
+
childExited = true;
|
|
362
|
+
if (terminalReason) return;
|
|
300
363
|
finish({
|
|
301
364
|
ok: code === 0,
|
|
302
365
|
exit_code: typeof code === "number" ? code : null,
|
|
303
366
|
signal: signal || null,
|
|
304
367
|
});
|
|
305
368
|
});
|
|
306
|
-
if (
|
|
369
|
+
if (abortSignal?.aborted) {
|
|
370
|
+
onAbort();
|
|
371
|
+
} else {
|
|
372
|
+
abortSignal?.addEventListener?.("abort", onAbort, { once: true });
|
|
373
|
+
}
|
|
374
|
+
if (!terminalReason && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
307
375
|
timeout = setTimeout(() => {
|
|
308
|
-
|
|
309
|
-
child.kill?.("SIGTERM");
|
|
310
|
-
finish({
|
|
311
|
-
ok: false,
|
|
312
|
-
exit_code: null,
|
|
313
|
-
signal: "timeout",
|
|
314
|
-
});
|
|
376
|
+
void terminate("timeout");
|
|
315
377
|
}, timeoutMs);
|
|
316
378
|
timeout.unref?.();
|
|
317
379
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS = 60 * 60;
|
|
2
|
+
export const MIN_SCHEDULER_RUN_TIMEOUT_SECONDS = 60;
|
|
3
|
+
export const MAX_SCHEDULER_RUN_TIMEOUT_SECONDS = 24 * 60 * 60;
|
|
4
|
+
|
|
5
|
+
function contractError(code, message) {
|
|
6
|
+
const error = new Error(message);
|
|
7
|
+
error.code = code;
|
|
8
|
+
return error;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function normalizeSchedulerRunTimeoutSeconds(
|
|
12
|
+
value,
|
|
13
|
+
{ fieldName = "run_timeout_seconds" } = {}
|
|
14
|
+
) {
|
|
15
|
+
if (!Number.isInteger(value)) {
|
|
16
|
+
throw contractError(
|
|
17
|
+
"scheduler_run_timeout_invalid",
|
|
18
|
+
`${fieldName} must be a whole number of seconds`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (
|
|
22
|
+
value < MIN_SCHEDULER_RUN_TIMEOUT_SECONDS ||
|
|
23
|
+
value > MAX_SCHEDULER_RUN_TIMEOUT_SECONDS
|
|
24
|
+
) {
|
|
25
|
+
throw contractError(
|
|
26
|
+
"scheduler_run_timeout_out_of_range",
|
|
27
|
+
`${fieldName} must be between ${MIN_SCHEDULER_RUN_TIMEOUT_SECONDS} and ${MAX_SCHEDULER_RUN_TIMEOUT_SECONDS} seconds`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveSchedulerRunTimeout(
|
|
34
|
+
value,
|
|
35
|
+
{ present = value !== undefined, source = null } = {}
|
|
36
|
+
) {
|
|
37
|
+
if (!present) {
|
|
38
|
+
return {
|
|
39
|
+
seconds: DEFAULT_SCHEDULER_RUN_TIMEOUT_SECONDS,
|
|
40
|
+
source: source || "default_missing",
|
|
41
|
+
explicit: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const seconds = normalizeSchedulerRunTimeoutSeconds(value);
|
|
45
|
+
const normalizedSource = ["default", "default_missing"].includes(source)
|
|
46
|
+
? source
|
|
47
|
+
: "explicit";
|
|
48
|
+
return {
|
|
49
|
+
seconds,
|
|
50
|
+
source: normalizedSource,
|
|
51
|
+
explicit: normalizedSource === "explicit",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function parseSchedulerRunTimeoutDuration(value) {
|
|
56
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
57
|
+
throw contractError(
|
|
58
|
+
"scheduler_run_timeout_invalid",
|
|
59
|
+
"--run-timeout requires a finite duration such as 15m, 2h, or 3600s"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const normalized = value.trim().toLowerCase();
|
|
63
|
+
if (["unlimited", "none", "infinite", "infinity", "no-limit"].includes(normalized)) {
|
|
64
|
+
throw contractError(
|
|
65
|
+
"scheduler_run_timeout_unlimited_forbidden",
|
|
66
|
+
"Scheduler run timeout must be finite; unlimited is not supported"
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const match = normalized.match(/^([0-9]+)(s|m|h)$/);
|
|
70
|
+
if (!match) {
|
|
71
|
+
throw contractError(
|
|
72
|
+
"scheduler_run_timeout_invalid",
|
|
73
|
+
"--run-timeout must be a whole-number duration using s, m, or h"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const amount = Number(match[1]);
|
|
77
|
+
const multiplier = match[2] === "h" ? 3600 : match[2] === "m" ? 60 : 1;
|
|
78
|
+
return normalizeSchedulerRunTimeoutSeconds(amount * multiplier, {
|
|
79
|
+
fieldName: "--run-timeout",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function schedulerRunTimeoutMilliseconds(value, options = {}) {
|
|
84
|
+
return resolveSchedulerRunTimeout(value, options).seconds * 1000;
|
|
85
|
+
}
|