@ixo/oracle-runtime-workers 0.12.0 → 0.15.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 (205) hide show
  1. package/README.md +32 -10
  2. package/package.json +14 -9
  3. package/src/artifacts/config.ts +158 -0
  4. package/src/artifacts/crypto.ts +94 -0
  5. package/src/artifacts/routes.ts +73 -0
  6. package/src/artifacts/store.ts +238 -0
  7. package/src/artifacts/sweep.ts +200 -0
  8. package/src/artifacts/tool.ts +103 -0
  9. package/src/artifacts/viewer-page.ts +614 -0
  10. package/src/attachments/download.ts +168 -45
  11. package/src/attachments/index.ts +9 -0
  12. package/src/attachments/magic.ts +33 -0
  13. package/src/attachments/pipeline.ts +105 -49
  14. package/src/attachments/sandbox-archive.ts +9 -8
  15. package/src/attachments/view-cache.ts +142 -0
  16. package/src/attachments/view.ts +53 -12
  17. package/src/channels/auth.ts +267 -0
  18. package/src/channels/contract.ts +137 -0
  19. package/src/channels/turns.ts +270 -0
  20. package/src/core/budgeted-llm.ts +223 -0
  21. package/src/core/capability-router.ts +273 -0
  22. package/src/core/context-budget.ts +97 -10
  23. package/src/core/env.ts +111 -0
  24. package/src/core/execution-profile.ts +17 -0
  25. package/src/core/index.ts +267 -9
  26. package/src/core/llm.ts +30 -9
  27. package/src/core/main-agent-types.ts +41 -1
  28. package/src/core/main-agent.ts +367 -65
  29. package/src/core/manifest.ts +34 -0
  30. package/src/core/meta-tools.ts +128 -18
  31. package/src/core/middlewares/capability-gate.ts +130 -12
  32. package/src/core/middlewares/context-guard.ts +55 -24
  33. package/src/core/middlewares/index.ts +20 -2
  34. package/src/core/middlewares/page-context.ts +46 -2
  35. package/src/core/middlewares/return-direct-first.ts +47 -0
  36. package/src/core/middlewares/summarization.ts +152 -10
  37. package/src/core/middlewares/tool-execution.ts +323 -0
  38. package/src/core/middlewares/tool-repetition-guard.ts +202 -35
  39. package/src/core/middlewares/turn-boundary.ts +162 -0
  40. package/src/core/openrouter-pricing.ts +155 -22
  41. package/src/core/prompt-composer.ts +112 -7
  42. package/src/core/registries.ts +253 -48
  43. package/src/core/request-admission.ts +159 -0
  44. package/src/core/runtime-context.ts +107 -8
  45. package/src/core/state.ts +1 -1
  46. package/src/core/sub-agent-fallback.ts +21 -2
  47. package/src/core/subagent-as-tool.ts +78 -43
  48. package/src/core/tool-access.ts +189 -0
  49. package/src/core/tool-scheduler.ts +125 -0
  50. package/src/core/turn-budget.ts +205 -0
  51. package/src/core/turn-time-note.ts +43 -0
  52. package/src/core/user-kv.ts +446 -0
  53. package/src/core/wrap-plugin-tool.ts +20 -0
  54. package/src/delivery/plan.ts +278 -0
  55. package/src/delivery/profile.ts +161 -0
  56. package/src/delivery/prompt.ts +27 -0
  57. package/src/delivery/schema.ts +61 -0
  58. package/src/delivery/shaper.ts +389 -0
  59. package/src/delivery/types.ts +84 -0
  60. package/src/do/ambient.ts +51 -15
  61. package/src/do/contracts.ts +198 -6
  62. package/src/do/idle-eviction.ts +38 -0
  63. package/src/do/matrix-turn-ledger.ts +24 -5
  64. package/src/do/result-store-test-do.ts +137 -3
  65. package/src/do/result-store.ts +239 -58
  66. package/src/do/room-mirror.ts +29 -0
  67. package/src/do/run-buffer.ts +64 -9
  68. package/src/do/run-coordinator.ts +451 -76
  69. package/src/do/run-request.ts +2 -0
  70. package/src/do/run-store-test-do.ts +301 -1
  71. package/src/do/run-store.ts +385 -43
  72. package/src/do/secrets-adapter.ts +22 -3
  73. package/src/do/sse-stream.ts +60 -9
  74. package/src/do/transcript.ts +48 -5
  75. package/src/do/turn-body.ts +105 -5
  76. package/src/do/ucan-service.ts +204 -34
  77. package/src/do/user-oracle-do.ts +1883 -264
  78. package/src/feedback/config.ts +127 -0
  79. package/src/feedback/contract.ts +148 -0
  80. package/src/feedback/linear-sink.ts +285 -0
  81. package/src/feedback/privacy.ts +150 -0
  82. package/src/feedback/reservation.ts +298 -0
  83. package/src/feedback/submit.ts +247 -0
  84. package/src/index.ts +138 -6
  85. package/src/llm/byo-adapter.ts +27 -6
  86. package/src/llm/byo-catalog.ts +13 -0
  87. package/src/llm/byo-model-fallback.ts +248 -0
  88. package/src/llm/byo-service.ts +45 -8
  89. package/src/llm/provider-error.ts +139 -2
  90. package/src/matrix/gateway-do.ts +523 -111
  91. package/src/matrix/group-chat.ts +66 -23
  92. package/src/matrix/inbox-store.ts +16 -1
  93. package/src/matrix/ingest.ts +79 -14
  94. package/src/matrix/media-deadline.ts +130 -0
  95. package/src/matrix/replay-format.ts +5 -3
  96. package/src/matrix/reply-parts.ts +92 -0
  97. package/src/matrix/room-alias.ts +96 -0
  98. package/src/matrix/user-homeserver.ts +166 -7
  99. package/src/owner-store/ixo-vfs-store.ts +170 -43
  100. package/src/owner-store/migrating-store.ts +31 -15
  101. package/src/owner-store/types.ts +18 -3
  102. package/src/plugin-api/index.ts +43 -2
  103. package/src/plugin-api/oracle-plugin.ts +35 -0
  104. package/src/plugin-api/request-admission.ts +41 -0
  105. package/src/plugin-api/tool-helper.ts +25 -1
  106. package/src/plugin-api/tool-plane.ts +92 -0
  107. package/src/plugin-api/types.ts +194 -6
  108. package/src/plugins/agui/agui.plugin.ts +31 -16
  109. package/src/plugins/composio/composio-tools.ts +57 -6
  110. package/src/plugins/composio/composio-ucan.ts +3 -2
  111. package/src/plugins/composio/composio.plugin.ts +2 -0
  112. package/src/plugins/delegated-capabilities.ts +25 -0
  113. package/src/plugins/editor/content-session.ts +173 -83
  114. package/src/plugins/editor/content-tools.ts +76 -17
  115. package/src/plugins/editor/editor-mx.ts +28 -9
  116. package/src/plugins/editor/editor.plugin.ts +3 -1
  117. package/src/plugins/editor/failures.ts +28 -7
  118. package/src/plugins/editor/prompts.ts +2 -1
  119. package/src/plugins/editor/provider.ts +553 -40
  120. package/src/plugins/editor/standalone-editor-tool.ts +61 -18
  121. package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
  122. package/src/plugins/flows/action-metadata.ts +111 -11
  123. package/src/plugins/flows/actions.ts +18 -0
  124. package/src/plugins/flows/decision-context.ts +31 -0
  125. package/src/plugins/flows/edit.ts +223 -52
  126. package/src/plugins/flows/errors.ts +22 -1
  127. package/src/plugins/flows/flow-doc.ts +92 -9
  128. package/src/plugins/flows/flows.plugin.ts +11 -5
  129. package/src/plugins/flows/forms.ts +34 -0
  130. package/src/plugins/flows/index.ts +2 -0
  131. package/src/plugins/flows/input-policy.ts +140 -0
  132. package/src/plugins/flows/prompts.ts +14 -11
  133. package/src/plugins/flows/read.ts +49 -9
  134. package/src/plugins/flows/ref-syntax.ts +129 -0
  135. package/src/plugins/flows/test-homeserver.ts +245 -0
  136. package/src/plugins/flows/tools/authoring.ts +216 -112
  137. package/src/plugins/flows/tools/discovery.ts +4 -1
  138. package/src/plugins/flows/tools/forms.ts +21 -2
  139. package/src/plugins/flows/tools/settings.ts +34 -13
  140. package/src/plugins/flows/translator.ts +13 -10
  141. package/src/plugins/flows/types.ts +167 -42
  142. package/src/plugins/index.ts +11 -1
  143. package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
  144. package/src/plugins/ixo-transaction/index.ts +2 -0
  145. package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
  146. package/src/plugins/ixo-transaction/receipts.ts +99 -0
  147. package/src/plugins/ixo-transaction/tools.ts +329 -0
  148. package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
  149. package/src/plugins/mcp-tool-adapter.ts +23 -2
  150. package/src/plugins/memory/memory-tools.ts +7 -2
  151. package/src/plugins/memory/memory-ucan.ts +3 -2
  152. package/src/plugins/memory/memory.plugin.ts +2 -0
  153. package/src/plugins/pod-creator/blueprint-store.ts +163 -0
  154. package/src/plugins/pod-creator/blueprint-types.ts +61 -0
  155. package/src/plugins/pod-creator/bounded-map.ts +87 -0
  156. package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
  157. package/src/plugins/pod-creator/chain-gateway.ts +110 -0
  158. package/src/plugins/pod-creator/config.ts +85 -0
  159. package/src/plugins/pod-creator/create-session-store.ts +180 -0
  160. package/src/plugins/pod-creator/create-tools.ts +538 -0
  161. package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
  162. package/src/plugins/pod-creator/index.ts +65 -0
  163. package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
  164. package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
  165. package/src/plugins/pod-creator/stage.ts +201 -0
  166. package/src/plugins/pod-creator/sub-agents.ts +189 -0
  167. package/src/plugins/portal/action-log.ts +21 -1
  168. package/src/plugins/portal/declared-tools.ts +90 -0
  169. package/src/plugins/portal/portal.plugin.ts +42 -16
  170. package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
  171. package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
  172. package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
  173. package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
  174. package/src/plugins/tasks/manifest.ts +1 -1
  175. package/src/plugins/tasks/middleware.ts +20 -7
  176. package/src/plugins/tasks/tasks-tools.ts +30 -3
  177. package/src/plugins/tasks/tasks.plugin.ts +64 -11
  178. package/src/plugins/user-preferences/schema.ts +12 -1
  179. package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
  180. package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
  181. package/src/plugins/vfs/vfs-client.ts +250 -71
  182. package/src/plugins/vfs/vfs-content.ts +30 -11
  183. package/src/plugins/vfs/vfs-errors.ts +22 -0
  184. package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
  185. package/src/plugins/vfs/vfs-tools.ts +60 -20
  186. package/src/realtime/frontend-call-registry.ts +228 -81
  187. package/src/realtime/realtime-endpoint.ts +215 -44
  188. package/src/realtime/session-socket-hub.ts +47 -2
  189. package/src/secrets/secrets-service.ts +9 -3
  190. package/src/shell/app.ts +529 -82
  191. package/src/shell/auth.ts +131 -37
  192. package/src/shell/turn-body-cap.ts +11 -13
  193. package/src/sqlite/blob-compactor.ts +61 -28
  194. package/src/sqlite/database.ts +156 -90
  195. package/src/sqlite/do-vfs.ts +416 -129
  196. package/src/sqlite/page-tier.ts +133 -40
  197. package/src/sqlite/serialization.ts +40 -0
  198. package/src/sqlite/sessions-store.ts +10 -2
  199. package/src/sqlite/sqlite-saver.ts +139 -19
  200. package/src/sqlite/user-kv-store.ts +317 -0
  201. package/src/tasks/schedule.ts +201 -12
  202. package/src/tasks/scheduler.ts +586 -98
  203. package/src/tasks/spec.ts +67 -3
  204. package/src/tasks/store.ts +292 -27
  205. package/src/tasks/topic-deliverables.ts +70 -0
