@sema-agent/core 7.29.0 → 7.30.1
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/CHANGELOG.md +393 -0
- package/dist/agents/child-model-seat.d.ts +59 -7
- package/dist/agents/child-model-seat.js +33 -7
- package/dist/agents/cross-session-judge.d.ts +27 -5
- package/dist/agents/cross-session-judge.js +23 -12
- package/dist/agents/list-agents-tool.d.ts +31 -6
- package/dist/agents/list-agents-tool.js +66 -14
- package/dist/agents/peer-admission.d.ts +20 -8
- package/dist/agents/peer-admission.js +21 -12
- package/dist/agents/peer-directory.d.ts +29 -0
- package/dist/agents/peer-directory.js +20 -0
- package/dist/agents/peer-held-queue.d.ts +31 -4
- package/dist/agents/peer-held-queue.js +32 -1
- package/dist/agents/peer-session-drain.d.ts +91 -13
- package/dist/agents/peer-session-drain.js +409 -102
- package/dist/agents/read-inbox-tool.d.ts +22 -0
- package/dist/agents/read-inbox-tool.js +39 -0
- package/dist/agents/roster-store.d.ts +2 -4
- package/dist/agents/roster-store.js +1 -1
- package/dist/agents/send-message-tool.d.ts +4 -4
- package/dist/agents/send-message-tool.js +22 -2
- package/dist/agents/subagent.js +62 -44
- package/dist/agents/team-role.d.ts +34 -0
- package/dist/agents/team-role.js +17 -0
- package/dist/agents/verify.js +2 -2
- package/dist/core/auto-compaction.d.ts +42 -0
- package/dist/core/auto-compaction.js +35 -2
- package/dist/core/child-clamp-seats.d.ts +11 -0
- package/dist/core/child-clamp-seats.js +3 -0
- package/dist/core/context-edit.d.ts +3 -2
- package/dist/core/context-edit.js +6 -5
- package/dist/core/context-guard.d.ts +2 -1
- package/dist/core/context-guard.js +3 -2
- package/dist/core/context-settings.d.ts +103 -0
- package/dist/core/context-settings.js +60 -0
- package/dist/core/engine-notice.d.ts +11 -1
- package/dist/core/frame-settlement.d.ts +97 -0
- package/dist/core/frame-settlement.js +36 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +10 -10
- package/dist/core/mailbox-store.d.ts +41 -7
- package/dist/core/mailbox-store.js +9 -3
- package/dist/core/parent-spec-seat.d.ts +14 -1
- package/dist/core/parent-spec-seat.js +6 -0
- package/dist/core/permission-rule-model.js +1 -1
- package/dist/core/roles.d.ts +20 -0
- package/dist/core/roles.js +35 -16
- package/dist/core/runner/assemble-result.d.ts +2 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/compaction-call-options.d.ts +26 -2
- package/dist/core/runner/compaction-call-options.js +9 -0
- package/dist/core/runner/compaction-seams.d.ts +4 -3
- package/dist/core/runner/compaction-seams.js +13 -6
- package/dist/core/runner/contracts.d.ts +56 -7
- package/dist/core/runner/decide-continuation.d.ts +2 -1
- package/dist/core/runner/decide-continuation.js +11 -10
- package/dist/core/runner/initial-run-state.js +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +9 -1
- package/dist/core/runner/prepare-config-doors.js +4 -1
- package/dist/core/runner/prepare-context-lane.d.ts +9 -2
- package/dist/core/runner/prepare-context-lane.js +15 -10
- package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
- package/dist/core/runner/prepare-delegation-surface.js +9 -3
- package/dist/core/runner/prepare-file-history.js +10 -8
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
- package/dist/core/runner/prepare-gate-stations.js +3 -1
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +6 -0
- package/dist/core/runner/prepare-prompt-inputs.js +2 -1
- package/dist/core/runner/prepare-task.js +22 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -1
- package/dist/core/runner/prepare-wiring-manifest.js +6 -1
- package/dist/core/runner/retired-runner-deps.d.ts +3 -1
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-compaction-machinery.d.ts +4 -2
- package/dist/core/runner/run-compaction-machinery.js +5 -3
- package/dist/core/runner/run-harness-handlers.js +1 -1
- package/dist/core/runner/run-identity-wiring.js +19 -1
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/run-notification-lane.js +8 -2
- package/dist/core/runner/run-recovery-lanes.js +9 -5
- package/dist/core/runner/run-settle-and-teardown.d.ts +4 -22
- package/dist/core/runner/run-settle-and-teardown.js +6 -10
- package/dist/core/runner/run-stop-and-final-verify.js +1 -0
- package/dist/core/runner/run-terminal-adoption.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +32 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +32 -1
- package/dist/core/runner-deps.d.ts +44 -13
- package/dist/core/secret-env.js +2 -1
- package/dist/core/swappable-deps.d.ts +20 -4
- package/dist/core/swappable-deps.js +6 -1
- package/dist/core/task-event.d.ts +5 -0
- package/dist/core/task-notification.d.ts +144 -2
- package/dist/core/task-notification.js +154 -5
- package/dist/core/task-result.d.ts +14 -0
- package/dist/core/task-spec.d.ts +7 -0
- package/dist/core/tool-catalog-entries.js +2 -1
- package/dist/core/tool-conformance.js +1 -0
- package/dist/core/tool-policy.d.ts +9 -4
- package/dist/core/tool-spec.d.ts +14 -0
- package/dist/core/wiring-manifest.d.ts +12 -0
- package/dist/core/wiring-manifest.js +5 -1
- package/dist/engine/harness/agent-harness.d.ts +14 -1
- package/dist/engine/harness/agent-harness.js +28 -3
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +8 -7
- package/dist/engine/llm/types.js +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +3 -0
- package/dist/engine/lsp/node-lsp-manager.js +5 -3
- package/dist/engine/session/import-validate.js +4 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +7 -5
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/workflow.d.ts +1 -1
- package/dist/orchestration/workflow.js +8 -5
- package/dist/shell/lex.d.ts +38 -54
- package/dist/shell/lex.js +239 -286
- package/dist/stores/cc/mailbox-store.js +3 -2
- package/dist/stores/file/corrupt-read-sink.d.ts +28 -2
- package/dist/stores/file/file-history-store.d.ts +2 -4
- package/dist/stores/file/file-history-store.js +1 -1
- package/dist/stores/file/fs-atomic.d.ts +16 -11
- package/dist/stores/file/fs-atomic.js +17 -6
- package/dist/stores/file/index.d.ts +5 -6
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/mailbox-store.d.ts +4 -9
- package/dist/stores/file/mailbox-store.js +29 -30
- package/dist/stores/file/session-policy-store.d.ts +2 -0
- package/dist/stores/file/session-policy-store.js +1 -1
- package/dist/stores/file/session-store.d.ts +11 -7
- package/dist/stores/file/session-store.js +23 -16
- package/dist/stores/file/shared-ledger.js +3 -14
- package/dist/stores/file/tool-result-store.d.ts +2 -0
- package/dist/stores/file/tool-result-store.js +5 -2
- package/dist/stores/file/workflow-journal-store.js +14 -9
- package/dist/tools/fs/bash-program-position.d.ts +15 -24
- package/dist/tools/fs/bash-program-position.js +11 -29
- package/dist/tools/fs/bash-readonly-classifier.d.ts +131 -117
- package/dist/tools/fs/bash-readonly-classifier.js +404 -272
- package/dist/tools/fs/shell-read-check.js +3 -3
- package/dist/tools/fs/shell-read-establish.d.ts +1 -1
- package/dist/tools/fs/shell-read-establish.js +5 -33
- package/dist/tools/fs/shell-read-identity.d.ts +13 -0
- package/dist/tools/fs/shell-read-identity.js +39 -0
- package/dist/tools/fs/shell-read-judge.d.ts +8 -0
- package/dist/tools/fs/shell-read-judge.js +7 -0
- package/dist/tools/fs/shell-read-plan.d.ts +15 -6
- package/dist/tools/fs/shell-read-plan.js +171 -195
- package/dist/tools/fs/shell-read-word.d.ts +64 -0
- package/dist/tools/fs/shell-read-word.js +94 -0
- package/dist/tools/fs/shell-reentry-text.d.ts +10 -0
- package/dist/tools/fs/shell-reentry-text.js +73 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +58 -8
- package/wire-changes/core@7.30.0.json +1821 -0
- package/wire-changes/core@7.30.1.json +154 -0
|
@@ -3,7 +3,7 @@ import type { CrossSessionHoldCause, CrossSessionInboundVerdict } from "./cross-
|
|
|
3
3
|
import { type PeerDeliveryReceiptState } from "./peer-notices.js";
|
|
4
4
|
/** The `cause` vocabulary of a `peer.inbound_disposition{disposition:"held"}` notice: the parity
|
|
5
5
|
* judge's causes plus the drain-stage gate's TRANSIENT `rate_limited` (a bucket-wait, not a review
|
|
6
|
-
* hold — it never enters the held queue; the drain stops at that seq and re-judges next
|
|
6
|
+
* hold — it never enters the held queue; the drain stops at that seq and re-judges next pass). */
|
|
7
7
|
export type PeerInboundHoldCause = CrossSessionHoldCause | "rate_limited";
|
|
8
8
|
export interface PeerHeldEntry {
|
|
9
9
|
/** The recipient box seq of the held record (its identity for the review face). */
|
|
@@ -26,7 +26,7 @@ export interface PeerHeldEntry {
|
|
|
26
26
|
released?: boolean;
|
|
27
27
|
}
|
|
28
28
|
export type PeerHeldSettlement = "expired" | "delivered" | "refused" | "denied" | "dropped";
|
|
29
|
-
export type PeerHeldSettleReason = "evicted" | "deadline" | "shutdown" | "rejudge" | "approved" | "denied" | "cancelled" | "inject_failed";
|
|
29
|
+
export type PeerHeldSettleReason = "evicted" | "deadline" | "shutdown" | "rejudge" | "approved" | "denied" | "cancelled" | "inject_failed" | "superseded";
|
|
30
30
|
/** The clock/timer seam (tests inject a virtual one). Handles are opaque. */
|
|
31
31
|
export interface PeerClock {
|
|
32
32
|
now(): number;
|
|
@@ -41,8 +41,10 @@ export interface PeerHeldQueueSink {
|
|
|
41
41
|
/** Re-judge an entry against the recipient's CURRENT settings and mode class (CC `M`). */
|
|
42
42
|
judge: (entry: PeerHeldEntry) => CrossSessionInboundVerdict;
|
|
43
43
|
/** Deliver a released entry into the live run's model context. Absent ⇒ no run is live. Throws ⇒
|
|
44
|
-
* the lane refused the frame (settled `dropped`, receipt sent).
|
|
45
|
-
|
|
44
|
+
* the lane refused the frame (settled `dropped`, receipt sent). Returns `"deferred"` ⇒ the run is
|
|
45
|
+
* live but this pass's delivery budget is spent: the entry stays released and goes at the next
|
|
46
|
+
* pass's flush (the same wait a run-less approval takes). */
|
|
47
|
+
inject?: (entry: PeerHeldEntry) => void | "deferred";
|
|
46
48
|
/** The receipt to the sender (the binding routes it into the sender's box). */
|
|
47
49
|
receipt: (entry: PeerHeldEntry, state: PeerDeliveryReceiptState) => void;
|
|
48
50
|
/** Announce an entry entering the queue (or its cause changing on re-judgment). */
|
|
@@ -76,6 +78,15 @@ export interface PeerHeldQueue {
|
|
|
76
78
|
refused: number;
|
|
77
79
|
held: number;
|
|
78
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* #1036 — the box is this queue's durable seat, so an entry whose record is no longer IN the box was
|
|
83
|
+
* settled by whoever held the lease meanwhile (a takeover acked it, delivered or refused). Called by a
|
|
84
|
+
* drain right after it claims the lease, with the seqs the claimed snapshot holds (a claim marks EVERY
|
|
85
|
+
* unacked record, so absence is absence): such entries leave the queue as `expired` / `superseded`,
|
|
86
|
+
* with no receipt (the owner that settled them sent one) and no decision recorded (there is no record
|
|
87
|
+
* left to decide about). Returns how many were retired.
|
|
88
|
+
*/
|
|
89
|
+
reconcile(present: ReadonlySet<number>): number;
|
|
79
90
|
/** The review face (CC `l0e`): approve re-judges then delivers (or parks `released` with no live
|
|
80
91
|
* run), deny/cancel drop with the matching receipt. `"gone"` when nothing is held under that seq. */
|
|
81
92
|
settle(seq: number, decision: "approve" | "deny" | "cancel"): PeerHeldSettleOutcome;
|
|
@@ -83,7 +94,23 @@ export interface PeerHeldQueue {
|
|
|
83
94
|
size(): number;
|
|
84
95
|
/** CC `HBe` — a graceful shutdown settles everything still held as `expired` with receipts. */
|
|
85
96
|
shutdown(): void;
|
|
97
|
+
/**
|
|
98
|
+
* #1036 — the TERMINAL DECISIONS this queue has taken about records that are still parked in
|
|
99
|
+
* the box, seq → the settlement word. Since a held record now stays in its durable box (the box is
|
|
100
|
+
* this queue's durable seat), a decision taken BETWEEN runs — a human's deny, a deadline, an
|
|
101
|
+
* eviction — has to survive the run whose ledger recorded it, or the session's next run re-judges
|
|
102
|
+
* the record from scratch and can deliver a message a person already refused. A decision is kept
|
|
103
|
+
* only until a drain that holds the lease acks past it ({@link PeerHeldQueue.forgetDecisions}); it
|
|
104
|
+
* is process-level, like the queue itself, and a crash loses it exactly as it loses the queue.
|
|
105
|
+
*/
|
|
106
|
+
decisions(): ReadonlyMap<number, PeerHeldSettlement>;
|
|
107
|
+
/** #1036 — a lease-owning drain acked these seqs out of the box: their decisions are spent. */
|
|
108
|
+
forgetDecisions(seqs: Iterable<number>): void;
|
|
86
109
|
}
|
|
110
|
+
/** #1036 — how many terminal decisions about still-parked records one box's queue remembers
|
|
111
|
+
* (see {@link PeerHeldQueue.decisions}). Several times the queue's own cap: the queue bounds what is
|
|
112
|
+
* LIVE, this bounds what has been decided and not yet observed as consumed. */
|
|
113
|
+
export declare const PEER_HELD_DECISION_MEMORY: number;
|
|
87
114
|
export declare function createPeerHeldQueue(key: string, opts?: {
|
|
88
115
|
cap?: number;
|
|
89
116
|
clock?: PeerClock;
|
|
@@ -10,11 +10,13 @@ export const realPeerClock = {
|
|
|
10
10
|
clearTimeout: (h) => clearTimeout(h),
|
|
11
11
|
};
|
|
12
12
|
const crashHandlers = new WeakMap();
|
|
13
|
+
export const PEER_HELD_DECISION_MEMORY = PEER_HELD_QUEUE_CAP * 4;
|
|
13
14
|
export function createPeerHeldQueue(key, opts = {}) {
|
|
14
15
|
const cap = opts.cap ?? PEER_HELD_QUEUE_CAP;
|
|
15
16
|
const clock = opts.clock ?? realPeerClock;
|
|
16
17
|
const entries = [];
|
|
17
18
|
const deadlines = new Map();
|
|
19
|
+
const decisions = new Map();
|
|
18
20
|
let cfg;
|
|
19
21
|
const sinkOrThrow = () => {
|
|
20
22
|
if (cfg === undefined)
|
|
@@ -44,6 +46,15 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
44
46
|
const settleOut = (entry, settlement, reason, receipt) => {
|
|
45
47
|
const sink = sinkOrThrow();
|
|
46
48
|
remove(entry);
|
|
49
|
+
if (settlement !== "delivered") {
|
|
50
|
+
decisions.set(entry.seq, settlement);
|
|
51
|
+
while (decisions.size > PEER_HELD_DECISION_MEMORY) {
|
|
52
|
+
const oldest = decisions.keys().next();
|
|
53
|
+
if (oldest.done === true)
|
|
54
|
+
break;
|
|
55
|
+
decisions.delete(oldest.value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
47
58
|
if (receipt !== undefined)
|
|
48
59
|
safe(() => sink.receipt(entry, receipt));
|
|
49
60
|
safe(() => sink.onSettled?.(entry, settlement, reason, entries.length));
|
|
@@ -71,7 +82,8 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
71
82
|
if (sink.inject === undefined)
|
|
72
83
|
return "no_leg";
|
|
73
84
|
try {
|
|
74
|
-
sink.inject(entry)
|
|
85
|
+
if (sink.inject(entry) === "deferred")
|
|
86
|
+
return "no_leg";
|
|
75
87
|
}
|
|
76
88
|
catch {
|
|
77
89
|
settleOut(entry, "dropped", "inject_failed", "dropped");
|
|
@@ -80,6 +92,18 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
80
92
|
settleOut(entry, "delivered", reason, "delivered");
|
|
81
93
|
return "delivered";
|
|
82
94
|
};
|
|
95
|
+
const reconcile = (present) => {
|
|
96
|
+
const sink = sinkOrThrow();
|
|
97
|
+
let retired = 0;
|
|
98
|
+
for (const entry of [...entries]) {
|
|
99
|
+
if (present.has(entry.seq))
|
|
100
|
+
continue;
|
|
101
|
+
remove(entry);
|
|
102
|
+
retired += 1;
|
|
103
|
+
safe(() => sink.onSettled?.(entry, "expired", "superseded", entries.length));
|
|
104
|
+
}
|
|
105
|
+
return retired;
|
|
106
|
+
};
|
|
83
107
|
const rejudge = () => {
|
|
84
108
|
const sink = sinkOrThrow();
|
|
85
109
|
let released = 0;
|
|
@@ -151,6 +175,7 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
151
175
|
safe(() => sink.receipt(entry, "held"));
|
|
152
176
|
},
|
|
153
177
|
rejudge,
|
|
178
|
+
reconcile,
|
|
154
179
|
settle(seq, decision) {
|
|
155
180
|
const sink = sinkOrThrow();
|
|
156
181
|
const entry = entries.find((e) => e.seq === seq);
|
|
@@ -186,6 +211,11 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
186
211
|
},
|
|
187
212
|
list: () => entries.map((e) => ({ ...e, ...(e.hopChain !== undefined ? { hopChain: [...e.hopChain] } : {}), ...(e.peerMeta !== undefined ? { peerMeta: { ...e.peerMeta } } : {}) })),
|
|
188
213
|
size: () => entries.length,
|
|
214
|
+
decisions: () => decisions,
|
|
215
|
+
forgetDecisions(seqs) {
|
|
216
|
+
for (const seq of seqs)
|
|
217
|
+
decisions.delete(seq);
|
|
218
|
+
},
|
|
189
219
|
shutdown() {
|
|
190
220
|
if (cfg === undefined) {
|
|
191
221
|
entries.length = 0;
|
|
@@ -199,6 +229,7 @@ export function createPeerHeldQueue(key, opts = {}) {
|
|
|
199
229
|
},
|
|
200
230
|
};
|
|
201
231
|
crashHandlers.set(queue, () => {
|
|
232
|
+
decisions.clear();
|
|
202
233
|
entries.length = 0;
|
|
203
234
|
for (const h of deadlines.values())
|
|
204
235
|
clock.clearTimeout(h);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type MailboxCrossProcessVerdict, type MailboxStore } from "../core/mailbox-store.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type SessionBoxBinding, type SystemInjectionPriority, type TaskNotificationPayload } from "../core/task-notification.js";
|
|
3
|
+
import { type FrameSettlement } from "../core/frame-settlement.js";
|
|
3
4
|
import { type EngineNotice, type RunnerDeps } from "../core/types.js";
|
|
4
5
|
import { type PermissionModeClass } from "./cross-session-envelope.js";
|
|
5
6
|
import { type CrossSessionInboundSettingLayers } from "./cross-session-judge.js";
|
|
6
|
-
import { type
|
|
7
|
+
import { type PeerLaneConfig } from "./peer-admission.js";
|
|
7
8
|
import { type PeerDirectory } from "./peer-directory.js";
|
|
8
9
|
import { createListAgentsTool } from "./list-agents-tool.js";
|
|
9
10
|
import type { AskEffective } from "../core/wiring-manifest.js";
|
|
@@ -46,13 +47,34 @@ export declare function judgePeerLaneMount(deps: {
|
|
|
46
47
|
* revival lane), and a drain per child on a box nobody writes would cost a lease claim per turn on
|
|
47
48
|
* every background agent. `undefined` on every refusal — silently: no host asked for a box here.
|
|
48
49
|
*/
|
|
49
|
-
export declare function judgeSessionBoxDrain(deps: Pick<RunnerDeps, "mailboxStore" | "onError">, leg: {
|
|
50
|
+
export declare function judgeSessionBoxDrain(deps: Pick<RunnerDeps, "mailboxStore" | "onError" | "sessionBoxEngineFrames">, leg: {
|
|
50
51
|
sessionId: string;
|
|
51
52
|
scope: string;
|
|
52
53
|
isDelegatedChild: boolean;
|
|
53
54
|
}): SessionBoxBinding | undefined;
|
|
54
|
-
/**
|
|
55
|
-
export
|
|
55
|
+
/** What one pull of the box handed back: the records read (already rendered frames), and what is still waiting. */
|
|
56
|
+
export interface PulledInbox {
|
|
57
|
+
delivered: TaskNotificationPayload[];
|
|
58
|
+
/** Records still leased in the box after this pull (the next pull, or the next boundaries, read them). */
|
|
59
|
+
pending: number;
|
|
60
|
+
/** How many of the waiting records their sender marked urgent. */
|
|
61
|
+
urgent: number;
|
|
62
|
+
}
|
|
63
|
+
/** The disposition arms of a `peer.inbound_disposition` notice (closed set; `detail.disposition`).
|
|
64
|
+
* #1036 adds `resupply_exhausted`: a record whose frame a compaction summarized away, was
|
|
65
|
+
* re-supplied once, and was summarized away AGAIN. It settles (leaves the box) — but as a DISCLOSED
|
|
66
|
+
* loss, never as "the model read it". The cap is a bound on delivery attempts; it does not
|
|
67
|
+
* manufacture a consumption proof. */
|
|
68
|
+
export type PeerInboundDisposition = "refused" | "held" | "admission_refused" | "notice_unrouted" | "notice_user_only" | "resupply_exhausted";
|
|
69
|
+
/**
|
|
70
|
+
* #1036 — how many times a record whose frame was summarized away is handed over again before the
|
|
71
|
+
* drain gives up and discloses. `1` = one repair, i.e. at most two occurrences of one record in one
|
|
72
|
+
* run. A cap exists because repair that cannot converge is a loop; it is this small because the
|
|
73
|
+
* second occurrence already says everything the first did. RUN-SCOPED (the ledger is in memory): a
|
|
74
|
+
* crash or a takeover re-serves the record at `attempt: 1`, the at-least-once posture this lane has
|
|
75
|
+
* always had, not a new hole.
|
|
76
|
+
*/
|
|
77
|
+
export declare const PEER_RESUPPLY_CAP = 1;
|
|
56
78
|
export interface PeerSessionDrainOptions {
|
|
57
79
|
mailbox: MailboxStore;
|
|
58
80
|
/** This session (the box owner) and this leg's cycle id (the lease owner). */
|
|
@@ -64,8 +86,8 @@ export interface PeerSessionDrainOptions {
|
|
|
64
86
|
inject: (notification: TaskNotificationPayload, opts?: {
|
|
65
87
|
priority?: SystemInjectionPriority;
|
|
66
88
|
}) => void;
|
|
67
|
-
/**
|
|
68
|
-
|
|
89
|
+
/** The peer lane's knob table (admission gate + delivery budgets + listing window; the same seat SendMessage reads). */
|
|
90
|
+
lane?: Partial<PeerLaneConfig>;
|
|
69
91
|
/** The recipient's self-token set for the hop check, read live (the run's peer identity ref). */
|
|
70
92
|
ownTokens: () => readonly string[];
|
|
71
93
|
/** The recipient's `crossSessionInbound` layers, read at every drain. */
|
|
@@ -74,7 +96,7 @@ export interface PeerSessionDrainOptions {
|
|
|
74
96
|
selfModeClass: () => PermissionModeClass | "unknown";
|
|
75
97
|
/** This session's display name (its directory row's name), for receipts and idle notices it authors. */
|
|
76
98
|
selfName?: () => string | undefined;
|
|
77
|
-
/** Awaited ONCE before the first
|
|
99
|
+
/** Awaited ONCE before the first pass (a directory lookup that fills `selfName`, say); a rejection is swallowed. */
|
|
78
100
|
warmup?: () => Promise<unknown>;
|
|
79
101
|
/** The deployment's `dialogExpiry` (raw; resolved at bind — an out-of-vocabulary value is announced
|
|
80
102
|
* once through `onError` and the default applies). Absent ⇒ the default (5 m). */
|
|
@@ -89,14 +111,55 @@ export interface PeerSessionDrainOptions {
|
|
|
89
111
|
sessionId: string;
|
|
90
112
|
classification: string;
|
|
91
113
|
}) => void;
|
|
114
|
+
/** #1036 — the LEG's settlement seam: the receipts the harness records when a frame reaches
|
|
115
|
+
* the transcript and the effects the compaction commit beat records. Absent ⇒ the drain mints a
|
|
116
|
+
* private one, which is honest but inert: nothing fills it, so every delivered record waits for a
|
|
117
|
+
* receipt that never comes and stays parked (at-least-once). Every production binding supplies it. */
|
|
118
|
+
settlement?: FrameSettlement;
|
|
92
119
|
leaseTtlMs?: number;
|
|
93
120
|
/** Tests: a virtual clock for the held deadlines and idle timers (honored when this process first sees the session). */
|
|
94
121
|
clock?: PeerClock;
|
|
95
122
|
}
|
|
96
123
|
export interface PeerSessionDrain {
|
|
97
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* One drain pass (serialized: a boundary never overlaps a still-running pass).
|
|
126
|
+
*
|
|
127
|
+
* @contract cross_session.settled_means_committed_and_not_summarized — a session-box record is acked
|
|
128
|
+
* only after a frame carrying it has been committed to the transcript and no compaction applied up to
|
|
129
|
+
* that settlement beat summarized that entry away. A summarized frame is re-supplied once (a new
|
|
130
|
+
* occurrence of the SAME record: same `task_id`, same `seq`, `attempt: 2`, `resupplied: true`), and a
|
|
131
|
+
* re-supplied occurrence summarized again settles as `resupply_exhausted`, disclosed — never as seen.
|
|
132
|
+
* OBSERVABLE: a `compacted` frame counting a not-yet-acked delivered record
|
|
133
|
+
* (`summarizedCrossSessionMessages >= 1`) is followed, in the same boundary's delivery half, by a
|
|
134
|
+
* re-supply frame for it or by a `resupply_exhausted` disposition — never by silence; a record
|
|
135
|
+
* committed and never summarized is never shown twice; no compaction implies no re-supply.
|
|
136
|
+
*/
|
|
98
137
|
drain(): Promise<void>;
|
|
99
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* The PULL face (the `ReadInbox` tool): one pass over the same box, same cursor, same judgments —
|
|
140
|
+
* but the records are RETURNED instead of injected, up to `limit` (default = the pass budget). A
|
|
141
|
+
* record a boundary already handed over is never returned again, and a record returned here is
|
|
142
|
+
* never injected later: one cursor (`settledUpTo`), two readers. Never acks (the next boundary does).
|
|
143
|
+
*/
|
|
144
|
+
pull(limit?: number, toolCallId?: string, charBudget?: number): Promise<PulledInbox>;
|
|
145
|
+
/**
|
|
146
|
+
* #1036 — THE TURN BOUNDARY's pass, called by the boundary lane as its tail so it runs AFTER that
|
|
147
|
+
* boundary's compaction (a hook of its own would have been registration-order luck). The settlement
|
|
148
|
+
* half always runs; the delivery half only when `continues` — a boundary this leg will not follow
|
|
149
|
+
* with another request cannot produce a transcript receipt for anything handed over there.
|
|
150
|
+
*/
|
|
151
|
+
boundaryRound(o: {
|
|
152
|
+
continues: boolean;
|
|
153
|
+
}): Promise<void>;
|
|
154
|
+
/** #1036 — "is this engine frame's record still parked in this box?" The terminal sweep's
|
|
155
|
+
* per-session pend must skip those: the box re-serves them itself, and a process-local copy beside
|
|
156
|
+
* a parked record is two sources of truth for one message. */
|
|
157
|
+
isBoxBacked(payload: unknown): boolean;
|
|
158
|
+
/** #1036 — "does this leg hold a record a landed compaction could still break?" The compaction
|
|
159
|
+
* commit beat asks before recording its effect, so a run that will never read one records none. */
|
|
160
|
+
wantsCompactionEffects(): boolean;
|
|
161
|
+
/** Run end: settle what reached the transcript, then release the lease so the session's next cycle
|
|
162
|
+
* claims at once. What did NOT reach it stays parked (at-least-once, next run re-serves it). */
|
|
100
163
|
finish(): Promise<void>;
|
|
101
164
|
/** The run began working (cancels a pending idle fire). */
|
|
102
165
|
busy(): void;
|
|
@@ -112,6 +175,15 @@ export declare function createPeerSessionDrain(opts: PeerSessionDrainOptions): P
|
|
|
112
175
|
* before it — both read this one cell. */
|
|
113
176
|
export interface PeerLaneRefs {
|
|
114
177
|
askEffective?: AskEffective;
|
|
178
|
+
/** The bound drain's pull face (filled by {@link bindPeerLaneDrain}); the `ReadInbox` tool reads it late. */
|
|
179
|
+
pull?: (limit?: number, toolCallId?: string, charBudget?: number) => Promise<PulledInbox>;
|
|
180
|
+
/** #1036 — the bound drain's BOUNDARY pass (filled by {@link bindPeerLaneDrain}); the leg's
|
|
181
|
+
* prepared seat copies it out so the turn-boundary lane can tail-call it. */
|
|
182
|
+
boundaryRound?: (o: {
|
|
183
|
+
continues: boolean;
|
|
184
|
+
}) => Promise<void>;
|
|
185
|
+
/** #1036 — the bound drain's box-backed predicate (filled by {@link bindPeerLaneDrain}). */
|
|
186
|
+
isBoxBacked?: (payload: unknown) => boolean;
|
|
115
187
|
}
|
|
116
188
|
/**
|
|
117
189
|
* The mount verdict + its loud half: `true` = the lane mounts on this leg; `false` = no seat, or the
|
|
@@ -153,7 +225,10 @@ export declare function listAgentsMountable(face: {
|
|
|
153
225
|
/** design/385 §5.1 — the ListAgents mount (alias ListPeers): read-only, concurrency-safe, on the peer
|
|
154
226
|
* seat's own arm beside SendMessage; a caller-supplied tool of the same name wins (skip, not throw). */
|
|
155
227
|
export declare function mountListAgents(args: {
|
|
156
|
-
|
|
228
|
+
/** The directory when the peer lane is up; absent on a delegation-only mount (agent lanes list, no peer rows). */
|
|
229
|
+
peerDirectory?: PeerDirectory;
|
|
230
|
+
/** The resolved listing freshness window (the peer lane's knob table). */
|
|
231
|
+
freshWindowMs?: number;
|
|
157
232
|
sessionId: string;
|
|
158
233
|
scope: string;
|
|
159
234
|
hostTaskId: string;
|
|
@@ -190,18 +265,21 @@ export interface PeerDrainHarness {
|
|
|
190
265
|
export declare function bindPeerSessionDrain(harness: PeerDrainHarness, opts: PeerSessionDrainOptions): PeerSessionDrain;
|
|
191
266
|
/**
|
|
192
267
|
* The leg-side spelling of {@link bindPeerSessionDrain}: the deployment seats the drain reads
|
|
193
|
-
* (`mailboxStore` / `
|
|
268
|
+
* (`mailboxStore` / `peerLane` / `crossSessionInbound` / `crossSessionDialogExpiry` / the two
|
|
194
269
|
* sinks) come straight off deps, the recipient's mode class folds off the lane refs — one binding call
|
|
195
270
|
* per leg, no per-seat plumbing.
|
|
196
271
|
*/
|
|
197
272
|
export declare function bindPeerLaneDrain(harness: PeerDrainHarness, args: {
|
|
198
|
-
deps: Pick<RunnerDeps, "mailboxStore" | "peerDirectory" | "
|
|
273
|
+
deps: Pick<RunnerDeps, "mailboxStore" | "peerDirectory" | "peerLane" | "crossSessionInbound" | "crossSessionDialogExpiry" | "onNotice" | "onError">;
|
|
199
274
|
sessionId: string;
|
|
200
275
|
runId: string;
|
|
201
276
|
scope: string;
|
|
202
277
|
inject: PeerSessionDrainOptions["inject"];
|
|
203
278
|
ownTokens: () => readonly string[];
|
|
204
279
|
refs: PeerLaneRefs;
|
|
280
|
+
/** #1036 — the leg's settlement seam (receipts + compaction effects), minted by the
|
|
281
|
+
* orchestrator and shared with the harness sink and the compaction commit beat. */
|
|
282
|
+
settlement?: FrameSettlement;
|
|
205
283
|
selfName?: () => string | undefined;
|
|
206
284
|
parked?: () => boolean;
|
|
207
285
|
lastTurnText?: () => string | undefined;
|