@cydm/happy-elves 0.1.0-beta.31 → 0.1.0-beta.310
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/apps/cli/dist/commands/app.js +10 -0
- package/apps/cli/dist/commands/daemon.js +92 -4
- package/apps/cli/dist/commands/gateway.d.ts +2 -0
- package/apps/cli/dist/commands/gateway.js +429 -0
- package/apps/cli/dist/commands/lib/args.js +49 -0
- package/apps/cli/dist/commands/lib/bootstrap-daemon.d.ts +2 -2
- package/apps/cli/dist/commands/lib/bootstrap-daemon.js +2 -2
- package/apps/cli/dist/commands/lib/doctor.d.ts +11 -0
- package/apps/cli/dist/commands/lib/doctor.js +276 -11
- package/apps/cli/dist/commands/lib/gateway-store.d.ts +38 -0
- package/apps/cli/dist/commands/lib/gateway-store.js +33 -0
- package/apps/cli/dist/commands/lib/index.d.ts +4 -2
- package/apps/cli/dist/commands/lib/index.js +2 -0
- package/apps/cli/dist/commands/lib/json.d.ts +2 -2
- package/apps/cli/dist/commands/lib/json.js +5 -2
- package/apps/cli/dist/commands/lib/local-daemon.d.ts +1 -0
- package/apps/cli/dist/commands/lib/local-daemon.js +14 -0
- package/apps/cli/dist/commands/lib/paths.d.ts +2 -0
- package/apps/cli/dist/commands/lib/paths.js +2 -0
- package/apps/cli/dist/commands/lib/relay-db-audit.d.ts +37 -0
- package/apps/cli/dist/commands/lib/relay-db-audit.js +300 -0
- package/apps/cli/dist/commands/lib/relay-http.d.ts +1 -1
- package/apps/cli/dist/commands/lib/scope.d.ts +1 -1
- package/apps/cli/dist/commands/lib/session-output.d.ts +15 -1
- package/apps/cli/dist/commands/lib/session-output.js +214 -6
- package/apps/cli/dist/commands/lib/session-view.d.ts +2 -2
- package/apps/cli/dist/commands/lib/status.js +5 -1
- package/apps/cli/dist/commands/lib/types.d.ts +4 -2
- package/apps/cli/dist/commands/lib/usage.js +123 -10
- package/apps/cli/dist/commands/lib/workspace.d.ts +2 -2
- package/apps/cli/dist/commands/lib/workspace.js +63 -10
- package/apps/cli/dist/commands/machine.js +50 -9
- package/apps/cli/dist/commands/memory.d.ts +2 -0
- package/apps/cli/dist/commands/memory.js +179 -0
- package/apps/cli/dist/commands/relay.js +29 -2
- package/apps/cli/dist/commands/release.d.ts +2 -0
- package/apps/cli/dist/commands/release.js +197 -0
- package/apps/cli/dist/commands/remote.js +10 -2
- package/apps/cli/dist/commands/schedule.d.ts +2 -0
- package/apps/cli/dist/commands/schedule.js +273 -0
- package/apps/cli/dist/commands/session.js +517 -5
- package/apps/cli/dist/commands/turn.js +225 -9
- package/apps/cli/dist/commands/workspace.d.ts +2 -0
- package/apps/cli/dist/commands/workspace.js +68 -0
- package/apps/cli/package.json +6 -0
- package/apps/daemon/dist/gateway/adapters.d.ts +38 -0
- package/apps/daemon/dist/gateway/adapters.js +2 -0
- package/apps/daemon/dist/gateway/feishu.d.ts +65 -0
- package/apps/daemon/dist/gateway/feishu.js +493 -0
- package/apps/daemon/dist/gateway/format.d.ts +2 -0
- package/apps/daemon/dist/gateway/format.js +19 -0
- package/apps/daemon/dist/gateway/handler.d.ts +7 -0
- package/apps/daemon/dist/gateway/handler.js +151 -0
- package/apps/daemon/dist/gateway/runtime.d.ts +33 -0
- package/apps/daemon/dist/gateway/runtime.js +850 -0
- package/apps/daemon/dist/gateway/store.d.ts +92 -0
- package/apps/daemon/dist/gateway/store.js +268 -0
- package/apps/daemon/dist/memory/handler.d.ts +7 -0
- package/apps/daemon/dist/memory/handler.js +105 -0
- package/apps/daemon/dist/memory/manager.d.ts +44 -0
- package/apps/daemon/dist/memory/manager.js +359 -0
- package/apps/daemon/dist/paths.d.ts +3 -0
- package/apps/daemon/dist/paths.js +3 -0
- package/apps/daemon/dist/relay/admission.d.ts +30 -0
- package/apps/daemon/dist/relay/admission.js +31 -0
- package/apps/daemon/dist/relay/connection.js +37 -1
- package/apps/daemon/dist/relay/devtools.d.ts +1 -1
- package/apps/daemon/dist/relay/register.js +2 -0
- package/apps/daemon/dist/relay/send.d.ts +2 -2
- package/apps/daemon/dist/relay/send.js +2 -0
- package/apps/daemon/dist/relay-http.d.ts +1 -1
- package/apps/daemon/dist/runtime/external-provider.d.ts +1 -1
- package/apps/daemon/dist/runtime/external-provider.js +187 -17
- package/apps/daemon/dist/runtime.d.ts +1 -2
- package/apps/daemon/dist/runtime.js +0 -3
- package/apps/daemon/dist/schedule/handler.d.ts +9 -0
- package/apps/daemon/dist/schedule/handler.js +148 -0
- package/apps/daemon/dist/schedule/runner.d.ts +21 -0
- package/apps/daemon/dist/schedule/runner.js +547 -0
- package/apps/daemon/dist/schedule/store.d.ts +9 -0
- package/apps/daemon/dist/schedule/store.js +143 -0
- package/apps/daemon/dist/session/create-session.d.ts +1 -1
- package/apps/daemon/dist/session/directory.d.ts +1 -1
- package/apps/daemon/dist/session/events.d.ts +15 -2
- package/apps/daemon/dist/session/events.js +76 -24
- package/apps/daemon/dist/session/file-preview.d.ts +1 -1
- package/apps/daemon/dist/session/lifecycle.d.ts +26 -3
- package/apps/daemon/dist/session/lifecycle.js +161 -22
- package/apps/daemon/dist/session/metadata.d.ts +2 -2
- package/apps/daemon/dist/session/metadata.js +42 -1
- package/apps/daemon/dist/session/primitives.d.ts +1 -1
- package/apps/daemon/dist/session/primitives.js +37 -6
- package/apps/daemon/dist/session/prompt.d.ts +28 -1
- package/apps/daemon/dist/session/prompt.js +340 -5
- package/apps/daemon/dist/session/reconcile.d.ts +3 -0
- package/apps/daemon/dist/session/reconcile.js +235 -0
- package/apps/daemon/dist/session/runtime-handle.d.ts +1 -1
- package/apps/daemon/dist/start.js +5 -1
- package/apps/daemon/dist/state.d.ts +2 -2
- package/apps/daemon/dist/turn-coordinator.d.ts +1 -0
- package/apps/daemon/dist/turn-coordinator.js +4 -0
- package/apps/daemon/dist/types.d.ts +16 -1
- package/apps/daemon/package.json +3 -25
- package/apps/relay/dist/connections.d.ts +2 -0
- package/apps/relay/dist/controller-handlers.d.ts +42 -2
- package/apps/relay/dist/controller-handlers.js +349 -37
- package/apps/relay/dist/db.js +296 -2
- package/apps/relay/dist/errors.d.ts +2 -1
- package/apps/relay/dist/errors.js +3 -1
- package/apps/relay/dist/http-routes.js +659 -33
- package/apps/relay/dist/http-schemas.d.ts +156 -0
- package/apps/relay/dist/http-schemas.js +85 -0
- package/apps/relay/dist/index.js +31 -1
- package/apps/relay/dist/machine-handlers.d.ts +15 -7
- package/apps/relay/dist/machine-handlers.js +567 -27
- package/apps/relay/dist/projections.d.ts +1 -1
- package/apps/relay/dist/projections.js +13 -0
- package/apps/relay/dist/relay-context.d.ts +41 -3
- package/apps/relay/dist/relay-context.js +244 -19
- package/apps/relay/dist/repositories.d.ts +2 -2
- package/apps/relay/dist/repositories.js +28 -1
- package/apps/relay/dist/runtime-bindings.d.ts +25 -0
- package/apps/relay/dist/runtime-bindings.js +49 -0
- package/apps/relay/dist/scope.d.ts +1 -1
- package/apps/relay/dist/types.d.ts +71 -1
- package/apps/relay/dist/websocket.js +2 -0
- package/package.json +5 -11
- package/packages/agent-sdk/dist/index.d.ts +80 -0
- package/packages/agent-sdk/dist/index.js +16 -0
- package/packages/client/dist/account.d.ts +35 -2
- package/packages/client/dist/account.js +98 -1
- package/packages/client/dist/client.d.ts +59 -6
- package/packages/client/dist/client.js +312 -58
- package/packages/client/dist/index.d.ts +1 -1
- package/packages/client/dist/parsers.d.ts +28 -2
- package/packages/client/dist/parsers.js +167 -0
- package/packages/client/dist/transport.d.ts +21 -1
- package/packages/client/dist/transport.js +69 -6
- package/packages/client/dist/types.d.ts +177 -1
- package/packages/client/dist/validation.d.ts +1 -1
- package/packages/runtime/dist/index.d.ts +66 -0
- package/packages/runtime-acpx/dist/index.d.ts +1 -1
- package/packages/runtime-acpx/dist/index.js +8 -0
- package/packages/runtime-cli/dist/claude-history.d.ts +1 -2
- package/packages/runtime-cli/dist/claude-history.js +0 -3
- package/packages/runtime-cli/dist/claude-session-store.d.ts +0 -1
- package/packages/runtime-cli/dist/claude-session-store.js +1 -1
- package/packages/runtime-cli/dist/claude-turn.d.ts +2 -1
- package/packages/runtime-cli/dist/claude-turn.js +29 -9
- package/packages/runtime-cli/dist/codex-app-server.d.ts +8 -3
- package/packages/runtime-cli/dist/codex-app-server.js +122 -25
- package/packages/runtime-cli/dist/codex-history.d.ts +1 -2
- package/packages/runtime-cli/dist/codex-history.js +11 -5
- package/packages/runtime-cli/dist/codex-protocol.d.ts +2 -2
- package/packages/runtime-cli/dist/codex-protocol.js +4 -4
- package/packages/runtime-cli/dist/history-merge.d.ts +1 -1
- package/packages/runtime-cli/dist/index.d.ts +1 -1
- package/packages/runtime-cli/dist/index.js +108 -2
- package/packages/runtime-cli/dist/permissions.d.ts +1 -1
- package/packages/runtime-cli/dist/session-store.d.ts +2 -1
- package/packages/runtime-cli/dist/session-store.js +9 -8
- package/packages/shared/dist/index.d.ts +2 -0
- package/packages/shared/dist/index.js +2 -0
- package/packages/shared/dist/protocol-schemas.d.ts +1077 -0
- package/packages/shared/dist/protocol-schemas.js +273 -0
- package/packages/shared/dist/protocol-types.d.ts +371 -3
- package/packages/shared/dist/protocol.d.ts +181 -2
- package/packages/shared/dist/protocol.js +197 -1
- package/packages/shared/dist/session-tags.d.ts +12 -0
- package/packages/shared/dist/session-tags.js +50 -0
- package/apps/cli/dist/commands/account.js.map +0 -1
- package/apps/cli/dist/commands/app.js.map +0 -1
- package/apps/cli/dist/commands/collect.js.map +0 -1
- package/apps/cli/dist/commands/command.js.map +0 -1
- package/apps/cli/dist/commands/config.js.map +0 -1
- package/apps/cli/dist/commands/daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/args.js.map +0 -1
- package/apps/cli/dist/commands/lib/audit.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-config.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap-output.js.map +0 -1
- package/apps/cli/dist/commands/lib/bootstrap.js.map +0 -1
- package/apps/cli/dist/commands/lib/config.js.map +0 -1
- package/apps/cli/dist/commands/lib/doctor.js.map +0 -1
- package/apps/cli/dist/commands/lib/exit.js.map +0 -1
- package/apps/cli/dist/commands/lib/index.js.map +0 -1
- package/apps/cli/dist/commands/lib/json.js.map +0 -1
- package/apps/cli/dist/commands/lib/local-daemon.js.map +0 -1
- package/apps/cli/dist/commands/lib/loop-store.js.map +0 -1
- package/apps/cli/dist/commands/lib/paths.js.map +0 -1
- package/apps/cli/dist/commands/lib/relay-http.js.map +0 -1
- package/apps/cli/dist/commands/lib/scope.js.map +0 -1
- package/apps/cli/dist/commands/lib/session-output.js.map +0 -1
- package/apps/cli/dist/commands/lib/session-view.js.map +0 -1
- package/apps/cli/dist/commands/lib/status.js.map +0 -1
- package/apps/cli/dist/commands/lib/types.js.map +0 -1
- package/apps/cli/dist/commands/lib/usage.js.map +0 -1
- package/apps/cli/dist/commands/lib/workspace.js.map +0 -1
- package/apps/cli/dist/commands/loop.js.map +0 -1
- package/apps/cli/dist/commands/machine.js.map +0 -1
- package/apps/cli/dist/commands/relay.js.map +0 -1
- package/apps/cli/dist/commands/remote.js.map +0 -1
- package/apps/cli/dist/commands/session.js.map +0 -1
- package/apps/cli/dist/commands/token.js.map +0 -1
- package/apps/cli/dist/commands/turn.js.map +0 -1
- package/apps/cli/dist/errors.js.map +0 -1
- package/apps/cli/dist/index.js.map +0 -1
- package/apps/daemon/dist/app.js.map +0 -1
- package/apps/daemon/dist/args.js.map +0 -1
- package/apps/daemon/dist/audit.js.map +0 -1
- package/apps/daemon/dist/cli.js.map +0 -1
- package/apps/daemon/dist/config.js.map +0 -1
- package/apps/daemon/dist/errors.js.map +0 -1
- package/apps/daemon/dist/loop/runner.js.map +0 -1
- package/apps/daemon/dist/loop/store.js.map +0 -1
- package/apps/daemon/dist/pair.js.map +0 -1
- package/apps/daemon/dist/paths.js.map +0 -1
- package/apps/daemon/dist/process.js.map +0 -1
- package/apps/daemon/dist/relay/connection.js.map +0 -1
- package/apps/daemon/dist/relay/devtools.js.map +0 -1
- package/apps/daemon/dist/relay/register.js.map +0 -1
- package/apps/daemon/dist/relay/send.js.map +0 -1
- package/apps/daemon/dist/relay-http.js.map +0 -1
- package/apps/daemon/dist/runtime/external-provider.js.map +0 -1
- package/apps/daemon/dist/runtime.js.map +0 -1
- package/apps/daemon/dist/session/create-session.js.map +0 -1
- package/apps/daemon/dist/session/directory.js.map +0 -1
- package/apps/daemon/dist/session/events.js.map +0 -1
- package/apps/daemon/dist/session/file-preview.js.map +0 -1
- package/apps/daemon/dist/session/lifecycle.js.map +0 -1
- package/apps/daemon/dist/session/metadata.js.map +0 -1
- package/apps/daemon/dist/session/primitives.js.map +0 -1
- package/apps/daemon/dist/session/prompt.js.map +0 -1
- package/apps/daemon/dist/session/runtime-handle.js.map +0 -1
- package/apps/daemon/dist/start.js.map +0 -1
- package/apps/daemon/dist/state.js.map +0 -1
- package/apps/daemon/dist/turn-coordinator.js.map +0 -1
- package/apps/daemon/dist/types.js.map +0 -1
- package/apps/daemon/dist/utils.js.map +0 -1
- package/apps/relay/dist/auth.js.map +0 -1
- package/apps/relay/dist/connections.js.map +0 -1
- package/apps/relay/dist/controller-handlers.js.map +0 -1
- package/apps/relay/dist/db.js.map +0 -1
- package/apps/relay/dist/errors.js.map +0 -1
- package/apps/relay/dist/http-routes.js.map +0 -1
- package/apps/relay/dist/http-schemas.js.map +0 -1
- package/apps/relay/dist/index.js.map +0 -1
- package/apps/relay/dist/machine-handlers.js.map +0 -1
- package/apps/relay/dist/projections.js.map +0 -1
- package/apps/relay/dist/relay-context.js.map +0 -1
- package/apps/relay/dist/repositories.js.map +0 -1
- package/apps/relay/dist/retention.js.map +0 -1
- package/apps/relay/dist/scope.js.map +0 -1
- package/apps/relay/dist/security.js.map +0 -1
- package/apps/relay/dist/types.js.map +0 -1
- package/apps/relay/dist/websocket.js.map +0 -1
- package/apps/relay/package.json +0 -29
- package/packages/agent-sdk/dist/index.d.ts.map +0 -1
- package/packages/agent-sdk/dist/index.js.map +0 -1
- package/packages/agent-sdk/package.json +0 -20
- package/packages/client/dist/account.d.ts.map +0 -1
- package/packages/client/dist/account.js.map +0 -1
- package/packages/client/dist/client.d.ts.map +0 -1
- package/packages/client/dist/client.js.map +0 -1
- package/packages/client/dist/errors.d.ts.map +0 -1
- package/packages/client/dist/errors.js.map +0 -1
- package/packages/client/dist/http.d.ts.map +0 -1
- package/packages/client/dist/http.js.map +0 -1
- package/packages/client/dist/index.d.ts.map +0 -1
- package/packages/client/dist/index.js.map +0 -1
- package/packages/client/dist/parsers.d.ts.map +0 -1
- package/packages/client/dist/parsers.js.map +0 -1
- package/packages/client/dist/transport.d.ts.map +0 -1
- package/packages/client/dist/transport.js.map +0 -1
- package/packages/client/dist/types.d.ts.map +0 -1
- package/packages/client/dist/types.js.map +0 -1
- package/packages/client/dist/validation.d.ts.map +0 -1
- package/packages/client/dist/validation.js.map +0 -1
- package/packages/client/package.json +0 -22
- package/packages/pie-provider/dist/index.d.ts.map +0 -1
- package/packages/pie-provider/dist/index.js.map +0 -1
- package/packages/pie-provider/package.json +0 -26
- package/packages/runtime/dist/index.d.ts.map +0 -1
- package/packages/runtime/dist/index.js.map +0 -1
- package/packages/runtime/package.json +0 -19
- package/packages/runtime-acpx/dist/index.d.ts.map +0 -1
- package/packages/runtime-acpx/dist/index.js.map +0 -1
- package/packages/runtime-acpx/package.json +0 -23
- package/packages/runtime-cli/dist/async-queue.d.ts.map +0 -1
- package/packages/runtime-cli/dist/async-queue.js.map +0 -1
- package/packages/runtime-cli/dist/claude-history.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-history.js.map +0 -1
- package/packages/runtime-cli/dist/claude-json.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-json.js.map +0 -1
- package/packages/runtime-cli/dist/claude-rename.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-rename.js.map +0 -1
- package/packages/runtime-cli/dist/claude-session-store.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-session-store.js.map +0 -1
- package/packages/runtime-cli/dist/claude-turn.d.ts.map +0 -1
- package/packages/runtime-cli/dist/claude-turn.js.map +0 -1
- package/packages/runtime-cli/dist/codex-app-server.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-app-server.js.map +0 -1
- package/packages/runtime-cli/dist/codex-approvals.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-approvals.js.map +0 -1
- package/packages/runtime-cli/dist/codex-history.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-history.js.map +0 -1
- package/packages/runtime-cli/dist/codex-json-rpc.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-json-rpc.js.map +0 -1
- package/packages/runtime-cli/dist/codex-lifecycle.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-lifecycle.js.map +0 -1
- package/packages/runtime-cli/dist/codex-protocol.d.ts.map +0 -1
- package/packages/runtime-cli/dist/codex-protocol.js.map +0 -1
- package/packages/runtime-cli/dist/command-resolver.d.ts.map +0 -1
- package/packages/runtime-cli/dist/command-resolver.js.map +0 -1
- package/packages/runtime-cli/dist/history-merge.d.ts.map +0 -1
- package/packages/runtime-cli/dist/history-merge.js.map +0 -1
- package/packages/runtime-cli/dist/index.d.ts.map +0 -1
- package/packages/runtime-cli/dist/index.js.map +0 -1
- package/packages/runtime-cli/dist/permissions.d.ts.map +0 -1
- package/packages/runtime-cli/dist/permissions.js.map +0 -1
- package/packages/runtime-cli/dist/session-store.d.ts.map +0 -1
- package/packages/runtime-cli/dist/session-store.js.map +0 -1
- package/packages/runtime-cli/package.json +0 -22
- package/packages/shared/dist/args.d.ts.map +0 -1
- package/packages/shared/dist/args.js.map +0 -1
- package/packages/shared/dist/crypto.d.ts.map +0 -1
- package/packages/shared/dist/crypto.js.map +0 -1
- package/packages/shared/dist/ids.d.ts.map +0 -1
- package/packages/shared/dist/ids.js.map +0 -1
- package/packages/shared/dist/index.d.ts.map +0 -1
- package/packages/shared/dist/index.js.map +0 -1
- package/packages/shared/dist/protocol-schemas.d.ts.map +0 -1
- package/packages/shared/dist/protocol-schemas.js.map +0 -1
- package/packages/shared/dist/protocol-types.d.ts.map +0 -1
- package/packages/shared/dist/protocol-types.js.map +0 -1
- package/packages/shared/dist/protocol.d.ts.map +0 -1
- package/packages/shared/dist/protocol.js.map +0 -1
- package/packages/shared/dist/session-name.d.ts.map +0 -1
- package/packages/shared/dist/session-name.js.map +0 -1
- package/packages/shared/dist/session-state.d.ts.map +0 -1
- package/packages/shared/dist/session-state.js.map +0 -1
- package/packages/shared/package.json +0 -24
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { encryptedEnvelopeSchema, pairingCode, randomId, } from "../../../packages/shared/dist/index.js";
|
|
3
|
-
import { controllerDeviceSchema, controllerInviteClaimSchema, controllerInviteCreateSchema, deviceRevokeSchema, pairingClaimSchema, pairingStartSchema, scopedTokenCreateSchema, scopedTokenRevokeSchema, sessionEventsQuerySchema, } from "./http-schemas.js";
|
|
2
|
+
import { controllerSessionCommandDecision, encryptedEnvelopeSchema, pairingCode, randomId, } from "../../../packages/shared/dist/index.js";
|
|
3
|
+
import { controllerDeviceSchema, controllerInviteClaimSchema, controllerInviteCreateSchema, deviceRenameSchema, deviceRevokeSchema, memoryDeleteSchema, memoryListQuerySchema, memorySaveSchema, memorySettingsSchema, machineMemoryDocumentSaveSchema, machineMemoryDocumentsQuerySchema, machineCreateSessionAdmissionSchema, machinePromptAdmissionSchema, pairingClaimSchema, pairingStartSchema, scopedTokenCreateSchema, scopedTokenRevokeSchema, sessionMetadataUpdateSchema, sessionHeadAdvanceSchema, sessionEventsQuerySchema, workspaceCwdPinSchema, workspaceCwdUnpinSchema, workspaceProjectPreferenceSchema, workspaceSessionRecentSchema, workspaceSessionStarSchema, } from "./http-schemas.js";
|
|
4
4
|
import { HttpError } from "./errors.js";
|
|
5
5
|
import { encodePairingCode, isPairingCodeMatch, tokenDigest } from "./security.js";
|
|
6
|
-
const visibleSessionEventWhere =
|
|
7
|
-
NOT EXISTS (
|
|
8
|
-
SELECT 1 FROM session_turn_aliases turn_alias
|
|
9
|
-
WHERE turn_alias.account_id = session_events.account_id
|
|
10
|
-
AND turn_alias.session_id = session_events.session_id
|
|
11
|
-
AND turn_alias.machine_id = session_events.machine_id
|
|
12
|
-
AND turn_alias.runtime_turn_id = session_events.turn_id
|
|
13
|
-
AND turn_alias.request_turn_id <> session_events.turn_id
|
|
14
|
-
AND (
|
|
15
|
-
SELECT COUNT(*) FROM session_events request_events
|
|
16
|
-
WHERE request_events.account_id = turn_alias.account_id
|
|
17
|
-
AND request_events.session_id = turn_alias.session_id
|
|
18
|
-
AND request_events.machine_id = turn_alias.machine_id
|
|
19
|
-
AND request_events.turn_id = turn_alias.request_turn_id
|
|
20
|
-
) >= 3
|
|
21
|
-
)
|
|
22
|
-
`;
|
|
6
|
+
const visibleSessionEventWhere = "1 = 1";
|
|
23
7
|
const sessionEventLogicalTime = `
|
|
24
8
|
CASE
|
|
25
9
|
WHEN occurred_at IS NOT NULL THEN occurred_at
|
|
@@ -91,6 +75,97 @@ function sessionEventLogicalSeqValue(row) {
|
|
|
91
75
|
return row.turn_seq;
|
|
92
76
|
return row.seq;
|
|
93
77
|
}
|
|
78
|
+
function canonicalEventRows(context, accountId, rows) {
|
|
79
|
+
const cache = new Map();
|
|
80
|
+
const seenHistorical = new Set();
|
|
81
|
+
const canonicalRows = [];
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
const key = `${row.session_id}:${row.machine_id}:${row.turn_id}`;
|
|
84
|
+
let requestTurnId = cache.get(key);
|
|
85
|
+
if (requestTurnId === undefined) {
|
|
86
|
+
const alias = context.db
|
|
87
|
+
.prepare(`SELECT request_turn_id FROM session_turn_aliases
|
|
88
|
+
WHERE account_id = ? AND session_id = ? AND machine_id = ? AND runtime_turn_id = ?`)
|
|
89
|
+
.get(accountId, row.session_id, row.machine_id, row.turn_id);
|
|
90
|
+
requestTurnId = alias?.request_turn_id ?? null;
|
|
91
|
+
cache.set(key, requestTurnId);
|
|
92
|
+
}
|
|
93
|
+
const canonicalRow = requestTurnId ? { ...row, turn_id: requestTurnId } : row;
|
|
94
|
+
const historicalKey = canonicalHistoricalEventKey(canonicalRow);
|
|
95
|
+
if (historicalKey) {
|
|
96
|
+
if (seenHistorical.has(historicalKey))
|
|
97
|
+
continue;
|
|
98
|
+
seenHistorical.add(historicalKey);
|
|
99
|
+
}
|
|
100
|
+
canonicalRows.push(canonicalRow);
|
|
101
|
+
}
|
|
102
|
+
return canonicalRows;
|
|
103
|
+
}
|
|
104
|
+
function canonicalHistoricalEventKey(row) {
|
|
105
|
+
if (!row.message_id || row.turn_seq === null || row.turn_seq === undefined)
|
|
106
|
+
return undefined;
|
|
107
|
+
if (!row.message_id.startsWith("hist_") && !row.message_id.startsWith("tail_"))
|
|
108
|
+
return undefined;
|
|
109
|
+
const digestStart = row.message_id.lastIndexOf("_");
|
|
110
|
+
const digest = digestStart >= 0 ? row.message_id.slice(digestStart + 1) : "";
|
|
111
|
+
if (!digest)
|
|
112
|
+
return undefined;
|
|
113
|
+
return `${row.session_id}:${row.machine_id}:${row.turn_id}:${row.turn_seq}:${digest}`;
|
|
114
|
+
}
|
|
115
|
+
function workspaceSyncState(context, accountId) {
|
|
116
|
+
const stars = context.db
|
|
117
|
+
.prepare(`SELECT session_id, starred_at
|
|
118
|
+
FROM session_stars
|
|
119
|
+
WHERE account_id = ?
|
|
120
|
+
ORDER BY starred_at DESC, session_id ASC`)
|
|
121
|
+
.all(accountId);
|
|
122
|
+
const recentSessions = context.db
|
|
123
|
+
.prepare(`SELECT session_id, last_selected_at
|
|
124
|
+
FROM account_recent_sessions
|
|
125
|
+
WHERE account_id = ?
|
|
126
|
+
ORDER BY last_selected_at DESC, session_id ASC`)
|
|
127
|
+
.all(accountId);
|
|
128
|
+
const cwdPins = context.db
|
|
129
|
+
.prepare(`SELECT machine_id, cwd, label, pinned_at
|
|
130
|
+
FROM account_workspace_cwd_pins
|
|
131
|
+
WHERE account_id = ?
|
|
132
|
+
ORDER BY pinned_at DESC, machine_id ASC, cwd ASC`)
|
|
133
|
+
.all(accountId);
|
|
134
|
+
const projectPreferences = context.db
|
|
135
|
+
.prepare(`SELECT project_key, machine_id, cwd, agent, memory_mode, updated_at
|
|
136
|
+
FROM account_project_preferences
|
|
137
|
+
WHERE account_id = ?
|
|
138
|
+
ORDER BY updated_at DESC, project_key ASC`)
|
|
139
|
+
.all(accountId);
|
|
140
|
+
return {
|
|
141
|
+
stars: stars.map((row) => ({ sessionId: row.session_id, starredAt: row.starred_at })),
|
|
142
|
+
recentSessions: recentSessions.map((row) => ({ sessionId: row.session_id, lastSelectedAt: row.last_selected_at })),
|
|
143
|
+
cwdPins: cwdPins.map((row) => ({
|
|
144
|
+
machineId: row.machine_id,
|
|
145
|
+
cwd: row.cwd,
|
|
146
|
+
...(row.label ? { label: row.label } : {}),
|
|
147
|
+
pinnedAt: row.pinned_at,
|
|
148
|
+
})),
|
|
149
|
+
projectPreferences: projectPreferences.map((row) => ({
|
|
150
|
+
projectKey: row.project_key,
|
|
151
|
+
...(row.machine_id ? { machineId: row.machine_id } : {}),
|
|
152
|
+
...(row.cwd ? { cwd: row.cwd } : {}),
|
|
153
|
+
...(row.agent ? { agent: row.agent } : {}),
|
|
154
|
+
...(isMemoryMode(row.memory_mode) ? { memoryMode: row.memory_mode } : {}),
|
|
155
|
+
updatedAt: row.updated_at,
|
|
156
|
+
})),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function isMemoryMode(value) {
|
|
160
|
+
return value === "off" || value === "readonly" || value === "readwrite";
|
|
161
|
+
}
|
|
162
|
+
function assertWorkspaceSession(context, accountId, sessionId) {
|
|
163
|
+
const row = context.db
|
|
164
|
+
.prepare("SELECT id FROM sessions WHERE account_id = ? AND id = ?")
|
|
165
|
+
.get(accountId, sessionId);
|
|
166
|
+
if (!row)
|
|
167
|
+
throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
|
|
168
|
+
}
|
|
94
169
|
export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
95
170
|
app.get("/health", async () => ({ ok: true, service: "happy-elves-relay" }));
|
|
96
171
|
app.post("/api/pairing/start", async (request) => {
|
|
@@ -171,6 +246,159 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
171
246
|
context.assertHttpScopedAction(token, "snapshot");
|
|
172
247
|
return context.filterSnapshotForScope(context.snapshot(token.account_id), context.tokenScope(token));
|
|
173
248
|
});
|
|
249
|
+
app.post("/api/machine/admissions/create-session", async (request) => {
|
|
250
|
+
const token = context.requireHttpToken(request.headers.authorization, "machine");
|
|
251
|
+
if (!token.machine_id)
|
|
252
|
+
throw new HttpError(403, "MACHINE_TOKEN_INVALID", "Machine token is missing machine id");
|
|
253
|
+
const body = machineCreateSessionAdmissionSchema.parse(request.body);
|
|
254
|
+
const previous = context.previousAdmissionAck(token.account_id, body.requestId);
|
|
255
|
+
if (previous)
|
|
256
|
+
return previous;
|
|
257
|
+
const machine = context.db
|
|
258
|
+
.prepare("SELECT * FROM machines WHERE id = ? AND account_id = ?")
|
|
259
|
+
.get(token.machine_id, token.account_id);
|
|
260
|
+
if (!machine)
|
|
261
|
+
throw new HttpError(404, "MACHINE_NOT_FOUND", "Machine not found");
|
|
262
|
+
const sessionId = body.sessionId ?? randomId("ses");
|
|
263
|
+
const existing = context.db
|
|
264
|
+
.prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
|
|
265
|
+
.get(sessionId, token.account_id);
|
|
266
|
+
if (existing)
|
|
267
|
+
throw new HttpError(409, "SESSION_ID_CONFLICT", "Session id already exists without matching admission");
|
|
268
|
+
const createdAt = context.now();
|
|
269
|
+
const machineCommand = {
|
|
270
|
+
type: "machine:createSession",
|
|
271
|
+
requestId: body.requestId,
|
|
272
|
+
sessionId,
|
|
273
|
+
agent: body.agent,
|
|
274
|
+
cwd: body.cwd,
|
|
275
|
+
name: body.name,
|
|
276
|
+
model: body.model,
|
|
277
|
+
encryptedMetadata: body.encryptedMetadata,
|
|
278
|
+
};
|
|
279
|
+
context.db.exec("BEGIN");
|
|
280
|
+
try {
|
|
281
|
+
context.db.prepare(`INSERT INTO sessions
|
|
282
|
+
(id, account_id, machine_id, agent, cwd, name, status, encrypted_metadata, created_at, updated_at)
|
|
283
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sessionId, token.account_id, token.machine_id, body.agent, body.cwd, body.name, "new", body.encryptedMetadata ? JSON.stringify(body.encryptedMetadata) : null, createdAt, createdAt);
|
|
284
|
+
context.db.prepare(`INSERT INTO relay_admissions
|
|
285
|
+
(account_id, request_id, session_id, machine_id, kind, turn_id, admission, dispatch, created_at, updated_at)
|
|
286
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, sessionId, token.machine_id, "createSession", null, "accepted", "queued", createdAt, createdAt);
|
|
287
|
+
context.db.prepare(`INSERT INTO relay_dispatch_outbox
|
|
288
|
+
(account_id, request_id, machine_id, session_id, command_type, message, state, created_at, updated_at)
|
|
289
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, token.machine_id, sessionId, machineCommand.type, JSON.stringify(machineCommand), "queued", createdAt, createdAt);
|
|
290
|
+
context.db.exec("COMMIT");
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
context.db.exec("ROLLBACK");
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
const sessionRow = context.db.prepare("SELECT * FROM sessions WHERE id = ?").get(sessionId);
|
|
297
|
+
if (sessionRow)
|
|
298
|
+
context.broadcastControllers(token.account_id, { type: "server:session", session: context.sessionSnapshot(sessionRow) });
|
|
299
|
+
const dispatch = context.deliverOutboxCommand(token.account_id, body.requestId);
|
|
300
|
+
return {
|
|
301
|
+
type: "server:ack",
|
|
302
|
+
requestId: body.requestId,
|
|
303
|
+
ok: true,
|
|
304
|
+
sessionId,
|
|
305
|
+
admission: "accepted",
|
|
306
|
+
dispatch,
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
app.post("/api/machine/admissions/prompt", async (request) => {
|
|
310
|
+
const token = context.requireHttpToken(request.headers.authorization, "machine");
|
|
311
|
+
if (!token.machine_id)
|
|
312
|
+
throw new HttpError(403, "MACHINE_TOKEN_INVALID", "Machine token is missing machine id");
|
|
313
|
+
const body = machinePromptAdmissionSchema.parse(request.body);
|
|
314
|
+
const previous = context.previousAdmissionAck(token.account_id, body.requestId);
|
|
315
|
+
if (previous)
|
|
316
|
+
return previous;
|
|
317
|
+
const sessionRow = context.db
|
|
318
|
+
.prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ? AND machine_id = ?")
|
|
319
|
+
.get(body.sessionId, token.account_id, token.machine_id);
|
|
320
|
+
if (!sessionRow)
|
|
321
|
+
throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
|
|
322
|
+
const decision = controllerSessionCommandDecision(sessionRow.status, "prompt");
|
|
323
|
+
if (!decision.allowed &&
|
|
324
|
+
!(decision.code === "SESSION_BUSY" &&
|
|
325
|
+
!context.activeSessionClaim(token.account_id, sessionRow.id) &&
|
|
326
|
+
!sessionLooksTranscriptPending(context, sessionRow))) {
|
|
327
|
+
throw new HttpError(409, decision.code, decision.message);
|
|
328
|
+
}
|
|
329
|
+
const createdAt = context.now();
|
|
330
|
+
const machineCommand = {
|
|
331
|
+
type: "machine:prompt",
|
|
332
|
+
requestId: body.requestId,
|
|
333
|
+
sessionId: sessionRow.id,
|
|
334
|
+
turnId: body.turnId,
|
|
335
|
+
encryptedPrompt: body.encryptedPrompt,
|
|
336
|
+
encryptedMetadata: sessionRow.encrypted_metadata ? context.parseJson(sessionRow.encrypted_metadata, undefined) : undefined,
|
|
337
|
+
permissionMode: body.permissionMode,
|
|
338
|
+
model: body.model,
|
|
339
|
+
encryptedMemoryOptions: body.encryptedMemoryOptions,
|
|
340
|
+
timeoutMs: body.timeoutMs,
|
|
341
|
+
};
|
|
342
|
+
let event;
|
|
343
|
+
let busyClaim;
|
|
344
|
+
context.db.exec("BEGIN");
|
|
345
|
+
try {
|
|
346
|
+
const claim = context.claimSessionTurn({
|
|
347
|
+
accountId: token.account_id,
|
|
348
|
+
sessionId: sessionRow.id,
|
|
349
|
+
machineId: token.machine_id,
|
|
350
|
+
requestId: body.requestId,
|
|
351
|
+
turnId: body.turnId,
|
|
352
|
+
commandType: "prompt",
|
|
353
|
+
createdAt,
|
|
354
|
+
});
|
|
355
|
+
if (!claim.ok) {
|
|
356
|
+
busyClaim = claim.claim;
|
|
357
|
+
context.db.exec("ROLLBACK");
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
event = context.insertEvent({
|
|
361
|
+
accountId: token.account_id,
|
|
362
|
+
sessionId: sessionRow.id,
|
|
363
|
+
machineId: token.machine_id,
|
|
364
|
+
source: "controller",
|
|
365
|
+
turnId: body.turnId,
|
|
366
|
+
payload: body.encryptedPrompt,
|
|
367
|
+
});
|
|
368
|
+
context.db.prepare(`INSERT INTO relay_admissions
|
|
369
|
+
(account_id, request_id, session_id, machine_id, kind, turn_id, admission, dispatch, created_at, updated_at)
|
|
370
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, sessionRow.id, token.machine_id, "prompt", body.turnId, "accepted", "queued", createdAt, createdAt);
|
|
371
|
+
context.db.prepare(`INSERT INTO relay_dispatch_outbox
|
|
372
|
+
(account_id, request_id, machine_id, session_id, command_type, message, state, created_at, updated_at)
|
|
373
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, token.machine_id, sessionRow.id, machineCommand.type, JSON.stringify(machineCommand), "queued", createdAt, createdAt);
|
|
374
|
+
context.db.exec("COMMIT");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
context.db.exec("ROLLBACK");
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
if (busyClaim) {
|
|
382
|
+
throw new HttpError(409, "SESSION_BUSY", "Session already has a running turn", {
|
|
383
|
+
sessionId: sessionRow.id,
|
|
384
|
+
activeRequestId: busyClaim.request_id,
|
|
385
|
+
activeTurnId: busyClaim.turn_id,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
if (!event)
|
|
389
|
+
throw new HttpError(500, "ADMISSION_FAILED", "Prompt admission did not persist an event");
|
|
390
|
+
context.broadcastControllers(token.account_id, { type: "server:event", event });
|
|
391
|
+
const dispatch = context.deliverOutboxCommand(token.account_id, body.requestId);
|
|
392
|
+
return {
|
|
393
|
+
type: "server:ack",
|
|
394
|
+
requestId: body.requestId,
|
|
395
|
+
ok: true,
|
|
396
|
+
sessionId: sessionRow.id,
|
|
397
|
+
turnId: body.turnId,
|
|
398
|
+
admission: "accepted",
|
|
399
|
+
dispatch,
|
|
400
|
+
};
|
|
401
|
+
});
|
|
174
402
|
app.get("/api/sessions/:sessionId/events", async (request) => {
|
|
175
403
|
const token = context.requireHttpToken(request.headers.authorization, "controller");
|
|
176
404
|
context.assertHttpScopedAction(token, "collect");
|
|
@@ -229,8 +457,9 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
229
457
|
rows = page.slice(0, limit).reverse();
|
|
230
458
|
}
|
|
231
459
|
const newestRow = rows.at(-1);
|
|
460
|
+
const canonicalRows = canonicalEventRows(context, token.account_id, rows);
|
|
232
461
|
return {
|
|
233
|
-
events:
|
|
462
|
+
events: canonicalRows.map(context.eventSnapshot),
|
|
234
463
|
hasMoreBefore,
|
|
235
464
|
oldestCursor: rows[0] ? encodeSessionEventCursor(params.sessionId, rows[0]) : undefined,
|
|
236
465
|
newestCursor: newestRow ? encodeSessionEventCursor(params.sessionId, newestRow) : undefined,
|
|
@@ -238,25 +467,359 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
238
467
|
newestEventId: newestRow?.id,
|
|
239
468
|
};
|
|
240
469
|
});
|
|
470
|
+
app.post("/api/sessions/:sessionId/head", async (request) => {
|
|
471
|
+
const token = context.requireHttpToken(request.headers.authorization, "controller");
|
|
472
|
+
context.assertHttpScopedAction(token, "session.repairHead");
|
|
473
|
+
const params = z.object({ sessionId: z.string().min(1) }).parse(request.params);
|
|
474
|
+
const body = sessionHeadAdvanceSchema.parse(request.body);
|
|
475
|
+
context.assertScopedSession(context.tokenScope(token), token.account_id, params.sessionId);
|
|
476
|
+
const session = context.db
|
|
477
|
+
.prepare("SELECT * FROM sessions WHERE account_id = ? AND id = ?")
|
|
478
|
+
.get(token.account_id, params.sessionId);
|
|
479
|
+
if (!session)
|
|
480
|
+
throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
|
|
481
|
+
if (session.status === "running" && !body.status) {
|
|
482
|
+
throw new HttpError(409, "SESSION_HEAD_REPAIR_RUNNING", "Cannot repair a running session head");
|
|
483
|
+
}
|
|
484
|
+
const basisEvent = context.db
|
|
485
|
+
.prepare(`SELECT * FROM session_events
|
|
486
|
+
WHERE account_id = ? AND session_id = ? AND machine_id = ? AND message_id = ?`)
|
|
487
|
+
.get(token.account_id, params.sessionId, session.machine_id, body.basis.eventMessageId);
|
|
488
|
+
if (!basisEvent)
|
|
489
|
+
throw new HttpError(409, "SESSION_HEAD_BASIS_MISSING", "Head advance basis event is not available");
|
|
490
|
+
if (basisEvent.turn_id !== body.basis.turnId || basisEvent.turn_seq !== body.basis.turnSeq) {
|
|
491
|
+
throw new HttpError(409, "SESSION_HEAD_BASIS_MISMATCH", "Head advance basis does not match the stored event");
|
|
492
|
+
}
|
|
493
|
+
const nextOrder = logicalEventOrderForRow(basisEvent);
|
|
494
|
+
const currentOrder = sessionHeadBasisOrderForRow(context.db, session);
|
|
495
|
+
const rewriteFenced = typeof session.head_basis_message_id === "string" &&
|
|
496
|
+
session.head_basis_message_id.startsWith("rewrite:");
|
|
497
|
+
const comparison = compareHeadOrders(nextOrder, currentOrder);
|
|
498
|
+
const currentHeadAlreadySettled = session.current_head === `event:${basisEvent.id}` ||
|
|
499
|
+
session.current_head === body.currentHead ||
|
|
500
|
+
session.last_turn_id === body.lastTurnId;
|
|
501
|
+
const advanced = !rewriteFenced && comparison > 0;
|
|
502
|
+
const statusChanged = !rewriteFenced &&
|
|
503
|
+
body.status !== undefined &&
|
|
504
|
+
session.status === "running" &&
|
|
505
|
+
comparison === 0 &&
|
|
506
|
+
currentHeadAlreadySettled;
|
|
507
|
+
if (advanced || statusChanged) {
|
|
508
|
+
const ts = context.now();
|
|
509
|
+
context.db.prepare(`UPDATE sessions
|
|
510
|
+
SET status = COALESCE(?, status),
|
|
511
|
+
current_head = COALESCE(?, current_head),
|
|
512
|
+
last_turn_id = COALESCE(?, last_turn_id),
|
|
513
|
+
head_basis_event_id = COALESCE(?, head_basis_event_id),
|
|
514
|
+
head_basis_message_id = COALESCE(?, head_basis_message_id),
|
|
515
|
+
head_basis_logical_time = COALESCE(?, head_basis_logical_time),
|
|
516
|
+
head_basis_turn_seq = COALESCE(?, head_basis_turn_seq),
|
|
517
|
+
updated_at = ?
|
|
518
|
+
WHERE id = ? AND account_id = ?`).run(body.status && session.status === "running" ? body.status : null, advanced ? body.currentHead : null, advanced ? body.lastTurnId : null, advanced ? basisEvent.id : null, advanced ? basisEvent.message_id : null, advanced ? nextOrder.time : null, advanced ? nextOrder.seq : null, ts, params.sessionId, token.account_id);
|
|
519
|
+
}
|
|
520
|
+
const row = context.db
|
|
521
|
+
.prepare("SELECT * FROM sessions WHERE account_id = ? AND id = ?")
|
|
522
|
+
.get(token.account_id, params.sessionId);
|
|
523
|
+
if (!row)
|
|
524
|
+
throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
|
|
525
|
+
const snapshot = context.sessionSnapshot(row);
|
|
526
|
+
context.broadcastControllers(token.account_id, { type: "server:session", session: snapshot });
|
|
527
|
+
return {
|
|
528
|
+
advanced,
|
|
529
|
+
session: snapshot,
|
|
530
|
+
basis: {
|
|
531
|
+
eventId: basisEvent.id,
|
|
532
|
+
messageId: basisEvent.message_id ?? undefined,
|
|
533
|
+
logicalTime: nextOrder.time,
|
|
534
|
+
turnSeq: nextOrder.seq,
|
|
535
|
+
},
|
|
536
|
+
...(statusChanged ? { settled: true } : {}),
|
|
537
|
+
...(rewriteFenced ? { reason: "rewrite-fenced" } : {}),
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
app.get("/api/workspace-sync", async (request) => {
|
|
541
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
542
|
+
return workspaceSyncState(context, token.account_id);
|
|
543
|
+
});
|
|
544
|
+
app.post("/api/workspace-sync/session-star", async (request) => {
|
|
545
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
546
|
+
const body = workspaceSessionStarSchema.parse(request.body);
|
|
547
|
+
assertWorkspaceSession(context, token.account_id, body.sessionId);
|
|
548
|
+
const ts = context.now();
|
|
549
|
+
if (body.starred) {
|
|
550
|
+
context.db
|
|
551
|
+
.prepare(`INSERT INTO session_stars (account_id, session_id, starred_at)
|
|
552
|
+
VALUES (?, ?, ?)
|
|
553
|
+
ON CONFLICT(account_id, session_id) DO UPDATE SET starred_at = excluded.starred_at`)
|
|
554
|
+
.run(token.account_id, body.sessionId, ts);
|
|
555
|
+
return { sessionId: body.sessionId, starred: true, starredAt: ts };
|
|
556
|
+
}
|
|
557
|
+
context.db
|
|
558
|
+
.prepare("DELETE FROM session_stars WHERE account_id = ? AND session_id = ?")
|
|
559
|
+
.run(token.account_id, body.sessionId);
|
|
560
|
+
return { sessionId: body.sessionId, starred: false, unstarredAt: ts };
|
|
561
|
+
});
|
|
562
|
+
app.post("/api/workspace-sync/session-recent", async (request) => {
|
|
563
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
564
|
+
const body = workspaceSessionRecentSchema.parse(request.body);
|
|
565
|
+
assertWorkspaceSession(context, token.account_id, body.sessionId);
|
|
566
|
+
const ts = context.now();
|
|
567
|
+
context.db
|
|
568
|
+
.prepare(`INSERT INTO account_recent_sessions (account_id, session_id, last_selected_at)
|
|
569
|
+
VALUES (?, ?, ?)
|
|
570
|
+
ON CONFLICT(account_id, session_id) DO UPDATE SET last_selected_at = excluded.last_selected_at`)
|
|
571
|
+
.run(token.account_id, body.sessionId, ts);
|
|
572
|
+
return { sessionId: body.sessionId, lastSelectedAt: ts };
|
|
573
|
+
});
|
|
574
|
+
app.post("/api/workspace-sync/cwd-pin", async (request) => {
|
|
575
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
576
|
+
const body = workspaceCwdPinSchema.parse(request.body);
|
|
577
|
+
const ts = context.now();
|
|
578
|
+
context.db
|
|
579
|
+
.prepare(`INSERT INTO account_workspace_cwd_pins (account_id, machine_id, cwd, label, pinned_at)
|
|
580
|
+
VALUES (?, ?, ?, ?, ?)
|
|
581
|
+
ON CONFLICT(account_id, machine_id, cwd) DO UPDATE SET
|
|
582
|
+
label = excluded.label,
|
|
583
|
+
pinned_at = excluded.pinned_at`)
|
|
584
|
+
.run(token.account_id, body.machineId, body.cwd, body.label ?? null, ts);
|
|
585
|
+
return {
|
|
586
|
+
machineId: body.machineId,
|
|
587
|
+
cwd: body.cwd,
|
|
588
|
+
...(body.label ? { label: body.label } : {}),
|
|
589
|
+
pinnedAt: ts,
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
app.post("/api/workspace-sync/cwd-unpin", async (request) => {
|
|
593
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
594
|
+
const body = workspaceCwdUnpinSchema.parse(request.body);
|
|
595
|
+
const ts = context.now();
|
|
596
|
+
context.db
|
|
597
|
+
.prepare("DELETE FROM account_workspace_cwd_pins WHERE account_id = ? AND machine_id = ? AND cwd = ?")
|
|
598
|
+
.run(token.account_id, body.machineId, body.cwd);
|
|
599
|
+
return { machineId: body.machineId, cwd: body.cwd, removed: true, removedAt: ts };
|
|
600
|
+
});
|
|
601
|
+
app.post("/api/workspace-sync/project-preference", async (request) => {
|
|
602
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
603
|
+
const body = workspaceProjectPreferenceSchema.parse(request.body);
|
|
604
|
+
const ts = context.now();
|
|
605
|
+
context.db
|
|
606
|
+
.prepare(`INSERT INTO account_project_preferences (account_id, project_key, machine_id, cwd, agent, memory_mode, updated_at)
|
|
607
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
608
|
+
ON CONFLICT(account_id, project_key) DO UPDATE SET
|
|
609
|
+
machine_id = excluded.machine_id,
|
|
610
|
+
cwd = excluded.cwd,
|
|
611
|
+
agent = excluded.agent,
|
|
612
|
+
memory_mode = excluded.memory_mode,
|
|
613
|
+
updated_at = excluded.updated_at`)
|
|
614
|
+
.run(token.account_id, body.projectKey, body.machineId ?? null, body.cwd ?? null, body.agent ?? null, body.memoryMode ?? null, ts);
|
|
615
|
+
return {
|
|
616
|
+
projectKey: body.projectKey,
|
|
617
|
+
...(body.machineId ? { machineId: body.machineId } : {}),
|
|
618
|
+
...(body.cwd ? { cwd: body.cwd } : {}),
|
|
619
|
+
...(body.agent ? { agent: body.agent } : {}),
|
|
620
|
+
...(body.memoryMode ? { memoryMode: body.memoryMode } : {}),
|
|
621
|
+
updatedAt: ts,
|
|
622
|
+
};
|
|
623
|
+
});
|
|
624
|
+
app.post("/api/sessions/:sessionId/metadata", async (request) => {
|
|
625
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
626
|
+
const params = z.object({ sessionId: z.string().min(1) }).parse(request.params);
|
|
627
|
+
const body = sessionMetadataUpdateSchema.parse(request.body);
|
|
628
|
+
const session = context.db
|
|
629
|
+
.prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
|
|
630
|
+
.get(params.sessionId, token.account_id);
|
|
631
|
+
if (!session)
|
|
632
|
+
throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
|
|
633
|
+
const ts = context.now();
|
|
634
|
+
context.db
|
|
635
|
+
.prepare("UPDATE sessions SET encrypted_metadata = ?, updated_at = ? WHERE id = ? AND account_id = ?")
|
|
636
|
+
.run(JSON.stringify(body.encryptedMetadata), ts, params.sessionId, token.account_id);
|
|
637
|
+
const updated = context.db
|
|
638
|
+
.prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
|
|
639
|
+
.get(params.sessionId, token.account_id);
|
|
640
|
+
const snapshot = context.sessionSnapshot(updated);
|
|
641
|
+
context.broadcastControllers(token.account_id, { type: "server:session", session: snapshot });
|
|
642
|
+
return { session: snapshot, updatedAt: ts };
|
|
643
|
+
});
|
|
644
|
+
app.get("/api/memory", async (request) => {
|
|
645
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
646
|
+
const query = memoryListQuerySchema.parse(request.query);
|
|
647
|
+
const rows = query.projectKey
|
|
648
|
+
? context.db
|
|
649
|
+
.prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
|
|
650
|
+
FROM account_memories
|
|
651
|
+
WHERE account_id = ? AND project_key = ?
|
|
652
|
+
ORDER BY updated_at DESC, id ASC`)
|
|
653
|
+
.all(token.account_id, query.projectKey)
|
|
654
|
+
: context.db
|
|
655
|
+
.prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
|
|
656
|
+
FROM account_memories
|
|
657
|
+
WHERE account_id = ?
|
|
658
|
+
ORDER BY updated_at DESC, id ASC`)
|
|
659
|
+
.all(token.account_id);
|
|
660
|
+
return {
|
|
661
|
+
memories: rows.map((row) => ({
|
|
662
|
+
id: row.id,
|
|
663
|
+
projectKey: row.project_key,
|
|
664
|
+
encryptedMemory: context.parseJson(row.encrypted_memory, undefined),
|
|
665
|
+
createdAt: row.created_at,
|
|
666
|
+
updatedAt: row.updated_at,
|
|
667
|
+
})),
|
|
668
|
+
};
|
|
669
|
+
});
|
|
670
|
+
app.post("/api/memory", async (request) => {
|
|
671
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
672
|
+
const body = memorySaveSchema.parse(request.body);
|
|
673
|
+
const encryptedMemory = encryptedEnvelopeSchema.parse(body.encryptedMemory);
|
|
674
|
+
const ts = context.now();
|
|
675
|
+
context.db
|
|
676
|
+
.prepare(`INSERT INTO account_memories (id, account_id, project_key, encrypted_memory, created_at, updated_at)
|
|
677
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
678
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
679
|
+
project_key = excluded.project_key,
|
|
680
|
+
encrypted_memory = excluded.encrypted_memory,
|
|
681
|
+
updated_at = excluded.updated_at`)
|
|
682
|
+
.run(body.id, token.account_id, body.projectKey, JSON.stringify(encryptedMemory), ts, ts);
|
|
683
|
+
return {
|
|
684
|
+
id: body.id,
|
|
685
|
+
projectKey: body.projectKey,
|
|
686
|
+
encryptedMemory,
|
|
687
|
+
createdAt: ts,
|
|
688
|
+
updatedAt: ts,
|
|
689
|
+
};
|
|
690
|
+
});
|
|
691
|
+
app.post("/api/memory/delete", async (request) => {
|
|
692
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
693
|
+
const body = memoryDeleteSchema.parse(request.body);
|
|
694
|
+
const ts = context.now();
|
|
695
|
+
context.db.prepare("DELETE FROM account_memories WHERE account_id = ? AND id = ?").run(token.account_id, body.id);
|
|
696
|
+
return { id: body.id, deleted: true, deletedAt: ts };
|
|
697
|
+
});
|
|
698
|
+
app.get("/api/memory/settings", async (request) => {
|
|
699
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
700
|
+
const query = memoryListQuerySchema.parse(request.query);
|
|
701
|
+
const rows = query.projectKey
|
|
702
|
+
? context.db
|
|
703
|
+
.prepare(`SELECT project_key, disabled, injection_enabled, updated_at
|
|
704
|
+
FROM account_memory_settings
|
|
705
|
+
WHERE account_id = ? AND project_key = ?
|
|
706
|
+
ORDER BY updated_at DESC`)
|
|
707
|
+
.all(token.account_id, query.projectKey)
|
|
708
|
+
: context.db
|
|
709
|
+
.prepare(`SELECT project_key, disabled, injection_enabled, updated_at
|
|
710
|
+
FROM account_memory_settings
|
|
711
|
+
WHERE account_id = ?
|
|
712
|
+
ORDER BY updated_at DESC, project_key ASC`)
|
|
713
|
+
.all(token.account_id);
|
|
714
|
+
return {
|
|
715
|
+
settings: rows.map((row) => ({
|
|
716
|
+
projectKey: row.project_key,
|
|
717
|
+
disabled: row.disabled === 1,
|
|
718
|
+
injectionEnabled: row.injection_enabled === 1,
|
|
719
|
+
updatedAt: row.updated_at,
|
|
720
|
+
})),
|
|
721
|
+
};
|
|
722
|
+
});
|
|
723
|
+
app.post("/api/memory/settings", async (request) => {
|
|
724
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
725
|
+
const body = memorySettingsSchema.parse(request.body);
|
|
726
|
+
const current = context.db
|
|
727
|
+
.prepare("SELECT disabled, injection_enabled FROM account_memory_settings WHERE account_id = ? AND project_key = ?")
|
|
728
|
+
.get(token.account_id, body.projectKey);
|
|
729
|
+
const disabled = body.disabled === undefined ? current?.disabled ?? 0 : body.disabled ? 1 : 0;
|
|
730
|
+
const injectionEnabled = body.injectionEnabled === undefined ? current?.injection_enabled ?? 0 : body.injectionEnabled ? 1 : 0;
|
|
731
|
+
const ts = context.now();
|
|
732
|
+
context.db
|
|
733
|
+
.prepare(`INSERT INTO account_memory_settings (account_id, project_key, disabled, injection_enabled, updated_at)
|
|
734
|
+
VALUES (?, ?, ?, ?, ?)
|
|
735
|
+
ON CONFLICT(account_id, project_key) DO UPDATE SET
|
|
736
|
+
disabled = excluded.disabled,
|
|
737
|
+
injection_enabled = excluded.injection_enabled,
|
|
738
|
+
updated_at = excluded.updated_at`)
|
|
739
|
+
.run(token.account_id, body.projectKey, disabled, injectionEnabled, ts);
|
|
740
|
+
return { projectKey: body.projectKey, disabled: disabled === 1, injectionEnabled: injectionEnabled === 1, updatedAt: ts };
|
|
741
|
+
});
|
|
742
|
+
app.get("/api/machine/memory-documents", async (request) => {
|
|
743
|
+
const token = context.requireHttpToken(request.headers.authorization, "machine");
|
|
744
|
+
const query = machineMemoryDocumentsQuerySchema.parse(request.query);
|
|
745
|
+
const projectKey = `memory-doc:${query.projectKey}`;
|
|
746
|
+
const rows = context.db
|
|
747
|
+
.prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
|
|
748
|
+
FROM account_memories
|
|
749
|
+
WHERE account_id = ? AND project_key = ?
|
|
750
|
+
ORDER BY updated_at DESC, id ASC`)
|
|
751
|
+
.all(token.account_id, projectKey);
|
|
752
|
+
return {
|
|
753
|
+
documents: rows.map((row) => ({
|
|
754
|
+
id: row.id,
|
|
755
|
+
projectKey: query.projectKey,
|
|
756
|
+
encryptedDocument: context.parseJson(row.encrypted_memory, undefined),
|
|
757
|
+
createdAt: row.created_at,
|
|
758
|
+
updatedAt: row.updated_at,
|
|
759
|
+
})),
|
|
760
|
+
};
|
|
761
|
+
});
|
|
762
|
+
app.post("/api/machine/memory-documents", async (request) => {
|
|
763
|
+
const token = context.requireHttpToken(request.headers.authorization, "machine");
|
|
764
|
+
const body = machineMemoryDocumentSaveSchema.parse(request.body);
|
|
765
|
+
const projectKey = `memory-doc:${body.projectKey}`;
|
|
766
|
+
const ts = context.now();
|
|
767
|
+
context.db
|
|
768
|
+
.prepare(`INSERT INTO account_memories (id, account_id, project_key, encrypted_memory, created_at, updated_at)
|
|
769
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
770
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
771
|
+
project_key = excluded.project_key,
|
|
772
|
+
encrypted_memory = excluded.encrypted_memory,
|
|
773
|
+
updated_at = excluded.updated_at`)
|
|
774
|
+
.run(body.id, token.account_id, projectKey, JSON.stringify(body.encryptedDocument), ts, ts);
|
|
775
|
+
return { id: body.id, projectKey: body.projectKey, encryptedDocument: body.encryptedDocument, createdAt: ts, updatedAt: ts };
|
|
776
|
+
});
|
|
241
777
|
app.get("/api/devices", async (request) => {
|
|
242
778
|
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
243
779
|
const rows = context.db
|
|
244
|
-
.prepare(`SELECT kind, machine_id, device_id, device_name,
|
|
780
|
+
.prepare(`SELECT tokens.kind, tokens.machine_id, tokens.device_id, tokens.device_name,
|
|
781
|
+
tokens.created_at, tokens.revoked_at, tokens.last_seen,
|
|
782
|
+
machines.last_seen AS machine_last_seen
|
|
245
783
|
FROM tokens
|
|
246
|
-
|
|
247
|
-
|
|
784
|
+
LEFT JOIN machines ON machines.account_id = tokens.account_id AND machines.id = tokens.machine_id
|
|
785
|
+
WHERE tokens.account_id = ? AND tokens.device_id IS NOT NULL
|
|
786
|
+
ORDER BY tokens.created_at DESC`)
|
|
248
787
|
.all(token.account_id);
|
|
788
|
+
const byDeviceId = new Map();
|
|
789
|
+
for (const row of rows) {
|
|
790
|
+
const existing = byDeviceId.get(row.device_id);
|
|
791
|
+
if (!existing) {
|
|
792
|
+
byDeviceId.set(row.device_id, row);
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (existing.revoked_at !== null && row.revoked_at === null) {
|
|
796
|
+
byDeviceId.set(row.device_id, row);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
249
799
|
return {
|
|
250
|
-
devices:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
800
|
+
devices: [...byDeviceId.values()].map((row) => {
|
|
801
|
+
const revoked = row.revoked_at !== null;
|
|
802
|
+
const online = revoked
|
|
803
|
+
? false
|
|
804
|
+
: row.kind === "machine" && row.machine_id
|
|
805
|
+
? context.connectedMachine(token.account_id, row.machine_id) !== undefined
|
|
806
|
+
: context.controllerDeviceOnline(token.account_id, row.device_id);
|
|
807
|
+
const lastSeen = row.kind === "machine"
|
|
808
|
+
? row.machine_last_seen ?? row.last_seen ?? row.created_at
|
|
809
|
+
: row.last_seen ?? row.created_at;
|
|
810
|
+
return {
|
|
811
|
+
deviceId: row.device_id,
|
|
812
|
+
name: row.device_name ?? row.device_id,
|
|
813
|
+
kind: row.kind,
|
|
814
|
+
current: row.kind === "controller" && row.device_id === token.device_id,
|
|
815
|
+
revoked,
|
|
816
|
+
online,
|
|
817
|
+
createdAt: row.created_at,
|
|
818
|
+
lastSeen,
|
|
819
|
+
machineId: row.machine_id ?? undefined,
|
|
820
|
+
revokedAt: row.revoked_at ?? undefined,
|
|
821
|
+
};
|
|
822
|
+
}),
|
|
260
823
|
};
|
|
261
824
|
});
|
|
262
825
|
app.post("/api/devices/controller", async (request) => {
|
|
@@ -280,6 +843,20 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
280
843
|
createdAt,
|
|
281
844
|
};
|
|
282
845
|
});
|
|
846
|
+
app.post("/api/devices/rename", async (request) => {
|
|
847
|
+
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
848
|
+
const body = deviceRenameSchema.parse(request.body);
|
|
849
|
+
const target = context.db
|
|
850
|
+
.prepare("SELECT * FROM tokens WHERE account_id = ? AND device_id = ? AND revoked_at IS NULL ORDER BY created_at DESC")
|
|
851
|
+
.get(token.account_id, body.deviceId);
|
|
852
|
+
if (!target)
|
|
853
|
+
throw new HttpError(404, "DEVICE_NOT_FOUND", "Device not found");
|
|
854
|
+
context.db.prepare("UPDATE tokens SET device_name = ? WHERE token = ?").run(body.name, target.token);
|
|
855
|
+
if (target.kind === "machine" && target.machine_id) {
|
|
856
|
+
context.db.prepare("UPDATE machines SET name = ?, updated_at = ? WHERE account_id = ? AND id = ?").run(body.name, context.now(), token.account_id, target.machine_id);
|
|
857
|
+
}
|
|
858
|
+
return { deviceId: body.deviceId, name: body.name, renamedAt: context.now() };
|
|
859
|
+
});
|
|
283
860
|
app.post("/api/controller-invites", async (request) => {
|
|
284
861
|
const token = context.requireFullControllerToken(request.headers.authorization);
|
|
285
862
|
const body = controllerInviteCreateSchema.parse(request.body);
|
|
@@ -392,4 +969,53 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
|
|
|
392
969
|
return { tokenId: body.tokenId, revokedAt: target.revoked_at ?? ts };
|
|
393
970
|
});
|
|
394
971
|
}
|
|
972
|
+
function compareHeadOrders(next, current) {
|
|
973
|
+
if (!current)
|
|
974
|
+
return 1;
|
|
975
|
+
if (next.time !== current.time)
|
|
976
|
+
return next.time > current.time ? 1 : -1;
|
|
977
|
+
if (next.seq !== current.seq)
|
|
978
|
+
return next.seq > current.seq ? 1 : -1;
|
|
979
|
+
if (next.id !== current.id)
|
|
980
|
+
return next.id > current.id ? 1 : -1;
|
|
981
|
+
return 0;
|
|
982
|
+
}
|
|
983
|
+
function logicalEventOrderForRow(row) {
|
|
984
|
+
if (row.occurred_at !== null && row.occurred_at !== undefined) {
|
|
985
|
+
return { time: row.occurred_at, seq: row.turn_seq ?? row.seq, id: row.id };
|
|
986
|
+
}
|
|
987
|
+
if (row.message_id?.startsWith("hist_") && row.turn_seq !== null && row.turn_seq !== undefined) {
|
|
988
|
+
return { time: 0, seq: row.turn_seq, id: row.id };
|
|
989
|
+
}
|
|
990
|
+
return { time: row.created_at, seq: row.turn_seq ?? row.seq, id: row.id };
|
|
991
|
+
}
|
|
992
|
+
function sessionHeadBasisOrderForRow(db, row) {
|
|
993
|
+
if (row.head_basis_logical_time !== null && row.head_basis_turn_seq !== null) {
|
|
994
|
+
return {
|
|
995
|
+
time: row.head_basis_logical_time,
|
|
996
|
+
seq: row.head_basis_turn_seq,
|
|
997
|
+
id: row.head_basis_event_id ?? 0,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
const eventId = row.current_head ? /^event:(\d+)$/.exec(row.current_head)?.[1] : undefined;
|
|
1001
|
+
if (!eventId)
|
|
1002
|
+
return undefined;
|
|
1003
|
+
const event = db
|
|
1004
|
+
.prepare("SELECT * FROM session_events WHERE account_id = ? AND session_id = ? AND machine_id = ? AND id = ?")
|
|
1005
|
+
.get(row.account_id, row.id, row.machine_id, Number(eventId));
|
|
1006
|
+
return event ? logicalEventOrderForRow(event) : undefined;
|
|
1007
|
+
}
|
|
1008
|
+
function sessionLooksTranscriptPending(context, sessionRow) {
|
|
1009
|
+
if (sessionRow.status !== "running")
|
|
1010
|
+
return false;
|
|
1011
|
+
if (!sessionRow.source_session_id || !sessionRow.source_checkpoint_id)
|
|
1012
|
+
return false;
|
|
1013
|
+
const row = context.db
|
|
1014
|
+
.prepare(`SELECT 1
|
|
1015
|
+
FROM session_events
|
|
1016
|
+
WHERE account_id = ? AND session_id = ? AND machine_id = ?
|
|
1017
|
+
LIMIT 1`)
|
|
1018
|
+
.get(sessionRow.account_id, sessionRow.id, sessionRow.machine_id);
|
|
1019
|
+
return !row;
|
|
1020
|
+
}
|
|
395
1021
|
//# sourceMappingURL=http-routes.js.map
|