@jungjaehoon/mama-core 2.4.1 → 4.0.0
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/README.md +76 -239
- package/db/migrations/030-case-first-memory-substrate.sql +13 -98
- package/db/migrations/034-add-connector-event-scope-columns.sql +9 -13
- package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +9 -95
- package/db/migrations/062-refresh-connector-event-sequences.sql +9 -113
- package/db/migrations/063-refresh-legacy-connector-event-updates.sql +9 -32
- package/db/migrations/067-add-connector-event-source-entity-id.sql +9 -13
- package/db/migrations/069-create-registry-nodes.sql +52 -0
- package/db/migrations/070-record-identity.sql +35 -0
- package/db/migrations/071-service-operation-origins.sql +187 -0
- package/db/migrations/072-observation-versions.sql +29 -0
- package/db/migrations/073-registry-corrections.sql +45 -0
- package/db/migrations/074-work-graph-ref-kinds.sql +39 -0
- package/db/migrations/075-agent-judgments.sql +58 -0
- package/db/migrations/076-scoped-checkpoint-bindings.sql +15 -0
- package/db/migrations/077-legacy-record-kind.sql +6 -0
- package/db/migrations/078-source-ingest-commands.sql +18 -0
- package/db/migrations/079-twin-edge-relations.sql +43 -0
- package/db/migrations/080-restore-duration-days.sql +7 -0
- package/db/migrations/081-mailbox.sql +61 -0
- package/db/migrations/082-commitments-row-id.sql +12 -0
- package/db/migrations/083-observation-is-the-evidence.sql +49 -0
- package/db/migrations/084-scope-kind-is-not-the-cores-to-list.sql +34 -0
- package/db/migrations/085-a-migration-has-a-source.sql +37 -0
- package/db/migrations/086-the-mailbox-is-generic.sql +110 -0
- package/db/migrations/087-mailbox-payload.sql +5 -0
- package/db/migrations/088-native-input-journal.sql +14 -0
- package/db/migrations/089-native-turn-receipt-lookup.sql +3 -0
- package/db/migrations/090-model-run-native-inputs.sql +29 -0
- package/db/migrations/091-native-turn-results.sql +11 -0
- package/db/migrations/092-restore-decision-fts.sql +34 -0
- package/db/migrations/093-native-turn-primary-kind.sql +14 -0
- package/db/migrations/094-commitment-run-provenance.sql +9 -0
- package/db/migrations/095-model-run-native-input-identity.sql +39 -0
- package/db/migrations/096-drop-connector-event-index-fts.sql +9 -0
- package/db/migrations/097-native-input-json-validity.sql +37 -0
- package/db/migrations/098-workflow-memory-kind.sql +105 -0
- package/dist/action-contracts.d.ts +212 -0
- package/dist/action-contracts.js +10 -0
- package/dist/api/catalog.d.ts +118 -0
- package/dist/api/catalog.js +1756 -0
- package/dist/api/dispatch.d.ts +76 -0
- package/dist/api/dispatch.js +372 -0
- package/dist/canonicalize.d.ts +2 -2
- package/dist/canonicalize.js +3 -3
- package/dist/client/client.d.ts +33 -0
- package/dist/client/client.js +123 -0
- package/dist/client/ipc.d.ts +81 -0
- package/dist/client/ipc.js +314 -0
- package/dist/db-adapter/base-adapter.d.ts +6 -66
- package/dist/db-adapter/base-adapter.js +6 -6
- package/dist/db-adapter/index.d.ts +5 -5
- package/dist/db-adapter/index.js +2 -5
- package/dist/db-adapter/node-sqlite-adapter.d.ts +85 -15
- package/dist/db-adapter/node-sqlite-adapter.js +1968 -570
- package/dist/db-manager.d.ts +53 -97
- package/dist/db-manager.js +121 -603
- package/dist/embedding/embedder.d.ts +132 -0
- package/dist/embedding/embedder.js +299 -0
- package/dist/identity/principal-repository.d.ts +2 -2
- package/dist/index.d.ts +43 -58
- package/dist/index.js +97 -145
- package/dist/{context-compile/channel-grant.d.ts → knowledge/access.d.ts} +12 -17
- package/dist/knowledge/access.js +663 -0
- package/dist/knowledge/case-errors.d.ts +31 -0
- package/dist/knowledge/case-errors.js +40 -0
- package/dist/{cases/search-rollup.d.ts → knowledge/case-search-rollup.d.ts} +5 -4
- package/dist/{cases/search-rollup.js → knowledge/case-search-rollup.js} +6 -8
- package/dist/{cases/store.d.ts → knowledge/case-store.d.ts} +3 -6
- package/dist/{cases/store.js → knowledge/case-store.js} +7 -42
- package/dist/{cases/timeline-range.d.ts → knowledge/case-timeline-range.d.ts} +3 -3
- package/dist/{cases/timeline-range.js → knowledge/case-timeline-range.js} +47 -25
- package/dist/{cases/types.d.ts → knowledge/case-types.d.ts} +8 -2
- package/dist/{cases/types.js → knowledge/case-types.js} +1 -1
- package/dist/knowledge/commitments.d.ts +188 -0
- package/dist/knowledge/commitments.js +370 -0
- package/dist/knowledge/decision-edges.d.ts +70 -0
- package/dist/knowledge/decision-edges.js +123 -0
- package/dist/{agent-graph/types.d.ts → knowledge/graph-query.d.ts} +80 -70
- package/dist/knowledge/graph-query.js +1585 -0
- package/dist/knowledge/identity.d.ts +16 -0
- package/dist/knowledge/identity.js +22 -0
- package/dist/knowledge/index.d.ts +47 -0
- package/dist/knowledge/index.js +90 -0
- package/dist/knowledge/judgments.d.ts +118 -0
- package/dist/knowledge/judgments.js +808 -0
- package/dist/knowledge/observations.d.ts +160 -0
- package/dist/knowledge/observations.js +449 -0
- package/dist/knowledge/question-type.d.ts +34 -0
- package/dist/knowledge/question-type.js +70 -0
- package/dist/{search → knowledge}/ranker-features.js +1 -1
- package/dist/{search → knowledge}/search-quality.d.ts +0 -1
- package/dist/knowledge/search.d.ts +38 -0
- package/dist/knowledge/search.js +91 -0
- package/dist/knowledge/source-ingest.d.ts +65 -0
- package/dist/knowledge/source-ingest.js +174 -0
- package/dist/{edges/types.d.ts → knowledge/twin-edge-types.d.ts} +31 -4
- package/dist/{edges/types.js → knowledge/twin-edge-types.js} +13 -2
- package/dist/knowledge/work-dates.d.ts +49 -0
- package/dist/knowledge/work-dates.js +120 -0
- package/dist/mama-api.d.ts +114 -778
- package/dist/mama-api.js +313 -2461
- package/dist/memory/api.d.ts +264 -24
- package/dist/memory/api.js +1633 -843
- package/dist/memory/bootstrap-builder.d.ts +2 -1
- package/dist/memory/bootstrap-builder.js +5 -5
- package/dist/memory/channel-summary-state-store.d.ts +3 -2
- package/dist/memory/channel-summary-state-store.js +53 -51
- package/dist/memory/channel-summary-store.d.ts +5 -3
- package/dist/memory/channel-summary-store.js +3 -8
- package/dist/memory/dashboard-read.d.ts +46 -0
- package/dist/memory/dashboard-read.js +94 -0
- package/dist/memory/event-store.d.ts +3 -3
- package/dist/memory/event-store.js +3 -10
- package/dist/memory/finding-store.d.ts +3 -2
- package/dist/memory/finding-store.js +2 -7
- package/dist/memory/graph-read.d.ts +52 -0
- package/dist/memory/graph-read.js +130 -0
- package/dist/memory/judgment-types.d.ts +394 -0
- package/dist/memory/judgment-types.js +16 -0
- package/dist/memory/provenance-audit.d.ts +5 -2
- package/dist/memory/provenance-audit.js +8 -14
- package/dist/memory/provenance-live.d.ts +141 -0
- package/dist/memory/provenance-live.js +409 -0
- package/dist/memory/provenance-query.d.ts +5 -4
- package/dist/memory/provenance-query.js +21 -29
- package/dist/memory/provenance-resolver.d.ts +191 -0
- package/dist/memory/provenance-resolver.js +155 -0
- package/dist/memory/provenance.d.ts +1 -11
- package/dist/memory/provenance.js +2 -33
- package/dist/memory/recall-sanitize.d.ts +55 -0
- package/dist/memory/recall-sanitize.js +144 -0
- package/dist/memory/secret-filter.d.ts +32 -0
- package/dist/memory/secret-filter.js +126 -0
- package/dist/memory/truth-store.d.ts +2 -1
- package/dist/memory/truth-store.js +1 -4
- package/dist/memory/types.d.ts +74 -24
- package/dist/memory/types.js +75 -5
- package/dist/memory/write-adapters.d.ts +98 -0
- package/dist/memory/write-adapters.js +242 -0
- package/dist/operations/owner-action-effects.d.ts +53 -0
- package/dist/operations/owner-action-effects.js +96 -0
- package/dist/provenance/source-ref.d.ts +1 -1
- package/dist/provenance/source-ref.js +0 -2
- package/dist/registry/corrections.d.ts +31 -0
- package/dist/registry/corrections.js +332 -0
- package/dist/registry/record-identity.d.ts +49 -0
- package/dist/registry/record-identity.js +133 -0
- package/dist/registry/store.d.ts +101 -0
- package/dist/registry/store.js +395 -0
- package/dist/registry/types.d.ts +28 -0
- package/dist/registry/types.js +5 -0
- package/dist/relevance-scorer.js +2 -2
- package/dist/runtime/agent-event-bus.d.ts +50 -0
- package/dist/runtime/agent-event-bus.js +101 -0
- package/dist/runtime/drivers/claude-cli-wrapper.d.ts +155 -0
- package/dist/runtime/drivers/claude-cli-wrapper.js +392 -0
- package/dist/runtime/drivers/cli-arg-redaction.d.ts +2 -0
- package/dist/runtime/drivers/cli-arg-redaction.js +18 -0
- package/dist/runtime/drivers/cli-secret-redaction.d.ts +17 -0
- package/dist/runtime/drivers/cli-secret-redaction.js +126 -0
- package/dist/runtime/drivers/codex-app-server-process.d.ts +277 -0
- package/dist/runtime/drivers/codex-app-server-process.js +2342 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.d.ts +17 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.js +267 -0
- package/dist/runtime/drivers/codex-home.d.ts +20 -0
- package/dist/runtime/drivers/codex-home.js +674 -0
- package/dist/runtime/drivers/codex-thread-registry.d.ts +63 -0
- package/dist/runtime/drivers/codex-thread-registry.js +433 -0
- package/dist/runtime/drivers/persistent-cli-adapter.d.ts +160 -0
- package/dist/runtime/drivers/persistent-cli-adapter.js +396 -0
- package/dist/runtime/drivers/persistent-cli-process.d.ts +424 -0
- package/dist/runtime/drivers/persistent-cli-process.js +1543 -0
- package/dist/runtime/drivers/types.d.ts +456 -0
- package/dist/runtime/drivers/types.js +110 -0
- package/dist/runtime/mailbox.d.ts +223 -0
- package/dist/runtime/mailbox.js +582 -0
- package/dist/runtime/model-run-store.d.ts +28 -0
- package/dist/{model-runs/store.js → runtime/model-run-store.js} +81 -41
- package/dist/{model-runs/types.d.ts → runtime/model-run-types.d.ts} +33 -4
- package/dist/{model-runs/types.js → runtime/model-run-types.js} +1 -1
- package/dist/runtime/native-effect-observer.d.ts +22 -0
- package/dist/runtime/native-effect-observer.js +103 -0
- package/dist/runtime/native-input-journal.d.ts +46 -0
- package/dist/runtime/native-input-journal.js +308 -0
- package/dist/runtime/native-prompt.d.ts +118 -0
- package/dist/runtime/native-prompt.js +496 -0
- package/dist/runtime/native-session.d.ts +10 -0
- package/dist/{agent-graph/index.js → runtime/native-session.js} +9 -6
- package/dist/runtime/native-tool-trace-observer.d.ts +5 -0
- package/dist/runtime/native-tool-trace-observer.js +65 -0
- package/dist/runtime/native-turn.d.ts +272 -0
- package/dist/runtime/native-turn.js +1041 -0
- package/dist/runtime/operations.d.ts +236 -0
- package/dist/runtime/operations.js +565 -0
- package/dist/runtime/post-tool-handler.d.ts +74 -0
- package/dist/runtime/post-tool-handler.js +137 -0
- package/dist/runtime/prompt-layers.d.ts +126 -0
- package/dist/runtime/prompt-layers.js +239 -0
- package/dist/runtime/runtime-process.d.ts +102 -0
- package/dist/runtime/runtime-process.js +206 -0
- package/dist/runtime/runtime.d.ts +278 -0
- package/dist/runtime/runtime.js +563 -0
- package/dist/runtime/session-pool.d.ts +176 -0
- package/dist/runtime/session-pool.js +369 -0
- package/dist/runtime/stimulus-payload.d.ts +5 -0
- package/dist/runtime/stimulus-payload.js +50 -0
- package/dist/runtime/subagent-bridge.d.ts +24 -0
- package/dist/{agent-graph/types.js → runtime/subagent-bridge.js} +1 -1
- package/dist/runtime/text-completion.d.ts +22 -0
- package/dist/{agent-situation/types.js → runtime/text-completion.js} +1 -1
- package/dist/runtime/token-estimator.d.ts +25 -0
- package/dist/runtime/token-estimator.js +75 -0
- package/dist/runtime/tool-trace-store.d.ts +22 -0
- package/dist/{model-runs → runtime}/tool-trace-store.js +46 -27
- package/dist/runtime/trace-summary.d.ts +5 -0
- package/dist/runtime/trace-summary.js +74 -0
- package/dist/runtime/turn-text.d.ts +23 -0
- package/dist/runtime/turn-text.js +41 -0
- package/dist/storage/database.d.ts +67 -0
- package/dist/storage/database.js +158 -0
- package/package.json +20 -34
- package/dist/agent-graph/alias-write.d.ts +0 -3
- package/dist/agent-graph/alias-write.js +0 -234
- package/dist/agent-graph/entity-resolve.d.ts +0 -3
- package/dist/agent-graph/entity-resolve.js +0 -271
- package/dist/agent-graph/errors.d.ts +0 -4
- package/dist/agent-graph/errors.js +0 -11
- package/dist/agent-graph/graph-query.d.ts +0 -5
- package/dist/agent-graph/graph-query.js +0 -360
- package/dist/agent-graph/index.d.ts +0 -6
- package/dist/agent-situation/builder.d.ts +0 -8
- package/dist/agent-situation/builder.js +0 -239
- package/dist/agent-situation/cache-key.d.ts +0 -4
- package/dist/agent-situation/cache-key.js +0 -64
- package/dist/agent-situation/index.d.ts +0 -7
- package/dist/agent-situation/index.js +0 -28
- package/dist/agent-situation/packet-store.d.ts +0 -27
- package/dist/agent-situation/packet-store.js +0 -228
- package/dist/agent-situation/ranking-policy.d.ts +0 -18
- package/dist/agent-situation/ranking-policy.js +0 -75
- package/dist/agent-situation/source-readers.d.ts +0 -52
- package/dist/agent-situation/source-readers.js +0 -300
- package/dist/agent-situation/types.d.ts +0 -195
- package/dist/cases/wiki-page-index.d.ts +0 -37
- package/dist/cases/wiki-page-index.js +0 -201
- package/dist/config-loader.d.ts +0 -68
- package/dist/config-loader.js +0 -239
- package/dist/connectors/event-index.d.ts +0 -24
- package/dist/connectors/event-index.js +0 -245
- package/dist/connectors/raw-query.d.ts +0 -40
- package/dist/connectors/raw-query.js +0 -412
- package/dist/connectors/types.d.ts +0 -117
- package/dist/connectors/types.js +0 -3
- package/dist/context-compile/boundary-defaults.d.ts +0 -12
- package/dist/context-compile/boundary-defaults.js +0 -105
- package/dist/context-compile/channel-grant.js +0 -66
- package/dist/context-compile/compiler-policy.d.ts +0 -39
- package/dist/context-compile/compiler-policy.js +0 -147
- package/dist/context-compile/compiler.d.ts +0 -18
- package/dist/context-compile/compiler.js +0 -404
- package/dist/context-compile/index.d.ts +0 -10
- package/dist/context-compile/index.js +0 -26
- package/dist/context-compile/packet-store.d.ts +0 -8
- package/dist/context-compile/packet-store.js +0 -239
- package/dist/context-compile/ref.d.ts +0 -7
- package/dist/context-compile/ref.js +0 -102
- package/dist/context-compile/source-readers.d.ts +0 -70
- package/dist/context-compile/source-readers.js +0 -840
- package/dist/context-compile/types.d.ts +0 -155
- package/dist/context-compile/types.js +0 -11
- package/dist/context-compile/visibility.d.ts +0 -21
- package/dist/context-compile/visibility.js +0 -224
- package/dist/decision-tracker.d.ts +0 -206
- package/dist/decision-tracker.js +0 -481
- package/dist/edges/ref-validation.d.ts +0 -7
- package/dist/edges/ref-validation.js +0 -321
- package/dist/edges/store.d.ts +0 -8
- package/dist/edges/store.js +0 -129
- package/dist/embedding-client.d.ts +0 -28
- package/dist/embedding-client.js +0 -53
- package/dist/embedding-server/index.d.ts +0 -67
- package/dist/embedding-server/index.js +0 -405
- package/dist/embedding-server/mobile/auth.d.ts +0 -44
- package/dist/embedding-server/mobile/auth.js +0 -150
- package/dist/embedding-server/mobile/daemon.d.ts +0 -129
- package/dist/embedding-server/mobile/daemon.js +0 -313
- package/dist/embedding-server/mobile/output-parser.d.ts +0 -115
- package/dist/embedding-server/mobile/output-parser.js +0 -241
- package/dist/embedding-server/mobile/session-api.d.ts +0 -57
- package/dist/embedding-server/mobile/session-api.js +0 -261
- package/dist/embedding-server/mobile/session-manager.d.ts +0 -138
- package/dist/embedding-server/mobile/session-manager.js +0 -378
- package/dist/embedding-server/mobile/websocket-handler.d.ts +0 -137
- package/dist/embedding-server/mobile/websocket-handler.js +0 -486
- package/dist/embeddings.d.ts +0 -68
- package/dist/embeddings.js +0 -244
- package/dist/entities/audit-metrics.d.ts +0 -68
- package/dist/entities/audit-metrics.js +0 -124
- package/dist/entities/candidate-generator.d.ts +0 -8
- package/dist/entities/candidate-generator.js +0 -24
- package/dist/entities/entity-linked-decision-counts.d.ts +0 -2
- package/dist/entities/entity-linked-decision-counts.js +0 -29
- package/dist/entities/entity-list.d.ts +0 -22
- package/dist/entities/entity-list.js +0 -3
- package/dist/entities/entity-orphan-list.d.ts +0 -17
- package/dist/entities/entity-orphan-list.js +0 -3
- package/dist/entities/entity-search.d.ts +0 -20
- package/dist/entities/entity-search.js +0 -3
- package/dist/entities/errors.d.ts +0 -55
- package/dist/entities/errors.js +0 -100
- package/dist/entities/lineage-store.d.ts +0 -28
- package/dist/entities/lineage-store.js +0 -204
- package/dist/entities/normalization.d.ts +0 -14
- package/dist/entities/normalization.js +0 -63
- package/dist/entities/policy-store.d.ts +0 -8
- package/dist/entities/policy-store.js +0 -75
- package/dist/entities/policy-types.d.ts +0 -72
- package/dist/entities/policy-types.js +0 -59
- package/dist/entities/projection.d.ts +0 -8
- package/dist/entities/projection.js +0 -82
- package/dist/entities/provenance-query.d.ts +0 -32
- package/dist/entities/provenance-query.js +0 -3
- package/dist/entities/read-identity.d.ts +0 -31
- package/dist/entities/read-identity.js +0 -264
- package/dist/entities/recall-bridge.d.ts +0 -5
- package/dist/entities/recall-bridge.js +0 -171
- package/dist/entities/resolution-engine.d.ts +0 -8
- package/dist/entities/resolution-engine.js +0 -3
- package/dist/entities/rollback-preview.d.ts +0 -35
- package/dist/entities/rollback-preview.js +0 -3
- package/dist/entities/source-locator.d.ts +0 -3
- package/dist/entities/source-locator.js +0 -32
- package/dist/entities/store.d.ts +0 -69
- package/dist/entities/store.js +0 -544
- package/dist/entities/types.d.ts +0 -166
- package/dist/entities/types.js +0 -30
- package/dist/memory/extraction-prompt.d.ts +0 -4
- package/dist/memory/extraction-prompt.js +0 -89
- package/dist/memory/scope-backfill.d.ts +0 -14
- package/dist/memory/scope-backfill.js +0 -3
- package/dist/memory/scope-store.d.ts +0 -4
- package/dist/memory/scope-store.js +0 -9
- package/dist/memory-store.d.ts +0 -87
- package/dist/memory-store.js +0 -78
- package/dist/model-runs/store.d.ts +0 -13
- package/dist/model-runs/tool-trace-store.d.ts +0 -8
- package/dist/notification-manager.d.ts +0 -7
- package/dist/notification-manager.js +0 -12
- package/dist/outcome-tracker.d.ts +0 -151
- package/dist/outcome-tracker.js +0 -271
- package/dist/query-intent.d.ts +0 -23
- package/dist/query-intent.js +0 -114
- package/dist/runtime/trusted-provenance.d.ts +0 -3
- package/dist/runtime/trusted-provenance.js +0 -6
- package/dist/search/question-type.d.ts +0 -5
- package/dist/search/question-type.js +0 -41
- package/dist/test-utils.d.ts +0 -80
- package/dist/test-utils.js +0 -218
- package/dist/tier-validator.d.ts +0 -43
- package/dist/tier-validator.js +0 -159
- package/dist/time-formatter.d.ts +0 -25
- package/dist/time-formatter.js +0 -93
- /package/dist/{search → knowledge}/feedback-store.d.ts +0 -0
- /package/dist/{search → knowledge}/feedback-store.js +0 -0
- /package/dist/{search → knowledge}/ranker-features.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.js +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.js +0 -0
- /package/dist/{search → knowledge}/search-quality.js +0 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StimulusQuarantine = void 0;
|
|
4
|
+
exports.startRuntime = startRuntime;
|
|
5
|
+
const types_js_1 = require("./drivers/types.js");
|
|
6
|
+
/**
|
|
7
|
+
* Runtime — the one subject that opens and owns the action surface.
|
|
8
|
+
*
|
|
9
|
+
* §4.3: `runtime.start` alone opens the DB, native session, model and socket;
|
|
10
|
+
* `stop` closes only what it opened. Product assembly states the paths — core
|
|
11
|
+
* never guesses `~/.mama`. This module is the socket·credential half of that
|
|
12
|
+
* subject: it issues this boot's session credential, mounts the action socket
|
|
13
|
+
* on the shared catalog/dispatch, and on `stop` releases exactly those two
|
|
14
|
+
* things. The adapter, catalog and dispatcher stay product-owned — the daemon
|
|
15
|
+
* opens its one DB and builds the one action surface, and the runtime serves
|
|
16
|
+
* them rather than opening a second stack. There is never a second start
|
|
17
|
+
* subject per profile.
|
|
18
|
+
*
|
|
19
|
+
* It is also the loop's intake. `accept` is the one door a stimulus enters by —
|
|
20
|
+
* an owner's message, a connector delta, a scheduled time — and it does exactly
|
|
21
|
+
* two things: prove the stated principal is one this runtime serves, and make
|
|
22
|
+
* the stimulus durable. It returns a receipt of acceptance and nothing more.
|
|
23
|
+
* What the stimulus MEANS, and what to do about it, is the agent's; a host that
|
|
24
|
+
* classified it here would be choosing the model's work for it.
|
|
25
|
+
*
|
|
26
|
+
* The intake is opened before the socket, for the same reason the credential
|
|
27
|
+
* table is: a producer must never reach a runtime whose intake does not exist
|
|
28
|
+
* yet.
|
|
29
|
+
*
|
|
30
|
+
* And the runtime carries what it accepted through to the loop: the claim, the
|
|
31
|
+
* lease, the retry backoff and the ack are the runtime's, so no producer has to
|
|
32
|
+
* hold a stimulus while a turn runs and no producer has to remember one that
|
|
33
|
+
* failed. What runs inside a delivery is the native session the product already
|
|
34
|
+
* has — this is the delivery half of the mailbox, never a second turn loop.
|
|
35
|
+
*/
|
|
36
|
+
const node_crypto_1 = require("node:crypto");
|
|
37
|
+
const node_fs_1 = require("node:fs");
|
|
38
|
+
const node_path_1 = require("node:path");
|
|
39
|
+
const ipc_js_1 = require("../client/ipc.js");
|
|
40
|
+
const mailbox_js_1 = require("./mailbox.js");
|
|
41
|
+
function writeCredential(path, credential) {
|
|
42
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
43
|
+
const temporaryPath = `${path}.${(0, node_crypto_1.randomUUID)()}.tmp`;
|
|
44
|
+
try {
|
|
45
|
+
(0, node_fs_1.writeFileSync)(temporaryPath, credential, { mode: 0o600, flag: 'wx' });
|
|
46
|
+
(0, node_fs_1.renameSync)(temporaryPath, path);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
(0, node_fs_1.rmSync)(temporaryPath, { force: true });
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A delivery that must NOT be tried again.
|
|
55
|
+
*
|
|
56
|
+
* Retry is the runtime's default because most failures are transient. Some are
|
|
57
|
+
* not: an external effect whose outcome is unknown cannot be replayed on a
|
|
58
|
+
* guess (§4.3), and a stimulus whose delivery already spent one must park where
|
|
59
|
+
* a person can see it rather than repeat it. Throwing this from `deliver` says
|
|
60
|
+
* exactly that — park the row dead, spend no attempt, and name it through
|
|
61
|
+
* `onDead`.
|
|
62
|
+
*/
|
|
63
|
+
class StimulusQuarantine extends Error {
|
|
64
|
+
constructor(reason) {
|
|
65
|
+
super(reason);
|
|
66
|
+
this.name = 'StimulusQuarantine';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.StimulusQuarantine = StimulusQuarantine;
|
|
70
|
+
/**
|
|
71
|
+
* Opens the action surface: issues the session credential and serves
|
|
72
|
+
* `catalog`/`dispatch` on the private socket. `stop` closes the socket and
|
|
73
|
+
* removes the credential file it wrote — nothing else.
|
|
74
|
+
*/
|
|
75
|
+
async function startRuntime(options) {
|
|
76
|
+
const maxPerTick = options.delivery?.maxPerTick ?? 8;
|
|
77
|
+
const maxPendingInputs = options.delivery?.maxPendingInputs ?? maxPerTick;
|
|
78
|
+
if (!Number.isSafeInteger(maxPendingInputs) || maxPendingInputs < 1) {
|
|
79
|
+
throw new Error('maxPendingInputs must be a positive integer');
|
|
80
|
+
}
|
|
81
|
+
const { socketPath } = options.paths;
|
|
82
|
+
if (options.principals.length === 0) {
|
|
83
|
+
throw new Error('startRuntime requires at least one principal');
|
|
84
|
+
}
|
|
85
|
+
const paths = options.principals.map((principal) => principal.credentialPath);
|
|
86
|
+
if (new Set(paths).size !== paths.length) {
|
|
87
|
+
throw new Error('each principal needs its own credential path');
|
|
88
|
+
}
|
|
89
|
+
const ids = options.principals.map((principal) => principal.access.principalId);
|
|
90
|
+
if (new Set(ids).size !== ids.length) {
|
|
91
|
+
throw new Error('each principal needs its own principal ID');
|
|
92
|
+
}
|
|
93
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(socketPath), { recursive: true });
|
|
94
|
+
// Opened before the socket, for the same reason the credential table is: a
|
|
95
|
+
// producer must never reach a runtime whose intake does not exist yet.
|
|
96
|
+
const mailbox = options.mailbox
|
|
97
|
+
? new mailbox_js_1.Mailbox(options.mailbox.adapter, ...(options.mailbox.clock ? [options.mailbox.clock] : []))
|
|
98
|
+
: undefined;
|
|
99
|
+
// Prepare the credential table before the socket, but publish files only
|
|
100
|
+
// after the socket is ours. A second start must not rotate or remove the
|
|
101
|
+
// credentials of a live runtime whose socket it failed to acquire.
|
|
102
|
+
const byCredential = new Map();
|
|
103
|
+
const byPrincipalId = new Map();
|
|
104
|
+
const credentialsToPublish = [];
|
|
105
|
+
const credentialPaths = new Set(paths);
|
|
106
|
+
const written = new Set();
|
|
107
|
+
const removeCredentials = () => {
|
|
108
|
+
for (const path of written) {
|
|
109
|
+
(0, node_fs_1.rmSync)(path, { force: true });
|
|
110
|
+
}
|
|
111
|
+
written.clear();
|
|
112
|
+
};
|
|
113
|
+
for (const principal of options.principals) {
|
|
114
|
+
const credential = (0, node_crypto_1.randomUUID)();
|
|
115
|
+
credentialsToPublish.push({ path: principal.credentialPath, credential });
|
|
116
|
+
byCredential.set(credential, principal);
|
|
117
|
+
byPrincipalId.set(principal.access.principalId, { principal, credential });
|
|
118
|
+
}
|
|
119
|
+
const mounted = await (0, ipc_js_1.createActionIpcServer)({
|
|
120
|
+
socketPath,
|
|
121
|
+
catalog: options.catalog,
|
|
122
|
+
dispatch: options.dispatch,
|
|
123
|
+
// The principal's stated run rides with whatever the host states per
|
|
124
|
+
// call. The credential selects it, so one caller's run is never another's.
|
|
125
|
+
sessionFacts: (access, request) => {
|
|
126
|
+
const stated = options.sessionFacts?.(access, request);
|
|
127
|
+
const principal = request.credential === undefined ? undefined : byCredential.get(request.credential);
|
|
128
|
+
const runEvidenceScope = principal?.runEvidenceScope;
|
|
129
|
+
if (!runEvidenceScope) {
|
|
130
|
+
return stated;
|
|
131
|
+
}
|
|
132
|
+
return { ...(stated ?? {}), runEvidenceScope };
|
|
133
|
+
},
|
|
134
|
+
reclaimStaleSocket: options.reclaimStaleSocket,
|
|
135
|
+
resolveAccess: (presented) => {
|
|
136
|
+
const principal = presented === undefined ? undefined : byCredential.get(presented);
|
|
137
|
+
if (principal === undefined) {
|
|
138
|
+
throw new Error('unresolved session credential');
|
|
139
|
+
}
|
|
140
|
+
return principal.access;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
for (const entry of credentialsToPublish) {
|
|
145
|
+
writeCredential(entry.path, entry.credential);
|
|
146
|
+
written.add(entry.path);
|
|
147
|
+
}
|
|
148
|
+
const requeued = mailbox?.recoverOrphanedClaims() ?? 0;
|
|
149
|
+
if (requeued > 0) {
|
|
150
|
+
console.info(`[Runtime] requeued orphaned mailbox claims count=${requeued}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
try {
|
|
155
|
+
await mounted.close();
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
removeCredentials();
|
|
159
|
+
}
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
const servedPrincipals = new Set(options.principals.map((principal) => principal.access.principalId));
|
|
163
|
+
const delivery = options.delivery;
|
|
164
|
+
const leaseMs = delivery?.leaseMs ?? 10 * 60_000;
|
|
165
|
+
// The replay's job is to return claims that outlived their lease, and the
|
|
166
|
+
// youngest thing it can find is `leaseMs` old - so running it on every drain
|
|
167
|
+
// (once per accept, plus every tick) spends a full scan and three prunes to
|
|
168
|
+
// find nothing. Half a lease is the most often it can report anything new.
|
|
169
|
+
// The floor paces WHEN the scan runs, never whether a loss is recorded: a
|
|
170
|
+
// row that spent its attempts is dead in the store and visible in `depth()`
|
|
171
|
+
// the moment it happens. What the pacing can delay is this drain REPORTING
|
|
172
|
+
// it, on a runtime configured with a lease under a minute.
|
|
173
|
+
const replayEveryMs = Math.max(leaseMs / 2, 30_000);
|
|
174
|
+
let lastReplayAt = 0;
|
|
175
|
+
let draining = false;
|
|
176
|
+
let currentScan;
|
|
177
|
+
let stopped = false;
|
|
178
|
+
const servePrincipal = (principal) => {
|
|
179
|
+
if (stopped)
|
|
180
|
+
throw new Error('Cannot serve a principal after runtime stop');
|
|
181
|
+
const principalId = principal.access.principalId;
|
|
182
|
+
if (!principalId.trim())
|
|
183
|
+
throw new Error('Runtime principal ID is required');
|
|
184
|
+
if (byPrincipalId.has(principalId))
|
|
185
|
+
throw new Error('Runtime principal is already served');
|
|
186
|
+
if (credentialPaths.has(principal.credentialPath)) {
|
|
187
|
+
throw new Error('Runtime credential path is already served');
|
|
188
|
+
}
|
|
189
|
+
const credential = (0, node_crypto_1.randomUUID)();
|
|
190
|
+
writeCredential(principal.credentialPath, credential);
|
|
191
|
+
written.add(principal.credentialPath);
|
|
192
|
+
credentialPaths.add(principal.credentialPath);
|
|
193
|
+
byCredential.set(credential, principal);
|
|
194
|
+
byPrincipalId.set(principalId, { principal, credential });
|
|
195
|
+
servedPrincipals.add(principalId);
|
|
196
|
+
};
|
|
197
|
+
const unservePrincipal = (principalId) => {
|
|
198
|
+
if (stopped)
|
|
199
|
+
throw new Error('Cannot unserve a principal after runtime stop');
|
|
200
|
+
const served = byPrincipalId.get(principalId);
|
|
201
|
+
if (!served)
|
|
202
|
+
return;
|
|
203
|
+
byCredential.delete(served.credential);
|
|
204
|
+
byPrincipalId.delete(principalId);
|
|
205
|
+
servedPrincipals.delete(principalId);
|
|
206
|
+
(0, node_fs_1.rmSync)(served.principal.credentialPath, { force: true });
|
|
207
|
+
credentialPaths.delete(served.principal.credentialPath);
|
|
208
|
+
written.delete(served.principal.credentialPath);
|
|
209
|
+
};
|
|
210
|
+
let reconciliationCursor = 0;
|
|
211
|
+
const activeDeliveries = new Map();
|
|
212
|
+
const reportedUncertain = new Set();
|
|
213
|
+
let drainScheduled = false;
|
|
214
|
+
const scheduleDrain = () => {
|
|
215
|
+
if (stopped || drainScheduled)
|
|
216
|
+
return;
|
|
217
|
+
drainScheduled = true;
|
|
218
|
+
setImmediate(() => {
|
|
219
|
+
drainScheduled = false;
|
|
220
|
+
void drainOnce().catch(() => { });
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
const activeNativeInputs = new Map();
|
|
224
|
+
const runNative = (nativeInputId, content, request) => {
|
|
225
|
+
const run = activeNativeInputs.get(nativeInputId);
|
|
226
|
+
if (stopped || !run)
|
|
227
|
+
return Promise.reject(new Error('Native execution requires a currently active input'));
|
|
228
|
+
return run(content, request);
|
|
229
|
+
};
|
|
230
|
+
const markUncertain = async (row, reason) => {
|
|
231
|
+
// The first recorded cause stays; a later reconcile pass only re-reports it.
|
|
232
|
+
if (mailbox.nativeInputs.get(row.id)?.state !== 'uncertain') {
|
|
233
|
+
mailbox.nativeInputs.uncertain(row.id, reason);
|
|
234
|
+
}
|
|
235
|
+
if (reportedUncertain.has(row.id))
|
|
236
|
+
return;
|
|
237
|
+
reportedUncertain.add(row.id);
|
|
238
|
+
try {
|
|
239
|
+
await delivery?.onUncertain?.(row, reason);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// The unresolved dispatch remains durable even if reporting fails.
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
// A loss is the runtime's to report, and reporting it may not break the
|
|
246
|
+
// drain: the next row is still owed delivery.
|
|
247
|
+
const announceDead = async (row, reason) => {
|
|
248
|
+
if (!delivery?.onDead)
|
|
249
|
+
return;
|
|
250
|
+
try {
|
|
251
|
+
await delivery.onDead(row, reason);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// The report failed; the death is already durable in the store.
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const deliverClaim = async (row) => {
|
|
258
|
+
const result = { delivered: 0, failed: 0, dead: 0 };
|
|
259
|
+
try {
|
|
260
|
+
if (!servedPrincipals.has(row.principalId))
|
|
261
|
+
throw new StimulusQuarantine('Input principal is no longer served');
|
|
262
|
+
const nativeDelivery = mailbox.nativeInputs.prepare(row.id);
|
|
263
|
+
if (!nativeDelivery.invocationId)
|
|
264
|
+
throw new StimulusQuarantine('Native input identity is unresolved');
|
|
265
|
+
const inputId = nativeDelivery.invocationId;
|
|
266
|
+
let invoked = false;
|
|
267
|
+
let nativeRun = null;
|
|
268
|
+
let nativeSteer = null;
|
|
269
|
+
const context = {
|
|
270
|
+
nativeInputId: inputId,
|
|
271
|
+
resultForReceipt: (receipt) => mailbox.nativeInputs.resultForReceipt(receipt, row.principalId),
|
|
272
|
+
onInputDispatch: (input) => mailbox.nativeInputs.dispatch(row.id, input),
|
|
273
|
+
onAccepted: (receipt) => {
|
|
274
|
+
const active = activeDeliveries.get(row.id);
|
|
275
|
+
if (!active)
|
|
276
|
+
throw new Error('Native acknowledgement requires an active input');
|
|
277
|
+
mailbox.nativeInputs.accept(row.id, receipt);
|
|
278
|
+
active.accepted = true;
|
|
279
|
+
scheduleDrain();
|
|
280
|
+
},
|
|
281
|
+
wasDispatched: () => {
|
|
282
|
+
const record = mailbox.nativeInputs.get(row.id);
|
|
283
|
+
if (!record)
|
|
284
|
+
throw new Error('Native input delivery record is missing');
|
|
285
|
+
return record.state !== 'prepared';
|
|
286
|
+
},
|
|
287
|
+
run: (content, request) => {
|
|
288
|
+
if (stopped || activeNativeInputs.get(inputId) !== context.run) {
|
|
289
|
+
return Promise.reject(new Error('Native execution requires a currently active input'));
|
|
290
|
+
}
|
|
291
|
+
if (invoked)
|
|
292
|
+
return Promise.reject(new Error('Native session already invoked for this input'));
|
|
293
|
+
if (!options.nativeSession?.runTurn)
|
|
294
|
+
return Promise.reject(new StimulusQuarantine('Native session execution is not configured'));
|
|
295
|
+
invoked = true;
|
|
296
|
+
const observer = request?.streamCallbacks;
|
|
297
|
+
const invoke = options.nativeSession.runTurn.bind(options.nativeSession);
|
|
298
|
+
nativeRun = Promise.resolve()
|
|
299
|
+
.then(() => invoke(content, {
|
|
300
|
+
...request,
|
|
301
|
+
nativeInputId: inputId,
|
|
302
|
+
streamCallbacks: {
|
|
303
|
+
...observer,
|
|
304
|
+
onInputDispatch: (input) => {
|
|
305
|
+
context.onInputDispatch(input);
|
|
306
|
+
observer?.onInputDispatch?.(input);
|
|
307
|
+
},
|
|
308
|
+
onAccepted: (receipt) => {
|
|
309
|
+
context.onAccepted(receipt);
|
|
310
|
+
observer?.onAccepted?.(receipt);
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
}))
|
|
314
|
+
.then((nativeResult) => {
|
|
315
|
+
if (!mailbox.nativeInputs.get(row.id)?.receipt) {
|
|
316
|
+
mailbox.nativeInputs.uncertain(row.id, 'Native result has no accepted receipt');
|
|
317
|
+
throw new Error('Native result has no accepted receipt');
|
|
318
|
+
}
|
|
319
|
+
mailbox.nativeInputs.storeResult(row.id, nativeResult);
|
|
320
|
+
return nativeResult;
|
|
321
|
+
});
|
|
322
|
+
// The drain observes this rejection even if its adapter forgot to await.
|
|
323
|
+
void nativeRun.catch(() => { });
|
|
324
|
+
return nativeRun;
|
|
325
|
+
},
|
|
326
|
+
steer: (content, target, sessionKey) => {
|
|
327
|
+
if (stopped || activeNativeInputs.get(inputId) !== context.run) {
|
|
328
|
+
return Promise.reject(new Error('Native steering requires a currently active input'));
|
|
329
|
+
}
|
|
330
|
+
if (invoked)
|
|
331
|
+
return Promise.reject(new Error('Native session already invoked for this input'));
|
|
332
|
+
if (!options.nativeSession?.steer)
|
|
333
|
+
return Promise.reject(new StimulusQuarantine('Native steering is not configured'));
|
|
334
|
+
if (target.backend !== 'codex' ||
|
|
335
|
+
!target.sessionId.trim() ||
|
|
336
|
+
!target.turnId.trim() ||
|
|
337
|
+
!sessionKey.trim()) {
|
|
338
|
+
return Promise.reject(new Error('Native steering requires an exact Codex turn'));
|
|
339
|
+
}
|
|
340
|
+
if (mailbox.nativeInputs.listByReceipt(target, row.principalId, { limit: 1 }).inputIds
|
|
341
|
+
.length === 0) {
|
|
342
|
+
return Promise.reject(new Error('Native steering target is not accepted for this principal'));
|
|
343
|
+
}
|
|
344
|
+
invoked = true;
|
|
345
|
+
const invoke = options.nativeSession.steer.bind(options.nativeSession);
|
|
346
|
+
nativeSteer = Promise.resolve()
|
|
347
|
+
.then(async () => {
|
|
348
|
+
if (stopped)
|
|
349
|
+
throw new Error('Native runtime stopped before steering dispatch');
|
|
350
|
+
const receipt = await invoke(content, target, sessionKey, () => context.onInputDispatch({
|
|
351
|
+
backend: 'codex',
|
|
352
|
+
sessionId: target.sessionId,
|
|
353
|
+
inputId,
|
|
354
|
+
}));
|
|
355
|
+
if (receipt.backend !== 'codex' ||
|
|
356
|
+
receipt.sessionId !== target.sessionId ||
|
|
357
|
+
receipt.turnId !== target.turnId) {
|
|
358
|
+
throw new Error('Native steer receipt does not match the target turn');
|
|
359
|
+
}
|
|
360
|
+
context.onAccepted(receipt);
|
|
361
|
+
return receipt;
|
|
362
|
+
})
|
|
363
|
+
.catch((error) => {
|
|
364
|
+
if (error instanceof types_js_1.NativeSteeringTargetUnavailableError &&
|
|
365
|
+
!context.wasDispatched()) {
|
|
366
|
+
// This attempt never reached the native transport. The same claimed
|
|
367
|
+
// input may open a fresh turn without spending a retry or duplicating IO.
|
|
368
|
+
invoked = false;
|
|
369
|
+
nativeSteer = null;
|
|
370
|
+
}
|
|
371
|
+
throw error;
|
|
372
|
+
});
|
|
373
|
+
void nativeSteer.catch(() => { });
|
|
374
|
+
return nativeSteer;
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
activeNativeInputs.set(inputId, context.run);
|
|
378
|
+
try {
|
|
379
|
+
await delivery.deliver({ ...row, nativeDelivery }, context);
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
try {
|
|
383
|
+
if (nativeRun)
|
|
384
|
+
await nativeRun;
|
|
385
|
+
if (nativeSteer)
|
|
386
|
+
await nativeSteer;
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
activeNativeInputs.delete(inputId);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
mailbox.nativeInputs.settle(row.id);
|
|
393
|
+
result.delivered += 1;
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
397
|
+
const native = mailbox.nativeInputs.get(row.id);
|
|
398
|
+
if (native && native.state !== 'prepared') {
|
|
399
|
+
await markUncertain(row, reason);
|
|
400
|
+
result.failed += 1;
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
if (error instanceof StimulusQuarantine) {
|
|
404
|
+
// The host says this one must not be tried again. No attempt is
|
|
405
|
+
// spent and no backoff applies: it parks where it can be seen.
|
|
406
|
+
mailbox.quarantine(row.id, reason);
|
|
407
|
+
result.failed += 1;
|
|
408
|
+
result.dead += 1;
|
|
409
|
+
await announceDead(row, reason);
|
|
410
|
+
return result;
|
|
411
|
+
}
|
|
412
|
+
// The host did not take it. The row is owed again under the backoff,
|
|
413
|
+
// and a row that has spent its attempts parks dead and visible -
|
|
414
|
+
// a permanent loss is never silent.
|
|
415
|
+
const outcome = mailbox.retry(row.id, reason);
|
|
416
|
+
result.failed += 1;
|
|
417
|
+
if (outcome === 'dead') {
|
|
418
|
+
result.dead += 1;
|
|
419
|
+
await announceDead(row, reason);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return result;
|
|
423
|
+
};
|
|
424
|
+
const drainOnce = async () => {
|
|
425
|
+
const result = { delivered: 0, failed: 0, dead: 0 };
|
|
426
|
+
if (!mailbox || !delivery || draining || stopped || delivery.ready?.() === false)
|
|
427
|
+
return result;
|
|
428
|
+
const started = [];
|
|
429
|
+
draining = true;
|
|
430
|
+
let finishScan;
|
|
431
|
+
currentScan = new Promise((resolve) => {
|
|
432
|
+
finishScan = resolve;
|
|
433
|
+
});
|
|
434
|
+
try {
|
|
435
|
+
const now = Date.now();
|
|
436
|
+
if (now - lastReplayAt >= replayEveryMs) {
|
|
437
|
+
lastReplayAt = now;
|
|
438
|
+
// These claims are still owned by live adapters, including pre-dispatch waits.
|
|
439
|
+
for (const [id, active] of activeDeliveries) {
|
|
440
|
+
if (!active.accepted)
|
|
441
|
+
mailbox.renewClaim(id, now);
|
|
442
|
+
}
|
|
443
|
+
const replay = mailbox.replayStaleDetailed(leaseMs, now);
|
|
444
|
+
result.dead += replay.newlyDead.length;
|
|
445
|
+
for (const dead of replay.newlyDead)
|
|
446
|
+
await announceDead(dead, 'lease expired repeatedly');
|
|
447
|
+
}
|
|
448
|
+
const unresolved = mailbox.unsettledNative(maxPerTick, reconciliationCursor);
|
|
449
|
+
reconciliationCursor =
|
|
450
|
+
unresolved.length === maxPerTick ? unresolved[unresolved.length - 1].id : 0;
|
|
451
|
+
for (const row of unresolved) {
|
|
452
|
+
if (activeDeliveries.has(row.id) || !servedPrincipals.has(row.principalId))
|
|
453
|
+
continue;
|
|
454
|
+
try {
|
|
455
|
+
if ((await delivery.reconcile?.(row)) === 'settled')
|
|
456
|
+
mailbox.nativeInputs.settle(row.id, true);
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
await markUncertain(row, error instanceof Error ? error.message : String(error));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
for (let count = 0; count < maxPerTick && !stopped; count++) {
|
|
463
|
+
const waiting = [...activeDeliveries.values()].filter((active) => !active.accepted).length;
|
|
464
|
+
if (waiting >= maxPendingInputs)
|
|
465
|
+
break;
|
|
466
|
+
const row = mailbox.claimNext(delivery.prefer ? { prefer: delivery.prefer } : {});
|
|
467
|
+
if (!row)
|
|
468
|
+
break;
|
|
469
|
+
const completion = Promise.resolve()
|
|
470
|
+
.then(() => deliverClaim(row))
|
|
471
|
+
.finally(() => {
|
|
472
|
+
activeDeliveries.delete(row.id);
|
|
473
|
+
scheduleDrain();
|
|
474
|
+
});
|
|
475
|
+
activeDeliveries.set(row.id, { accepted: false, result: completion });
|
|
476
|
+
started.push(completion);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
finally {
|
|
480
|
+
// Scanning/claiming is serialized; an accepted native turn's result is not the next input's gate.
|
|
481
|
+
draining = false;
|
|
482
|
+
currentScan = undefined;
|
|
483
|
+
finishScan();
|
|
484
|
+
}
|
|
485
|
+
for (const outcome of await Promise.all(started)) {
|
|
486
|
+
result.delivered += outcome.delivered;
|
|
487
|
+
result.failed += outcome.failed;
|
|
488
|
+
result.dead += outcome.dead;
|
|
489
|
+
}
|
|
490
|
+
return result;
|
|
491
|
+
};
|
|
492
|
+
// Accepting is what usually has something to deliver, so the drain follows
|
|
493
|
+
// acceptance rather than waiting for the next tick. The interval is the
|
|
494
|
+
// floor underneath it: a retry's backoff expires on nobody's accept.
|
|
495
|
+
const intervalMs = delivery?.intervalMs ?? 5_000;
|
|
496
|
+
const timer = mailbox && delivery && intervalMs > 0
|
|
497
|
+
? setInterval(() => {
|
|
498
|
+
void drainOnce().catch(() => { });
|
|
499
|
+
}, intervalMs)
|
|
500
|
+
: undefined;
|
|
501
|
+
timer?.unref?.();
|
|
502
|
+
return {
|
|
503
|
+
runNative,
|
|
504
|
+
socketPath: mounted.socketPath,
|
|
505
|
+
...(options.runner ? { runner: options.runner } : {}),
|
|
506
|
+
...(mailbox ? { mailbox } : {}),
|
|
507
|
+
accept: (stimulus) => {
|
|
508
|
+
if (!mailbox) {
|
|
509
|
+
throw new Error('runtime.accept requires a mailbox store stated at start');
|
|
510
|
+
}
|
|
511
|
+
if (!(0, mailbox_js_1.isStimulusKind)(stimulus.kind)) {
|
|
512
|
+
throw new Error(`runtime.accept: unknown stimulus kind "${String(stimulus.kind)}"`);
|
|
513
|
+
}
|
|
514
|
+
if (!servedPrincipals.has(stimulus.principalId)) {
|
|
515
|
+
throw new Error(`runtime.accept: principal "${stimulus.principalId}" is not served here`);
|
|
516
|
+
}
|
|
517
|
+
if (stimulus.channelKey.trim() === '') {
|
|
518
|
+
throw new Error('runtime.accept requires the stimulus channel identity');
|
|
519
|
+
}
|
|
520
|
+
const rowId = mailbox.enqueue(stimulus);
|
|
521
|
+
if (rowId !== null && delivery) {
|
|
522
|
+
// After the row is durable, never before: a producer's receipt must
|
|
523
|
+
// mean "kept", and a drain that ran first could ack what was never
|
|
524
|
+
// written.
|
|
525
|
+
scheduleDrain();
|
|
526
|
+
}
|
|
527
|
+
return rowId === null
|
|
528
|
+
? { inputId: null, state: 'duplicate' }
|
|
529
|
+
: { inputId: String(rowId), state: 'accepted' };
|
|
530
|
+
},
|
|
531
|
+
drainOnce,
|
|
532
|
+
...(options.nativeSession
|
|
533
|
+
? {
|
|
534
|
+
nativeSession: {
|
|
535
|
+
stop: () => options.nativeSession.stop(),
|
|
536
|
+
resetSession: async (sessionKey) => {
|
|
537
|
+
if (!options.nativeSession.resetSession)
|
|
538
|
+
throw new Error('Native session reset is not configured');
|
|
539
|
+
await options.nativeSession.resetSession(sessionKey);
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
}
|
|
543
|
+
: {}),
|
|
544
|
+
principalFor: (credential) => credential === undefined ? undefined : byCredential.get(credential),
|
|
545
|
+
servesPrincipal: (principalId) => servedPrincipals.has(principalId),
|
|
546
|
+
servePrincipal,
|
|
547
|
+
unservePrincipal,
|
|
548
|
+
stop: async () => {
|
|
549
|
+
stopped = true;
|
|
550
|
+
if (timer)
|
|
551
|
+
clearInterval(timer);
|
|
552
|
+
// The harness goes before the socket: a turn still running holds action
|
|
553
|
+
// calls, and closing the door under it would fail it on the way out.
|
|
554
|
+
await options.nativeSession?.stop();
|
|
555
|
+
await currentScan;
|
|
556
|
+
// Result/outbox writers still own their claims until their adapters finish.
|
|
557
|
+
await Promise.allSettled([...activeDeliveries.values()].map((active) => active.result));
|
|
558
|
+
await mounted.close();
|
|
559
|
+
removeCredentials();
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
//# sourceMappingURL=runtime.js.map
|