@segmentstream-cloud/ontology 0.18.1 → 0.19.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/dist/action.d.ts CHANGED
@@ -18,13 +18,15 @@ export interface ActionContext<Input, Prepared = undefined> extends PrepareConte
18
18
  /** What `prepare` may fail with: the action errors, and connection calls that failed. */
19
19
  export type PrepareError = ActionError | ConnectionError;
20
20
  /**
21
- * How an action behaves, as MCP tool annotations tell agents. Clients such as Claude ask the user
22
- * before they run a destructive tool. Without hints an action is taken as destructive and not
23
- * idempotent.
21
+ * How an action behaves. Agents confirm destructive or irreversible actions before preparation.
22
+ * Custom actions without safety labels require confirmation for backwards compatibility.
23
+ * Idempotence does not decide whether confirmation is needed.
24
24
  */
25
25
  export interface ActionHints {
26
- /** Whether the action may overwrite or remove what someone decided. Default true. */
26
+ /** Whether the action destroys records or established decisions. Unlabelled actions default true. */
27
27
  readonly destructive?: boolean;
28
+ /** Whether the effect cannot be fully undone, such as sending a notification. Default false. */
29
+ readonly irreversible?: boolean;
28
30
  /** Whether running it again with the same input changes nothing more. Default false. */
29
31
  readonly idempotent?: boolean;
30
32
  }
@@ -79,7 +81,8 @@ interface ActionOptions<Input extends Schema.Top> {
79
81
  readonly permission?: string;
80
82
  /**
81
83
  * How the action behaves, for the agents that run it over MCP. Set `destructive: false` for an
82
- * action that only adds or that history makes easy to reverse, and `idempotent: true` for one
84
+ * routine reversible action, `irreversible: true` for an effect without a complete undo, and
85
+ * `idempotent: true` for one
83
86
  * that a repeated call leaves as it was.
84
87
  */
85
88
  readonly hints?: ActionHints;
package/dist/analyze.js CHANGED
@@ -641,8 +641,8 @@ function resolveActions(candidate, scope) {
641
641
  }
642
642
  return actions;
643
643
  }
