@fungi.computer/shiitake 0.0.0-stage → 0.0.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.
Files changed (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +454 -2
  3. package/dist/agar-extension.d.ts +9 -0
  4. package/dist/agar-extension.js +38 -0
  5. package/dist/agent-control.d.ts +72 -0
  6. package/dist/agent-control.js +431 -0
  7. package/dist/client-entry.d.ts +211 -0
  8. package/dist/client-entry.js +101 -0
  9. package/dist/client-wire.d.ts +199 -0
  10. package/dist/client-wire.js +271 -0
  11. package/dist/client.d.ts +439 -0
  12. package/dist/client.js +4 -0
  13. package/dist/cloudflare.d.ts +220 -0
  14. package/dist/cloudflare.js +531 -0
  15. package/dist/effect.d.ts +42 -0
  16. package/dist/effect.js +20 -0
  17. package/dist/errors.d.ts +50 -0
  18. package/dist/errors.js +81 -0
  19. package/dist/extension-client.d.ts +38 -0
  20. package/dist/extension-client.js +56 -0
  21. package/dist/extension-contract.d.ts +73 -0
  22. package/dist/extension-contract.js +11 -0
  23. package/dist/extensions.d.ts +589 -0
  24. package/dist/extensions.js +263 -0
  25. package/dist/fleet.d.ts +297 -0
  26. package/dist/fleet.js +515 -0
  27. package/dist/hosted.d.ts +133 -0
  28. package/dist/hosted.js +265 -0
  29. package/dist/http-routes.d.ts +772 -0
  30. package/dist/http-routes.js +242 -0
  31. package/dist/http.d.ts +1 -0
  32. package/dist/http.js +1 -0
  33. package/dist/index.d.ts +35 -0
  34. package/dist/index.js +13 -0
  35. package/dist/internal/accepted-run-executor.d.ts +23 -0
  36. package/dist/internal/accepted-run-executor.js +129 -0
  37. package/dist/internal/admission-door.d.ts +75 -0
  38. package/dist/internal/admission-door.js +105 -0
  39. package/dist/internal/agent-close.d.ts +4 -0
  40. package/dist/internal/agent-close.js +42 -0
  41. package/dist/internal/browser-sessions.d.ts +31 -0
  42. package/dist/internal/browser-sessions.js +173 -0
  43. package/dist/internal/client-boundary.d.ts +262 -0
  44. package/dist/internal/client-boundary.js +202 -0
  45. package/dist/internal/compaction/compaction.d.ts +54 -0
  46. package/dist/internal/compaction/compaction.js +292 -0
  47. package/dist/internal/compaction/session-compaction.d.ts +69 -0
  48. package/dist/internal/compaction/session-compaction.js +330 -0
  49. package/dist/internal/compaction/settings.d.ts +11 -0
  50. package/dist/internal/compaction/settings.js +40 -0
  51. package/dist/internal/compaction/utils.d.ts +45 -0
  52. package/dist/internal/compaction/utils.js +319 -0
  53. package/dist/internal/contracts.d.ts +235 -0
  54. package/dist/internal/contracts.js +1 -0
  55. package/dist/internal/coordinator.d.ts +18 -0
  56. package/dist/internal/coordinator.js +362 -0
  57. package/dist/internal/document-codec.d.ts +11 -0
  58. package/dist/internal/document-codec.js +39 -0
  59. package/dist/internal/effect-client.d.ts +13 -0
  60. package/dist/internal/effect-client.js +102 -0
  61. package/dist/internal/effect-contracts.d.ts +236 -0
  62. package/dist/internal/effect-contracts.js +2 -0
  63. package/dist/internal/extension-command-registry.d.ts +11 -0
  64. package/dist/internal/extension-command-registry.js +29 -0
  65. package/dist/internal/extension-command-tools.d.ts +17 -0
  66. package/dist/internal/extension-command-tools.js +50 -0
  67. package/dist/internal/extension-context.d.ts +25 -0
  68. package/dist/internal/extension-context.js +35 -0
  69. package/dist/internal/extension-definitions.d.ts +5 -0
  70. package/dist/internal/extension-definitions.js +6 -0
  71. package/dist/internal/extension-hook.d.ts +25 -0
  72. package/dist/internal/extension-hook.js +46 -0
  73. package/dist/internal/extension-roster.d.ts +23 -0
  74. package/dist/internal/extension-roster.js +228 -0
  75. package/dist/internal/extension-runtime.d.ts +63 -0
  76. package/dist/internal/extension-runtime.js +1004 -0
  77. package/dist/internal/failure.d.ts +2 -0
  78. package/dist/internal/failure.js +9 -0
  79. package/dist/internal/host-meta.d.ts +41 -0
  80. package/dist/internal/host-meta.js +39 -0
  81. package/dist/internal/json-value.d.ts +4 -0
  82. package/dist/internal/json-value.js +3 -0
  83. package/dist/internal/model-acquisition.d.ts +21 -0
  84. package/dist/internal/model-acquisition.js +98 -0
  85. package/dist/internal/model-preference.d.ts +21 -0
  86. package/dist/internal/model-preference.js +20 -0
  87. package/dist/internal/observation-kernel.d.ts +28 -0
  88. package/dist/internal/observation-kernel.js +169 -0
  89. package/dist/internal/promise-facade.d.ts +18 -0
  90. package/dist/internal/promise-facade.js +172 -0
  91. package/dist/internal/run-scope.d.ts +27 -0
  92. package/dist/internal/run-scope.js +214 -0
  93. package/dist/internal/runtime-coordinator.d.ts +34 -0
  94. package/dist/internal/runtime-coordinator.js +233 -0
  95. package/dist/internal/runtime-core.d.ts +19 -0
  96. package/dist/internal/runtime-core.js +39 -0
  97. package/dist/internal/runtime-reconciliation.d.ts +18 -0
  98. package/dist/internal/runtime-reconciliation.js +162 -0
  99. package/dist/internal/session-context.d.ts +11 -0
  100. package/dist/internal/session-context.js +67 -0
  101. package/dist/internal/session-status.d.ts +10 -0
  102. package/dist/internal/session-status.js +52 -0
  103. package/dist/internal/woodstock/agent-reply.d.ts +25 -0
  104. package/dist/internal/woodstock/agent-reply.js +67 -0
  105. package/dist/internal/woodstock/client-projection.d.ts +29 -0
  106. package/dist/internal/woodstock/client-projection.js +512 -0
  107. package/dist/internal/woodstock/compaction-activity.d.ts +17 -0
  108. package/dist/internal/woodstock/compaction-activity.js +32 -0
  109. package/dist/internal/woodstock/cursor.d.ts +9 -0
  110. package/dist/internal/woodstock/cursor.js +44 -0
  111. package/dist/internal/woodstock/extension-store.d.ts +72 -0
  112. package/dist/internal/woodstock/extension-store.js +262 -0
  113. package/dist/internal/woodstock/fleet-store.d.ts +32 -0
  114. package/dist/internal/woodstock/fleet-store.js +93 -0
  115. package/dist/internal/woodstock/identities.d.ts +6 -0
  116. package/dist/internal/woodstock/identities.js +11 -0
  117. package/dist/internal/woodstock/index.d.ts +170 -0
  118. package/dist/internal/woodstock/index.js +546 -0
  119. package/dist/internal/woodstock/progress.d.ts +32 -0
  120. package/dist/internal/woodstock/progress.js +89 -0
  121. package/dist/internal/woodstock/run-recovery.d.ts +8 -0
  122. package/dist/internal/woodstock/run-recovery.js +95 -0
  123. package/dist/internal/woodstock/session-attention.d.ts +16 -0
  124. package/dist/internal/woodstock/session-attention.js +88 -0
  125. package/dist/internal/woodstock/session-documents.d.ts +18 -0
  126. package/dist/internal/woodstock/session-documents.js +51 -0
  127. package/dist/internal/woodstock/session-entry.d.ts +478 -0
  128. package/dist/internal/woodstock/session-entry.js +484 -0
  129. package/dist/internal/woodstock/session-grants.d.ts +24 -0
  130. package/dist/internal/woodstock/session-grants.js +84 -0
  131. package/dist/internal/woodstock/session-inbox.d.ts +56 -0
  132. package/dist/internal/woodstock/session-inbox.js +307 -0
  133. package/dist/internal/woodstock/session-store.d.ts +432 -0
  134. package/dist/internal/woodstock/session-store.js +2006 -0
  135. package/dist/internal/woodstock/storage-admission.d.ts +14 -0
  136. package/dist/internal/woodstock/storage-admission.js +44 -0
  137. package/dist/model-preference.d.ts +20 -0
  138. package/dist/model-preference.js +30 -0
  139. package/dist/node.d.ts +64 -0
  140. package/dist/node.js +256 -0
  141. package/dist/remote-client.d.ts +11 -0
  142. package/dist/remote-client.js +298 -0
  143. package/dist/session-attention.d.ts +35 -0
  144. package/dist/session-attention.js +22 -0
  145. package/dist/session-documents.d.ts +28 -0
  146. package/dist/session-documents.js +1 -0
  147. package/dist/testing.d.ts +54 -0
  148. package/dist/testing.js +215 -0
  149. package/docs/api/README.md +26 -0
  150. package/docs/api/agar-extension.md +40 -0
  151. package/docs/api/agent-control.md +151 -0
  152. package/docs/api/client-entry.md +344 -0
  153. package/docs/api/client.md +1112 -0
  154. package/docs/api/cloudflare.md +500 -0
  155. package/docs/api/effect.md +904 -0
  156. package/docs/api/extensions.md +2003 -0
  157. package/docs/api/fleet.md +706 -0
  158. package/docs/api/hosted.md +382 -0
  159. package/docs/api/http.md +111 -0
  160. package/docs/api/index.md +1116 -0
  161. package/docs/api/model-preference.md +151 -0
  162. package/docs/api/node.md +142 -0
  163. package/docs/api/testing.md +250 -0
  164. package/package.json +130 -3
@@ -0,0 +1,162 @@
1
+ import { agentJobFromInput, decodeAgentJob, } from "@fungi.computer/bird-dog/effect";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Exit from "effect/Exit";
4
+ import * as Schema from "effect/Schema";
5
+ import { agentTurnRecovery } from "./accepted-run-executor.js";
6
+ import { ShiitakeError } from "../errors.js";
7
+ import { ExtensionId } from "../extension-contract.js";
8
+ import { coreConsumer } from "./extension-runtime.js";
9
+ import { publishOwnerObservation } from "./observation-kernel.js";
10
+ import { advanceCursor, readCursor, readSettledRunFacts, readSettlementContent, readSettlementDiagnostic, readSettlementMeta, recordWorkSettlement, settleControls, } from "./woodstock/index.js";
11
+ const pageSize = 64;
12
+ function runtimeFailure(message) {
13
+ return new ShiitakeError("runtime", "rejected", message);
14
+ }
15
+ function fanOutSettlement(store, mailRoom, job) {
16
+ if (mailRoom === undefined)
17
+ return Effect.void;
18
+ return Effect.gen(function* () {
19
+ const payload = yield* Effect.try({
20
+ try: () => decodeAgentJob(job),
21
+ catch: () => runtimeFailure("Agent work payload is invalid"),
22
+ });
23
+ const content = job.outcome === "cancelled"
24
+ ? ""
25
+ : yield* readSettlementContent(store, payload.work);
26
+ const meta = yield* readSettlementMeta(store, payload.work, job.outcome);
27
+ const diagnostic = job.outcome === "completed" || job.outcome === "cancelled"
28
+ ? undefined
29
+ : yield* readSettlementDiagnostic(store, payload.work);
30
+ const settled = diagnostic === undefined
31
+ ? { job, content }
32
+ : { job, content, diagnostic };
33
+ yield* mailRoom
34
+ .fanOut(meta === undefined ? settled : { ...settled, meta })
35
+ .pipe(Effect.mapError(() => runtimeFailure("Agent reply fan-out is unavailable")));
36
+ });
37
+ }
38
+ function repairWorkSettlement(store, job) {
39
+ const outcome = job.outcome;
40
+ if (outcome !== "interrupted" &&
41
+ outcome !== "cancelled" &&
42
+ outcome !== "failed")
43
+ return Effect.void;
44
+ return Effect.gen(function* () {
45
+ const payload = yield* Effect.try({
46
+ try: () => decodeAgentJob(job),
47
+ catch: () => runtimeFailure("Agent work payload is invalid"),
48
+ });
49
+ yield* recordWorkSettlement(store, {
50
+ sessionId: payload.work.sessionId,
51
+ commandFactId: payload.work.commandFactId,
52
+ workId: job.jobId,
53
+ outcome,
54
+ diagnostic: job.failureReason,
55
+ });
56
+ });
57
+ }
58
+ function settleTargetedControls(store, watchdog, job) {
59
+ return Effect.gen(function* () {
60
+ const payload = yield* Effect.try({
61
+ try: () => decodeAgentJob(job),
62
+ catch: () => runtimeFailure("Agent work payload is invalid"),
63
+ });
64
+ const outcome = job.outcome === "outcome_unknown" ? "interrupted" : job.outcome;
65
+ yield* settleControls(store, {
66
+ sessionId: payload.work.sessionId,
67
+ parentWorkId: job.jobId,
68
+ outcome,
69
+ enqueue: (work) => {
70
+ watchdog.transactional.enqueueAcceptedJob(agentJobFromInput({ work: { ...work, recovery: agentTurnRecovery } }));
71
+ },
72
+ });
73
+ });
74
+ }
75
+ function drainMail(mailRoom) {
76
+ if (mailRoom === undefined)
77
+ return Effect.void;
78
+ return mailRoom
79
+ .drainOutbox()
80
+ .pipe(Effect.mapError(() => runtimeFailure("Agent reply delivery is unavailable")));
81
+ }
82
+ /** Join one core-processed settled record with its exact Woodstock facts. */
83
+ function settledRun(store, record) {
84
+ const payload = decodeAgentJob(record.job);
85
+ const facts = readSettledRunFacts(store, payload.work.sessionId, payload.work.commandFactId, record.job.jobId);
86
+ return Object.freeze({
87
+ sequence: record.sequence,
88
+ sessionId: payload.work.sessionId,
89
+ workId: record.job.jobId,
90
+ outcome: record.job.outcome,
91
+ terminal: facts.terminal,
92
+ toolCalls: facts.toolCalls,
93
+ origin: facts.origin === null
94
+ ? null
95
+ : Object.freeze({
96
+ extensionId: Schema.decodeUnknownSync(ExtensionId)(facts.origin.extensionId),
97
+ correlation: facts.origin.correlation,
98
+ }),
99
+ });
100
+ }
101
+ /**
102
+ * The `core` settled consumer: publish the idle owner observation, repair
103
+ * terminals, settle targeted controls, fan out mail, then advance the durable core cursor, one record
104
+ * at a time. Every step is idempotent, so a crash before the cursor advance
105
+ * repeats work without duplicating a reply.
106
+ */
107
+ function settleCore(store, watchdog, mailRoom, records) {
108
+ return Effect.forEach(records, (record) => Effect.gen(function* () {
109
+ // Watchdog has durably settled the work: its queue head is idle now.
110
+ // Announce that before any Session repair, so a repair failure cannot
111
+ // leave live watchers reporting the finished run as active. A replay
112
+ // after a crash repeats the same announcement.
113
+ publishOwnerObservation(store, record.job.queue);
114
+ yield* repairWorkSettlement(store, record.job);
115
+ yield* settleTargetedControls(store, watchdog, record.job);
116
+ yield* fanOutSettlement(store, mailRoom, record.job);
117
+ yield* Effect.try({
118
+ try: () => {
119
+ advanceCursor(store, coreConsumer, record.sequence);
120
+ },
121
+ catch: () => runtimeFailure("Settled cursor is unavailable"),
122
+ });
123
+ }), { discard: true });
124
+ }
125
+ /**
126
+ * One reconcile tick. Each step is its own Exit boundary: a failed extension
127
+ * stops only itself, and mail always drains. Only a core failure fails the
128
+ * tick, so the host backs off only for core.
129
+ */
130
+ export function reconcileRuntime(input) {
131
+ return Effect.gen(function* () {
132
+ const result = yield* input.watchdog.tick();
133
+ if (result.status === "busy")
134
+ return yield* Effect.fail(runtimeFailure("Runtime reconciliation is already active"));
135
+ const coreCursor = yield* Effect.try({
136
+ try: () => readCursor(input.store, coreConsumer) ?? 0,
137
+ catch: () => runtimeFailure("Settled cursor is unavailable"),
138
+ });
139
+ const consumers = input.extensions.settledConsumers();
140
+ const after = Math.min(coreCursor, ...consumers.map(({ cursor }) => cursor));
141
+ const page = yield* input.watchdog.readSettled(pageSize, after);
142
+ const core = yield* Effect.exit(settleCore(input.store, input.watchdog, input.mailRoom, page.filter(({ sequence }) => sequence > coreCursor)));
143
+ const runs = yield* Effect.try({
144
+ try: () => {
145
+ const processed = readCursor(input.store, coreConsumer) ?? 0;
146
+ return page
147
+ .filter(({ sequence }) => sequence <= processed)
148
+ .map((record) => settledRun(input.store, record));
149
+ },
150
+ catch: () => runtimeFailure("Settled run facts are unavailable"),
151
+ });
152
+ yield* input.extensions.deliverSettled(runs, readCursor(input.store, coreConsumer) ?? 0);
153
+ const moreTimers = yield* input.extensions.fireDueTimers(Date.now());
154
+ if (page.length === pageSize || moreTimers)
155
+ yield* input.requestWake;
156
+ yield* drainMail(input.mailRoom);
157
+ if (Exit.isFailure(core))
158
+ return yield* Effect.failCause(core.cause);
159
+ }).pipe(Effect.mapError((failure) => failure instanceof ShiitakeError
160
+ ? failure
161
+ : runtimeFailure("Runtime reconciliation failed")));
162
+ }
@@ -0,0 +1,11 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type { SessionContext } from "../client.js";
3
+ import type { ShiitakeEffectOptions } from "./effect-contracts.js";
4
+ import type { RuntimeCoordinator } from "./runtime-coordinator.js";
5
+ /**
6
+ * The Session's context use as compaction measures it, against the window of
7
+ * the model a run would select now. Undefined while the Session has no
8
+ * context or its context cannot be read; a window the host cannot describe is
9
+ * null, never guessed.
10
+ */
11
+ export declare function readSessionContext(options: ShiitakeEffectOptions, runtime: RuntimeCoordinator, sessionId: string): Effect.Effect<SessionContext | undefined>;
@@ -0,0 +1,67 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { estimateThresholdContextTokens } from "./compaction/session-compaction.js";
3
+ import { reportCatchFailure } from "./failure.js";
4
+ import { buildSessionContext, readSessionContextPath, readSessionModelPreference, } from "./woodstock/index.js";
5
+ /**
6
+ * The context size compaction measures against the model's window, for the
7
+ * Session's settled context: the next run starts from it. Null before the
8
+ * Session has any context.
9
+ */
10
+ function settledContextTokens(store, sessionId) {
11
+ const path = readSessionContextPath(store, { sessionId });
12
+ const messages = buildSessionContext(path);
13
+ return messages.length === 0
14
+ ? null
15
+ : estimateThresholdContextTokens(path, messages);
16
+ }
17
+ /**
18
+ * Each runtime keeps the last described window per Session. A saved model
19
+ * preference changes the key, so `/model` is described again at once.
20
+ */
21
+ const describedWindows = new WeakMap();
22
+ function windowsFor(runtime) {
23
+ const existing = describedWindows.get(runtime);
24
+ if (existing !== undefined)
25
+ return existing;
26
+ const created = new Map();
27
+ describedWindows.set(runtime, created);
28
+ return created;
29
+ }
30
+ /**
31
+ * The Session's context use as compaction measures it, against the window of
32
+ * the model a run would select now. Undefined while the Session has no
33
+ * context or its context cannot be read; a window the host cannot describe is
34
+ * null, never guessed.
35
+ */
36
+ export function readSessionContext(options, runtime, sessionId) {
37
+ return Effect.gen(function* () {
38
+ // A context too large to read safely has no footer figure.
39
+ const read = yield* Effect.result(Effect.try({
40
+ try: () => ({
41
+ tokens: settledContextTokens(options.store, sessionId),
42
+ preference: readSessionModelPreference(options.store, sessionId),
43
+ }),
44
+ catch: (error) => error,
45
+ }));
46
+ if (read._tag === "Failure" || read.success.tokens === null)
47
+ return undefined;
48
+ const { tokens, preference } = read.success;
49
+ const windows = windowsFor(runtime);
50
+ const key = JSON.stringify(preference);
51
+ const cached = windows.get(sessionId);
52
+ if (cached?.preference === key)
53
+ return Object.freeze({ tokens, window: cached.window });
54
+ const describe = options.model.describe;
55
+ if (describe === undefined)
56
+ return Object.freeze({ tokens, window: null });
57
+ const described = yield* describe({ sessionId, preference }).pipe(Effect.map(({ contextWindow }) => contextWindow), Effect.catch((error) => Effect.sync(() => {
58
+ reportCatchFailure("session-context.describe", error);
59
+ return undefined;
60
+ })));
61
+ // A failed description is retried on the next read, never cached.
62
+ if (described === undefined)
63
+ return Object.freeze({ tokens, window: null });
64
+ windows.set(sessionId, { preference: key, window: described });
65
+ return Object.freeze({ tokens, window: described });
66
+ });
67
+ }
@@ -0,0 +1,10 @@
1
+ import type { WatchdogQueueHead } from "@fungi.computer/watchdog/effect";
2
+ import type { AgentRunningWork, SessionActivity, SessionProgress } from "../client.js";
3
+ import type { SqliteOwner } from "./contracts.js";
4
+ /** Public activity facts plus the process-local progress read with them. */
5
+ export type ObservedActivity = Readonly<{
6
+ facts: SessionActivity;
7
+ progress: SessionProgress | null;
8
+ }>;
9
+ /** Join Watchdog authority with Woodstock facts and process-local progress. */
10
+ export declare function readSessionActivity(owner: SqliteOwner, sessionId: string, head: WatchdogQueueHead, runningWork: AgentRunningWork | null): ObservedActivity;
@@ -0,0 +1,52 @@
1
+ import { readProcessProgressForWork, readProcessCompaction, readLatestLiveRetryForWork, readActiveToolForWork, readLastModelChoiceSequence, } from "./woodstock/index.js";
2
+ /** Join Watchdog authority with Woodstock facts and process-local progress. */
3
+ export function readSessionActivity(owner, sessionId, head, runningWork) {
4
+ const lastModelChoiceSequence = readLastModelChoiceSequence(owner, sessionId);
5
+ if (head.state === "idle")
6
+ return {
7
+ facts: {
8
+ queue: { state: "idle" },
9
+ cancellationRequested: false,
10
+ retry: null,
11
+ activeTool: null,
12
+ lastModelChoiceSequence,
13
+ compaction: null,
14
+ },
15
+ progress: null,
16
+ };
17
+ if (head.state === "queued")
18
+ // The queue job id is the admitted command id: the one identity a person
19
+ // may cancel while this Session's work waits for the Agent.
20
+ return {
21
+ facts: {
22
+ queue: { state: "queued", commandId: head.jobId, runningWork },
23
+ cancellationRequested: head.cancellationRequested,
24
+ retry: null,
25
+ activeTool: null,
26
+ lastModelChoiceSequence,
27
+ compaction: null,
28
+ },
29
+ progress: null,
30
+ };
31
+ const latestRetry = readLatestLiveRetryForWork(owner, sessionId, head.jobId);
32
+ const retry = latestRetry?.event.type === "auto_retry_start"
33
+ ? {
34
+ attempt: latestRetry.event.attempt,
35
+ maxAttempts: latestRetry.event.maxAttempts,
36
+ delayMs: latestRetry.event.delayMs,
37
+ errorMessage: latestRetry.event.errorMessage,
38
+ scheduledAt: latestRetry.timestamp,
39
+ }
40
+ : null;
41
+ return {
42
+ facts: {
43
+ queue: { state: "running", commandId: head.jobId },
44
+ cancellationRequested: head.cancellationRequested,
45
+ retry,
46
+ activeTool: readActiveToolForWork(owner, sessionId, head.jobId) ?? null,
47
+ lastModelChoiceSequence,
48
+ compaction: readProcessCompaction(owner, sessionId),
49
+ },
50
+ progress: readProcessProgressForWork(owner, sessionId, head.jobId),
51
+ };
52
+ }
@@ -0,0 +1,25 @@
1
+ import type { UserMessage } from "@fungi.computer/mule/effect";
2
+ import * as Schema from "effect/Schema";
3
+ /**
4
+ * A delegated Agent's reply after the admitting host authenticated it against
5
+ * the mail it sent: the replying Agent and Session plus BirdDog's settlement
6
+ * facts. Only `acceptMail` stores it, and only the host passes it.
7
+ */
8
+ export declare const VerifiedAgentReplySchema: Schema.Struct<{
9
+ readonly commandId: Schema.String;
10
+ readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
11
+ readonly diagnostic: Schema.optionalKey<Schema.String>;
12
+ readonly agentId: Schema.String;
13
+ readonly sessionId: Schema.String;
14
+ }>;
15
+ /** A delegated Agent's reply after the admitting host authenticated it. */
16
+ export type VerifiedAgentReply = typeof VerifiedAgentReplySchema.Type;
17
+ /** The tag that marks a host-verified Agent reply in model context. */
18
+ export declare const AGENT_REPLY_TAG = "fungi_agent_reply";
19
+ /**
20
+ * What a model sees of one stored user-role line. Every typed, mailed or
21
+ * operator text is neutralized, so only Shiitake can open the verified-reply
22
+ * tag; a line the host admitted with a verified reply is wrapped in it. The
23
+ * provider role stays `user`.
24
+ */
25
+ export declare function userContextMessage(message: UserMessage, reply?: VerifiedAgentReply): UserMessage;
@@ -0,0 +1,67 @@
1
+ import { BirdDogReplySchema } from "@fungi.computer/bird-dog/effect";
2
+ import * as Schema from "effect/Schema";
3
+ const Identifier = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
4
+ /**
5
+ * A delegated Agent's reply after the admitting host authenticated it against
6
+ * the mail it sent: the replying Agent and Session plus BirdDog's settlement
7
+ * facts. Only `acceptMail` stores it, and only the host passes it.
8
+ */
9
+ export const VerifiedAgentReplySchema = Schema.Struct({
10
+ agentId: Identifier,
11
+ sessionId: Identifier,
12
+ ...BirdDogReplySchema.fields,
13
+ });
14
+ /** The tag that marks a host-verified Agent reply in model context. */
15
+ export const AGENT_REPLY_TAG = "fungi_agent_reply";
16
+ // `<` before the tag name, allowing the spacing and case a model would still read as the tag.
17
+ const tagOpening = new RegExp(`<(?=\\s*/?\\s*${AGENT_REPLY_TAG})`, "giu");
18
+ /** Make untrusted text unable to open or close the verified-reply tag. */
19
+ function neutralize(text) {
20
+ return text.replace(tagOpening, "&lt;");
21
+ }
22
+ function attribute(value) {
23
+ return value
24
+ .replaceAll("&", "&amp;")
25
+ .replaceAll('"', "&quot;")
26
+ .replaceAll("<", "&lt;")
27
+ .replaceAll(">", "&gt;");
28
+ }
29
+ function opening(reply) {
30
+ const attributes = [
31
+ ["agent_id", reply.agentId],
32
+ ["session_id", reply.sessionId],
33
+ ["command_id", reply.commandId],
34
+ ["outcome", reply.outcome],
35
+ ];
36
+ if (reply.diagnostic !== undefined)
37
+ attributes.push(["diagnostic", reply.diagnostic]);
38
+ const rendered = attributes
39
+ .map(([name, value]) => `${name}="${attribute(value)}"`)
40
+ .join(" ");
41
+ return `<${AGENT_REPLY_TAG} ${rendered}>`;
42
+ }
43
+ /**
44
+ * What a model sees of one stored user-role line. Every typed, mailed or
45
+ * operator text is neutralized, so only Shiitake can open the verified-reply
46
+ * tag; a line the host admitted with a verified reply is wrapped in it. The
47
+ * provider role stays `user`.
48
+ */
49
+ export function userContextMessage(message, reply) {
50
+ const content = Array.isArray(message.content)
51
+ ? message.content.map((part) => part.type === "text" ? { ...part, text: neutralize(part.text) } : part)
52
+ : neutralize(message.content);
53
+ if (reply === undefined)
54
+ return { ...message, content };
55
+ const open = opening(reply);
56
+ const close = `</${AGENT_REPLY_TAG}>`;
57
+ return {
58
+ ...message,
59
+ content: Array.isArray(content)
60
+ ? [
61
+ { type: "text", text: open },
62
+ ...content,
63
+ { type: "text", text: close },
64
+ ]
65
+ : `${open}\n${content}\n${close}`,
66
+ };
67
+ }
@@ -0,0 +1,29 @@
1
+ import type { SessionFrame, SessionExtensionSlots, SessionSnapshot } from "../../client.js";
2
+ import type { ClientReadOptions } from "../client-boundary.js";
3
+ import type { CommandSettlement, SqliteOwner } from "../contracts.js";
4
+ import type { ObservedActivity } from "../session-status.js";
5
+ import { type StoredSessionRow } from "./session-store.js";
6
+ /**
7
+ * Newest failure message each work recorded: a work-terminal diagnostic, an
8
+ * assistant error or a final retry error. Raw text; clients and the reply
9
+ * fan-out decide how to present and bound it.
10
+ */
11
+ export declare function diagnosticsByWorkId(path: readonly StoredSessionRow[]): ReadonlyMap<string, string>;
12
+ /**
13
+ * Read one bounded public baseline from the selected Session lineage. Only
14
+ * the page's rows are decoded; `before` pages back through the whole saved
15
+ * history, including what compaction summarized away.
16
+ */
17
+ export declare function readClientSnapshot(owner: SqliteOwner, sessionId: string, options: ClientReadOptions, activity: ObservedActivity, projectExtensions?: () => SessionExtensionSlots): SessionSnapshot;
18
+ /** One bounded durable replay decision for the process-local observation stream. */
19
+ export type ClientReplayPage = Readonly<{
20
+ kind: "page";
21
+ frames: readonly SessionFrame[];
22
+ hasMore: boolean;
23
+ }> | Readonly<{
24
+ kind: "reset";
25
+ }>;
26
+ /** Read strict-after history plus the current waiting-admission view. */
27
+ export declare function readClientReplayPage(owner: SqliteOwner, sessionId: string, after: string | undefined, delivery: "replay" | "live", activity: ObservedActivity, projectExtensions?: () => SessionExtensionSlots, includeSandboxFailures?: boolean): ClientReplayPage;
28
+ /** Project one exact command's durable facts without selecting a transcript lineage. */
29
+ export declare function projectCommandSettlement(owner: SqliteOwner, sessionId: string, commandId: string): CommandSettlement | undefined;