@fungi.computer/shiitake 0.0.0-stage → 0.1.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.
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 +77 -0
  6. package/dist/agent-control.js +434 -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 +313 -0
  26. package/dist/fleet.js +540 -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 +291 -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 +36 -0
  114. package/dist/internal/woodstock/fleet-store.js +100 -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 +884 -0
  156. package/docs/api/extensions.md +2003 -0
  157. package/docs/api/fleet.md +734 -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,95 @@
1
+ /** Derive continuation from facts, without introducing a second checkpoint store. */
2
+ export function recoverAssistantTurn(rows) {
3
+ const assistant = [...rows]
4
+ .reverse()
5
+ .find(({ entry }) => entry.kind === "message" && entry.value.role === "assistant");
6
+ if (assistant?.entry.kind !== "message" ||
7
+ assistant.entry.value.role !== "assistant")
8
+ return undefined;
9
+ const after = rows.filter(({ sequence }) => sequence > assistant.sequence);
10
+ // A later request was already dispatched. Resend that request from the
11
+ // completed transcript, not the previous assistant turn.
12
+ if (after.some(({ entry }) => entry.kind === "turn_started" ||
13
+ (entry.kind === "compaction" && entry.resumesRun === true) ||
14
+ (entry.kind === "live_retry" &&
15
+ entry.event.type === "auto_retry_start")))
16
+ return undefined;
17
+ const toolResults = after.flatMap(({ entry }) => entry.kind === "message" && entry.value.role === "toolResult"
18
+ ? [entry.value]
19
+ : []);
20
+ const tools = assistant.entry.value.content.flatMap((call) => {
21
+ if (call.type !== "toolCall")
22
+ return [];
23
+ const outcome = [...after]
24
+ .reverse()
25
+ .find(({ entry }) => entry.kind === "tool_outcome" && entry.call.id === call.id)?.entry;
26
+ if (outcome?.kind === "tool_outcome")
27
+ return [
28
+ {
29
+ call,
30
+ state: "completed",
31
+ result: outcome.result,
32
+ message: toolResults.find((result) => result.toolCallId === call.id),
33
+ },
34
+ ];
35
+ const intent = [...after]
36
+ .reverse()
37
+ .find(({ entry }) => entry.kind === "tool_intent" && entry.call.id === call.id)?.entry;
38
+ return [
39
+ intent?.kind === "tool_intent"
40
+ ? { call, state: "started", replay: intent.replay }
41
+ : { call, state: "pending" },
42
+ ];
43
+ });
44
+ return { message: assistant.entry.value, tools };
45
+ }
46
+ /** One visibility rule for model context and compaction, including recovered runs. */
47
+ export function contextMessageIds(path, activeWorkId) {
48
+ const terminalWork = new Set(path.flatMap(({ entry }) => entry.kind === "run_terminal" ? [entry.workId] : []));
49
+ const messages = new Set();
50
+ const lastWorkAssistant = new Map();
51
+ for (const row of path) {
52
+ const entry = row.entry;
53
+ if (entry.kind === "message") {
54
+ if (entry.value.role === "assistant") {
55
+ lastWorkAssistant.set(entry.workId, row.id);
56
+ }
57
+ if (terminalWork.has(entry.workId) || entry.workId === activeWorkId)
58
+ messages.add(row.id);
59
+ }
60
+ if ((entry.kind === "compaction" && entry.resumesRun === true) ||
61
+ (entry.kind === "live_retry" && entry.event.type === "auto_retry_start")) {
62
+ const failed = lastWorkAssistant.get(entry.workId);
63
+ if (failed !== undefined)
64
+ messages.delete(failed);
65
+ }
66
+ }
67
+ return messages;
68
+ }
69
+ /** Returned assistant failures still pass through compaction; abnormal exits do not. */
70
+ export function abnormalTerminal(rows) {
71
+ const reversed = [...rows].reverse();
72
+ const terminal = reversed.find(({ entry }) => entry.kind === "run_terminal");
73
+ if (terminal?.entry.kind !== "run_terminal")
74
+ return undefined;
75
+ if (rows.some(({ sequence, entry }) => sequence > terminal.sequence &&
76
+ (entry.kind === "run_started" ||
77
+ (entry.kind === "compaction" && entry.resumesRun === true))))
78
+ return undefined;
79
+ if (terminal.entry.outcome === "cancelled")
80
+ return "cancelled";
81
+ if (terminal.entry.outcome !== "failed")
82
+ return undefined;
83
+ const turn = reversed.find(({ entry }) => entry.kind === "turn_started");
84
+ const assistant = reversed.find(({ sequence, entry }) => entry.kind === "message" &&
85
+ entry.value.role === "assistant" &&
86
+ sequence > (turn?.sequence ?? 0));
87
+ return assistant?.entry.kind === "message" &&
88
+ assistant.entry.value.role === "assistant" &&
89
+ assistant.entry.value.stopReason === "error" &&
90
+ !rows.some(({ sequence, entry }) => sequence > assistant.sequence &&
91
+ entry.kind === "live_retry" &&
92
+ entry.event.type === "auto_retry_start")
93
+ ? undefined
94
+ : "failed";
95
+ }
@@ -0,0 +1,16 @@
1
+ import { type AttentionItem, type SessionAttention } from "../../session-attention.js";
2
+ import type { SqliteOwner } from "../contracts.js";
3
+ /** Woodstock owns the durable attention rows alongside their Session. */
4
+ export declare function prepareSessionAttention(owner: SqliteOwner): void;
5
+ /** Read the complete bounded attention projection inside the owner's transaction. */
6
+ export declare function readSessionAttention(owner: SqliteOwner, sessionId: string): readonly SessionAttention[];
7
+ /** Whether any owner still requests attention; no wall-clock expiry heuristic. */
8
+ export declare function hasSessionAttention(owner: SqliteOwner, sessionId: string): boolean;
9
+ /** Upsert only the calling extension's item, in the calling transaction. */
10
+ export declare function setSessionAttention(owner: SqliteOwner, sessionId: string, extensionId: string, item: AttentionItem): void;
11
+ /** Clear only the calling extension's item; an absent item is a no-op. */
12
+ export declare function clearSessionAttention(owner: SqliteOwner, sessionId: string, extensionId: string, id: string): void;
13
+ /** Bounded keyset traversal for refreshing an owner's attention on stop/resume. */
14
+ export declare function readAttentionSessionPage(owner: SqliteOwner, extensionId: string, after: string): readonly string[];
15
+ /** Apply current grants and return the bounded roster identities whose visibility changed. */
16
+ export declare function replaceAttentionGrants(owner: SqliteOwner, ids: readonly string[]): readonly string[];
@@ -0,0 +1,88 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { ShiitakeError } from "../../errors.js";
3
+ import { AttentionItemSchema, maximumSessionAttention, maximumExtensionAttention, SessionAttentionSchema, } from "../../session-attention.js";
4
+ const decodeRow = Schema.decodeUnknownSync(Schema.Struct({ extension_id: Schema.String, value_json: Schema.String }));
5
+ const decodeItem = Schema.decodeUnknownSync(Schema.fromJsonString(AttentionItemSchema));
6
+ const decodePublic = Schema.decodeUnknownSync(SessionAttentionSchema);
7
+ const encodeItem = Schema.encodeSync(Schema.fromJsonString(AttentionItemSchema));
8
+ /** Woodstock owns the durable attention rows alongside their Session. */
9
+ export function prepareSessionAttention(owner) {
10
+ owner.sql.exec(`CREATE TABLE IF NOT EXISTS session_attention_grants (
11
+ extension_id TEXT PRIMARY KEY
12
+ )`);
13
+ owner.sql.exec(`CREATE TABLE IF NOT EXISTS session_attention (
14
+ session_id TEXT NOT NULL REFERENCES sessions(id),
15
+ extension_id TEXT NOT NULL,
16
+ id TEXT NOT NULL,
17
+ value_json TEXT NOT NULL,
18
+ PRIMARY KEY (session_id, extension_id, id)
19
+ )`);
20
+ owner.sql.exec("CREATE INDEX IF NOT EXISTS idx_session_attention_owner ON session_attention(extension_id, session_id)");
21
+ }
22
+ /** Read the complete bounded attention projection inside the owner's transaction. */
23
+ export function readSessionAttention(owner, sessionId) {
24
+ return owner.sql
25
+ .exec("SELECT a.extension_id, a.value_json FROM session_attention a JOIN shiitake_extension_status s ON s.extension_id = a.extension_id AND s.status = 'running' JOIN session_attention_grants g ON g.extension_id = a.extension_id WHERE a.session_id = ? ORDER BY a.extension_id, a.id LIMIT ?", sessionId, maximumSessionAttention)
26
+ .toArray()
27
+ .map((raw) => {
28
+ const row = decodeRow(raw);
29
+ return decodePublic({
30
+ ...decodeItem(row.value_json),
31
+ extensionId: row.extension_id,
32
+ });
33
+ });
34
+ }
35
+ /** Whether any owner still requests attention; no wall-clock expiry heuristic. */
36
+ export function hasSessionAttention(owner, sessionId) {
37
+ return (owner.sql
38
+ .exec("SELECT 1 FROM session_attention a JOIN shiitake_extension_status s ON s.extension_id = a.extension_id AND s.status = 'running' JOIN session_attention_grants g ON g.extension_id = a.extension_id WHERE a.session_id = ? LIMIT 1", sessionId)
39
+ .toArray().length > 0);
40
+ }
41
+ /** Upsert only the calling extension's item, in the calling transaction. */
42
+ export function setSessionAttention(owner, sessionId, extensionId, item) {
43
+ const usage = Schema.decodeUnknownSync(Schema.Struct({ count: Schema.Int }))(owner.sql
44
+ .exec("SELECT COUNT(*) AS count FROM session_attention WHERE session_id = ? AND NOT (extension_id = ? AND id = ?)", sessionId, extensionId, item.id)
45
+ .one());
46
+ if (usage.count >= maximumSessionAttention)
47
+ throw new ShiitakeError("contract", "invalid", "Session attention capacity reached");
48
+ const extensionUsage = Schema.decodeUnknownSync(Schema.Struct({ count: Schema.Int }))(owner.sql
49
+ .exec("SELECT COUNT(*) AS count FROM session_attention WHERE extension_id = ? AND NOT (session_id = ? AND id = ?)", extensionId, sessionId, item.id)
50
+ .one());
51
+ if (extensionUsage.count >= maximumExtensionAttention)
52
+ throw new ShiitakeError("contract", "invalid", "Extension attention capacity reached");
53
+ owner.sql.exec(`INSERT INTO session_attention (session_id, extension_id, id, value_json) VALUES (?, ?, ?, ?)
54
+ ON CONFLICT (session_id, extension_id, id) DO UPDATE SET value_json = excluded.value_json`, sessionId, extensionId, item.id, encodeItem(item));
55
+ }
56
+ /** Clear only the calling extension's item; an absent item is a no-op. */
57
+ export function clearSessionAttention(owner, sessionId, extensionId, id) {
58
+ owner.sql.exec("DELETE FROM session_attention WHERE session_id = ? AND extension_id = ? AND id = ?", sessionId, extensionId, id);
59
+ }
60
+ /** Bounded keyset traversal for refreshing an owner's attention on stop/resume. */
61
+ export function readAttentionSessionPage(owner, extensionId, after) {
62
+ const decode = Schema.decodeUnknownSync(Schema.Struct({ session_id: Schema.String }));
63
+ return owner.sql
64
+ .exec("SELECT DISTINCT session_id FROM session_attention WHERE extension_id = ? AND session_id > ? ORDER BY session_id LIMIT 64", extensionId, after)
65
+ .toArray()
66
+ .map((row) => decode(row).session_id);
67
+ }
68
+ /** Apply current grants and return the bounded roster identities whose visibility changed. */
69
+ export function replaceAttentionGrants(owner, ids) {
70
+ const decode = Schema.decodeUnknownSync(Schema.Struct({ extension_id: Schema.String }));
71
+ const prior = new Set(owner.sql
72
+ .exec("SELECT extension_id FROM session_attention_grants LIMIT 64")
73
+ .toArray()
74
+ .map((row) => decode(row).extension_id));
75
+ const next = new Set(ids);
76
+ const changed = [];
77
+ for (const id of prior)
78
+ if (!next.has(id)) {
79
+ owner.sql.exec("DELETE FROM session_attention_grants WHERE extension_id = ?", id);
80
+ changed.push(id);
81
+ }
82
+ for (const id of next)
83
+ if (!prior.has(id)) {
84
+ owner.sql.exec("INSERT INTO session_attention_grants (extension_id) VALUES (?)", id);
85
+ changed.push(id);
86
+ }
87
+ return changed;
88
+ }
@@ -0,0 +1,18 @@
1
+ import type { SqliteOwner } from "../contracts.js";
2
+ import type { DocumentForkPolicyV0 } from "../../session-documents.js";
3
+ import type { StoredSessionEntry } from "./session-entry.js";
4
+ export type StoredDocumentSpec = Readonly<{
5
+ extensionId: string;
6
+ name: string;
7
+ initialJson: string;
8
+ fork: DocumentForkPolicyV0;
9
+ maxBytes: number;
10
+ }>;
11
+ /** Policy is durable too, so a fork never depends on a process-local extension. */
12
+ export declare function prepareSessionDocuments(owner: SqliteOwner): void;
13
+ /** Activation commits a validated declaration; it does not rewrite document history. */
14
+ export declare function declareSessionDocument(owner: SqliteOwner, spec: StoredDocumentSpec): void;
15
+ /** Baselines appended after the copied history in the same fork transaction. */
16
+ export declare function forkDocumentEntries(owner: SqliteOwner,
17
+ /** Latest value on the source's current lineage, not a global last write. */
18
+ currentValue: (extensionId: string, name: string) => string | undefined): readonly StoredSessionEntry[];
@@ -0,0 +1,51 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { ShiitakeError } from "../../errors.js";
3
+ const SpecRow = Schema.Struct({
4
+ extension_id: Schema.String,
5
+ name: Schema.String,
6
+ initial_json: Schema.String,
7
+ fork: Schema.Literals(["inherit-at-fork-point", "current", "fresh"]),
8
+ max_bytes: Schema.Int.pipe(Schema.check(Schema.isGreaterThan(0))),
9
+ });
10
+ /** Policy is durable too, so a fork never depends on a process-local extension. */
11
+ export function prepareSessionDocuments(owner) {
12
+ owner.sql.exec(`CREATE TABLE IF NOT EXISTS session_document_specs (
13
+ extension_id TEXT NOT NULL, name TEXT NOT NULL, initial_json TEXT NOT NULL,
14
+ fork TEXT NOT NULL, max_bytes INTEGER NOT NULL,
15
+ PRIMARY KEY (extension_id, name)
16
+ )`);
17
+ }
18
+ /** Activation commits a validated declaration; it does not rewrite document history. */
19
+ export function declareSessionDocument(owner, spec) {
20
+ owner.sql.exec(`INSERT INTO session_document_specs
21
+ (extension_id, name, initial_json, fork, max_bytes) VALUES (?, ?, ?, ?, ?)
22
+ ON CONFLICT(extension_id, name) DO UPDATE SET initial_json = excluded.initial_json,
23
+ fork = excluded.fork, max_bytes = excluded.max_bytes`, spec.extensionId, spec.name, spec.initialJson, spec.fork, spec.maxBytes);
24
+ }
25
+ /** Baselines appended after the copied history in the same fork transaction. */
26
+ export function forkDocumentEntries(owner,
27
+ /** Latest value on the source's current lineage, not a global last write. */
28
+ currentValue) {
29
+ const specs = owner.sql
30
+ .exec("SELECT extension_id, name, initial_json, fork, max_bytes FROM session_document_specs ORDER BY extension_id, name")
31
+ .toArray();
32
+ return specs.flatMap((raw) => {
33
+ const spec = Schema.decodeUnknownSync(SpecRow)(raw);
34
+ if (spec.fork === "inherit-at-fork-point")
35
+ return [];
36
+ const valueJson = spec.fork === "fresh"
37
+ ? spec.initial_json
38
+ : (currentValue(spec.extension_id, spec.name) ?? spec.initial_json);
39
+ if (new TextEncoder().encode(valueJson).byteLength > spec.max_bytes)
40
+ throw new ShiitakeError("store", "rejected", "Session document exceeds its declared size bound");
41
+ return [
42
+ {
43
+ kind: "document",
44
+ workId: null,
45
+ extensionId: spec.extension_id,
46
+ name: spec.name,
47
+ valueJson,
48
+ },
49
+ ];
50
+ });
51
+ }