@opencxh/domain 1.264.5 → 1.265.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.
@@ -45,7 +45,20 @@ export interface ToolCatalogEntry {
45
45
  surface?: "interactive";
46
46
  /** What it leaves for a human to finish. See {@link AiToolDescriptor.produces}. */
47
47
  produces?: "draft";
48
+ /** Whether it can be taken back. See {@link AiToolDescriptor.reversible}. */
49
+ reversible?: "self" | "none" | "idempotent";
48
50
  }
51
+ /**
52
+ * How an action can be taken back. Reads the fail-safe default in one place, so no caller has to
53
+ * remember that absent means irreversible.
54
+ *
55
+ * A **read** is `"idempotent"` by construction: it changed nothing, so there is nothing to
56
+ * reverse and running it again is free. Only writes have a real answer here.
57
+ */
58
+ export declare function toolReversible(tool: {
59
+ access?: "read" | "write";
60
+ reversible?: "self" | "none" | "idempotent";
61
+ }): "self" | "none" | "idempotent";
49
62
  /**
50
63
  * Can this tool reach outside the building? A read never does; an `effect: "internal"` write says
51
64
  * so itself. Everything else — including a tool that declares neither — counts as outward, so a
@@ -199,6 +212,41 @@ export interface AiToolDescriptor extends AiTool {
199
212
  * whichever one its description matched. See `tools/mode.ts` in the ai app.
200
213
  */
201
214
  produces?: "draft";
215
+ /**
216
+ * Can this be taken back, and how?
217
+ *
218
+ * The question a rule engine forces: one badly written rule does not touch one conversation
219
+ * but forty, and "undo this run" then has to say honestly what it can and cannot put back.
220
+ * This generalises `DocumentOperation.undoable`, which already carries the idea for document
221
+ * edits — *"'I changed it, undo if you disagree' is a promise that has to hold."*
222
+ *
223
+ * - `"self"` — **the same call with the previous value undoes it** (status, assignee, an
224
+ * attribute, an SLA profile). The executor records what was there before; undo replays that
225
+ * through this same tool, and nothing else has to know anything.
226
+ *
227
+ * Deliberately narrow. `add_tag` is reversible too, but by a *different* tool
228
+ * (`remove_tag`), and expressing that needs a second field naming the counter-action. That
229
+ * field can arrive the day an undo actually wants it; until then such a tool stays on the
230
+ * default and simply is not offered as undoable. Under-promising is the safe direction.
231
+ * - `"none"` — irreversible. A sent mail, a webhook, a row at a third party. The gate belongs
232
+ * *before* it; afterwards there is nothing to do but send a second message, and that is a
233
+ * human's decision, not an undo.
234
+ * - `"idempotent"` — running it again is the repair (a sync landing). Replay, never reverse.
235
+ *
236
+ * **Absent means `"none"`.** The same fail-safe-by-absence as {@link AiToolDescriptor.effect},
237
+ * and for the same reason. Guessing wrong this way costs a button that stays grey; the other
238
+ * way it costs a promise that breaks.
239
+ *
240
+ * **What `"self"` demands before you declare it, and why nothing does yet.** Replaying the
241
+ * previous value only works if the tool can express *every* previous value — including "there
242
+ * was nothing here". Today several cannot: `update_interaction` does
243
+ * `if (assignedUserId) patch.assignedUserId = …`, so an empty string is falsy and an
244
+ * assignment cannot be taken back; the same shape as the older scar where `null` never
245
+ * survives the invoke transport and clearing a field needs an explicit clear-list. So a tool
246
+ * becomes `"self"` when its clear-semantics are fixed, not when someone decides it feels
247
+ * reversible — which is exactly why the default is the careful one.
248
+ */
249
+ reversible?: "self" | "none" | "idempotent";
202
250
  }
203
251
  /**
204
252
  * Body of `POST /provider/ai-tools/invoke`. Replaces five inline structural types (the caller in
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.264.5",
3
+ "version": "1.265.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",