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
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTINUITY_STATES, advanceContinuity, continuityDigest, continuityFailure, readContinuity, qualifyContinuityHost,
|
|
3
|
+
qualifyContinuityRestoreResult, qualifyContinuitySessions, qualifyContinuityTunnel,
|
|
4
|
+
recordContinuityRosterPending,
|
|
5
|
+
} from "./lifecycle-continuity.mjs";
|
|
6
|
+
import { fetchContinuityObservation } from "./lifecycle-continuity-probe.mjs";
|
|
7
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
export function resolveContinuityQualificationOrigin({
|
|
11
|
+
connectionMode,
|
|
12
|
+
managedEndpoint,
|
|
13
|
+
directHostUrl,
|
|
14
|
+
loopbackOrigin,
|
|
15
|
+
}) {
|
|
16
|
+
// Old receipts did not retain connection_mode. Keep their Managed-first
|
|
17
|
+
// interpretation so a later Direct login cannot rewrite their authority.
|
|
18
|
+
// New receipts bind the selected runtime route explicitly: a dormant
|
|
19
|
+
// Managed endpoint remains identity metadata while Direct mode is active.
|
|
20
|
+
const retainedRoute = connectionMode === "direct"
|
|
21
|
+
? directHostUrl
|
|
22
|
+
: managedEndpoint || directHostUrl;
|
|
23
|
+
const retainedOrigin = retainedRoute ? new URL(retainedRoute).origin : null;
|
|
24
|
+
if (!retainedOrigin) return loopbackOrigin;
|
|
25
|
+
const retainedUrl = new URL(retainedOrigin);
|
|
26
|
+
return retainedUrl.protocol === "https:" ||
|
|
27
|
+
["127.0.0.1", "localhost", "[::1]"].includes(retainedUrl.hostname)
|
|
28
|
+
? retainedOrigin
|
|
29
|
+
: loopbackOrigin;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// beta.9 did not store the room or adapter declaration in its outer-v1
|
|
33
|
+
// baseline. Its committed anchor must still resolve in the retained binding,
|
|
34
|
+
// or an empty Session must retain its exact deterministic Host-to-room binding,
|
|
35
|
+
// before the target may attach the room to this read-only legacy observation.
|
|
36
|
+
function qualifyLegacyObservation(rows, peer, configDir) {
|
|
37
|
+
if (rows.every((row) => row.matrix_room_id && row.adapter_resume_identity_kind)) return rows;
|
|
38
|
+
const bindingPath = join(configDir, "routec-matrix-bindings.json");
|
|
39
|
+
let bindings;
|
|
40
|
+
try {
|
|
41
|
+
const stat = lstatSync(bindingPath);
|
|
42
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 8 * 1024 * 1024) return rows;
|
|
43
|
+
bindings = JSON.parse(readFileSync(bindingPath, "utf8"));
|
|
44
|
+
if (!bindings || typeof bindings !== "object" || Array.isArray(bindings)) return rows;
|
|
45
|
+
} catch { return rows; }
|
|
46
|
+
return rows.map((row) => {
|
|
47
|
+
if (row.matrix_room_id && row.adapter_resume_identity_kind) return row;
|
|
48
|
+
const after = peer.find((candidate) => candidate.session_id === row.session_id);
|
|
49
|
+
const binding = bindings[row.session_id];
|
|
50
|
+
const anchors = row.history_anchors;
|
|
51
|
+
const anchored = Array.isArray(anchors) && anchors.length > 0 &&
|
|
52
|
+
anchors.every((anchor) => after?.history_anchors?.some((candidate) =>
|
|
53
|
+
candidate.event_id === anchor.event_id && candidate.sha256 === anchor.sha256));
|
|
54
|
+
const emptyDeterministic = Array.isArray(anchors) && anchors.length === 0 &&
|
|
55
|
+
Array.isArray(after?.history_anchors) && after.history_anchors.length === 0 &&
|
|
56
|
+
binding?.binding_source === "host_session_id_deterministic_room" &&
|
|
57
|
+
binding.matrix_room_id === `!oysterun-routec-${row.session_id}:oysterun.local`;
|
|
58
|
+
if (!after || !binding || binding.host_session_id !== row.session_id ||
|
|
59
|
+
!["codex", "claude", "xai"].includes(row.provider) ||
|
|
60
|
+
row.provider !== after.provider || !(anchored || emptyDeterministic)) return row;
|
|
61
|
+
return { ...row, matrix_binding_host_session_id: binding.host_session_id,
|
|
62
|
+
matrix_room_id: binding.matrix_room_id, adapter_resume_identity_kind: "provider_metadata",
|
|
63
|
+
adapter_registered: true };
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Shared qualification used by the existing runner and the one-time adapter.
|
|
68
|
+
* Observations are live authenticated reads; the retained baseline is never
|
|
69
|
+
* replaced by a new observation after a restart or a failed qualification.
|
|
70
|
+
*/
|
|
71
|
+
export async function qualifyLifecycleContinuity({
|
|
72
|
+
path, operationId, configDir, healthUrl, version, definitionDigest,
|
|
73
|
+
rollback = false, requireRestore = true, restartId = null,
|
|
74
|
+
timeoutMs = 180_000, onStage = () => {}, observe = fetchContinuityObservation,
|
|
75
|
+
}) {
|
|
76
|
+
const receipt = readContinuity(path, operationId);
|
|
77
|
+
const baseline = receipt.baseline;
|
|
78
|
+
const loopbackOrigin = new URL(healthUrl).origin;
|
|
79
|
+
// Fixed/local task stacks legitimately have no public route configured.
|
|
80
|
+
// Their retained user route is the exact loopback health origin selected by
|
|
81
|
+
// the canonical lifecycle command; it still receives the same authenticated
|
|
82
|
+
// user-path, Host, Session and history qualification below.
|
|
83
|
+
// The lifecycle grant is a privileged one-operation credential. Never send
|
|
84
|
+
// it to a configured plaintext Direct URL: qualify the same Host through
|
|
85
|
+
// the retained loopback listener instead. HTTPS Managed/Direct routes keep
|
|
86
|
+
// their normal authenticated user-path qualification.
|
|
87
|
+
const expectedOrigin = resolveContinuityQualificationOrigin({
|
|
88
|
+
connectionMode: baseline.host.connection_mode,
|
|
89
|
+
managedEndpoint: baseline.host.managed_endpoint,
|
|
90
|
+
directHostUrl: baseline.host.direct_host_url,
|
|
91
|
+
loopbackOrigin,
|
|
92
|
+
});
|
|
93
|
+
const result = {};
|
|
94
|
+
const advance = (stage, proof) => {
|
|
95
|
+
if (rollback) return;
|
|
96
|
+
const current = readContinuity(path, operationId);
|
|
97
|
+
if (CONTINUITY_STATES.indexOf(current.state) < CONTINUITY_STATES.indexOf(stage)) {
|
|
98
|
+
advanceContinuity(path, operationId, stage, proof);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const waitFor = async (stage, callback) => {
|
|
102
|
+
await onStage(stage);
|
|
103
|
+
const deadline = Date.now() + timeoutMs;
|
|
104
|
+
let failure;
|
|
105
|
+
do {
|
|
106
|
+
try { return await callback(); } catch (error) {
|
|
107
|
+
failure = error;
|
|
108
|
+
// Identity, lost history, explicit failed/skipped restores and changed
|
|
109
|
+
// resume IDs cannot be repaired by waiting. Provider boot and route
|
|
110
|
+
// readiness may be transient.
|
|
111
|
+
const transient = error.transient === true || new Set([
|
|
112
|
+
"p233_runtime_not_ready", "p233_http_status", "p233_authenticated_path",
|
|
113
|
+
"p233_restart_not_consumed", "p233_user_path_pending",
|
|
114
|
+
]).has(error.code) || ["TypeError", "TimeoutError", "AbortError"].includes(error.name);
|
|
115
|
+
if (!transient) throw error;
|
|
116
|
+
if (Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, Math.min(1000, deadline - Date.now())));
|
|
117
|
+
}
|
|
118
|
+
} while (Date.now() < deadline);
|
|
119
|
+
throw failure;
|
|
120
|
+
};
|
|
121
|
+
const local = () => observe({ configDir, operationId, origin: loopbackOrigin });
|
|
122
|
+
const checkHost = (observation) => {
|
|
123
|
+
const proof = qualifyContinuityHost(baseline.host, observation.host, {
|
|
124
|
+
version, runtimeIdentityDigest: receipt.source.runtime_identity_digest,
|
|
125
|
+
expectNewPid: requireRestore, definitionDigest,
|
|
126
|
+
});
|
|
127
|
+
if (result.host && proof.pid !== result.host.pid) throw continuityFailure("host_qualification", "qualification_pid_changed", "Host PID changed during qualification; retain recovery assets.");
|
|
128
|
+
return proof;
|
|
129
|
+
};
|
|
130
|
+
const checkSessions = (observation) => {
|
|
131
|
+
if (requireRestore && (observation.restart_state !== "consumed" || observation.restart_id !== restartId)) {
|
|
132
|
+
throw continuityFailure("session_qualification", "restart_not_consumed", "Waiting for the exact fresh Session restore receipt.");
|
|
133
|
+
}
|
|
134
|
+
const source = qualifyLegacyObservation(baseline.sessions, observation.sessions, configDir);
|
|
135
|
+
const target = qualifyLegacyObservation(observation.sessions, source, configDir);
|
|
136
|
+
if (requireRestore && source.length > target.length) {
|
|
137
|
+
const expectedIds = new Set(source.map((row) => row.session_id));
|
|
138
|
+
const observedIds = target.map((row) => row.session_id);
|
|
139
|
+
const observedIsExpectedSubset = new Set(observedIds).size === observedIds.length &&
|
|
140
|
+
observedIds.every((sessionId) => expectedIds.has(sessionId));
|
|
141
|
+
if (observedIsExpectedSubset) {
|
|
142
|
+
const restoreResult = observation.restore_result?.session_restore;
|
|
143
|
+
// Failed/skipped or unproved restores are terminal even while another
|
|
144
|
+
// provider runtime is still becoming observable.
|
|
145
|
+
qualifyContinuityRestoreResult(source, restoreResult);
|
|
146
|
+
recordContinuityRosterPending(source, target, { path, operationId });
|
|
147
|
+
// Validate every already-observable Session before waiting for the
|
|
148
|
+
// absent runtime. Identity, room, history or observation errors fail
|
|
149
|
+
// immediately instead of being hidden by the partial roster.
|
|
150
|
+
const presentSource = target.map((row) => source.find((candidate) =>
|
|
151
|
+
candidate.session_id === row.session_id));
|
|
152
|
+
qualifyContinuitySessions(presentSource, target, restoreResult,
|
|
153
|
+
{ requireRestore, path, operationId });
|
|
154
|
+
const waiting = continuityFailure("session_qualification", "roster_changed",
|
|
155
|
+
"Waiting for every receipt-restored Session to become observable and ready.");
|
|
156
|
+
waiting.transient = true;
|
|
157
|
+
throw waiting;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return qualifyContinuitySessions(source, target,
|
|
161
|
+
observation.restore_result?.session_restore, { requireRestore, path, operationId });
|
|
162
|
+
};
|
|
163
|
+
result.host = await waitFor("HOST_QUALIFIED", async () => checkHost(await local()));
|
|
164
|
+
advance("HOST_QUALIFIED", result.host);
|
|
165
|
+
result.sessions = await waitFor("SESSIONS_QUALIFIED", async () => {
|
|
166
|
+
const observation = await local();
|
|
167
|
+
checkHost(observation);
|
|
168
|
+
return checkSessions(observation);
|
|
169
|
+
});
|
|
170
|
+
advance("SESSIONS_QUALIFIED", result.sessions);
|
|
171
|
+
result.tunnel = await waitFor("TUNNEL_QUALIFIED", async () => {
|
|
172
|
+
const observation = await observe({ configDir, operationId, origin: expectedOrigin });
|
|
173
|
+
// A provider can exit during transport qualification. Commit only a fresh
|
|
174
|
+
// observation that still proves the same Host and complete live Sessions.
|
|
175
|
+
const freshHost = checkHost(observation);
|
|
176
|
+
const freshSessions = checkSessions(observation);
|
|
177
|
+
// The scoped route probe proves transport and Host identity; a real
|
|
178
|
+
// dashboard-authenticated request on that origin proves the normal user
|
|
179
|
+
// path separately. Waiting for a person is not part of mutation completion.
|
|
180
|
+
return {
|
|
181
|
+
...qualifyContinuityTunnel({ origin: expectedOrigin, device_id: baseline.host.device_id, operation_id: operationId }, observation.route),
|
|
182
|
+
requalified_at: new Date().toISOString(), host: freshHost, sessions: freshSessions,
|
|
183
|
+
history_anchors_digest: continuityDigest(observation.sessions.map(row => ({
|
|
184
|
+
session_id: row.session_id, history_anchors: row.history_anchors,
|
|
185
|
+
}))),
|
|
186
|
+
authenticated_user_path: observation.authenticated_user_path?.pid === freshHost.pid
|
|
187
|
+
? observation.authenticated_user_path : null,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
advance("TUNNEL_QUALIFIED", result.tunnel);
|
|
191
|
+
return result;
|
|
192
|
+
}
|