@fungi.computer/shiitake 0.4.0 → 0.6.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 +1 -10
- package/dist/agent-control.d.ts +98 -5
- package/dist/agent-control.js +111 -8
- package/dist/client-entry.d.ts +20 -0
- package/dist/client-entry.js +5 -0
- package/dist/client-visitor.d.ts +31 -0
- package/dist/client-visitor.js +226 -0
- package/dist/client-wire.d.ts +5 -0
- package/dist/client-wire.js +6 -0
- package/dist/client.d.ts +4 -3
- package/dist/client.js +1 -0
- package/dist/delegation.d.ts +100 -77
- package/dist/delegation.js +73 -23
- package/dist/effect.d.ts +25 -0
- package/dist/effect.js +14 -1
- package/dist/extensions.d.ts +21 -2
- package/dist/extensions.js +1 -1
- package/dist/fleet.d.ts +22 -2
- package/dist/fleet.js +127 -36
- package/dist/hosted.js +3 -0
- package/dist/http-routes.d.ts +11 -0
- package/dist/internal/accepted-run-executor.js +2 -1
- package/dist/internal/admission-door.d.ts +5 -0
- package/dist/internal/admission-door.js +34 -3
- package/dist/internal/contracts.d.ts +14 -1
- package/dist/internal/coordinator.js +8 -2
- package/dist/internal/effect-client.js +1 -1
- package/dist/internal/effect-contracts.d.ts +4 -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 +13 -5
- 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/run-scope.d.ts +14 -0
- package/dist/internal/run-scope.js +32 -9
- package/dist/internal/runtime-coordinator.js +8 -1
- package/dist/internal/runtime-reconciliation.js +119 -24
- 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 +9 -1
- package/dist/internal/woodstock/fleet-store.d.ts +1 -0
- package/dist/internal/woodstock/fleet-store.js +28 -1
- package/dist/internal/woodstock/index.d.ts +4 -0
- package/dist/internal/woodstock/index.js +7 -0
- package/dist/internal/woodstock/session-delegator.d.ts +12 -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 +2 -0
- package/dist/internal/woodstock/session-store.js +20 -0
- package/dist/internal/woodstock/work-senders.d.ts +32 -0
- package/dist/internal/woodstock/work-senders.js +70 -0
- package/dist/remote-client.js +7 -2
- package/dist/server-visitor-stream.d.ts +13 -0
- package/dist/server-visitor-stream.js +115 -0
- package/dist/session-attention.d.ts +30 -0
- package/dist/session-attention.js +14 -0
- package/docs/api/agent-control.md +209 -13
- package/docs/api/client-entry.md +3 -0
- package/docs/api/client.md +136 -1
- package/docs/api/cloudflare.md +7 -1
- package/docs/api/delegation.md +234 -61
- package/docs/api/effect.md +16 -6
- package/docs/api/extensions.md +65 -4
- package/docs/api/fleet.md +69 -16
- package/docs/api/index.md +6 -4
- package/package.json +10 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HostMetaOptions } from "./host-meta.js";
|
|
2
2
|
import type { ExtensionRosterV0 } from "../extensions.js";
|
|
3
|
-
import type { Api, AssistantMessage, Model, MuleStreamFunction, RetryPolicy } from "@fungi.computer/mule/effect";
|
|
3
|
+
import type { Api, AssistantMessage, Model, MuleStreamFunction, ProviderRejection, RetryPolicy } from "@fungi.computer/mule/effect";
|
|
4
4
|
import type { CapabilitySource, ExecutionOptions, ModuleRegistration } from "@fungi.computer/mycelium";
|
|
5
5
|
import type { CommandSettlement, GrantsDelivery, GrantsDeliveryResult, ToolAccess } from "./contracts.js";
|
|
6
6
|
import type { Sandbox } from "@fungi.computer/mycelium/effect";
|
|
@@ -26,6 +26,8 @@ export type ModelAcquisitionEffect = Readonly<{
|
|
|
26
26
|
retryable: (message: AssistantMessage) => boolean;
|
|
27
27
|
/** Owner-authored reason for this door's failure message, never SDK prose. */
|
|
28
28
|
failureReason?: (message: AssistantMessage) => string | undefined;
|
|
29
|
+
/** Bounded provider facts for this door's exact failure, never SDK prose. */
|
|
30
|
+
rejection?: (message: AssistantMessage) => ProviderRejection | undefined;
|
|
29
31
|
/** Provider prose for this door's exact failed turn; never a diagnostic. */
|
|
30
32
|
providerError?: (message: AssistantMessage) => string | undefined;
|
|
31
33
|
/** Effect finalizer for acquired model resources. */
|
|
@@ -201,7 +203,7 @@ export type ShiitakeEffectSession = Readonly<{
|
|
|
201
203
|
/** Effect-native Session-scope extension commands. */
|
|
202
204
|
export type ShiitakeEffectSessionExtension = Readonly<{
|
|
203
205
|
/** Run one command with its encoded input; succeeds with the encoded output. */
|
|
204
|
-
call: (command: string, input: Json) => Effect.Effect<Json, ShiitakeError>;
|
|
206
|
+
call: (command: string, input: Json, caller?: import("../extensions.js").ExtensionCommandCallerV0) => Effect.Effect<Json, ShiitakeError>;
|
|
205
207
|
}>;
|
|
206
208
|
/** Effect-native Agent-scope extension controls. */
|
|
207
209
|
export type ShiitakeEffectAgentExtensions = Readonly<{
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Result from "effect/Result";
|
|
3
3
|
import * as Schema from "effect/Schema";
|
|
4
|
-
import type { AdmissionError, CommandHandlersV0, CommandsV0, ExtensionCommandError, ExtensionRead, ExtensionStateError, ExtensionTx, Json } from "../extensions.js";
|
|
4
|
+
import type { AdmissionError, CommandHandlersV0, CommandsV0, ExtensionCommandError, ExtensionCommandCallerV0, ExtensionRead, ExtensionStateError, ExtensionTx, Json } from "../extensions.js";
|
|
5
5
|
/** A parsed invocation retains its typed handler until the transaction runs. */
|
|
6
6
|
export type RegisteredExtensionCommandV0 = (frame: Readonly<{
|
|
7
7
|
sessionId: string | null;
|
|
8
8
|
input: Json;
|
|
9
|
+
caller?: ExtensionCommandCallerV0;
|
|
9
10
|
}>) => Result.Result<() => Effect.Effect<Json, ExtensionCommandError | ExtensionStateError | AdmissionError, ExtensionTx | ExtensionRead>, Schema.SchemaError>;
|
|
10
11
|
/** Capture each declared command's schemas and matching handler at activation. */
|
|
11
12
|
export declare function registerExtensionCommands<C extends CommandsV0>(specs: C, handlers: CommandHandlersV0<C>): ReadonlyMap<string, RegisteredExtensionCommandV0>;
|
|
@@ -13,7 +13,9 @@ function bindCommand(spec, handler) {
|
|
|
13
13
|
const decodeInput = Schema.decodeUnknownResult(spec.input, {
|
|
14
14
|
onExcessProperty: "error",
|
|
15
15
|
});
|
|
16
|
-
return (frame) => Result.flatMap(decodeSession(frame.sessionId), (sessionId) => Result.map(decodeInput(frame.input), (input) => () => invokeTransactionHook(() => handler(
|
|
16
|
+
return (frame) => Result.flatMap(decodeSession(frame.sessionId), (sessionId) => Result.map(decodeInput(frame.input), (input) => () => invokeTransactionHook(() => handler(frame.caller === undefined
|
|
17
|
+
? { sessionId, input }
|
|
18
|
+
: { sessionId, input, caller: frame.caller })).pipe(Effect.flatMap((output) => Schema.encodeEffect(spec.output)(output).pipe(Effect.flatMap(Schema.decodeUnknownEffect(JsonValueSchema)), Effect.orDie)))));
|
|
17
19
|
}
|
|
18
20
|
/** Capture each declared command's schemas and matching handler at activation. */
|
|
19
21
|
export function registerExtensionCommands(specs, handlers) {
|
|
@@ -9,6 +9,8 @@ import type { SqliteOwner } from "./contracts.js";
|
|
|
9
9
|
import type { AdmittedExtensionV0 } from "./extension-roster.js";
|
|
10
10
|
/** The settled consumer name core itself uses for mail and control repair. */
|
|
11
11
|
export declare const coreConsumer = "core";
|
|
12
|
+
/** Core reports to delegators after the extensions have seen a settlement. */
|
|
13
|
+
export declare const reportConsumer = "core-report";
|
|
12
14
|
/** Core's generic view of rostered v1 extensions. */
|
|
13
15
|
export type ExtensionRuntime = Readonly<{
|
|
14
16
|
/** Prepare additions without replacing any live member; apply only between turns. */
|
|
@@ -53,7 +55,7 @@ export type ExtensionRuntime = Readonly<{
|
|
|
53
55
|
sessionId: string;
|
|
54
56
|
} | {
|
|
55
57
|
scope: "agent";
|
|
56
|
-
}>, extensionId: string, command: string, input: Json) => Effect.Effect<Json, ShiitakeError>;
|
|
58
|
+
}>, extensionId: string, command: string, input: Json, caller?: import("../extensions.js").ExtensionCommandCallerV0) => Effect.Effect<Json, ShiitakeError>;
|
|
57
59
|
list: () => readonly ExtensionStatusV0[];
|
|
58
60
|
resume: (id: string) => Effect.Effect<ExtensionStatusV0, ShiitakeError>;
|
|
59
61
|
close: Effect.Effect<void, ShiitakeError>;
|
|
@@ -12,11 +12,13 @@ import { ShiitakeError } from "../errors.js";
|
|
|
12
12
|
import { AdmissionError, ExtensionCommandError, ExtensionRead, ExtensionStateError, ExtensionStop, ExtensionTx, } from "../extensions.js";
|
|
13
13
|
import { decodeClientSessionId } from "./client-boundary.js";
|
|
14
14
|
import { AsyncExtensionTransactionError, invokeExtensionHook, invokeSyncHandler, } from "./extension-hook.js";
|
|
15
|
-
import { setSessionAttention, clearSessionAttention, projectSessionAttention, readAttentionSessionPage, replaceAttentionGrants, hasStoredSession, readSessionLineage, hasPendingSessionSteer, hasSuccessfulToolResult, advanceCursor, declareSessionDocument, readSessionDocument, writeSessionDocument, deleteAllRecords, deleteRecord, deleteTimer, deleteExtensionTimers, readAllRecords, readCursor, readDueTimers, readRecord, readRecordPage, readStateLedger, readStatus, readTimer, readTimerWake, writeRecord, writeStateLedger, writeStatus, writeTimer, } from "./woodstock/index.js";
|
|
15
|
+
import { hasSessionAttention, setSessionAttention, clearSessionAttention, projectSessionAttention, readAttentionSessionPage, replaceAttentionGrants, hasStoredSession, readSessionLineage, hasPendingSessionSteer, readWorkSender, hasSuccessfulToolResult, advanceCursor, declareSessionDocument, readSessionDocument, writeSessionDocument, deleteAllRecords, deleteRecord, deleteTimer, deleteExtensionTimers, readAllRecords, readCursor, readDueTimers, readRecord, readRecordPage, readStateLedger, readStatus, readTimer, readTimerWake, writeRecord, writeStateLedger, writeStatus, writeTimer, } from "./woodstock/index.js";
|
|
16
16
|
import { JsonValueSchema } from "./json-value.js";
|
|
17
|
-
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
17
|
+
import { hasOwnerObservers, publishOwnerObservation, } from "./observation-kernel.js";
|
|
18
18
|
/** The settled consumer name core itself uses for mail and control repair. */
|
|
19
19
|
export const coreConsumer = "core";
|
|
20
|
+
/** Core reports to delegators after the extensions have seen a settlement. */
|
|
21
|
+
export const reportConsumer = "core-report";
|
|
20
22
|
const maximumKeyLength = 256;
|
|
21
23
|
const maximumRecordBytes = 1024 * 1024;
|
|
22
24
|
const maximumReasonLength = 512;
|
|
@@ -288,7 +290,7 @@ export function openExtensionRuntime(input) {
|
|
|
288
290
|
set: (sessionId, item) => {
|
|
289
291
|
const decoded = Schema.decodeUnknownResult(AttentionItemSchema, {
|
|
290
292
|
onExcessProperty: "error",
|
|
291
|
-
})(item);
|
|
293
|
+
})(item.waitingOn === undefined ? item : { ...item, inbox: false });
|
|
292
294
|
return decoded._tag === "Failure"
|
|
293
295
|
? Effect.fail(stateError("bound", "Attention item is invalid"))
|
|
294
296
|
: mutate(sessionId, () => {
|
|
@@ -297,6 +299,7 @@ export function openExtensionRuntime(input) {
|
|
|
297
299
|
setSessionAttention(owner, sessionId, id, decoded.success);
|
|
298
300
|
});
|
|
299
301
|
},
|
|
302
|
+
awaitsPerson: (sessionId) => requireRead(id).pipe(Effect.tap(() => grantGuard(member, "attention")), Effect.andThen(storeStep(() => hasSessionAttention(owner, sessionId)))),
|
|
300
303
|
clear: (sessionId, itemId) => boundedKey(itemId, "An attention id").pipe(Effect.andThen(mutate(sessionId, () => {
|
|
301
304
|
clearSessionAttention(owner, sessionId, id, itemId);
|
|
302
305
|
}))),
|
|
@@ -323,6 +326,7 @@ export function openExtensionRuntime(input) {
|
|
|
323
326
|
extensionId: id,
|
|
324
327
|
correlation: request.correlation ?? null,
|
|
325
328
|
},
|
|
329
|
+
continuesWorkId: request.continuesWorkId,
|
|
326
330
|
});
|
|
327
331
|
return result.status === "accepted"
|
|
328
332
|
? {
|
|
@@ -567,6 +571,7 @@ export function openExtensionRuntime(input) {
|
|
|
567
571
|
send: sendFor(member),
|
|
568
572
|
timers: timersFor(member),
|
|
569
573
|
attention: attentionFor(member),
|
|
574
|
+
isObserved: (sessionId) => member.status === "running" && hasOwnerObservers(owner, sessionId),
|
|
570
575
|
sessionPosition: (sessionId) => requireRead(member.admitted.id).pipe(Effect.andThen(grantGuard(member, "state")), Effect.andThen(storeStep(() => {
|
|
571
576
|
const id = decodeClientSessionId(sessionId);
|
|
572
577
|
if (!hasStoredSession(owner, id))
|
|
@@ -575,6 +580,7 @@ export function openExtensionRuntime(input) {
|
|
|
575
580
|
?.newestSequence ?? 0);
|
|
576
581
|
}))),
|
|
577
582
|
hasPendingSteer: (run) => requireRead(member.admitted.id).pipe(Effect.andThen(grantGuard(member, "run_tools")), Effect.andThen(storeStep(() => hasPendingSessionSteer(owner, decodeClientSessionId(run.sessionId), run.workId)))),
|
|
583
|
+
workSender: (run) => requireRead(member.admitted.id).pipe(Effect.andThen(grantGuard(member, "run_tools")), Effect.andThen(storeStep(() => readWorkSender(owner, decodeClientSessionId(run.sessionId), run.workId) ?? null))),
|
|
578
584
|
hasSuccessfulToolResult: (run, toolCallId) => requireRead(member.admitted.id).pipe(Effect.andThen(grantGuard(member, "run_tools")), Effect.andThen(storeStep(() => hasSuccessfulToolResult(owner, decodeClientSessionId(run.sessionId), run.workId, toolCallId)))),
|
|
579
585
|
observe: (sessionId) => requireFrame(member.admitted.id).pipe(Effect.map((frame) => {
|
|
580
586
|
frame.observed.add(sessionId);
|
|
@@ -1020,7 +1026,7 @@ export function openExtensionRuntime(input) {
|
|
|
1020
1026
|
}
|
|
1021
1027
|
return Object.freeze(slots);
|
|
1022
1028
|
},
|
|
1023
|
-
call: (target, extensionId, command, raw) => Effect.gen(function* () {
|
|
1029
|
+
call: (target, extensionId, command, raw, caller) => Effect.gen(function* () {
|
|
1024
1030
|
const member = members.get(extensionId);
|
|
1025
1031
|
if (member?.admitted.grants.has("commands") !== true)
|
|
1026
1032
|
return yield* Effect.fail(new ShiitakeError("extensions", "unavailable", "Extension is unavailable"));
|
|
@@ -1049,7 +1055,9 @@ export function openExtensionRuntime(input) {
|
|
|
1049
1055
|
yield* recordStop(member, `command ${command} has no handler`, undefined, "own-transaction");
|
|
1050
1056
|
return yield* Effect.fail(new ShiitakeError("extensions", "unavailable", "Extension is stopped"));
|
|
1051
1057
|
}
|
|
1052
|
-
const prepared =
|
|
1058
|
+
const prepared = caller === undefined
|
|
1059
|
+
? handler({ sessionId, input: raw })
|
|
1060
|
+
: handler({ sessionId, input: raw, caller });
|
|
1053
1061
|
if (prepared._tag === "Failure")
|
|
1054
1062
|
return yield* Effect.fail(new ShiitakeError("contract", "invalid", "Extension command input is invalid"));
|
|
1055
1063
|
const body = prepared.success();
|
|
@@ -23,6 +23,8 @@ function asEffectAcquisition(acquisition) {
|
|
|
23
23
|
shared = { ...shared, failureReason: acquisition.failureReason };
|
|
24
24
|
if (acquisition.providerError !== undefined)
|
|
25
25
|
shared = { ...shared, providerError: acquisition.providerError };
|
|
26
|
+
if (acquisition.rejection !== undefined)
|
|
27
|
+
shared = { ...shared, rejection: acquisition.rejection };
|
|
26
28
|
if (acquisition.stream === undefined)
|
|
27
29
|
return acquisition.retryAfterMs === undefined
|
|
28
30
|
? Object.freeze(shared)
|
|
@@ -4,6 +4,8 @@ import type { SqliteOwner } from "./contracts.js";
|
|
|
4
4
|
/** Finite process-local signal; durable owner state remains authoritative. */
|
|
5
5
|
export type ObservationSignal = "closed" | "reset" | "durable" | "activity" | "progress";
|
|
6
6
|
export type ObservationReason = "durable" | "activity" | "progress" | "settlement";
|
|
7
|
+
/** Actual live-view demand, excluding exact-command settlement waiters. */
|
|
8
|
+
export declare function hasOwnerObservers(store: SqliteOwner, sessionId: string): boolean;
|
|
7
9
|
/** One scoped process-local observation registration. */
|
|
8
10
|
export type ObservationRegistration = Readonly<{
|
|
9
11
|
/** Inspect and consume one pending finite signal without waiting. */
|
|
@@ -4,6 +4,13 @@ import { reportCatchFailure } from "./failure.js";
|
|
|
4
4
|
const MAX_OBSERVATIONS = 64;
|
|
5
5
|
const MAX_PENDING_HINTS = 256;
|
|
6
6
|
const ownerPublishers = new WeakMap();
|
|
7
|
+
/** Actual live-view demand, excluding exact-command settlement waiters. */
|
|
8
|
+
export function hasOwnerObservers(store, sessionId) {
|
|
9
|
+
for (const publisher of ownerPublishers.get(store) ?? [])
|
|
10
|
+
if (publisher.observes(sessionId))
|
|
11
|
+
return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
7
14
|
/** Combine coalesced reasons without allowing a progress hint to hide a commit. */
|
|
8
15
|
export function mergeObservationSignals(first, second) {
|
|
9
16
|
if (first === "closed" || second === "closed")
|
|
@@ -55,9 +62,9 @@ export function publishOwnerObservation(store, sessionId, reason = "durable") {
|
|
|
55
62
|
const publishers = ownerPublishers.get(store);
|
|
56
63
|
if (publishers === undefined)
|
|
57
64
|
return;
|
|
58
|
-
for (const
|
|
65
|
+
for (const publisher of publishers) {
|
|
59
66
|
try {
|
|
60
|
-
publish({ kind: "session", sessionId, reason });
|
|
67
|
+
publisher.publish({ kind: "session", sessionId, reason });
|
|
61
68
|
}
|
|
62
69
|
catch (cause) {
|
|
63
70
|
reportCatchFailure("owner-observation-publish", cause);
|
|
@@ -70,9 +77,9 @@ export function publishAgentExecutionChange(store) {
|
|
|
70
77
|
const publishers = ownerPublishers.get(store);
|
|
71
78
|
if (publishers === undefined)
|
|
72
79
|
return;
|
|
73
|
-
for (const
|
|
80
|
+
for (const publisher of publishers) {
|
|
74
81
|
try {
|
|
75
|
-
publish({ kind: "agent-execution" });
|
|
82
|
+
publisher.publish({ kind: "agent-execution" });
|
|
76
83
|
}
|
|
77
84
|
catch (cause) {
|
|
78
85
|
reportCatchFailure("owner-execution-change-publish", cause);
|
|
@@ -140,7 +147,11 @@ export function openObservationKernel(store) {
|
|
|
140
147
|
deliverObservation(listener, publication.reason === "settlement" ? "durable" : publication.reason);
|
|
141
148
|
}
|
|
142
149
|
};
|
|
143
|
-
|
|
150
|
+
const publisher = {
|
|
151
|
+
publish,
|
|
152
|
+
observes: (sessionId) => [...(sessions.get(sessionId) ?? [])].some((listener) => !listener.closed && !listener.settlementOnly),
|
|
153
|
+
};
|
|
154
|
+
publishers.add(publisher);
|
|
144
155
|
ownerPublishers.set(store, publishers);
|
|
145
156
|
return Object.freeze({
|
|
146
157
|
register: (sessionId, interest) => Effect.suspend(() => {
|
|
@@ -194,7 +205,7 @@ export function openObservationKernel(store) {
|
|
|
194
205
|
if (released)
|
|
195
206
|
return;
|
|
196
207
|
released = true;
|
|
197
|
-
publishers.delete(
|
|
208
|
+
publishers.delete(publisher);
|
|
198
209
|
if (publishers.size === 0)
|
|
199
210
|
ownerPublishers.delete(store);
|
|
200
211
|
for (const listener of active)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FileTool } from "@fungi.computer/knapsack";
|
|
2
|
+
import { type MuleError } from "@fungi.computer/mule/effect";
|
|
2
3
|
import type { ModuleRegistration } from "@fungi.computer/mycelium";
|
|
3
4
|
import * as Effect from "effect/Effect";
|
|
4
5
|
import { ShiitakeError } from "../errors.js";
|
|
@@ -17,6 +18,19 @@ export declare class ToolSetupError extends ShiitakeError {
|
|
|
17
18
|
constructor(phase: keyof typeof toolSetupMessages);
|
|
18
19
|
get diagnostic(): string;
|
|
19
20
|
}
|
|
21
|
+
/** Closed reasons a rejected run names, never a foreign error's own text. */
|
|
22
|
+
declare const runRejectedDiagnostics: {
|
|
23
|
+
readonly model: "model-failed";
|
|
24
|
+
readonly tool: "tool-failed";
|
|
25
|
+
readonly control: "control-failed";
|
|
26
|
+
readonly listener: "listener-failed";
|
|
27
|
+
};
|
|
28
|
+
/** A run Mule ended with a non-model error; it names a closed reason. */
|
|
29
|
+
export declare class RunRejectedError extends ShiitakeError {
|
|
30
|
+
private readonly reason;
|
|
31
|
+
constructor(reason: keyof typeof runRejectedDiagnostics, underlying: MuleError);
|
|
32
|
+
get diagnostic(): string;
|
|
33
|
+
}
|
|
20
34
|
/** Run one accepted command with Pi compaction explicitly composed around Mule. */
|
|
21
35
|
export declare function runAcceptedCommand(options: ShiitakeEffectOptions, prepared: PreparedSessionRun, executionContract: RunContract, extensionModules: readonly ModuleRegistration[], extensionFileTools: readonly FileTool[], compactionHandoff: CompactionHandoffV0 | undefined, promptSections?: () => Effect.Effect<readonly PromptSectionV0[], ShiitakeError>): Effect.Effect<{
|
|
22
36
|
run: import("@fungi.computer/mule/effect").MuleRunOutcome;
|
|
@@ -10,9 +10,6 @@ import { openCompactionSettings } from "./compaction/settings.js";
|
|
|
10
10
|
import { acquireOwnedModel } from "./model-acquisition.js";
|
|
11
11
|
import { returnedFailureReason, unavailableRunReason, } from "./run-failure-reason.js";
|
|
12
12
|
import { isCompactCommand, openMuleRecorder, readSessionGrants, recordLiveRetryEvent, recordSandboxFailure, takeSessionControls, } from "./woodstock/index.js";
|
|
13
|
-
function runtimeFailure(message) {
|
|
14
|
-
return new ShiitakeError("runtime", "rejected", message);
|
|
15
|
-
}
|
|
16
13
|
const toolSetupMessages = {
|
|
17
14
|
missing: "Instruction capabilities have no tool runtime",
|
|
18
15
|
runtime: "Tool runtime failed",
|
|
@@ -29,6 +26,28 @@ export class ToolSetupError extends ShiitakeError {
|
|
|
29
26
|
return toolSetupMessages[this.phase];
|
|
30
27
|
}
|
|
31
28
|
}
|
|
29
|
+
/** Closed reasons a rejected run names, never a foreign error's own text. */
|
|
30
|
+
const runRejectedDiagnostics = {
|
|
31
|
+
model: "model-failed",
|
|
32
|
+
tool: "tool-failed",
|
|
33
|
+
control: "control-failed",
|
|
34
|
+
listener: "listener-failed",
|
|
35
|
+
};
|
|
36
|
+
/** A run Mule ended with a non-model error; it names a closed reason. */
|
|
37
|
+
export class RunRejectedError extends ShiitakeError {
|
|
38
|
+
reason;
|
|
39
|
+
constructor(reason, underlying) {
|
|
40
|
+
super("runtime", "rejected", "Mule rejected the durable command run", undefined, underlying);
|
|
41
|
+
this.reason = reason;
|
|
42
|
+
}
|
|
43
|
+
get diagnostic() {
|
|
44
|
+
return runRejectedDiagnostics[this.reason];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Name a non-model Mule error by its closed reason. */
|
|
48
|
+
function rejectedRun(error) {
|
|
49
|
+
return new RunRejectedError(error.phase, error);
|
|
50
|
+
}
|
|
32
51
|
/**
|
|
33
52
|
* Map Mule's retry transitions onto Woodstock's retained `live_retry` facts.
|
|
34
53
|
* A started attempt ends the backoff countdown before streaming resumes.
|
|
@@ -229,22 +248,26 @@ export function runAcceptedCommand(options, prepared, executionContract, extensi
|
|
|
229
248
|
acquired.failureReason?.(exit.value.message) ??
|
|
230
249
|
returnedFailureReason(exit.value);
|
|
231
250
|
failureReason = (failedModelAttempts > 1 &&
|
|
232
|
-
exit.value.modelLimitReason === undefined
|
|
251
|
+
exit.value.modelLimitReason === undefined &&
|
|
252
|
+
acquired.rejection?.(exit.value.message)?.code !==
|
|
253
|
+
"usage-limit-reached"
|
|
233
254
|
? `The model failed ${String(failedModelAttempts)} times on this conversation: ${cause}`
|
|
234
255
|
: cause).slice(0, 200);
|
|
235
256
|
return recorder(failedEnd, failureReason);
|
|
236
257
|
}
|
|
237
258
|
const error = Cause.findErrorOption(exit.cause);
|
|
238
|
-
return recorder(failedEnd, Option.
|
|
239
|
-
?
|
|
240
|
-
: unavailableRunReason({
|
|
259
|
+
return recorder(failedEnd, Option.isNone(error)
|
|
260
|
+
? unavailableRunReason({
|
|
241
261
|
runId: failedEnd.run.value,
|
|
242
262
|
provider: acquired.model.provider,
|
|
243
263
|
model: acquired.model.id,
|
|
244
|
-
})
|
|
264
|
+
})
|
|
265
|
+
: error.value.phase === "model"
|
|
266
|
+
? `model:rejected: ${error.value.message}`.slice(0, 200)
|
|
267
|
+
: `runtime:rejected: ${rejectedRun(error.value).diagnostic}`);
|
|
245
268
|
}), Effect.mapError((error) => error.phase === "model"
|
|
246
269
|
? new ShiitakeError("model", "rejected", error.message, undefined, error)
|
|
247
|
-
:
|
|
270
|
+
: rejectedRun(error)));
|
|
248
271
|
},
|
|
249
272
|
});
|
|
250
273
|
return { run, failureReason };
|
|
@@ -9,7 +9,7 @@ import { agentTurnRecovery, executeAcceptedRun, } from "./accepted-run-executor.
|
|
|
9
9
|
import { openAdmissionDoor } from "./admission-door.js";
|
|
10
10
|
import { openCompactionSettings } from "./compaction/settings.js";
|
|
11
11
|
import { admitExtensionRosterV0, } from "./extension-roster.js";
|
|
12
|
-
import { coreConsumer, openExtensionRuntime, } from "./extension-runtime.js";
|
|
12
|
+
import { coreConsumer, reportConsumer, openExtensionRuntime, } from "./extension-runtime.js";
|
|
13
13
|
import { reconcileRuntime } from "./runtime-reconciliation.js";
|
|
14
14
|
import { advanceCursor, createSessionWithinTransaction, deliverSessionGrants, prepareExtensionStore, projectSessionRun, openStorageAdmission, readCursor, } from "./woodstock/index.js";
|
|
15
15
|
function runtimeFailure(message) {
|
|
@@ -141,6 +141,12 @@ export function openRuntimeCoordinator(options, coreDigest, admitted, mailRoom)
|
|
|
141
141
|
options.store.transactionSync(() => {
|
|
142
142
|
advanceCursor(options.store, coreConsumer, 0);
|
|
143
143
|
});
|
|
144
|
+
// A store that predates reports starts them at the core cursor
|
|
145
|
+
// instead of reporting its history.
|
|
146
|
+
if (readCursor(options.store, reportConsumer) === undefined)
|
|
147
|
+
options.store.transactionSync(() => {
|
|
148
|
+
advanceCursor(options.store, reportConsumer, readCursor(options.store, coreConsumer) ?? 0);
|
|
149
|
+
});
|
|
144
150
|
},
|
|
145
151
|
catch: () => new ShiitakeError("store", "unavailable", "Extension store is unavailable"),
|
|
146
152
|
});
|
|
@@ -165,6 +171,7 @@ export function openRuntimeCoordinator(options, coreDigest, admitted, mailRoom)
|
|
|
165
171
|
const cursors = yield* Effect.try({
|
|
166
172
|
try: () => [
|
|
167
173
|
readCursor(options.store, coreConsumer) ?? 0,
|
|
174
|
+
readCursor(options.store, reportConsumer) ?? 0,
|
|
168
175
|
...extensions.settledConsumers().map(({ cursor }) => cursor),
|
|
169
176
|
],
|
|
170
177
|
catch: () => runtimeFailure("Settled cursor is unavailable"),
|
|
@@ -5,21 +5,19 @@ import * as Schema from "effect/Schema";
|
|
|
5
5
|
import { agentTurnRecovery } from "./accepted-run-executor.js";
|
|
6
6
|
import { ShiitakeError } from "../errors.js";
|
|
7
7
|
import { ExtensionId } from "../extension-contract.js";
|
|
8
|
-
import { coreConsumer } from "./extension-runtime.js";
|
|
8
|
+
import { coreConsumer, reportConsumer, } from "./extension-runtime.js";
|
|
9
9
|
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
10
|
-
import { advanceCursor, readCursor, readExtensionAdmissionOrigin, readSettledRunFacts, readSettlementContent, readSettlementDiagnostic, readSettlementMeta, recordWorkSettlement, sessionLineageRefused, settleControls, } from "./woodstock/index.js";
|
|
10
|
+
import { advanceCursor, readCursor, readExtensionAdmissionOrigin, readSettledRunFacts, readSettlementContent, readSettlementDiagnostic, readSessionDelegator, readSettlementMeta, readWaitingOn, readWaitingOnSender, recordWorkSettlement, sessionLineageRefused, settleControls, } from "./woodstock/index.js";
|
|
11
11
|
const pageSize = 64;
|
|
12
12
|
function runtimeFailure(message) {
|
|
13
13
|
return new ShiitakeError("runtime", "rejected", message);
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/** BirdDog's settlement facts for one settled job, without its correspondents. */
|
|
16
|
+
function settlementFacts(store, job,
|
|
17
|
+
/** The open items this settlement names as waiting on its recipient. */
|
|
18
|
+
waitingOnYou) {
|
|
18
19
|
return Effect.gen(function* () {
|
|
19
|
-
const payload = yield*
|
|
20
|
-
try: () => decodeAgentJob(job),
|
|
21
|
-
catch: () => runtimeFailure("Agent work payload is invalid"),
|
|
22
|
-
});
|
|
20
|
+
const payload = yield* decodeJob(job);
|
|
23
21
|
const content = job.outcome === "cancelled"
|
|
24
22
|
? ""
|
|
25
23
|
: yield* readSettlementContent(store, payload.work);
|
|
@@ -27,14 +25,103 @@ function fanOutSettlement(store, mailRoom, job) {
|
|
|
27
25
|
const diagnostic = job.outcome === "completed" || job.outcome === "cancelled"
|
|
28
26
|
? undefined
|
|
29
27
|
: yield* readSettlementDiagnostic(store, payload.work);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
let settled = { job, content };
|
|
29
|
+
if (diagnostic !== undefined)
|
|
30
|
+
settled = { ...settled, diagnostic };
|
|
31
|
+
// A cancelled run names nothing; what it left open is the canceller's to see.
|
|
32
|
+
const waiting = job.outcome === "cancelled" ? [] : yield* waitingOnYou;
|
|
33
|
+
if (waiting.length > 0)
|
|
34
|
+
settled = { ...settled, waitingOnYou: waiting };
|
|
35
|
+
return meta === undefined ? settled : { ...settled, meta };
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
function decodeJob(job) {
|
|
39
|
+
return Effect.try({
|
|
40
|
+
try: () => decodeAgentJob(job),
|
|
41
|
+
catch: () => runtimeFailure("Agent work payload is invalid"),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function fanOut(mailRoom, settled) {
|
|
45
|
+
return mailRoom
|
|
46
|
+
.fanOut(settled)
|
|
47
|
+
.pipe(Effect.mapError(() => runtimeFailure("Agent reply fan-out is unavailable")));
|
|
48
|
+
}
|
|
49
|
+
/** Reply to the delegating mail that admitted this work, if it asked for one. */
|
|
50
|
+
function replyToMail(store, mailRoom, job) {
|
|
51
|
+
if (mailRoom === undefined)
|
|
52
|
+
return Effect.void;
|
|
53
|
+
return Effect.gen(function* () {
|
|
54
|
+
const payload = yield* decodeJob(job);
|
|
55
|
+
if ((payload.mail?.replyTo.length ?? 0) === 0)
|
|
56
|
+
return;
|
|
57
|
+
// The reply goes to the mail's sender: name what waits on that sender.
|
|
58
|
+
const waiting = Effect.try({
|
|
59
|
+
try: () => readWaitingOnSender(store, payload.work.sessionId, payload.work.commandFactId),
|
|
60
|
+
catch: () => runtimeFailure("Session attention is unavailable"),
|
|
61
|
+
});
|
|
62
|
+
yield* fanOut(mailRoom, yield* settlementFacts(store, job, waiting));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Report work no delegating mail answered to the Session's delegator of
|
|
67
|
+
* record. It runs after the extensions have seen the settlement, so a goal
|
|
68
|
+
* or loop that admitted the next turn keeps the Session busy and the stretch
|
|
69
|
+
* of turns reports once, when the Session finally rests. Another Agent's
|
|
70
|
+
* original mail is that Agent's correspondence and never reported here.
|
|
71
|
+
*/
|
|
72
|
+
function reportToDelegator(store, watchdog, mailRoom, job) {
|
|
73
|
+
if (mailRoom === undefined)
|
|
74
|
+
return Effect.void;
|
|
75
|
+
return Effect.gen(function* () {
|
|
76
|
+
const payload = yield* decodeJob(job);
|
|
77
|
+
const mail = payload.mail;
|
|
78
|
+
if (mail !== undefined && mail.replyTo.length > 0)
|
|
79
|
+
return;
|
|
80
|
+
const head = yield* watchdog
|
|
81
|
+
.readQueueHead(job.queue)
|
|
82
|
+
.pipe(Effect.mapError(() => runtimeFailure("Session queue head is unavailable")));
|
|
83
|
+
if (head.state !== "idle")
|
|
84
|
+
return;
|
|
85
|
+
const delegator = yield* Effect.try({
|
|
86
|
+
try: () => readSessionDelegator(store, payload.work.sessionId),
|
|
87
|
+
catch: () => runtimeFailure("Session delegator is unavailable"),
|
|
88
|
+
});
|
|
89
|
+
if (delegator === undefined)
|
|
90
|
+
return;
|
|
91
|
+
const from = mail?.reply === undefined ? mail?.from : undefined;
|
|
92
|
+
const anotherAgentsMail = from !== undefined &&
|
|
93
|
+
!(from.kind === "agent" &&
|
|
94
|
+
delegator.replyTo.some(({ agentId }) => agentId === from.agentId));
|
|
95
|
+
if (anotherAgentsMail)
|
|
96
|
+
return;
|
|
97
|
+
// The report goes to the delegator: name what waits on it.
|
|
98
|
+
const waiting = Effect.try({
|
|
99
|
+
try: () => readWaitingOn(store, payload.work.sessionId, delegator.replyTo),
|
|
100
|
+
catch: () => runtimeFailure("Session attention is unavailable"),
|
|
101
|
+
});
|
|
102
|
+
const settled = yield* settlementFacts(store, job, waiting);
|
|
103
|
+
yield* fanOut(mailRoom, { ...settled, delegator });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The `core-report` settled consumer: after the extensions' turn, report each
|
|
108
|
+
* Session's last settled job in the page, one record at a time.
|
|
109
|
+
*/
|
|
110
|
+
function reportSettled(store, watchdog, mailRoom, records) {
|
|
111
|
+
return Effect.forEach(records, (record, index) => Effect.gen(function* () {
|
|
112
|
+
const later = records
|
|
113
|
+
.slice(index + 1)
|
|
114
|
+
.some(({ job }) => job.queue === record.job.queue);
|
|
115
|
+
if (!later && !(yield* settledInRefusedSession(store, record.job)))
|
|
116
|
+
yield* reportToDelegator(store, watchdog, mailRoom, record.job);
|
|
117
|
+
yield* Effect.try({
|
|
118
|
+
try: () => {
|
|
119
|
+
advanceCursor(store, reportConsumer, record.sequence);
|
|
120
|
+
},
|
|
121
|
+
catch: () => runtimeFailure("Settled cursor is unavailable"),
|
|
122
|
+
});
|
|
123
|
+
}), { discard: true });
|
|
124
|
+
}
|
|
38
125
|
function repairWorkSettlement(store, job) {
|
|
39
126
|
const outcome = job.outcome;
|
|
40
127
|
if (outcome !== "interrupted" &&
|
|
@@ -148,7 +235,7 @@ function settleCore(store, watchdog, mailRoom, records) {
|
|
|
148
235
|
if (!(yield* settledInRefusedSession(store, record.job))) {
|
|
149
236
|
yield* repairWorkSettlement(store, record.job);
|
|
150
237
|
yield* settleTargetedControls(store, watchdog, record.job);
|
|
151
|
-
yield*
|
|
238
|
+
yield* replyToMail(store, mailRoom, record.job);
|
|
152
239
|
}
|
|
153
240
|
yield* Effect.try({
|
|
154
241
|
try: () => {
|
|
@@ -172,26 +259,34 @@ export function reconcileRuntime(input) {
|
|
|
172
259
|
try: () => readCursor(input.store, coreConsumer) ?? 0,
|
|
173
260
|
catch: () => runtimeFailure("Settled cursor is unavailable"),
|
|
174
261
|
});
|
|
262
|
+
const reportCursor = yield* Effect.try({
|
|
263
|
+
try: () => readCursor(input.store, reportConsumer) ?? coreCursor,
|
|
264
|
+
catch: () => runtimeFailure("Settled cursor is unavailable"),
|
|
265
|
+
});
|
|
175
266
|
const consumers = input.extensions.settledConsumers();
|
|
176
|
-
const after = Math.min(coreCursor, ...consumers.map(({ cursor }) => cursor));
|
|
267
|
+
const after = Math.min(coreCursor, reportCursor, ...consumers.map(({ cursor }) => cursor));
|
|
177
268
|
const page = yield* input.watchdog.readSettled(pageSize, after);
|
|
178
269
|
const core = yield* Effect.exit(settleCore(input.store, input.watchdog, input.mailRoom, page.filter(({ sequence }) => sequence > coreCursor)));
|
|
270
|
+
const processed = yield* Effect.try({
|
|
271
|
+
try: () => readCursor(input.store, coreConsumer) ?? 0,
|
|
272
|
+
catch: () => runtimeFailure("Settled cursor is unavailable"),
|
|
273
|
+
});
|
|
179
274
|
const runs = yield* Effect.try({
|
|
180
|
-
try: () =>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.filter(({ sequence }) => sequence <= processed)
|
|
184
|
-
.map((record) => settledRun(input.store, record));
|
|
185
|
-
},
|
|
275
|
+
try: () => page
|
|
276
|
+
.filter(({ sequence }) => sequence <= processed)
|
|
277
|
+
.map((record) => settledRun(input.store, record)),
|
|
186
278
|
catch: () => runtimeFailure("Settled run facts are unavailable"),
|
|
187
279
|
});
|
|
188
|
-
yield* input.extensions.deliverSettled(runs,
|
|
280
|
+
yield* input.extensions.deliverSettled(runs, processed);
|
|
281
|
+
const reports = yield* Effect.exit(reportSettled(input.store, input.watchdog, input.mailRoom, page.filter(({ sequence }) => sequence > reportCursor && sequence <= processed)));
|
|
189
282
|
const moreTimers = yield* input.extensions.fireDueTimers(Date.now());
|
|
190
283
|
if (page.length === pageSize || moreTimers)
|
|
191
284
|
yield* input.requestWake;
|
|
192
285
|
yield* drainMail(input.mailRoom);
|
|
193
286
|
if (Exit.isFailure(core))
|
|
194
287
|
return yield* Effect.failCause(core.cause);
|
|
288
|
+
if (Exit.isFailure(reports))
|
|
289
|
+
return yield* Effect.failCause(reports.cause);
|
|
195
290
|
}).pipe(Effect.mapError((failure) => failure instanceof ShiitakeError
|
|
196
291
|
? failure
|
|
197
292
|
: runtimeFailure("Runtime reconciliation failed")));
|
|
@@ -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, } from "./session-store.js";
|
|
7
9
|
const maximumPagePayloadBytes = 2_097_152;
|
|
8
10
|
const replayPageLimit = 200;
|
|
9
11
|
const maximumRowsPerPage = lineageRowLimit;
|
|
@@ -503,6 +505,10 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
503
505
|
const admission = readSessionCommandAdmission(owner, sessionId, commandId);
|
|
504
506
|
if (admission === undefined)
|
|
505
507
|
throw unavailable("Accepted Session command is unavailable");
|
|
508
|
+
// A live work has no settlement to project. The owner's terminal index
|
|
509
|
+
// answers this without decoding the work again on every polling tick.
|
|
510
|
+
if (!hasSessionWorkTerminal(owner, sessionId, admission.entry.workId))
|
|
511
|
+
return undefined;
|
|
506
512
|
const rows = readSessionEntriesForWork(owner, sessionId, admission.entry.workId);
|
|
507
513
|
const work = workFacts(owner, sessionId, rows);
|
|
508
514
|
const terminal = rows
|
|
@@ -521,6 +527,7 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
521
527
|
const sender = messageAuthor(admission.entry.meta);
|
|
522
528
|
return {
|
|
523
529
|
acceptedSender: sender?.kind === "agent" ? sender.id : null,
|
|
530
|
+
delegator: readSessionDelegator(owner, sessionId)?.replyTo[0]?.agentId ?? null,
|
|
524
531
|
terminal: meta === undefined ? terminal : { ...terminal, meta },
|
|
525
532
|
content: terminal.outcome === "cancelled"
|
|
526
533
|
? ""
|
|
@@ -534,5 +541,6 @@ export function projectCommandSettlement(owner, sessionId, commandId) {
|
|
|
534
541
|
usage,
|
|
535
542
|
})),
|
|
536
543
|
toolCalls: new Set(rows.flatMap(({ entry }) => entry.kind === "tool_intent" ? [entry.call.id] : [])).size,
|
|
544
|
+
waitingOnYou: readWaitingOnSender(owner, sessionId, admission.entry.workId),
|
|
537
545
|
};
|
|
538
546
|
}
|
|
@@ -34,4 +34,5 @@ export declare function createFleetDelegationStore(storage: WatchdogSqliteOwner)
|
|
|
34
34
|
reserve(requestId: string, input: string, mail: string): void;
|
|
35
35
|
recordReceipt(requestId: string, receipt: string, expectedMail: string, acceptedMail: string): void;
|
|
36
36
|
recordHeld(requestId: string): void;
|
|
37
|
+
recordUndeliverable(requestId: string): boolean;
|
|
37
38
|
};
|