644
- const HINT_NAMES = ["destructive", "idempotent"];
645
- /** The action's `hints`: an object of booleans, `destructive` and `idempotent`. */
644
+ const HINT_NAMES = ["destructive", "irreversible", "idempotent"];
645
+ /** The action's `hints`: an object of booleans, `destructive`, `irreversible` and `idempotent`. */
646
646
  function resolveHints(path, action, report) {
647
647
  const declared = action.hints;
648
648
  if (declared === undefined) {
@@ -108,10 +108,11 @@ export declare const ActionManifest: Schema.Struct<{
108
108
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
109
109
  /**
110
110
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
111
- * declares hints; absent reads as destructive and not idempotent.
111
+ * declares hints; absent safety labels require agent confirmation for compatibility.
112
112
  */
113
113
  readonly hints: Schema.optionalKey<Schema.Struct<{
114
114
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
115
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
115
116
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
116
117
  }>>;
117
118
  }>;
@@ -244,10 +245,11 @@ export declare const EntityManifest: Schema.Struct<{
244
245
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
245
246
  /**
246
247
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
247
- * declares hints; absent reads as destructive and not idempotent.
248
+ * declares hints; absent safety labels require agent confirmation for compatibility.
248
249
  */
249
250
  readonly hints: Schema.optionalKey<Schema.Struct<{
250
251
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
252
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
251
253
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
252
254
  }>>;
253
255
  }>>;
@@ -391,10 +393,11 @@ export declare const OntologyManifestWire: Schema.encodeKeys<Schema.Struct<{
391
393
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
392
394
  /**
393
395
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
394
- * declares hints; absent reads as destructive and not idempotent.
396
+ * declares hints; absent safety labels require agent confirmation for compatibility.
395
397
  */
396
398
  readonly hints: Schema.optionalKey<Schema.Struct<{
397
399
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
400
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
398
401
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
399
402
  }>>;
400
403
  }>>;
@@ -541,10 +544,11 @@ export declare const OntologyManifest: Schema.Union<readonly [Schema.encodeKeys<
541
544
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
542
545
  /**
543
546
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
544
- * declares hints; absent reads as destructive and not idempotent.
547
+ * declares hints; absent safety labels require agent confirmation for compatibility.
545
548
  */
546
549
  readonly hints: Schema.optionalKey<Schema.Struct<{
547
550
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
551
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
548
552
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
549
553
  }>>;
550
554
  }>>;
@@ -690,10 +694,11 @@ export declare const OntologyManifest: Schema.Union<readonly [Schema.encodeKeys<
690
694
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
691
695
  /**
692
696
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
693
- * declares hints; absent reads as destructive and not idempotent.
697
+ * declares hints; absent safety labels require agent confirmation for compatibility.
694
698
  */
695
699
  readonly hints: Schema.optionalKey<Schema.Struct<{
696
700
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
701
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
697
702
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
698
703
  }>>;
699
704
  }>>;
@@ -789,6 +794,7 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
789
794
  readonly connections?: readonly string[] | undefined;
790
795
  readonly hints?: {
791
796
  readonly destructive?: boolean | undefined;
797
+ readonly irreversible?: boolean | undefined;
792
798
  readonly idempotent?: boolean | undefined;
793
799
  } | undefined;
794
800
  }[];
@@ -894,6 +900,7 @@ export declare const decodeManifestDocument: (value: unknown) => Effect.Effect<{
894
900
  readonly connections?: readonly string[] | undefined;
895
901
  readonly hints?: {
896
902
  readonly destructive?: boolean | undefined;
903
+ readonly irreversible?: boolean | undefined;
897
904
  readonly idempotent?: boolean | undefined;
898
905
  } | undefined;
899
906
  }[];
package/dist/manifest.js CHANGED
@@ -104,10 +104,11 @@ export const ActionManifest = Schema.Struct({
104
104
  connections: Schema.optionalKey(Schema.Array(Schema.String)),
105
105
  /**
106
106
  * How the action behaves, as MCP tool annotations tell agents. Written only when the action
107
- * declares hints; absent reads as destructive and not idempotent.
107
+ * declares hints; absent safety labels require agent confirmation for compatibility.
108
108
  */
109
109
  hints: Schema.optionalKey(Schema.Struct({
110
110
  destructive: Schema.optionalKey(Schema.Boolean),
111
+ irreversible: Schema.optionalKey(Schema.Boolean),
111
112
  idempotent: Schema.optionalKey(Schema.Boolean),
112
113
  })),
113
114
  });
@@ -128,6 +128,7 @@ export declare const MigrationPlan: Schema.Struct<{
128
128
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
129
129
  readonly hints: Schema.optionalKey<Schema.Struct<{
130
130
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
131
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
131
132
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
132
133
  }>>;
133
134
  }>>;
@@ -240,6 +241,7 @@ export declare const MigrationPlan: Schema.Struct<{
240
241
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
241
242
  readonly hints: Schema.optionalKey<Schema.Struct<{
242
243
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
244
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
243
245
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
244
246
  }>>;
245
247
  }>>;
@@ -6,6 +6,8 @@ import type { PlannedAction, PlannedEntity } from "../plan.js";
6
6
  export interface McpToolAnnotations {
7
7
  readonly readOnlyHint: boolean;
8
8
  readonly destructiveHint?: boolean;
9
+ /** Ontology extension: whether the action has an effect without a complete undo. */
10
+ readonly irreversibleHint?: boolean;
9
11
  readonly idempotentHint?: boolean;
10
12
  /** Whether the tool reaches beyond the ontology: true for actions that call connections. */
11
13
  readonly openWorldHint: boolean;
@@ -65,22 +65,28 @@ function historyInput(label) {
65
65
  const READ = { readOnlyHint: true, openWorldHint: false };
66
66
  /** The hints of generated actions, which only their name tells apart. */
67
67
  const GENERATED_HINTS = {
68
- create: { destructive: false, idempotent: false },
69
- update: { destructive: true, idempotent: true },
70
- delete: { destructive: true, idempotent: true },
71
- link: { destructive: false, idempotent: true },
72
- unlink: { destructive: true, idempotent: true },
68
+ create: { destructive: false, irreversible: false, idempotent: false },
69
+ update: { destructive: false, irreversible: false, idempotent: true },
70
+ delete: { destructive: true, irreversible: false, idempotent: true },
71
+ link: { destructive: false, irreversible: false, idempotent: true },
72
+ unlink: { destructive: false, irreversible: false, idempotent: true },
73
73
  };
74
74
  /** MCP's defaults for a tool that writes: it may destroy, and repeating it may change more. */
75
- const AUTHORED_DEFAULT = { destructive: true, idempotent: false };
75
+ const AUTHORED_DEFAULT = {
76
+ destructive: true,
77
+ irreversible: false,
78
+ idempotent: false,
79
+ };
76
80
  function hintsOf(planned, { name, generated }) {
77
81
  if (generated) {
78
82
  return name.startsWith(GENERATED_UPSERT_PREFIX)
79
- ? { destructive: true, idempotent: true }
83
+ ? { destructive: false, irreversible: false, idempotent: true }
80
84
  : (GENERATED_HINTS[name] ?? AUTHORED_DEFAULT);
81
85
  }
82
86
  const declared = planned.resolved.actions.find((candidate) => candidate.name === name)?.hints;
83
- return { ...AUTHORED_DEFAULT, ...declared };
87
+ // A safety label classifies the action; idempotence alone does not opt out of confirmation.
88
+ const classified = declared?.destructive !== undefined || declared?.irreversible !== undefined;
89
+ return { ...AUTHORED_DEFAULT, ...(classified ? { destructive: false } : {}), ...declared };
84
90
  }
85
91
  /** How an agent reaches the records a relationship leads to, with the tools it has. */
86
92
  function relatedHint(planned, traversal) {
@@ -208,6 +214,7 @@ function actionTool(planned, plannedAction) {
208
214
  annotations: {
209
215
  readOnlyHint: false,
210
216
  destructiveHint: hints.destructive,
217
+ irreversibleHint: hints.irreversible,
211
218
  idempotentHint: hints.idempotent,
212
219
  openWorldHint: action.connections.length > 0,
213
220
  },
@@ -235,6 +235,7 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
235
235
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
236
236
  readonly hints: Schema.optionalKey<Schema.Struct<{
237
237
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
238
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
238
239
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
239
240
  }>>;
240
241
  }>>;
@@ -353,6 +354,7 @@ export declare const Catalog: Schema.decodeTo<Schema.Struct<{
353
354
  readonly connections: Schema.optionalKey<Schema.$Array<Schema.String>>;
354
355
  readonly hints: Schema.optionalKey<Schema.Struct<{
355
356
  readonly destructive: Schema.optionalKey<Schema.Boolean>;
357
+ readonly irreversible: Schema.optionalKey<Schema.Boolean>;
356
358
  readonly idempotent: Schema.optionalKey<Schema.Boolean>;
357
359
  }>>;
358
360
  }>>;
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { Effect } from "effect";
3
- import { acceptsBlobContentType, BLOB_UPLOAD_ANNOTATION, MAX_BLOB_BYTES } from "../blob.js";
3
+ import { acceptsBlobContentType, isBlobContentType, BLOB_UPLOAD_ANNOTATION, MAX_BLOB_BYTES, } from "../blob.js";
4
4
  import { ActionRejected, Conflict, NotFound } from "../errors.js";
5
5
  import { canonicalJson } from "../manifest.js";
6
6
  import { actionInputJsonSchema } from "../runtime/generated-actions.js";
@@ -142,7 +142,7 @@ export function attachmentRuntime(ontology, sql, provider) {
142
142
  input.filename === "" ||
143
143
  input.filename.length > 255 ||
144
144
  /[\x00-\x1f\x7f/\\]/.test(input.filename) ||
145
- !/^[\w.+-]+\/[\w.+-]+$/.test(input.contentType) ||
145
+ !isBlobContentType(input.contentType) ||
146
146
  !Number.isInteger(input.size) ||
147
147
  input.size <= 0 ||
148
148
  !SHA256.test(input.sha256)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segmentstream-cloud/ontology",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "Define a SegmentStream workspace app's ontology: entities, relationships, roles and actions, written with Effect.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "SegmentStream",