@@ -8,7 +8,6 @@
8
8
  * per tool.
9
9
  */
10
10
 
11
- import type { MatrixClient } from 'matrix-js-sdk';
12
11
  import type * as Y from 'yjs';
13
12
  import { z } from 'zod';
14
13
 
@@ -18,7 +17,7 @@ import {
18
17
  markdownToBlockContainers,
19
18
  markdownToInlineContent,
20
19
  } from './blocknote-bridge';
21
- import { applyDocumentEdit, withDocument } from './content-session';
20
+ import { applyDocumentEdit, type DocumentSource } from './content-session';
22
21
  import {
23
22
  appendBlocks,
24
23
  flattenBlocks,
@@ -47,16 +46,17 @@ import {
47
46
  type EditorFailure,
48
47
  } from './failures';
49
48
  import {
49
+ classifyBlockType,
50
50
  filterWritableProps,
51
51
  isTextEditable,
52
52
  redactProps,
53
53
  } from './prop-policy';
54
- import type { AppConfig } from './provider';
55
-
56
54
  export interface ContentToolsOptions {
57
- matrixClient: MatrixClient;
58
- /** Provider config with the target room already baked in. */
59
- appConfig: AppConfig;
55
+ /**
56
+ * The one document every tool reads and writes, shared across the calls of
57
+ * one `call_editor_agent` run.
58
+ */
59
+ documents: DocumentSource;
60
60
  }
61
61
 
62
62
  const DEFAULT_READ_LIMIT = 60;
@@ -84,6 +84,45 @@ function redactFlat(block: FlatDocumentBlock): FlatDocumentBlock {
84
84
  return { ...block, props: redactProps(block.type, block.props) };
85
85
  }
86
86
 
87
+ /**
88
+ * The first locked block (`secrets`, `skills`) in `blockId`'s subtree,
89
+ * itself included. Deleting or moving a block rewrites its whole subtree, so
90
+ * a locked block nested anywhere inside refuses the operation.
91
+ */
92
+ function findLockedBlock(
93
+ doc: Y.Doc,
94
+ blockId: string,
95
+ ): { id: string; type: string } | undefined {
96
+ const visit = (block: DocumentBlock): DocumentBlock | undefined => {
97
+ if (classifyBlockType(block.type) === 'locked') return block;
98
+ for (const child of block.children) {
99
+ const hit = visit(child);
100
+ if (hit) return hit;
101
+ }
102
+ return undefined;
103
+ };
104
+ const root = readBlockById(doc, blockId);
105
+ const hit = root ? visit(root) : undefined;
106
+ return hit ? { id: hit.id, type: hit.type } : undefined;
107
+ }
108
+
109
+ /** The refusal for a delete/move that would touch a locked block. */
110
+ function lockedBlockRefusal(
111
+ blockId: string,
112
+ blockType: string,
113
+ locked: { id: string; type: string },
114
+ verb: 'deleted' | 'moved',
115
+ ): EditorFailure {
116
+ const where =
117
+ locked.id === blockId ? '' : ` (block ${locked.id} is nested inside it)`;
118
+ return propNotEditable(blockId, blockType, [
119
+ {
120
+ prop: 'block',
121
+ reason: `'${locked.type}' blocks are never editable by the assistant, so this block cannot be ${verb}${where}`,
122
+ },
123
+ ]);
124
+ }
125
+
87
126
  /** Case-insensitive substring match. */
88
127
  function matches(haystack: string, needle: string): boolean {
89
128
  return haystack.toLowerCase().includes(needle.toLowerCase());
@@ -118,7 +157,7 @@ function createReadDocumentTool(options: ContentToolsOptions): PluginTool {
118
157
  const start = args.start ?? 0;
119
158
  const limit = args.limit ?? DEFAULT_READ_LIMIT;
120
159
 
121
- const result = await withDocument(options, async (session) => {
160
+ const result = await options.documents.use(async (session) => {
122
161
  const blocks = flattenBlocks(readDocumentBlocks(session.doc)).map(
123
162
  redactFlat,
124
163
  );
@@ -161,7 +200,7 @@ function createReadBlockTool(options: ContentToolsOptions): PluginTool {
161
200
  return pluginTool(
162
201
  async (rawArgs) => {
163
202
  const args = schema.parse(rawArgs);
164
- const result = await withDocument(options, async (session) => {
203
+ const result = await options.documents.use(async (session) => {
165
204
  const block = readBlockById(session.doc, args.block_id);
166
205
  if (!block) return blockNotFound(args.block_id);
167
206
  return {
@@ -199,7 +238,7 @@ function createSearchDocumentTool(options: ContentToolsOptions): PluginTool {
199
238
  const args = schema.parse(rawArgs);
200
239
  const limit = args.limit ?? 20;
201
240
 
202
- const result = await withDocument(options, async (session) => {
241
+ const result = await options.documents.use(async (session) => {
203
242
  const blocks = flattenBlocks(readDocumentBlocks(session.doc)).map(
204
243
  redactFlat,
205
244
  );
@@ -292,7 +331,7 @@ function createInsertContentTool(options: ContentToolsOptions): PluginTool {
292
331
  );
293
332
  }
294
333
 
295
- const result = await withDocument(options, async (session) =>
334
+ const result = await options.documents.use(async (session) =>
296
335
  applyDocumentEdit(session, {
297
336
  plan: (doc) => {
298
337
  // Anchored inserts need a live reference; end/start do not.
@@ -395,7 +434,7 @@ function createEditBlockTool(options: ContentToolsOptions): PluginTool {
395
434
  parsedText.set(index, await markdownToInlineContent(edit.text));
396
435
  }
397
436
 
398
- const result = await withDocument(options, async (session) =>
437
+ const result = await options.documents.use(async (session) =>
399
438
  applyDocumentEdit(session, {
400
439
  plan: (doc): PreparedEdit[] | EditorFailure => {
401
440
  const prepared: PreparedEdit[] = [];
@@ -484,7 +523,7 @@ function createDeleteBlockTool(options: ContentToolsOptions): PluginTool {
484
523
  async (rawArgs) => {
485
524
  const args = schema.parse(rawArgs);
486
525
 
487
- const result = await withDocument(options, async (session) =>
526
+ const result = await options.documents.use(async (session) =>
488
527
  applyDocumentEdit(session, {
489
528
  plan: (doc) => {
490
529
  const located = locateBlock(
@@ -493,7 +532,17 @@ function createDeleteBlockTool(options: ContentToolsOptions): PluginTool {
493
532
  );
494
533
  if (!located) return blockNotFound(args.block_id);
495
534
  const content = getContentElement(located.container);
496
- return { blockType: content?.nodeName ?? 'unknown' };
535
+ const blockType = content?.nodeName ?? 'unknown';
536
+ const locked = findLockedBlock(doc, args.block_id);
537
+ if (locked) {
538
+ return lockedBlockRefusal(
539
+ args.block_id,
540
+ blockType,
541
+ locked,
542
+ 'deleted',
543
+ );
544
+ }
545
+ return { blockType };
497
546
  },
498
547
  apply: (doc, plan) => {
499
548
  const removed = removeBlock(doc, args.block_id);
@@ -537,13 +586,23 @@ function createMoveBlockTool(options: ContentToolsOptions): PluginTool {
537
586
  async (rawArgs) => {
538
587
  const args = schema.parse(rawArgs);
539
588
 
540
- const result = await withDocument(options, async (session) =>
589
+ const result = await options.documents.use(async (session) =>
541
590
  applyDocumentEdit(session, {
542
591
  plan: (doc) => {
543
592
  const fragment = doc.getXmlFragment(DOCUMENT_FRAGMENT_NAME);
544
- if (!locateBlock(fragment, args.block_id)) {
593
+ const source = locateBlock(fragment, args.block_id);
594
+ if (!source) {
545
595
  return blockNotFound(args.block_id);
546
596
  }
597
+ const locked = findLockedBlock(doc, args.block_id);
598
+ if (locked) {
599
+ return lockedBlockRefusal(
600
+ args.block_id,
601
+ getContentElement(source.container)?.nodeName ?? 'unknown',
602
+ locked,
603
+ 'moved',
604
+ );
605
+ }
547
606
  if (!locateBlock(fragment, args.reference_block_id)) {
548
607
  return blockNotFound(args.reference_block_id);
549
608
  }
@@ -619,7 +678,7 @@ function createReplaceTextTool(options: ContentToolsOptions): PluginTool {
619
678
  async (rawArgs) => {
620
679
  const args = schema.parse(rawArgs);
621
680
 
622
- const result = await withDocument(options, async (session) =>
681
+ const result = await options.documents.use(async (session) =>
623
682
  applyDocumentEdit(session, {
624
683
  plan: (doc) => {
625
684
  if (args.block_id) {
@@ -11,7 +11,8 @@ export interface EditorMatrixClientConfig {
11
11
  const logger = createConsoleLogger({ module: 'editor-mx' });
12
12
 
13
13
  /**
14
- * Per-isolate singleton wrapper around a `matrix-js-sdk` `MatrixClient`. Used
14
+ * Per-isolate wrapper around a `matrix-js-sdk` `MatrixClient`, one live
15
+ * instance per set of credentials (see {@link EditorMatrixClient.getInstance}). Used
15
16
  * by `@ixo/matrix-crdt` to read/write BlockNote Y.js documents.
16
17
  *
17
18
  * No `startClient()` / sync is performed — matrix-crdt's `MatrixReader`
@@ -35,15 +36,32 @@ export class EditorMatrixClient {
35
36
  }
36
37
 
37
38
  /**
38
- * Get the singleton instance. First caller supplies the Matrix admin
39
- * config used for the lifetime of the isolate; subsequent callers
40
- * receive the same instance and the config argument is ignored.
39
+ * Get the isolate's instance for these credentials. Callers pass the
40
+ * credentials the gateway handed out for the current request; when they
41
+ * differ from the cached instance's (the gateway re-minted the device
42
+ * token, or the homeserver or user changed) a new instance replaces it, so
43
+ * a warm isolate never keeps sending a revoked token. Requests already
44
+ * holding the previous client finish with it; it runs no sync loop, so
45
+ * nothing needs stopping.
41
46
  */
42
47
  public static getInstance(cfg: EditorMatrixClientConfig): EditorMatrixClient {
43
- if (!EditorMatrixClient.instance) {
44
- EditorMatrixClient.instance = new EditorMatrixClient(cfg);
45
- }
46
- return EditorMatrixClient.instance;
48
+ const current = EditorMatrixClient.instance;
49
+ if (current && current.hasCredentials(cfg)) return current;
50
+ const next = new EditorMatrixClient({
51
+ baseUrl: cfg.baseUrl,
52
+ userId: cfg.userId,
53
+ accessToken: cfg.accessToken,
54
+ });
55
+ EditorMatrixClient.instance = next;
56
+ return next;
57
+ }
58
+
59
+ private hasCredentials(cfg: EditorMatrixClientConfig): boolean {
60
+ return (
61
+ this.cfg.baseUrl === cfg.baseUrl &&
62
+ this.cfg.userId === cfg.userId &&
63
+ this.cfg.accessToken === cfg.accessToken
64
+ );
47
65
  }
48
66
 
49
67
  /**
@@ -113,7 +131,8 @@ export class EditorMatrixClient {
113
131
  /**
114
132
  * Resolve the `MatrixClient` used by editor tools. Prefers the
115
133
  * `matrixClient` from the plugin's runtime config (host/test-provided);
116
- * otherwise lazily constructs the internal singleton.
134
+ * otherwise lazily constructs the internal instance, rebuilt whenever the
135
+ * credentials passed in differ from the ones it was built with.
117
136
  *
118
137
  * Centralised so every call site (standalone-editor-tool, the flows plugin's
119
138
  * flow-doc) goes through one resolution path.
@@ -133,7 +133,9 @@ export class EditorPlugin extends OraclePlugin {
133
133
  // the import, so only the first turn in an isolate pays for it.
134
134
  const { createStandaloneEditorTool } =
135
135
  await import('./standalone-editor-tool');
136
- return [createStandaloneEditorTool({ toolsConfig })];
136
+ return [
137
+ createStandaloneEditorTool({ toolsConfig, pluginName: this.name }),
138
+ ];
137
139
  } catch (error) {
138
140
  const detail = error instanceof Error ? error.message : String(error);
139
141
  rtCtx.logger.error(`[editor] failed to build standalone tool: ${detail}`);
@@ -16,7 +16,7 @@ export type EditorFailureCode =
16
16
  | 'block_not_found'
17
17
  | 'not_a_member'
18
18
  | 'no_document'
19
- | 'flush_timeout'
19
+ | 'write_not_saved'
20
20
  | 'error';
21
21
 
22
22
  export interface EditorFailure {
@@ -120,19 +120,40 @@ export function notAMember(roomId: string): EditorFailure {
120
120
  };
121
121
  }
122
122
 
123
- /** The write was made locally but could not be confirmed as sent. */
124
- export function flushTimeout(roomId: string): EditorFailure {
123
+ /**
124
+ * The write was made locally but its delivery could not be confirmed: the
125
+ * homeserver kept failing (5xx, timeouts, network) until the retry budget ran
126
+ * out, so the change may or may not have been stored. Not an access problem —
127
+ * the assistant may write here.
128
+ */
129
+ export function writeNotSaved(roomId: string, detail: string): EditorFailure {
125
130
  return {
126
131
  ok: false,
127
- code: 'flush_timeout',
132
+ code: 'write_not_saved',
128
133
  roomId,
129
134
  message:
130
- `The edit could not be confirmed as saved within the time limit — treat ` +
131
- `it as NOT applied. Tell the user the document service is not responding ` +
132
- `and they can retry shortly. Do not claim the edit succeeded.`,
135
+ `The edit could not be confirmed as saved — the document service did ` +
136
+ `not acknowledge it (${detail}), so it may or may not have landed. Do ` +
137
+ `not claim it succeeded. It can be retried: in a later request, read ` +
138
+ `the document first, then apply it again only if it is missing.`,
133
139
  };
134
140
  }
135
141
 
142
+ /**
143
+ * The homeserver refused the write outright (an oversized change, a revoked
144
+ * token, a malformed request): nothing was stored, and sending the same
145
+ * change again fails the same way. Not an access problem in the room.
146
+ */
147
+ export function writeRefused(roomId: string, detail: string): EditorFailure {
148
+ return editorError(
149
+ `The edit was NOT saved — the document service refused it (${detail}). ` +
150
+ `Nothing was stored. Do not claim it succeeded, and do not send the ` +
151
+ `same change again: if it was a large change, split it into smaller ` +
152
+ `edits.`,
153
+ roomId,
154
+ );
155
+ }
156
+
136
157
  /**
137
158
  * Nothing to act on: no document is open and the call named no `room_id`.
138
159
  * Distinct from `not_a_member` — there is no room to be a member of yet.
@@ -42,7 +42,8 @@ Tool results carry a \`code\` you must act on:
42
42
  - \`block_not_found\` — the id is stale. Re-read the document and use current ids.
43
43
  - \`not_a_member\` — the document is not the user's. Say so plainly. Do not retry.
44
44
  - \`no_document\` — no document is open and no room id was given. Return the code; do not invent a room id.
45
- - \`flush_timeout\` — the edit could **not** be confirmed as saved. Treat it as not applied and say so.
45
+ - \`write_not_saved\` — the edit could **not** be confirmed as saved: the document service did not acknowledge it, so it may or may not have landed. Say so; do not claim it succeeded. Do not repeat it in this task — further edits in this task are refused too. In a later request, re-read the document first and apply the edit again only if it is missing.
46
+ - \`error\` after an edit — the document service refused it outright (for example, the change was too large): nothing was stored. Say so; do not claim it succeeded, and never send the same change again unchanged — split a large change into smaller edits.
46
47
 
47
48
  Never present a refusal as a success, and never silently retry a refusal.`;
48
49