@plurnk/plurnk-service 3.0.0 → 3.2.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/.env.defaults +38 -21
- package/INSTALL.md +38 -1
- package/README.md +3 -0
- package/SPEC.md +681 -268
- package/dist/build-info.json +1 -1
- package/dist/completions/_plurnk-service +13 -0
- package/dist/completions/plurnk-service.bash +19 -0
- package/dist/completions/plurnk-service.fish +94 -0
- package/dist/content/path-mimetype.d.ts.map +1 -1
- package/dist/content/path-mimetype.js +2 -8
- package/dist/content/path-mimetype.js.map +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts +2 -2
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +36 -32
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts +7 -2
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +36 -5
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/{schemes/_derivation-use.d.ts → core/ContentUse.d.ts} +3 -3
- package/dist/core/ContentUse.d.ts.map +1 -0
- package/dist/{schemes/_derivation-use.js → core/ContentUse.js} +8 -7
- package/dist/core/ContentUse.js.map +1 -0
- package/dist/core/DataStatementRunner.d.ts.map +1 -1
- package/dist/core/DataStatementRunner.js +8 -20
- package/dist/core/DataStatementRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +11 -3
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +186 -109
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/EditMutations.js +1 -1
- package/dist/core/EditMutations.js.map +1 -1
- package/dist/core/Engine.d.ts +1 -1
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +4 -7
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/EnvFlags.js +1 -1
- package/dist/core/EnvFlags.js.map +1 -1
- package/dist/core/FabricatedLog.d.ts +1 -0
- package/dist/core/FabricatedLog.d.ts.map +1 -1
- package/dist/core/FabricatedLog.js +8 -0
- package/dist/core/FabricatedLog.js.map +1 -1
- package/dist/core/FileAddress.d.ts +12 -0
- package/dist/core/FileAddress.d.ts.map +1 -0
- package/dist/core/FileAddress.js +73 -0
- package/dist/core/FileAddress.js.map +1 -0
- package/dist/core/FilePublication.d.ts +15 -0
- package/dist/core/FilePublication.d.ts.map +1 -0
- package/dist/core/FilePublication.js +143 -0
- package/dist/core/FilePublication.js.map +1 -0
- package/dist/core/FileSnapshot.d.ts +26 -0
- package/dist/core/FileSnapshot.d.ts.map +1 -0
- package/dist/core/FileSnapshot.js +179 -0
- package/dist/core/FileSnapshot.js.map +1 -0
- package/dist/core/KillHandler.d.ts.map +1 -1
- package/dist/core/KillHandler.js +4 -1
- package/dist/core/KillHandler.js.map +1 -1
- package/dist/core/LiveSubscription.d.ts.map +1 -1
- package/dist/core/LiveSubscription.js +14 -2
- package/dist/core/LiveSubscription.js.map +1 -1
- package/dist/core/LiveSubscriptions.d.ts +3 -1
- package/dist/core/LiveSubscriptions.d.ts.map +1 -1
- package/dist/core/LiveSubscriptions.js +16 -1
- package/dist/core/LiveSubscriptions.js.map +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +3 -1
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LoopDriver.d.ts.map +1 -1
- package/dist/core/LoopDriver.js +3 -0
- package/dist/core/LoopDriver.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +1 -0
- package/dist/core/MembershipMaterialization.d.ts.map +1 -1
- package/dist/core/MembershipMaterialization.js +87 -143
- package/dist/core/MembershipMaterialization.js.map +1 -1
- package/dist/core/MessageResources.sql +31 -0
- package/dist/core/MutationEffects.d.ts +2 -1
- package/dist/core/MutationEffects.d.ts.map +1 -1
- package/dist/core/MutationEffects.js +14 -1
- package/dist/core/MutationEffects.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +2 -2
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +123 -51
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +21 -14
- package/dist/core/PathGlob.d.ts +6 -0
- package/dist/core/PathGlob.d.ts.map +1 -0
- package/dist/core/PathGlob.js +24 -0
- package/dist/core/PathGlob.js.map +1 -0
- package/dist/core/ProposalLifecycle.d.ts +2 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +45 -33
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProposalLifecycle.sql +4 -0
- package/dist/core/ReplyMetadata.d.ts +10 -0
- package/dist/core/ReplyMetadata.d.ts.map +1 -0
- package/dist/core/ReplyMetadata.js +20 -0
- package/dist/core/ReplyMetadata.js.map +1 -0
- package/dist/core/ResourceBindings.d.ts.map +1 -1
- package/dist/core/ResourceBindings.js +4 -3
- package/dist/core/ResourceBindings.js.map +1 -1
- package/dist/core/ResourceSelector.d.ts +1 -2
- package/dist/core/ResourceSelector.d.ts.map +1 -1
- package/dist/core/ResourceSelector.js +30 -9
- package/dist/core/ResourceSelector.js.map +1 -1
- package/dist/core/ResourceTransfers.d.ts +1 -1
- package/dist/core/ResourceTransfers.d.ts.map +1 -1
- package/dist/core/ResourceTransfers.js +26 -11
- package/dist/core/ResourceTransfers.js.map +1 -1
- package/dist/core/ServiceHelp.d.ts +6 -0
- package/dist/core/ServiceHelp.d.ts.map +1 -0
- package/dist/core/ServiceHelp.js +35 -0
- package/dist/core/ServiceHelp.js.map +1 -0
- package/dist/core/StoredPacket.d.ts +1 -0
- package/dist/core/StoredPacket.d.ts.map +1 -1
- package/dist/core/StoredPacket.js +4 -1
- package/dist/core/StoredPacket.js.map +1 -1
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +2 -2
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TokenCalibration.d.ts +2 -2
- package/dist/core/TokenCalibration.d.ts.map +1 -1
- package/dist/core/TokenCalibration.js +12 -16
- package/dist/core/TokenCalibration.js.map +1 -1
- package/dist/core/TokenCalibration.sql +29 -6
- package/dist/core/TurnDispositionHandler.d.ts +1 -2
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +30 -26
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/TurnMaterialization.d.ts.map +1 -1
- package/dist/core/TurnMaterialization.js +11 -1
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnOps.d.ts +5 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +29 -2
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +122 -56
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/TurnSources.sql +2 -2
- package/dist/core/caps/SchemeCtxImpl.d.ts +1 -0
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +5 -0
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/git-membership.d.ts +1 -0
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +9 -11
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/glob_match.js +3 -3
- package/dist/core/glob_match.js.map +1 -1
- package/dist/core/mutation-types.d.ts +3 -0
- package/dist/core/mutation-types.d.ts.map +1 -1
- package/dist/core/namespace.d.ts +4 -1
- package/dist/core/namespace.d.ts.map +1 -1
- package/dist/core/namespace.js +14 -3
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/packet-wire.d.ts +7 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +98 -36
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +3 -2
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +23 -11
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/recap-lines.d.ts +4 -0
- package/dist/core/recap-lines.d.ts.map +1 -0
- package/dist/core/recap-lines.js +11 -0
- package/dist/core/recap-lines.js.map +1 -0
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/sql-functions.d.ts +2 -0
- package/dist/core/sql-functions.d.ts.map +1 -0
- package/dist/core/sql-functions.js +5 -0
- package/dist/core/sql-functions.js.map +1 -0
- package/dist/core/sqlite-synchronous.d.ts +2 -0
- package/dist/core/sqlite-synchronous.d.ts.map +1 -0
- package/dist/core/sqlite-synchronous.js +4 -0
- package/dist/core/sqlite-synchronous.js.map +1 -0
- package/dist/core/turn-scheduler.d.ts.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/evidence/EvidenceReader.d.ts.map +1 -1
- package/dist/evidence/EvidenceReader.js +23 -3
- package/dist/evidence/EvidenceReader.js.map +1 -1
- package/dist/evidence/digest.sql +7 -1
- package/dist/man/plurnk-service.1 +499 -0
- package/dist/schemes/Env.d.ts +26 -0
- package/dist/schemes/Env.d.ts.map +1 -0
- package/dist/schemes/Env.js +133 -0
- package/dist/schemes/Env.js.map +1 -0
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +15 -15
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +3 -2
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +9 -0
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +1 -0
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +233 -268
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +97 -17
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +9 -0
- package/dist/schemes/Worker.js +1 -1
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +2 -0
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +8 -5
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +15 -15
- package/dist/schemes/_entry-find.d.ts +2 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +31 -19
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts +2 -2
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +10 -8
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +9 -7
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_path-scope.d.ts.map +1 -1
- package/dist/schemes/_path-scope.js +12 -4
- package/dist/schemes/_path-scope.js.map +1 -1
- package/dist/schemes/_search-index.js +3 -3
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/schemes/exec-abort.js +2 -2
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/schemes/exec-env.d.ts.map +1 -1
- package/dist/schemes/exec-env.js +7 -22
- package/dist/schemes/exec-env.js.map +1 -1
- package/dist/server/Daemon.d.ts +1 -0
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +45 -68
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +2 -3
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +34 -6
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/EnvFunctionality.d.ts +4 -2
- package/dist/server/EnvFunctionality.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.js +23 -12
- package/dist/server/EnvFunctionality.js.map +1 -1
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +15 -8
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.d.ts +1 -1
- package/dist/server/FunctionalityManager.d.ts.map +1 -1
- package/dist/server/FunctionalityManager.js +5 -1
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts.map +1 -1
- package/dist/server/MembersFunctionality.js +12 -12
- package/dist/server/MembersFunctionality.js.map +1 -1
- package/dist/server/Retention.d.ts.map +1 -1
- package/dist/server/Retention.js +5 -5
- package/dist/server/Retention.js.map +1 -1
- package/dist/server/Retention.sql +49 -23
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +12 -3
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/drain.sql +10 -9
- package/dist/server/lifecycle-recovery.sql +4 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +9 -31
- package/dist/service.js.map +1 -1
- package/docs/copy-move.md +3 -0
- package/docs/env.md +32 -4
- package/docs/members.md +16 -1
- package/migrations/018_worker_owners.sql +1 -0
- package/migrations/019_interactive_owners.sql +1 -0
- package/migrations/020_log_projection.sql +1 -0
- package/migrations/021_packet_roles.sql +86 -0
- package/migrations/022_message_completion.sql +24 -0
- package/migrations/023_file_revisions.sql +54 -0
- package/migrations/024_proposal_decisions.sql +34 -0
- package/migrations/025_response_retention.sql +30 -0
- package/package.json +38 -34
- package/dist/schemes/_derivation-use.d.ts.map +0 -1
- package/dist/schemes/_derivation-use.js.map +0 -1
package/dist/core/Dispatcher.js
CHANGED
|
@@ -3,10 +3,11 @@ import { parsePath } from "@plurnk/plurnk-parser";
|
|
|
3
3
|
import { ConfigurationError, Knob } from "@plurnk/plurnk-meta";
|
|
4
4
|
import { TurnDisposition, UNKNOWN_POSITION } from "@plurnk/plurnk-contracts";
|
|
5
5
|
import Turn from "./Turn.js";
|
|
6
|
-
import { foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
|
|
6
|
+
import { entryAddress, foldAuthorityIntoPath, renderAddress, renderAuthoredTarget, renderTarget, schemeNameOf } from "./plurnk-uri.js";
|
|
7
7
|
import WorkerName from "./WorkerName.js";
|
|
8
8
|
import { PathSyntax } from "@plurnk/plurnk-contracts";
|
|
9
9
|
import Namespace from "./namespace.js";
|
|
10
|
+
import FileAddress from "./FileAddress.js";
|
|
10
11
|
import CapabilityResolver from "./CapabilityResolver.js";
|
|
11
12
|
import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
|
|
12
13
|
import Results from "./results.js";
|
|
@@ -22,13 +23,14 @@ import BodyPreview from "../content/body-preview.js";
|
|
|
22
23
|
import ReadResolve from "../content/read-resolve.js";
|
|
23
24
|
import LogVisibility from "./LogVisibility.js";
|
|
24
25
|
import PacketWire from "./packet-wire.js";
|
|
25
|
-
import { resultPrefix, resultSize, unfitResult } from "./ContextFit.js";
|
|
26
|
+
import { isContextReceipt, reserved, resultPrefix, resultSize, unfitResult } from "./ContextFit.js";
|
|
26
27
|
import { TextCoordinates } from "@plurnk/plurnk-mimetypes";
|
|
27
28
|
import EntryAddressBinding from "./EntryAddressBinding.js";
|
|
28
29
|
import WorkerControlHandler from "./WorkerControlHandler.js";
|
|
29
30
|
import WorkerControlAddress from "./WorkerControlAddress.js";
|
|
30
31
|
import KillHandler from "./KillHandler.js";
|
|
31
32
|
import TurnDispositionHandler from "./TurnDispositionHandler.js";
|
|
33
|
+
import ReplyMetadata from "./ReplyMetadata.js";
|
|
32
34
|
import LogWriter from "./LogWriter.js";
|
|
33
35
|
import LogEntryProjection from "./LogEntryProjection.js";
|
|
34
36
|
import DurableStatement from "./DurableStatement.js";
|
|
@@ -90,7 +92,6 @@ class Dispatcher {
|
|
|
90
92
|
this.#resourceSelector = new ResourceSelector({
|
|
91
93
|
schemes,
|
|
92
94
|
admitRead: (statement, ctx) => this.#checkCapabilities(statement, ctx),
|
|
93
|
-
canonicalFilePath: (pathname, workspaceId) => this.#canonicalFilePath(pathname, workspaceId),
|
|
94
95
|
prepareDataRepresentation: (args) => this.#prepareDataRepresentation({
|
|
95
96
|
...args,
|
|
96
97
|
handler: args.handler,
|
|
@@ -246,13 +247,10 @@ class Dispatcher {
|
|
|
246
247
|
if (key !== null)
|
|
247
248
|
target.pathname = key;
|
|
248
249
|
}
|
|
249
|
-
async #canonicalFilePath(pathname, workspaceId) {
|
|
250
|
-
return Namespace.canonicalizeSpelling(pathname, await this.#workspaceRoot(workspaceId));
|
|
251
|
-
}
|
|
252
250
|
async #editTargetIdentity(statement, workspaceId, _workerId) {
|
|
253
251
|
const target = this.#extractTarget(statement.target, workspaceId);
|
|
254
252
|
await this.#canonColumns(target, workspaceId);
|
|
255
|
-
return renderTarget(target);
|
|
253
|
+
return renderTarget(target, await this.#workspaceRoot(workspaceId));
|
|
256
254
|
}
|
|
257
255
|
// {§kill-scope-entry} — a scoped KILL on an entry-bearing scheme is one EDIT with an empty
|
|
258
256
|
// body over the same marker: prepared, gated, and merged as an EDIT while its log row
|
|
@@ -294,9 +292,9 @@ class Dispatcher {
|
|
|
294
292
|
async dispatch(context) {
|
|
295
293
|
await this.#operationEvent(context, "started");
|
|
296
294
|
await this.#noticeFilePaths(context);
|
|
297
|
-
const
|
|
298
|
-
await this.#operationEvent(context, "settled", result);
|
|
299
|
-
return
|
|
295
|
+
const execution = await this.#dispatch(context);
|
|
296
|
+
await this.#operationEvent(context, "settled", execution.result);
|
|
297
|
+
return execution;
|
|
300
298
|
}
|
|
301
299
|
// {§file-path-normalization} Authored operands, once, before internal fan-out.
|
|
302
300
|
async #noticeFilePaths({ statement, origin, workspaceId, workerId, loopId }) {
|
|
@@ -305,21 +303,29 @@ class Dispatcher {
|
|
|
305
303
|
const targets = statement.op === "COPY" || statement.op === "MOVE"
|
|
306
304
|
? [statement.source.target, statement.destination.target]
|
|
307
305
|
: [statement.target];
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
306
|
+
const normalized = targets.flatMap((target) => {
|
|
307
|
+
if (target === null || schemeNameOf(target) !== "file")
|
|
308
|
+
return [];
|
|
309
|
+
const pathname = FileAddress.pathname(target);
|
|
310
|
+
return typeof pathname === "string" && (pathname.startsWith("/") || Namespace.isHomeRelative(pathname))
|
|
311
|
+
? [{ target, pathname }] : [];
|
|
312
|
+
});
|
|
313
|
+
if (normalized.length === 0)
|
|
312
314
|
return;
|
|
313
315
|
const root = await this.#workspaceRoot(workspaceId);
|
|
314
316
|
if (root === null || root === "/")
|
|
315
317
|
return;
|
|
316
|
-
for (const pathname of
|
|
318
|
+
for (const { target, pathname } of normalized) {
|
|
317
319
|
const key = Namespace.canonicalizeSpelling(pathname, root);
|
|
318
320
|
if (key === null)
|
|
319
321
|
continue;
|
|
322
|
+
const address = renderTarget({ scheme: null, pathname: key }, root, target);
|
|
323
|
+
if (address?.startsWith("file://"))
|
|
324
|
+
continue;
|
|
320
325
|
this.#notices.push(workspaceId, workerId, loopId, {
|
|
321
326
|
source: "scheme:file", kind: "path_normalized", level: "warn",
|
|
322
|
-
|
|
327
|
+
directive: "YOU SHOULD address the resolved path.",
|
|
328
|
+
message: `Path resolved to '${address || "."}'.`,
|
|
323
329
|
});
|
|
324
330
|
}
|
|
325
331
|
}
|
|
@@ -334,11 +340,9 @@ class Dispatcher {
|
|
|
334
340
|
...(result === undefined ? {} : { result }),
|
|
335
341
|
});
|
|
336
342
|
}
|
|
337
|
-
async #dispatch(context) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
let result = await ResourceBindings.using(this.#schemes, this.#buildSchemeCtx(context), (ctx) => this.#dispatchOne(context, ctx));
|
|
343
|
+
async #dispatch(context, targetsResolved = false) {
|
|
344
|
+
const execution = await ResourceBindings.using(this.#schemes, this.#buildSchemeCtx(context), (ctx) => this.#dispatchOne(context, ctx, targetsResolved));
|
|
345
|
+
let { result } = execution;
|
|
342
346
|
const edit = context.statement.op === "EDIT" ? context.statement : this.#scopedEntryEdits.get(context.statement);
|
|
343
347
|
if (edit !== undefined) {
|
|
344
348
|
// {§edit-batch-merges} — a proposal's apply result replaces the projected one; the
|
|
@@ -359,7 +363,7 @@ class Dispatcher {
|
|
|
359
363
|
await ResourceBindings.using(this.#schemes, this.#buildSchemeCtx(noteContext), (ctx) => this.#dispatchOne(noteContext, ctx));
|
|
360
364
|
result = Results.assert({ ...result, rowsWritten: (result.rowsWritten ?? 1) + 1 });
|
|
361
365
|
}
|
|
362
|
-
return result;
|
|
366
|
+
return { ...execution, result };
|
|
363
367
|
}
|
|
364
368
|
// {§matcher-refusal} — a matcher the parser could not read refuses its one operation with the parser's
|
|
365
369
|
// diagnostic and working form: the operation's own row, soft on the rail, never a contract strike.
|
|
@@ -378,56 +382,109 @@ class Dispatcher {
|
|
|
378
382
|
return statement.body.trim() === "" ? null : statement.body;
|
|
379
383
|
}
|
|
380
384
|
static #globTarget(target) {
|
|
381
|
-
|
|
385
|
+
if (target === null)
|
|
386
|
+
return false;
|
|
387
|
+
if (target.kind === "local")
|
|
388
|
+
return PathSyntax.hasGlob(target.raw);
|
|
389
|
+
// Authority-only worker addresses name actors, not their resource trees.
|
|
390
|
+
if (target.scheme === "worker" && (target.pathname === "" || target.pathname === "/"))
|
|
391
|
+
return false;
|
|
392
|
+
return PathSyntax.hasGlob(target.pathname) || PathSyntax.hasGlob(target.hostname ?? "");
|
|
382
393
|
}
|
|
383
|
-
// {§
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
async #fanOutRead(context, statement) {
|
|
389
|
-
const survey = {
|
|
390
|
-
op: "FIND", aside: statement.aside, target: statement.target, metadata: statement.metadata,
|
|
391
|
-
matcher: statement.matcher, lineMarker: null, body: null, position: statement.position,
|
|
392
|
-
};
|
|
393
|
-
const found = await ResourceBindings.using(this.#schemes, this.#buildSchemeCtx(context), (ctx) => this.#dataRun.run(schemeNameOf(statement.target), survey, ctx));
|
|
394
|
+
// {§bulk-resource-targeting} Selection and exact execution share the ordinary owners.
|
|
395
|
+
async #findTargets(survey, ctx) {
|
|
396
|
+
const found = await this.#dataRun.run(schemeNameOf(survey.target), survey, ctx);
|
|
397
|
+
if (found.status >= 400)
|
|
398
|
+
return { result: found };
|
|
394
399
|
const paths = found.status < 300
|
|
395
400
|
? (found.results ?? []).flatMap((item) => Array.isArray(item) && !("items" in item[0]) ? [item[0].path] : [])
|
|
396
401
|
: [];
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
: { status: 204, matched: 0, matcher: statement.matcher.raw, detail: `No line under ${statement.target.raw} matched the pattern.` });
|
|
401
|
-
const logEntryId = await this.#logWriter.writeLog({
|
|
402
|
-
statement, result, workspaceId: context.workspaceId, workerId: context.workerId, loopId: context.loopId,
|
|
403
|
-
turnId: context.turnId, sequence: context.sequence, origin: context.origin, curationPlan: null, modelCallId: null,
|
|
404
|
-
});
|
|
405
|
-
context.onDispatch?.(logEntryId);
|
|
406
|
-
await this.#notifySettled(context, logEntryId);
|
|
407
|
-
return result;
|
|
402
|
+
const matched = typeof found.matchingPathCount === "number" ? found.matchingPathCount : paths.length;
|
|
403
|
+
if (survey.lineMarker !== null && matched !== paths.length) {
|
|
404
|
+
throw new InvalidOperationResultError("Unbounded FIND did not return its complete resource selection.");
|
|
408
405
|
}
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const target = parsePath(path);
|
|
406
|
+
const targets = paths.map((path) => {
|
|
407
|
+
const selected = survey.target?.fragment == null ? path : `${path.split("#")[0]}#${survey.target.fragment}`;
|
|
408
|
+
const target = parsePath(selected);
|
|
413
409
|
if (target === null)
|
|
414
|
-
throw new InvalidOperationResultError(`FIND named an unparseable path: ${
|
|
415
|
-
|
|
416
|
-
|
|
410
|
+
throw new InvalidOperationResultError(`FIND named an unparseable path: ${selected}`);
|
|
411
|
+
return target;
|
|
412
|
+
});
|
|
413
|
+
return { targets, matched };
|
|
414
|
+
}
|
|
415
|
+
async #selectTargets(statement, ctx) {
|
|
416
|
+
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
417
|
+
if (![statement.source, statement.destination].some(({ target }) => _a.#globTarget(target)))
|
|
418
|
+
return null;
|
|
419
|
+
let resolved = statement;
|
|
420
|
+
for (const operand of ["source", "destination"]) {
|
|
421
|
+
const selection = statement[operand];
|
|
422
|
+
if (!_a.#globTarget(selection.target))
|
|
423
|
+
continue;
|
|
424
|
+
const found = await this.#findTargets({
|
|
425
|
+
...selection, op: "FIND", matcher: null, lineMarker: { marks: [1, -1] },
|
|
426
|
+
aside: statement.aside, position: statement.position, body: null,
|
|
427
|
+
}, ctx);
|
|
428
|
+
if ("result" in found)
|
|
429
|
+
return found;
|
|
430
|
+
if (found.targets.length !== 1)
|
|
431
|
+
return { result: _a.#failure("target-cardinality", 400, `${statement.op} ${operand} matched ${found.matched} resources; expected one.`, {}, { operand, matchingPathCount: found.matched, recovery: "Name one source and one destination per pair.", retryable: false }) };
|
|
432
|
+
resolved = { ...resolved, [operand]: { ...selection, target: found.targets[0] } };
|
|
433
|
+
}
|
|
434
|
+
return { statements: [resolved] };
|
|
417
435
|
}
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
if (!(statement.op === "READ" || statement.op === "EDIT" || statement.op === "KILL"))
|
|
437
|
+
return null;
|
|
438
|
+
if (!_a.#globTarget(statement.target) || (statement.op === "KILL" && schemeNameOf(statement.target) === "log"))
|
|
439
|
+
return null;
|
|
440
|
+
const found = await this.#findTargets({
|
|
441
|
+
op: "FIND", aside: statement.aside, target: statement.target, metadata: statement.metadata,
|
|
442
|
+
matcher: statement.op === "READ" ? statement.matcher : null,
|
|
443
|
+
lineMarker: statement.op === "READ" ? null : { marks: [1, -1] }, body: null, position: statement.position,
|
|
444
|
+
}, ctx);
|
|
445
|
+
if ("result" in found)
|
|
446
|
+
return found;
|
|
447
|
+
if (found.targets.length === 0)
|
|
448
|
+
return { result: Results.assert(statement.op === "READ" && statement.matcher !== null
|
|
449
|
+
? { status: 204, matched: 0, matcher: statement.matcher.raw, detail: `No line under ${statement.target.raw} matched the pattern.` }
|
|
450
|
+
: { status: 204, detail: `No path matched ${statement.target.raw}.` }) };
|
|
451
|
+
if (statement.op === "READ" && found.matched > found.targets.length) {
|
|
452
|
+
this.#notices.push(ctx.workspaceId, ctx.workerId, ctx.loopId, {
|
|
453
|
+
source: "engine:dispatcher", kind: "read_fanout_bounded", level: "warn",
|
|
454
|
+
directive: "YOU SHOULD narrow the glob, or FIND first.",
|
|
455
|
+
message: `READ ${statement.target.raw} matched ${found.matched} paths; the first ${found.targets.length} were selected.`,
|
|
424
456
|
});
|
|
425
457
|
}
|
|
426
|
-
return
|
|
458
|
+
return { statements: found.targets.map((target) => ({ ...statement, target })), fanout: { target: statement.target.raw, matched: found.matched } };
|
|
459
|
+
}
|
|
460
|
+
async #dispatchSelection(context, selection) {
|
|
461
|
+
const admitted = selection.statements.slice(0, context.remainingCommands);
|
|
462
|
+
const results = [];
|
|
463
|
+
let rowsWritten = 0;
|
|
464
|
+
for (const [index, statement] of admitted.entries()) {
|
|
465
|
+
const { result } = await this.#dispatch({
|
|
466
|
+
...context, statement, sequence: context.sequence + rowsWritten,
|
|
467
|
+
...(selection.fanout === undefined ? {} : { fanout: { ...selection.fanout, index, count: admitted.length } }),
|
|
468
|
+
...(context.fit === undefined ? {} : { fit: reserved(context.fit, admitted.length - index - 1) }),
|
|
469
|
+
}, true);
|
|
470
|
+
results.push(result);
|
|
471
|
+
rowsWritten += result.rowsWritten ?? 1;
|
|
472
|
+
if (context.failOnOperationError && result.status >= 400 && !isContextReceipt(result))
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
if (results.length === 0)
|
|
476
|
+
throw new Error("Dispatch requires an admitted operation.");
|
|
477
|
+
return { result: { ...(results.find(({ status }) => status >= 400) ?? results.at(-1)), rowsWritten },
|
|
478
|
+
commandsExecuted: results.length, commandsOmitted: selection.statements.length - admitted.length };
|
|
427
479
|
}
|
|
428
|
-
|
|
480
|
+
// {§bulk-resource-targeting} Dispatch accounting is core-owned, never a scheme's extension data.
|
|
481
|
+
static #singleExecution(result) {
|
|
482
|
+
return { result, commandsExecuted: 1, commandsOmitted: 0 };
|
|
483
|
+
}
|
|
484
|
+
async #dispatchOne(context, schemeCtx, targetsResolved = false) {
|
|
429
485
|
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
|
|
430
486
|
let result;
|
|
487
|
+
let resolved = null;
|
|
431
488
|
let curationPlan = null;
|
|
432
489
|
const denial = this.#checkWritable(statement, origin, workspaceId)
|
|
433
490
|
?? await this.#checkCapabilities(statement, schemeCtx)
|
|
@@ -442,17 +499,19 @@ class Dispatcher {
|
|
|
442
499
|
// skips it. Logging failures (#writeLog throws) are NOT caught —
|
|
443
500
|
// those are system failures.
|
|
444
501
|
try {
|
|
445
|
-
|
|
502
|
+
const selection = targetsResolved ? null : await this.#selectTargets(statement, schemeCtx);
|
|
503
|
+
if (selection !== null && !("result" in selection)) {
|
|
504
|
+
resolved = selection;
|
|
505
|
+
}
|
|
506
|
+
else if (selection !== null) {
|
|
507
|
+
result = selection.result;
|
|
508
|
+
}
|
|
509
|
+
else if (statement.op === "EDIT") {
|
|
446
510
|
result = await this.#resourceMutations.edit(statement, schemeCtx, context.editSequence);
|
|
447
511
|
}
|
|
448
512
|
else if (statement.op === "SEND" && statement.target === null) {
|
|
449
513
|
result = await this.#respond(statement, schemeCtx, loopId);
|
|
450
514
|
}
|
|
451
|
-
else if (TurnDisposition.isCompletion(statement)) {
|
|
452
|
-
result = await this.#disposition.completion({ workerId, loopId, turnId, origin }, context.finalResponse === true);
|
|
453
|
-
if (result.status === 200)
|
|
454
|
-
result = await this.#respond(statement, schemeCtx, loopId);
|
|
455
|
-
}
|
|
456
515
|
else if (statement.op === "NOTE") {
|
|
457
516
|
await Turn.recordSource(this.#db, turnId, "note", statement.body ?? "", { sequence });
|
|
458
517
|
const coordinate = await this.#db.engine_loop_turn_seqs.get({ loop_id: loopId, turn_id: turnId });
|
|
@@ -518,6 +577,12 @@ class Dispatcher {
|
|
|
518
577
|
}
|
|
519
578
|
}
|
|
520
579
|
}
|
|
580
|
+
// Child dispatch includes persistence and proposal settlement; their failures are
|
|
581
|
+
// internal, not exceptions from a scheme handler.
|
|
582
|
+
if (resolved !== null)
|
|
583
|
+
return this.#dispatchSelection(context, resolved);
|
|
584
|
+
if (result === undefined)
|
|
585
|
+
throw new Error("Operation dispatch produced neither targets nor a result.");
|
|
521
586
|
result = await this.#fitResult(context, statement, result);
|
|
522
587
|
// {§configuration-repair-path}: decide admission before persisting a proposed row.
|
|
523
588
|
let autoAccept = false;
|
|
@@ -540,7 +605,7 @@ class Dispatcher {
|
|
|
540
605
|
}
|
|
541
606
|
}
|
|
542
607
|
}
|
|
543
|
-
// {§
|
|
608
|
+
// {§bulk-resource-targeting} — a row on behalf of a glob names the statement it came from.
|
|
544
609
|
if (context.fanout !== undefined) {
|
|
545
610
|
const attrs = typeof result.attrs === "object" && result.attrs !== null ? result.attrs : {};
|
|
546
611
|
result = { ...result, attrs: { ...attrs, fanout: context.fanout } };
|
|
@@ -571,6 +636,7 @@ class Dispatcher {
|
|
|
571
636
|
// result the caller sees, so runTurn never branches on a pending state.
|
|
572
637
|
if (_a.#isProposal(statement, result)) {
|
|
573
638
|
if (autoAccept) {
|
|
639
|
+
await this.#proposals.recordDecision(logEntryId, { decision: "accept" });
|
|
574
640
|
const initialSettlement = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, loopId, turnId, resources: schemeCtx.resources });
|
|
575
641
|
const effective = await this.#resourceMutations.settleProposal({
|
|
576
642
|
statement,
|
|
@@ -581,7 +647,7 @@ class Dispatcher {
|
|
|
581
647
|
});
|
|
582
648
|
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
583
649
|
await this.#notifySettled(context, logEntryId);
|
|
584
|
-
return post;
|
|
650
|
+
return _a.#singleExecution(post);
|
|
585
651
|
}
|
|
586
652
|
// Register the resolution waiter SYNCHRONOUSLY before any await
|
|
587
653
|
// yields. A same-tick resolveProposal() (e.g. from a test that
|
|
@@ -621,10 +687,10 @@ class Dispatcher {
|
|
|
621
687
|
});
|
|
622
688
|
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
623
689
|
await this.#notifySettled(context, logEntryId);
|
|
624
|
-
return post;
|
|
690
|
+
return _a.#singleExecution(post);
|
|
625
691
|
}
|
|
626
692
|
await this.#notifySettled(context, logEntryId);
|
|
627
|
-
return result;
|
|
693
|
+
return _a.#singleExecution(result);
|
|
628
694
|
}
|
|
629
695
|
// {§context-fit} — a result the model asked for arrives whole when it fits the remaining budget;
|
|
630
696
|
// otherwise the row is its size and address — a READ of lines above the longest prefix of them that
|
|
@@ -726,11 +792,9 @@ class Dispatcher {
|
|
|
726
792
|
return null;
|
|
727
793
|
if (resolved.address === null)
|
|
728
794
|
return null;
|
|
729
|
-
const rendered =
|
|
730
|
-
?
|
|
731
|
-
:
|
|
732
|
-
if (rendered === null)
|
|
733
|
-
throw new TypeError("Resolved entry target did not render.");
|
|
795
|
+
const rendered = resolved.address.scheme === "file"
|
|
796
|
+
? entryAddress("file", "", resolved.address.pathname, await this.#workspaceRoot(workspaceId))
|
|
797
|
+
: renderAuthoredTarget({ ...target, fragment: null });
|
|
734
798
|
return {
|
|
735
799
|
scheme: resolved.address.scheme,
|
|
736
800
|
authority: resolved.address.authority,
|
|
@@ -873,7 +937,7 @@ class Dispatcher {
|
|
|
873
937
|
#checkWritable(statement, origin, workspaceId) {
|
|
874
938
|
if (!isExecution(statement) && !MUTATING_OPS.has(statement.op))
|
|
875
939
|
return null;
|
|
876
|
-
if (TurnDisposition.is(statement) ||
|
|
940
|
+
if (TurnDisposition.is(statement) || statement.op === "SEND" && statement.target === null)
|
|
877
941
|
return null;
|
|
878
942
|
// An execution's operation authority always belongs to the exec scheme;
|
|
879
943
|
// runtime-specific resource authority is gated separately below.
|
|
@@ -891,17 +955,17 @@ class Dispatcher {
|
|
|
891
955
|
// body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
|
|
892
956
|
// {§machine-processes}
|
|
893
957
|
if (this.#isWorkerControl(statement))
|
|
894
|
-
return this.#denyIfDisallowed("worker", origin, workspaceId);
|
|
958
|
+
return this.#denyIfDisallowed("worker", origin, workspaceId, statement.target);
|
|
895
959
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
896
960
|
const dst = statement.destination.target;
|
|
897
961
|
const dstScheme = schemeNameOf(dst);
|
|
898
|
-
const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workspaceId);
|
|
962
|
+
const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workspaceId, dst);
|
|
899
963
|
if (dstDenial !== null)
|
|
900
964
|
return dstDenial;
|
|
901
965
|
if (statement.op === "MOVE") {
|
|
902
966
|
const srcScheme = schemeNameOf(statement.source.target);
|
|
903
967
|
if (srcScheme !== dstScheme) {
|
|
904
|
-
const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workspaceId);
|
|
968
|
+
const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workspaceId, statement.source.target);
|
|
905
969
|
if (srcDenial !== null)
|
|
906
970
|
return srcDenial;
|
|
907
971
|
}
|
|
@@ -909,7 +973,7 @@ class Dispatcher {
|
|
|
909
973
|
return null;
|
|
910
974
|
}
|
|
911
975
|
const target = schemeNameOf(statement.target);
|
|
912
|
-
const denial = this.#denyIfDisallowed(target, origin, workspaceId);
|
|
976
|
+
const denial = this.#denyIfDisallowed(target, origin, workspaceId, statement.target);
|
|
913
977
|
// {§send-target-recipient} — SEND addresses recipients, not otherwise
|
|
914
978
|
// read-only resources. State that boundary without guessing whether the
|
|
915
979
|
// model intended a reply, deletion, or directed message.
|
|
@@ -917,19 +981,21 @@ class Dispatcher {
|
|
|
917
981
|
return _a.#failure("send-target-not-a-recipient", 400, "The addressed scheme is not a SEND recipient.", {}, {
|
|
918
982
|
target: statement.target?.raw ?? String(target),
|
|
919
983
|
stage: "dispatch",
|
|
920
|
-
recovery: "
|
|
984
|
+
recovery: "Name a message address or a recipient that implements SEND.",
|
|
921
985
|
retryable: false,
|
|
922
986
|
});
|
|
923
987
|
}
|
|
924
988
|
return denial;
|
|
925
989
|
}
|
|
926
|
-
|
|
990
|
+
// {§runtime-resource-binding} The manifest that governs the coordinate decides: a runtime
|
|
991
|
+
// scheme's facet may make its claimed resources writable where the stored output is not.
|
|
992
|
+
#denyIfDisallowed(schemeName, origin, workspaceId, target = null) {
|
|
927
993
|
if (schemeName === null)
|
|
928
994
|
return null;
|
|
929
995
|
const handler = this.#schemes.get(schemeName, workspaceId);
|
|
930
996
|
if (handler === undefined)
|
|
931
997
|
return null;
|
|
932
|
-
const manifest = this.#schemes.
|
|
998
|
+
const manifest = this.#schemes.manifestAt(schemeName, target, workspaceId);
|
|
933
999
|
if (manifest === undefined)
|
|
934
1000
|
throw new Error(`registered scheme '${schemeName}' has no manifest`);
|
|
935
1001
|
if (manifest.writableBy.includes(origin))
|
|
@@ -1055,11 +1121,11 @@ class Dispatcher {
|
|
|
1055
1121
|
if (reasoning.length === 0)
|
|
1056
1122
|
return null;
|
|
1057
1123
|
const pathname = `/${loopSeq}/${turnSeq}`;
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
const tail = BodyPreview.selectTail(reasoning, pageLines);
|
|
1062
|
-
const lineMarker = tail.whole ? null : tail.marker;
|
|
1124
|
+
const pageLines = Knob.optionalInteger("PLURNK_SERVICE_REASONING_TRAILING_LINES", -1) ?? Knob.integer("PLURNK_SERVICE_PREVIEW_LINES", 1);
|
|
1125
|
+
if (pageLines === 0)
|
|
1126
|
+
throw new ConfigurationError("PLURNK_SERVICE_REASONING_TRAILING_LINES", "PLURNK_SERVICE_REASONING_TRAILING_LINES must be -1 or a positive integer.");
|
|
1127
|
+
const tail = pageLines === -1 ? null : BodyPreview.selectTail(reasoning, pageLines);
|
|
1128
|
+
const lineMarker = tail === null || tail.whole ? null : tail.marker;
|
|
1063
1129
|
const projected = lineMarker === null ? null : await ReadResolve.resolve({ content: reasoning, mimetype: "text/markdown", lineMarker });
|
|
1064
1130
|
const statement = {
|
|
1065
1131
|
op: "READ", aside: null, metadata: null, lineMarker, matcher: null, body: null, position: UNKNOWN_POSITION,
|
|
@@ -1184,21 +1250,21 @@ class Dispatcher {
|
|
|
1184
1250
|
}
|
|
1185
1251
|
// {§send-response-receipt}: message content is literal; only its header selects a destination.
|
|
1186
1252
|
async #respond(statement, schemeCtx, loopId) {
|
|
1187
|
-
const
|
|
1188
|
-
if ("failure" in
|
|
1189
|
-
return
|
|
1253
|
+
const options = ReplyMetadata.read(statement.metadata);
|
|
1254
|
+
if ("failure" in options)
|
|
1255
|
+
return options.failure;
|
|
1190
1256
|
const target = statement.target;
|
|
1191
1257
|
let answers;
|
|
1258
|
+
let speechOnly = false;
|
|
1192
1259
|
if (target === null) {
|
|
1193
|
-
const
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
if (
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
}
|
|
1260
|
+
const open = await this.#db.engine_open_messages.get({ loop_id: loopId });
|
|
1261
|
+
// {§message-completion} — with none open, the reply is speech to the loop's latest message:
|
|
1262
|
+
// delivered to its originating client, its outcome unchanged.
|
|
1263
|
+
const message = open ?? await this.#db.message_latest_published.get({ loop_id: loopId });
|
|
1264
|
+
if (message === undefined)
|
|
1265
|
+
return _a.#failure("send-target-required", 400, "SEND has no recipient and its loop has no published message.", {}, { recovery: "Name a recipient address.", retryable: false });
|
|
1266
|
+
answers = [message.path];
|
|
1267
|
+
speechOnly = open === undefined;
|
|
1202
1268
|
}
|
|
1203
1269
|
else {
|
|
1204
1270
|
const message = await this.#db.message_source_by_address.get({
|
|
@@ -1208,16 +1274,20 @@ class Dispatcher {
|
|
|
1208
1274
|
return _a.#failure("message-not-found", 404, `No accepted message exists at ${target.raw}.`, {}, { retryable: false });
|
|
1209
1275
|
answers = [message.path];
|
|
1210
1276
|
}
|
|
1277
|
+
const captured = await MessageAttachments.capture(options.metadata, schemeCtx.resources, "message:reply");
|
|
1278
|
+
if ("failure" in captured)
|
|
1279
|
+
return captured.failure;
|
|
1211
1280
|
return { status: 200, answers,
|
|
1281
|
+
...(options.completion === null || speechOnly ? {} : { completion: options.completion }),
|
|
1212
1282
|
...(captured.attachments.length === 0 ? {} : { attachments: MessageAttachments.receipts(captured.attachments) }) };
|
|
1213
1283
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
return
|
|
1284
|
+
async settleProgram(ctx, waits, completionAllowed) {
|
|
1285
|
+
const result = await this.#disposition.settle(ctx, waits, completionAllowed);
|
|
1286
|
+
if (typeof result.detail === "string")
|
|
1287
|
+
this.#notices.push(ctx.workspaceId, ctx.workerId, ctx.loopId, {
|
|
1288
|
+
source: "engine:turn", kind: "results_pending", level: "info", message: result.detail,
|
|
1289
|
+
});
|
|
1290
|
+
return result.status;
|
|
1221
1291
|
}
|
|
1222
1292
|
// {§send-premature-terminate}: judge observation boundaries after the whole program settles.
|
|
1223
1293
|
async #pendingSet(workerId, turnId, loopId) {
|
|
@@ -1301,7 +1371,7 @@ class Dispatcher {
|
|
|
1301
1371
|
static #isProposal(statement, result) {
|
|
1302
1372
|
if (result.status !== 202)
|
|
1303
1373
|
return false;
|
|
1304
|
-
return !TurnDisposition.is(statement)
|
|
1374
|
+
return !TurnDisposition.is(statement);
|
|
1305
1375
|
}
|
|
1306
1376
|
// Normalize a parsed target for log storage. Bare paths and `file:///...`
|
|
1307
1377
|
// inputs collapse to scheme=null in log target metadata because both render
|
|
@@ -1313,7 +1383,14 @@ class Dispatcher {
|
|
|
1313
1383
|
// `local` (bare path) carries no URL parts — store the raw text as the pathname for the log record, scheme=null.
|
|
1314
1384
|
if (path.kind === "local")
|
|
1315
1385
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: PathSyntax.decodeParens(path.raw), query: null, fragment: path.fragment ?? null }; // {§path-parentheses} {§local-path-fragment}
|
|
1316
|
-
|
|
1386
|
+
if (path.scheme === "file") {
|
|
1387
|
+
const pathname = FileAddress.pathname(path);
|
|
1388
|
+
// An invalid URL has no resolved identity; retain the refused input alongside its failure.
|
|
1389
|
+
if (typeof pathname !== "string")
|
|
1390
|
+
return { ...path };
|
|
1391
|
+
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname, query: null, fragment: path.fragment };
|
|
1392
|
+
}
|
|
1393
|
+
const scheme = path.scheme;
|
|
1317
1394
|
// The registered scheme owns authority disposition. Namespace authority
|
|
1318
1395
|
// is path syntax; resource and owner authorities remain explicit in the
|
|
1319
1396
|
// durable operation evidence.
|