@fungi.computer/shiitake 0.5.0 → 0.7.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 +59 -21
- package/dist/agent-control.d.ts +94 -2
- package/dist/agent-control.js +147 -26
- package/dist/client-entry.d.ts +20 -0
- package/dist/client-entry.js +5 -0
- package/dist/client-visitor.d.ts +33 -0
- package/dist/client-visitor.js +231 -0
- package/dist/client-wire.d.ts +9 -0
- package/dist/client-wire.js +24 -3
- package/dist/client.d.ts +27 -6
- package/dist/client.js +23 -0
- package/dist/cloudflare.d.ts +20 -0
- package/dist/cloudflare.js +4 -1
- package/dist/delegation.d.ts +79 -53
- package/dist/delegation.js +26 -21
- package/dist/effect.d.ts +26 -1
- package/dist/effect.js +14 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -0
- package/dist/extensions.d.ts +22 -3
- package/dist/extensions.js +1 -1
- package/dist/fleet.d.ts +25 -2
- package/dist/fleet.js +108 -24
- package/dist/hosted.js +3 -0
- package/dist/http-routes.d.ts +23 -9
- package/dist/http.d.ts +1 -0
- package/dist/http.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/internal/accepted-run-executor.js +2 -1
- package/dist/internal/admission-door.d.ts +6 -1
- package/dist/internal/admission-door.js +33 -3
- package/dist/internal/contracts.d.ts +19 -1
- package/dist/internal/coordinator.js +115 -52
- package/dist/internal/effect-client.js +1 -1
- package/dist/internal/effect-contracts.d.ts +9 -2
- package/dist/internal/extension-command-registry.d.ts +2 -1
- package/dist/internal/extension-command-registry.js +3 -1
- package/dist/internal/extension-runtime.d.ts +3 -1
- package/dist/internal/extension-runtime.js +18 -8
- package/dist/internal/model-acquisition.js +2 -0
- package/dist/internal/observation-kernel.d.ts +2 -0
- package/dist/internal/observation-kernel.js +17 -6
- package/dist/internal/promise-facade.js +2 -0
- package/dist/internal/run-scope.d.ts +14 -0
- package/dist/internal/run-scope.js +58 -15
- package/dist/internal/runtime-coordinator.d.ts +14 -0
- package/dist/internal/runtime-coordinator.js +60 -11
- package/dist/internal/runtime-core.d.ts +3 -0
- package/dist/internal/runtime-core.js +1 -0
- package/dist/internal/runtime-reconciliation.js +123 -25
- package/dist/internal/session-context.d.ts +5 -5
- package/dist/internal/session-context.js +80 -83
- package/dist/internal/session-status.d.ts +2 -2
- package/dist/internal/session-status.js +5 -3
- package/dist/internal/woodstock/agent-reply.d.ts +5 -0
- package/dist/internal/woodstock/agent-reply.js +3 -0
- package/dist/internal/woodstock/client-projection.js +13 -11
- package/dist/internal/woodstock/fleet-store.d.ts +3 -2
- package/dist/internal/woodstock/fleet-store.js +24 -4
- package/dist/internal/woodstock/index.d.ts +7 -3
- package/dist/internal/woodstock/index.js +23 -6
- package/dist/internal/woodstock/progress.d.ts +11 -1
- package/dist/internal/woodstock/progress.js +126 -25
- package/dist/internal/woodstock/session-delegator.d.ts +13 -0
- package/dist/internal/woodstock/session-delegator.js +44 -0
- package/dist/internal/woodstock/session-entry.d.ts +12 -2
- package/dist/internal/woodstock/session-store.d.ts +16 -2
- package/dist/internal/woodstock/session-store.js +349 -66
- package/dist/internal/woodstock/work-senders.d.ts +32 -0
- package/dist/internal/woodstock/work-senders.js +70 -0
- package/dist/node.js +11 -2
- package/dist/remote-client.js +7 -2
- package/dist/server-visitor-stream.d.ts +13 -0
- package/dist/server-visitor-stream.js +124 -0
- package/dist/session-attention.d.ts +30 -0
- package/dist/session-attention.js +14 -0
- package/docs/api/agent-control.md +212 -15
- package/docs/api/client-entry.md +3 -0
- package/docs/api/client.md +209 -5
- package/docs/api/cloudflare.md +23 -1
- package/docs/api/delegation.md +284 -77
- package/docs/api/effect.md +100 -12
- package/docs/api/extensions.md +73 -12
- package/docs/api/fleet.md +138 -25
- package/docs/api/http.md +28 -0
- package/docs/api/index.md +43 -17
- package/docs/api/testing.md +9 -7
- package/package.json +10 -6
|
@@ -14,103 +14,100 @@ function settledContextTokens(store, sessionId) {
|
|
|
14
14
|
return messages.length === 0 ? null : estimateContextTokens(messages).tokens;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* null, never guessed.
|
|
17
|
+
* Read the stored figure synchronously with the Session baseline. Only model
|
|
18
|
+
* description remains in the returned Effect, outside the SQLite transaction
|
|
19
|
+
* and the request's ancestry scope. Undefined without readable context; a model
|
|
20
|
+
* window the host cannot describe is null, never guessed.
|
|
21
21
|
*/
|
|
22
|
-
export function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return yield* describe({ sessionId, preference }).pipe(Effect.map(({ contextWindow }) => {
|
|
46
|
-
if (previousValue.window === contextWindow)
|
|
47
|
-
return previousValue;
|
|
48
|
-
const value = Object.freeze({
|
|
49
|
-
tokens: previousValue.tokens,
|
|
50
|
-
window: contextWindow,
|
|
51
|
-
});
|
|
52
|
-
if (cache && ownerCache.get(sessionId)?.cursor === cursor)
|
|
53
|
-
ownerCache.set(sessionId, {
|
|
54
|
-
cursor,
|
|
55
|
-
preference: preferenceKey,
|
|
56
|
-
value,
|
|
57
|
-
});
|
|
58
|
-
return value;
|
|
59
|
-
}), Effect.catch((error) => Effect.sync(() => {
|
|
60
|
-
reportCatchFailure("session-context.describe", error);
|
|
22
|
+
export function prepareSessionContext(options, sessionId, cursor, cache = true) {
|
|
23
|
+
let preference;
|
|
24
|
+
try {
|
|
25
|
+
preference = readSessionModelPreference(options.store, sessionId);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
reportCatchFailure("session-context.preference", error);
|
|
29
|
+
return Effect.succeed(undefined);
|
|
30
|
+
}
|
|
31
|
+
const preferenceKey = JSON.stringify(preference);
|
|
32
|
+
const ownerCache = contextByOwner.get(options) ?? new Map();
|
|
33
|
+
contextByOwner.set(options, ownerCache);
|
|
34
|
+
const previous = cache ? ownerCache.get(sessionId) : undefined;
|
|
35
|
+
const describe = options.model.describe;
|
|
36
|
+
if (previous?.cursor === cursor && previous.preference === preferenceKey) {
|
|
37
|
+
if (previous.value === undefined || describe === undefined)
|
|
38
|
+
return Effect.succeed(previous.value);
|
|
39
|
+
const previousValue = previous.value;
|
|
40
|
+
// A null Session override follows a mutable Agent default. Check the
|
|
41
|
+
// selected window without decoding history again; a changed window is
|
|
42
|
+
// a distinct effective preference for this cursor.
|
|
43
|
+
return describe({ sessionId, preference }).pipe(Effect.map(({ contextWindow }) => {
|
|
44
|
+
if (previousValue.window === contextWindow)
|
|
61
45
|
return previousValue;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// the whole window. A cold-cache connection also reads the whole kept
|
|
68
|
-
// window.
|
|
69
|
-
const contextRead = yield* Effect.result(Effect.try({
|
|
70
|
-
try: () => settledContextTokens(options.store, sessionId),
|
|
71
|
-
catch: (error) => error,
|
|
72
|
-
}));
|
|
73
|
-
if (contextRead._tag === "Failure") {
|
|
74
|
-
reportCatchFailure("session-context.figure", contextRead.failure);
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
const tokens = contextRead.success;
|
|
78
|
-
if (tokens === null) {
|
|
79
|
-
if (cache)
|
|
46
|
+
const value = Object.freeze({
|
|
47
|
+
tokens: previousValue.tokens,
|
|
48
|
+
window: contextWindow,
|
|
49
|
+
});
|
|
50
|
+
if (cache && ownerCache.get(sessionId)?.cursor === cursor)
|
|
80
51
|
ownerCache.set(sessionId, {
|
|
81
52
|
cursor,
|
|
82
53
|
preference: preferenceKey,
|
|
83
|
-
value
|
|
54
|
+
value,
|
|
84
55
|
});
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
87
|
-
if (describe === undefined) {
|
|
88
|
-
const value = Object.freeze({ tokens, window: null });
|
|
89
|
-
if (cache)
|
|
90
|
-
ownerCache.set(sessionId, { cursor, preference: preferenceKey, value });
|
|
91
56
|
return value;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
57
|
+
}), Effect.catch((error) => Effect.sync(() => {
|
|
58
|
+
reportCatchFailure("session-context.describe", error);
|
|
59
|
+
return previousValue;
|
|
60
|
+
})));
|
|
61
|
+
}
|
|
62
|
+
// At most one cached whole-window read per Session, cursor, and
|
|
63
|
+
// preference: a late describe may publish only while this cursor still
|
|
64
|
+
// owns the cache entry. Reads before the tip bypass the cache and each read
|
|
65
|
+
// the whole window. A cold-cache connection also reads the whole kept
|
|
66
|
+
// window.
|
|
67
|
+
let tokens;
|
|
68
|
+
try {
|
|
69
|
+
tokens = settledContextTokens(options.store, sessionId);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
reportCatchFailure("session-context.figure", error);
|
|
73
|
+
return Effect.succeed(undefined);
|
|
74
|
+
}
|
|
75
|
+
if (tokens === null) {
|
|
96
76
|
if (cache)
|
|
97
77
|
ownerCache.set(sessionId, {
|
|
98
78
|
cursor,
|
|
99
79
|
preference: preferenceKey,
|
|
100
|
-
value:
|
|
80
|
+
value: undefined,
|
|
101
81
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
82
|
+
return Effect.succeed(undefined);
|
|
83
|
+
}
|
|
84
|
+
if (describe === undefined) {
|
|
85
|
+
const value = Object.freeze({ tokens, window: null });
|
|
86
|
+
if (cache)
|
|
87
|
+
ownerCache.set(sessionId, { cursor, preference: preferenceKey, value });
|
|
88
|
+
return Effect.succeed(value);
|
|
89
|
+
}
|
|
90
|
+
// Publish the decoded figure before yielding to the host's model
|
|
91
|
+
// description. Concurrent watchers then reuse these tokens rather than
|
|
92
|
+
// decoding the same Session window while `describe` is in flight.
|
|
93
|
+
if (cache)
|
|
94
|
+
ownerCache.set(sessionId, {
|
|
95
|
+
cursor,
|
|
96
|
+
preference: preferenceKey,
|
|
97
|
+
value: Object.freeze({ tokens, window: null }),
|
|
98
|
+
});
|
|
99
|
+
// A null Session override follows a mutable Agent default. Resolve through
|
|
100
|
+
// the host's turn-model selector on every read, without acquiring a turn.
|
|
101
|
+
return describe({
|
|
102
|
+
sessionId,
|
|
103
|
+
preference,
|
|
104
|
+
}).pipe(Effect.map(({ contextWindow }) => contextWindow), Effect.catch((error) => Effect.sync(() => {
|
|
105
|
+
reportCatchFailure("session-context.describe", error);
|
|
106
|
+
return null;
|
|
107
|
+
})), Effect.map((window) => {
|
|
111
108
|
const value = Object.freeze({ tokens, window });
|
|
112
109
|
if (cache && ownerCache.get(sessionId)?.cursor === cursor)
|
|
113
110
|
ownerCache.set(sessionId, { cursor, preference: preferenceKey, value });
|
|
114
111
|
return value;
|
|
115
|
-
});
|
|
112
|
+
}));
|
|
116
113
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { WatchdogQueueHead } from "@fungi.computer/watchdog/effect";
|
|
2
2
|
import type { AgentRunningWork, SessionActivity, SessionProgress, SessionToolOutput } from "../client.js";
|
|
3
3
|
import type { SqliteOwner } from "./contracts.js";
|
|
4
|
-
/**
|
|
4
|
+
/** Activity facts plus an explicit, on-demand bounded progress read. */
|
|
5
5
|
export type ObservedActivity = Readonly<{
|
|
6
6
|
facts: SessionActivity;
|
|
7
|
-
|
|
7
|
+
readProgress: () => SessionProgress | null;
|
|
8
8
|
toolOutput: readonly SessionToolOutput[];
|
|
9
9
|
}>;
|
|
10
10
|
/** Join Watchdog authority with Woodstock facts and process-local progress. */
|
|
@@ -12,7 +12,7 @@ export function readSessionActivity(owner, sessionId, head, runningWork) {
|
|
|
12
12
|
lastModelChoiceSequence,
|
|
13
13
|
compaction: null,
|
|
14
14
|
},
|
|
15
|
-
|
|
15
|
+
readProgress: () => null,
|
|
16
16
|
toolOutput: [],
|
|
17
17
|
};
|
|
18
18
|
if (head.state === "queued")
|
|
@@ -27,7 +27,7 @@ export function readSessionActivity(owner, sessionId, head, runningWork) {
|
|
|
27
27
|
lastModelChoiceSequence,
|
|
28
28
|
compaction: null,
|
|
29
29
|
},
|
|
30
|
-
|
|
30
|
+
readProgress: () => null,
|
|
31
31
|
toolOutput: [],
|
|
32
32
|
};
|
|
33
33
|
const latestRetry = readLatestLiveRetryForWork(owner, sessionId, head.jobId);
|
|
@@ -49,7 +49,9 @@ export function readSessionActivity(owner, sessionId, head, runningWork) {
|
|
|
49
49
|
lastModelChoiceSequence,
|
|
50
50
|
compaction: readProcessCompaction(owner, sessionId),
|
|
51
51
|
},
|
|
52
|
-
|
|
52
|
+
// Only baselines need a flattened bounded tail. Live subscribers read
|
|
53
|
+
// appends directly and must not materialize it on every token.
|
|
54
|
+
readProgress: () => readProcessProgressForWork(owner, sessionId, head.jobId),
|
|
53
55
|
toolOutput: readProcessToolOutputForWork(owner, sessionId, head.jobId),
|
|
54
56
|
};
|
|
55
57
|
}
|
|
@@ -9,6 +9,11 @@ export declare const VerifiedAgentReplySchema: Schema.Union<readonly [Schema.Str
|
|
|
9
9
|
readonly commandId: Schema.String;
|
|
10
10
|
readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
|
|
11
11
|
readonly diagnostic: Schema.optionalKey<Schema.String>;
|
|
12
|
+
readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
13
|
+
readonly itemId: Schema.String;
|
|
14
|
+
readonly kind: Schema.String;
|
|
15
|
+
readonly summary: Schema.String;
|
|
16
|
+
}>>>;
|
|
12
17
|
readonly agentId: Schema.String;
|
|
13
18
|
readonly sessionId: Schema.String;
|
|
14
19
|
}>, Schema.Struct<{
|
|
@@ -42,6 +42,9 @@ function opening(reply) {
|
|
|
42
42
|
attributes.push(["outcome", reply.outcome]);
|
|
43
43
|
if (reply.outcome !== "undeliverable" && reply.diagnostic !== undefined)
|
|
44
44
|
attributes.push(["diagnostic", reply.diagnostic]);
|
|
45
|
+
if (reply.outcome !== "undeliverable" && reply.waitingOnYou !== undefined)
|
|
46
|
+
// A host fact, outside the child's text: the items the child left waiting on the recipient.
|
|
47
|
+
attributes.push(["waiting_on_you", JSON.stringify(reply.waitingOnYou)]);
|
|
45
48
|
if (reply.outcome === "undeliverable")
|
|
46
49
|
attributes.push(["reason", reply.reason]);
|
|
47
50
|
const rendered = attributes
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { messageAuthor } from "@fungi.computer/agar/agents";
|
|
2
2
|
import { readSessionAttention } from "./session-attention.js";
|
|
3
|
+
import { readWaitingOnSender } from "./work-senders.js";
|
|
4
|
+
import { readSessionDelegator } from "./session-delegator.js";
|
|
3
5
|
import { ShiitakeError } from "../../errors.js";
|
|
4
6
|
import { decodeWoodstockCursor, encodeWoodstockCursor } from "./cursor.js";
|
|
5
7
|
import { operatorClientMessage, promptCommand } from "./session-entry.js";
|
|
6
|
-
import { lineageEntrySequence, lineageHasSequence, lineageRowLimit, readLineageWindow, readLineageWorkFacts, readSessionEntriesForWork, readSessionLineage, readPendingSessionCommands, readSessionCommandAdmission, readNewestPendingSessionCommand, } from "./session-store.js";
|
|
8
|
+
import { lineageEntrySequence, lineageHasSequence, lineageRowLimit, readLineageWindow, readLineageWorkFacts, hasSessionWorkTerminal, readSessionEntriesForWork, readSessionLineage, readPendingSessionCommands, readSessionCommandAdmission, readNewestPendingSessionCommand, withSessionReadTransaction, } from "./session-store.js";
|
|
7
9
|
const maximumPagePayloadBytes = 2_097_152;
|
|
8
10
|
const replayPageLimit = 200;
|
|
9
11
|
const maximumRowsPerPage = lineageRowLimit;
|
|
@@ -342,7 +344,7 @@ function resolveHistoryAnchor(owner, lineage, before) {
|
|
|
342
344
|
* history, including what compaction summarized away.
|
|
343
345
|
*/
|
|
344
346
|
export function readClientSnapshot(owner, sessionId, options, activity, projectExtensions = () => ({})) {
|
|
345
|
-
return owner
|
|
347
|
+
return withSessionReadTransaction(owner, sessionId, () => {
|
|
346
348
|
const pending = readPendingPrompts(owner, sessionId);
|
|
347
349
|
const lineage = readSessionLineage(owner, { sessionId });
|
|
348
350
|
if (lineage === undefined)
|
|
@@ -357,7 +359,7 @@ export function readClientSnapshot(owner, sessionId, options, activity, projectE
|
|
|
357
359
|
compactionNotices: Object.freeze([]),
|
|
358
360
|
pending,
|
|
359
361
|
activity: activity.facts,
|
|
360
|
-
progress: activity.
|
|
362
|
+
progress: activity.readProgress(),
|
|
361
363
|
toolOutput: activity.toolOutput,
|
|
362
364
|
extensions: projectExtensions(),
|
|
363
365
|
attention: readSessionAttention(owner, sessionId),
|
|
@@ -392,7 +394,7 @@ export function readClientSnapshot(owner, sessionId, options, activity, projectE
|
|
|
392
394
|
compactionNotices: compactionNoticesIn(consumedRows),
|
|
393
395
|
pending,
|
|
394
396
|
activity: activity.facts,
|
|
395
|
-
progress: options.before === undefined ? activity.
|
|
397
|
+
progress: options.before === undefined ? activity.readProgress() : null,
|
|
396
398
|
toolOutput: options.before === undefined ? activity.toolOutput : [],
|
|
397
399
|
extensions: projectExtensions(),
|
|
398
400
|
attention: readSessionAttention(owner, sessionId),
|
|
@@ -440,12 +442,6 @@ function replayFrames(owner, lineage, start, delivery, activity, extensions, att
|
|
|
440
442
|
? { ...frame, sandboxFailures: page.sandboxFailures }
|
|
441
443
|
: frame),
|
|
442
444
|
];
|
|
443
|
-
if (delivery === "live" && !page.hasMore && activity.progress !== null)
|
|
444
|
-
frames.push(Object.freeze({
|
|
445
|
-
kind: "progress",
|
|
446
|
-
progress: activity.progress,
|
|
447
|
-
activity: activity.facts,
|
|
448
|
-
}));
|
|
449
445
|
return Object.freeze({
|
|
450
446
|
kind: "page",
|
|
451
447
|
frames: Object.freeze(!page.hasMore
|
|
@@ -466,7 +462,7 @@ export function readClientReplayPage(owner, sessionId, after, delivery, activity
|
|
|
466
462
|
const position = after === undefined ? undefined : decodeWoodstockCursor(after);
|
|
467
463
|
if (after !== undefined && position === undefined)
|
|
468
464
|
return resetReplayPage;
|
|
469
|
-
return owner
|
|
465
|
+
return withSessionReadTransaction(owner, sessionId, () => {
|
|
470
466
|
const pending = readPendingPrompts(owner, sessionId);
|
|
471
467
|
const lineage = readSessionLineage(owner, { sessionId });
|
|
472
468
|
if (lineage === undefined) {
|
|
@@ -503,6 +499,10 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
503
499
|
const admission = readSessionCommandAdmission(owner, sessionId, commandId);
|
|
504
500
|
if (admission === undefined)
|
|
505
501
|
throw unavailable("Accepted Session command is unavailable");
|
|
502
|
+
// A live work has no settlement to project. The owner's terminal index
|
|
503
|
+
// answers this without decoding the work again on every polling tick.
|
|
504
|
+
if (!hasSessionWorkTerminal(owner, sessionId, admission.entry.workId))
|
|
505
|
+
return undefined;
|
|
506
506
|
const rows = readSessionEntriesForWork(owner, sessionId, admission.entry.workId);
|
|
507
507
|
const work = workFacts(owner, sessionId, rows);
|
|
508
508
|
const terminal = rows
|
|
@@ -521,6 +521,7 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
521
521
|
const sender = messageAuthor(admission.entry.meta);
|
|
522
522
|
return {
|
|
523
523
|
acceptedSender: sender?.kind === "agent" ? sender.id : null,
|
|
524
|
+
delegator: readSessionDelegator(owner, sessionId)?.replyTo[0]?.agentId ?? null,
|
|
524
525
|
terminal: meta === undefined ? terminal : { ...terminal, meta },
|
|
525
526
|
content: terminal.outcome === "cancelled"
|
|
526
527
|
? ""
|
|
@@ -534,5 +535,6 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
534
535
|
usage,
|
|
535
536
|
})),
|
|
536
537
|
toolCalls: new Set(rows.flatMap(({ entry }) => entry.kind === "tool_intent" ? [entry.call.id] : [])).size,
|
|
538
|
+
waitingOnYou: readWaitingOnSender(owner, sessionId, admission.entry.workId),
|
|
537
539
|
};
|
|
538
540
|
}
|
|
@@ -27,10 +27,11 @@ export declare function createFleetDelegationStore(storage: WatchdogSqliteOwner)
|
|
|
27
27
|
read: (requestId: string) => {
|
|
28
28
|
readonly [x: string]: string | number | ArrayBuffer | null;
|
|
29
29
|
} | undefined;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/** Accepted sends not yet observed settled, for owner-local archive admission. */
|
|
31
|
+
unsettled: () => readonly {
|
|
32
32
|
readonly [x: string]: string | number | ArrayBuffer | null;
|
|
33
33
|
}[];
|
|
34
|
+
recordSettled(requestId: string): void;
|
|
34
35
|
reserve(requestId: string, input: string, mail: string): void;
|
|
35
36
|
recordReceipt(requestId: string, receipt: string, expectedMail: string, acceptedMail: string): void;
|
|
36
37
|
recordHeld(requestId: string): void;
|
|
@@ -90,6 +90,12 @@ export function createFleetDelegationStore(storage) {
|
|
|
90
90
|
storage.sql
|
|
91
91
|
.exec(`CREATE TABLE IF NOT EXISTS shiitake_fleet_undeliverable_delegations (
|
|
92
92
|
request_id TEXT PRIMARY KEY
|
|
93
|
+
) STRICT`);
|
|
94
|
+
// A remote settlement confirmed by this owner. Without this local fact an
|
|
95
|
+
// accepted sender receipt remains indistinguishable from outstanding work.
|
|
96
|
+
storage.sql
|
|
97
|
+
.exec(`CREATE TABLE IF NOT EXISTS shiitake_fleet_settled_delegations (
|
|
98
|
+
request_id TEXT PRIMARY KEY
|
|
93
99
|
) STRICT`);
|
|
94
100
|
return {
|
|
95
101
|
read: (requestId) => storage.sql
|
|
@@ -108,13 +114,27 @@ export function createFleetDelegationStore(storage) {
|
|
|
108
114
|
ON terminal.request_id = delegation.request_id
|
|
109
115
|
WHERE delegation.request_id = ?`, requestId)
|
|
110
116
|
.toArray()[0],
|
|
111
|
-
/**
|
|
112
|
-
|
|
117
|
+
/** Accepted sends not yet observed settled, for owner-local archive admission. */
|
|
118
|
+
unsettled: () => storage.sql
|
|
113
119
|
.exec(`SELECT input, mail, 'accepted' AS state, receipt
|
|
114
|
-
FROM shiitake_fleet_delegations
|
|
120
|
+
FROM shiitake_fleet_delegations AS delegation
|
|
115
121
|
WHERE receipt IS NOT NULL
|
|
116
|
-
|
|
122
|
+
AND NOT EXISTS (
|
|
123
|
+
SELECT 1 FROM shiitake_fleet_settled_delegations AS settled
|
|
124
|
+
WHERE settled.request_id = delegation.request_id
|
|
125
|
+
)
|
|
126
|
+
ORDER BY delegation.rowid DESC`)
|
|
117
127
|
.toArray(),
|
|
128
|
+
recordSettled(requestId) {
|
|
129
|
+
storage.transactionSync(() => {
|
|
130
|
+
const accepted = storage.sql
|
|
131
|
+
.exec("SELECT 1 FROM shiitake_fleet_delegations WHERE request_id = ? AND receipt IS NOT NULL", requestId)
|
|
132
|
+
.toArray();
|
|
133
|
+
if (accepted.length !== 1)
|
|
134
|
+
throw new Error("Cannot record settlement without acceptance");
|
|
135
|
+
storage.sql.exec("INSERT OR IGNORE INTO shiitake_fleet_settled_delegations(request_id) VALUES(?)", requestId);
|
|
136
|
+
});
|
|
137
|
+
},
|
|
118
138
|
reserve(requestId, input, mail) {
|
|
119
139
|
storage.transactionSync(() => {
|
|
120
140
|
storage.sql.exec("INSERT OR IGNORE INTO shiitake_fleet_delegations(request_id, input, mail) VALUES (?, ?, ?)", requestId, input, mail);
|
|
@@ -15,6 +15,8 @@ export * from "./extension-store.js";
|
|
|
15
15
|
export { readLatestPendingPrompt } from "./client-projection.js";
|
|
16
16
|
export { createFleetDirectoryStore, createFleetDelegationStore, } from "./fleet-store.js";
|
|
17
17
|
export * from "./session-attention.js";
|
|
18
|
+
export * from "./work-senders.js";
|
|
19
|
+
export * from "./session-delegator.js";
|
|
18
20
|
export { deliverSessionGrants, prepareSessionGrants, readSessionGrants, type GrantsDelivery, type GrantsDeliveryResult, } from "./session-grants.js";
|
|
19
21
|
export * from "./progress.js";
|
|
20
22
|
export * from "./compaction-activity.js";
|
|
@@ -117,7 +119,7 @@ export declare function readSettlementDiagnostic(owner: SqliteOwner, input: Read
|
|
|
117
119
|
commandFactId: string;
|
|
118
120
|
}>): Effect.Effect<string | undefined, ShiitakeError>;
|
|
119
121
|
/** Read one bounded public Session snapshot. */
|
|
120
|
-
export declare function readSession(owner: SqliteOwner, sessionId: string, activity: ObservedActivity, input
|
|
122
|
+
export declare function readSession<A>(owner: SqliteOwner, sessionId: string, activity: () => ObservedActivity, input: ReadOptions | undefined, projectExtensions: () => SessionExtensionSlots, complete: (snapshot: SessionSnapshot) => A): Effect.Effect<A, ShiitakeError>;
|
|
121
123
|
/** Read one bounded folder-resume summary through Woodstock's Effect boundary. */
|
|
122
124
|
export declare function readSessionSummary(owner: SqliteOwner, sessionId: string): Effect.Effect<import("./session-store.js").StoredSessionSummary | undefined, ShiitakeError>;
|
|
123
125
|
/**
|
|
@@ -138,8 +140,10 @@ export declare function readSessionFactPage(owner: SqliteOwner, after?: string):
|
|
|
138
140
|
export declare function readColdSessionPage(owner: SqliteOwner, request: ColdSessionPageRequest): Effect.Effect<ColdSessionPage, ShiitakeError>;
|
|
139
141
|
/** Read one bounded inbox page in tier, then newest-activity order. */
|
|
140
142
|
export declare function readSessionInbox(owner: SqliteOwner, after: string | undefined): Effect.Effect<import("../../client.js").SessionSummaryPage, ShiitakeError>;
|
|
141
|
-
/** Read
|
|
142
|
-
export declare function
|
|
143
|
+
/** Read a Session's open attention items from the store alone. */
|
|
144
|
+
export declare function readStoredSessionAttention(owner: SqliteOwner, sessionId: string): Effect.Effect<readonly import("../../client.js").SessionAttention[], ShiitakeError>;
|
|
145
|
+
/** Read and complete one strict-after page in the same Session transaction. */
|
|
146
|
+
export declare function readSessionReplayPage<A>(owner: SqliteOwner, sessionId: string, after: string | undefined, delivery: "replay" | "live", activity: ObservedActivity, projectExtensions: () => SessionExtensionSlots, includeSandboxFailures: boolean, complete: (page: ClientReplayPage) => A): Effect.Effect<A, ShiitakeError>;
|
|
143
147
|
/** Persist an operator-only sandbox diagnostic beside its accepted Session work. */
|
|
144
148
|
export declare function recordSandboxFailure(owner: SqliteOwner, accepted: Pick<AcceptedCommand, "sessionId" | "commandId">, event: SandboxFailureEvent): Effect.Effect<void, ShiitakeError>;
|
|
145
149
|
/** Extension that admitted one accepted command; null for other producers. */
|
|
@@ -5,14 +5,18 @@ import { publishOwnerObservation } from "../observation-kernel.js";
|
|
|
5
5
|
import { readColdSessionPageOwned } from "./cold-sessions.js";
|
|
6
6
|
import { diagnosticsByWorkId, projectCommandSettlement, readClientReplayPage, readClientSnapshot, } from "./client-projection.js";
|
|
7
7
|
import { sessionIds } from "./identities.js";
|
|
8
|
+
import { decodeWoodstockCursor } from "./cursor.js";
|
|
8
9
|
import { openProgress, openToolProgress } from "./progress.js";
|
|
9
10
|
import { encodeStoredSessionEntry, } from "./session-entry.js";
|
|
10
|
-
import { appendSessionEntry, appendCompactionTransitionOwned, appendCompactionNoticeOwned, placeCompactionTransitionOwned, acceptSessionAbort, acceptSessionCancellation, completeSessionLeaf, prepareSessionRun, prepareSessionStore, readSessionEntriesForWork, readSessionCommandAdmission, settleSessionControls, takePendingSessionControls, readSessionEntryById, selectSessionBranch, forkSessionAt, readStoredSessionSummary as readSessionSummaryOwned, readStoredSessionFactPage, createStoredSession, createStoredSessionWithinTransaction, deleteStoredSession, appendOperatorEntry, } from "./session-store.js";
|
|
11
|
+
import { appendSessionEntry, appendCompactionTransitionOwned, appendCompactionNoticeOwned, placeCompactionTransitionOwned, acceptSessionAbort, acceptSessionCancellation, completeSessionLeaf, prepareSessionRun, prepareSessionStore, readSessionEntriesForWork, readSessionCommandAdmission, settleSessionControls, takePendingSessionControls, readSessionEntryById, selectSessionBranch, forkSessionAt, readStoredSessionSummary as readSessionSummaryOwned, readStoredSessionFactPage, createStoredSession, createStoredSessionWithinTransaction, deleteStoredSession, appendOperatorEntry, withSessionRead, } from "./session-store.js";
|
|
11
12
|
import { readSessionInboxPage, recordSessionInboxDeletion, } from "./session-inbox.js";
|
|
13
|
+
import { readSessionAttention } from "./session-attention.js";
|
|
12
14
|
export * from "./extension-store.js";
|
|
13
15
|
export { readLatestPendingPrompt } from "./client-projection.js";
|
|
14
16
|
export { createFleetDirectoryStore, createFleetDelegationStore, } from "./fleet-store.js";
|
|
15
17
|
export * from "./session-attention.js";
|
|
18
|
+
export * from "./work-senders.js";
|
|
19
|
+
export * from "./session-delegator.js";
|
|
16
20
|
export { deliverSessionGrants, prepareSessionGrants, readSessionGrants, } from "./session-grants.js";
|
|
17
21
|
export * from "./progress.js";
|
|
18
22
|
export * from "./compaction-activity.js";
|
|
@@ -188,8 +192,14 @@ export function readSettlementDiagnostic(owner, input) {
|
|
|
188
192
|
});
|
|
189
193
|
}
|
|
190
194
|
/** Read one bounded public Session snapshot. */
|
|
191
|
-
export function readSession(owner, sessionId, activity, input, projectExtensions
|
|
192
|
-
return storeEffect(() =>
|
|
195
|
+
export function readSession(owner, sessionId, activity, input, projectExtensions, complete) {
|
|
196
|
+
return storeEffect(() => {
|
|
197
|
+
const id = decodeClientSessionId(sessionId);
|
|
198
|
+
const options = decodeClientReadOptions(input);
|
|
199
|
+
return withSessionRead(owner, id, () => complete(readClientSnapshot(owner, id, options, activity(), projectExtensions)), options.before === undefined
|
|
200
|
+
? undefined
|
|
201
|
+
: decodeWoodstockCursor(options.before)?.sequence);
|
|
202
|
+
});
|
|
193
203
|
}
|
|
194
204
|
/** Read one bounded folder-resume summary through Woodstock's Effect boundary. */
|
|
195
205
|
export function readSessionSummary(owner, sessionId) {
|
|
@@ -228,9 +238,16 @@ export function readColdSessionPage(owner, request) {
|
|
|
228
238
|
export function readSessionInbox(owner, after) {
|
|
229
239
|
return storeEffect(() => readSessionInboxPage(owner, after));
|
|
230
240
|
}
|
|
231
|
-
/** Read
|
|
232
|
-
export function
|
|
233
|
-
return storeEffect(() =>
|
|
241
|
+
/** Read a Session's open attention items from the store alone. */
|
|
242
|
+
export function readStoredSessionAttention(owner, sessionId) {
|
|
243
|
+
return storeEffect(() => readSessionAttention(owner, sessionId));
|
|
244
|
+
}
|
|
245
|
+
/** Read and complete one strict-after page in the same Session transaction. */
|
|
246
|
+
export function readSessionReplayPage(owner, sessionId, after, delivery, activity, projectExtensions, includeSandboxFailures, complete) {
|
|
247
|
+
return storeEffect(() => {
|
|
248
|
+
const id = decodeClientSessionId(sessionId);
|
|
249
|
+
return withSessionRead(owner, id, () => complete(readClientReplayPage(owner, id, after, delivery, activity, projectExtensions, includeSandboxFailures)));
|
|
250
|
+
});
|
|
234
251
|
}
|
|
235
252
|
function sameEntry(left, right) {
|
|
236
253
|
return encodeStoredSessionEntry(left) === encodeStoredSessionEntry(right);
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { MuleEvent } from "@fungi.computer/mule";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import type * as Scope from "effect/Scope";
|
|
4
|
-
import type { SessionProgress, SessionToolOutput } from "../../client.js";
|
|
4
|
+
import type { SessionProgress, SessionProgressDelta, SessionToolOutput } from "../../client.js";
|
|
5
5
|
import { ShiitakeError } from "../../errors.js";
|
|
6
6
|
import type { SqliteOwner } from "../contracts.js";
|
|
7
7
|
import type { AcceptedCommand } from "./session-store.js";
|
|
8
|
+
/** Per-watch position; a gap in the retained tail requires a fresh baseline. */
|
|
9
|
+
export type ProgressPosition = Readonly<{
|
|
10
|
+
messageId: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
text: number;
|
|
13
|
+
thinking: number;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function progressPosition(value: SessionProgress | null): ProgressPosition | null;
|
|
16
|
+
/** Undefined means continuity was lost; null means no new model progress. */
|
|
17
|
+
export declare function readProcessProgressDelta(owner: SqliteOwner, sessionId: string, workId: string, after: ProgressPosition | null): SessionProgressDelta | null | undefined;
|
|
8
18
|
export declare function readProcessToolOutputForWork(owner: SqliteOwner, sessionId: string, workId: string): readonly SessionToolOutput[];
|
|
9
19
|
/** Run-scoped, replaceable tool snapshots: never stored in Woodstock. */
|
|
10
20
|
export declare function openToolProgress(owner: SqliteOwner, accepted: AcceptedCommand): Effect.Effect<{
|