@intutic/shared-types 2.9.1 → 3.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 (119) hide show
  1. package/dist/decide.d.ts +269 -0
  2. package/dist/decide.d.ts.map +1 -0
  3. package/dist/decide.js +781 -0
  4. package/dist/decide.js.map +1 -0
  5. package/dist/decideEngine.d.ts +195 -0
  6. package/dist/decideEngine.d.ts.map +1 -0
  7. package/dist/decideEngine.js +720 -0
  8. package/dist/decideEngine.js.map +1 -0
  9. package/dist/decideEvaluate.d.ts +250 -0
  10. package/dist/decideEvaluate.d.ts.map +1 -0
  11. package/dist/decideEvaluate.js +518 -0
  12. package/dist/decideEvaluate.js.map +1 -0
  13. package/dist/decideHostGuard.d.ts +56 -0
  14. package/dist/decideHostGuard.d.ts.map +1 -0
  15. package/dist/decideHostGuard.js +200 -0
  16. package/dist/decideHostGuard.js.map +1 -0
  17. package/dist/decideLog.d.ts +287 -0
  18. package/dist/decideLog.d.ts.map +1 -0
  19. package/dist/decideLog.js +543 -0
  20. package/dist/decideLog.js.map +1 -0
  21. package/dist/decideLogRedaction.d.ts +12 -0
  22. package/dist/decideLogRedaction.d.ts.map +1 -0
  23. package/dist/decideLogRedaction.js +19 -0
  24. package/dist/decideLogRedaction.js.map +1 -0
  25. package/dist/decideRecord.d.ts +128 -0
  26. package/dist/decideRecord.d.ts.map +1 -0
  27. package/dist/decideRecord.js +266 -0
  28. package/dist/decideRecord.js.map +1 -0
  29. package/dist/decideSdk.d.ts +86 -0
  30. package/dist/decideSdk.d.ts.map +1 -0
  31. package/dist/decideSdk.js +151 -0
  32. package/dist/decideSdk.js.map +1 -0
  33. package/dist/errors.d.ts +11 -1
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/errors.js +12 -1
  36. package/dist/errors.js.map +1 -1
  37. package/dist/gateLimits.d.ts +27 -0
  38. package/dist/gateLimits.d.ts.map +1 -1
  39. package/dist/gateLimits.js +29 -1
  40. package/dist/gateLimits.js.map +1 -1
  41. package/dist/gateRuleRecord.d.ts +244 -0
  42. package/dist/gateRuleRecord.d.ts.map +1 -0
  43. package/dist/gateRuleRecord.js +474 -0
  44. package/dist/gateRuleRecord.js.map +1 -0
  45. package/dist/guardrailIr.d.ts +301 -10
  46. package/dist/guardrailIr.d.ts.map +1 -1
  47. package/dist/guardrailIr.js +86 -7
  48. package/dist/guardrailIr.js.map +1 -1
  49. package/dist/guardrailRender.d.ts +35 -2
  50. package/dist/guardrailRender.d.ts.map +1 -1
  51. package/dist/guardrailRender.js +133 -7
  52. package/dist/guardrailRender.js.map +1 -1
  53. package/dist/index.d.ts +28 -6
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +162 -8
  56. package/dist/index.js.map +1 -1
  57. package/dist/lanes.d.ts +94 -0
  58. package/dist/lanes.d.ts.map +1 -0
  59. package/dist/lanes.js +160 -0
  60. package/dist/lanes.js.map +1 -0
  61. package/dist/loopVerify.d.ts +49 -0
  62. package/dist/loopVerify.d.ts.map +1 -0
  63. package/dist/loopVerify.js +77 -0
  64. package/dist/loopVerify.js.map +1 -0
  65. package/dist/mcpBudgets.d.ts +8 -8
  66. package/dist/notifications.d.ts +1 -1
  67. package/dist/notifications.d.ts.map +1 -1
  68. package/dist/notifications.js +14 -0
  69. package/dist/notifications.js.map +1 -1
  70. package/dist/providers.d.ts +13 -0
  71. package/dist/providers.d.ts.map +1 -1
  72. package/dist/providers.js +31 -0
  73. package/dist/providers.js.map +1 -1
  74. package/dist/refusalDetail.d.ts +58 -0
  75. package/dist/refusalDetail.d.ts.map +1 -0
  76. package/dist/refusalDetail.js +128 -0
  77. package/dist/refusalDetail.js.map +1 -0
  78. package/dist/responses/decide.d.ts +1858 -0
  79. package/dist/responses/decide.d.ts.map +1 -0
  80. package/dist/responses/decide.js +166 -0
  81. package/dist/responses/decide.js.map +1 -0
  82. package/dist/responses/gateways.d.ts +745 -101
  83. package/dist/responses/gateways.d.ts.map +1 -1
  84. package/dist/responses/gateways.js +34 -2
  85. package/dist/responses/gateways.js.map +1 -1
  86. package/dist/responses/guardrails.d.ts +1434 -144
  87. package/dist/responses/guardrails.d.ts.map +1 -1
  88. package/dist/responses/identity.d.ts +6 -6
  89. package/dist/responses/index.d.ts +1 -0
  90. package/dist/responses/index.d.ts.map +1 -1
  91. package/dist/responses/index.js +1 -0
  92. package/dist/responses/index.js.map +1 -1
  93. package/dist/responses/integrations.d.ts +84 -84
  94. package/dist/responses/observability.d.ts +272 -92
  95. package/dist/responses/observability.d.ts.map +1 -1
  96. package/dist/responses/observability.js +31 -1
  97. package/dist/responses/observability.js.map +1 -1
  98. package/dist/responses/rules.d.ts +1026 -187
  99. package/dist/responses/rules.d.ts.map +1 -1
  100. package/dist/responses/rules.js +47 -1
  101. package/dist/responses/rules.js.map +1 -1
  102. package/dist/scopeMatch.d.ts +156 -0
  103. package/dist/scopeMatch.d.ts.map +1 -0
  104. package/dist/scopeMatch.js +451 -0
  105. package/dist/scopeMatch.js.map +1 -0
  106. package/dist/sopKeySince.d.ts +41 -0
  107. package/dist/sopKeySince.d.ts.map +1 -0
  108. package/dist/sopKeySince.js +81 -0
  109. package/dist/sopKeySince.js.map +1 -0
  110. package/dist/testOutcome.d.ts +164 -0
  111. package/dist/testOutcome.d.ts.map +1 -0
  112. package/dist/testOutcome.js +454 -0
  113. package/dist/testOutcome.js.map +1 -0
  114. package/dist/upstreamRetry.d.ts +2 -2
  115. package/dist/workspaceSettings.d.ts +91 -0
  116. package/dist/workspaceSettings.d.ts.map +1 -1
  117. package/dist/workspaceSettings.js +48 -2
  118. package/dist/workspaceSettings.js.map +1 -1
  119. package/package.json +2 -2
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Decide rules in the policy snapshot: the `@format` record, the `@decide`
3
+ * record, and the id and reason of a rule's fallback line.
4
+ *
5
+ * A gate that cannot evaluate a decide rule must refuse what the rule covers,
6
+ * never skip it. So the snapshot writer emits, for each rule a gate is to
7
+ * enforce, two lines inside the digest:
8
+ *
9
+ * - an `@decide` record of five tab-separated columns (the tag, the fallback
10
+ * line's id or `-`, the stamped rollout, the comma-joined tools, and base64
11
+ * of the record's canonical JSON). Every 2.x rule parser needs six columns,
12
+ * so it skips the record;
13
+ * - a fallback line, an ordinary `block` rule on the rule's tools whose id
14
+ * starts `decide-fallback:`, which every gate since 2.0 enforces.
15
+ *
16
+ * A gate that reads decide rules skips a fallback line only when a record
17
+ * with that `fallbackId` decodes ({@link decodeDecideRecord}): a damaged
18
+ * record leaves its fallback line in force, which refuses. The `@format`
19
+ * record names the snapshot format, 2 once any decide line is written; a
20
+ * snapshot without one is byte-identical to a format 1 snapshot.
21
+ *
22
+ * Self-contained (no imports, no module state) so `@intutic/gate` can carry
23
+ * a byte-identical copy (`packages/gate-js/src/decideRecord.ts`) and the
24
+ * JavaScript hook gates can embed the decoder ({@link DECIDE_RECORD_JS_SOURCE}).
25
+ * The rule itself is checked by the caller's {@link DecideRuleCheck}, which
26
+ * runs `validateDecideRule` and `decideRuleHash` from `./decide.ts`. The
27
+ * Python gate's port is `intutic_clawde/gate/decide_record.py`; both run the
28
+ * `record` group of `fixtures/decide-vectors.json`.
29
+ *
30
+ * @module
31
+ */
32
+ import type { DecideRule } from './decide.js';
33
+ /** First column of the snapshot format record: `@format<TAB><integer>`. */
34
+ export declare const SNAPSHOT_FORMAT_RECORD_TAG = "@format";
35
+ /**
36
+ * The newest snapshot format a gate of this generation reads: 3, which adds
37
+ * the `@rule` records of the loops, graphs and gates release (`./gateRuleRecord.ts`). A gate reading a
38
+ * newer one reports `snapshot_format_newer` and enforces the fallback lines
39
+ * of the records it does not know.
40
+ */
41
+ export declare const SNAPSHOT_FORMAT = 3;
42
+ /**
43
+ * The format a snapshot carrying decide lines and no `@rule` record is
44
+ * written in, so such a snapshot stays byte-identical to one written before rule records existed. One
45
+ * with any `@rule` record is format 3 (`RULE_SNAPSHOT_FORMAT`).
46
+ */
47
+ export declare const DECIDE_SNAPSHOT_FORMAT = 2;
48
+ /** First column of a decide rule's record. */
49
+ export declare const DECIDE_RECORD_TAG = "@decide";
50
+ /** The id prefix of a decide rule's fallback line. */
51
+ export declare const DECIDE_FALLBACK_PREFIX = "decide-fallback:";
52
+ /** A record carries the first this many bytes of its SOP's body. */
53
+ export declare const DECIDE_RULE_TEXT_MAX_BYTES = 4096;
54
+ /** The longest SOP title a record carries, in code points. */
55
+ export declare const DECIDE_SOP_TITLE_MAX_CHARS = 80;
56
+ /** Why a rule's stamped rollout is what it is. Only `rule` can stamp `enforce`. */
57
+ export type DecideStamp = 'rule' | 'local-disable' | 'server-shadow' | 'silent-log' | 'no-pins';
58
+ /** A calibration a rule may enforce under: the model digest it was evaluated on, and what the evaluation fitted. */
59
+ export interface DecidePin {
60
+ modelDigest: string;
61
+ temperature?: Record<string, number>;
62
+ band?: {
63
+ yes_at: number;
64
+ no_below: number;
65
+ };
66
+ combiner?: {
67
+ bias: number;
68
+ weights: Record<string, number>;
69
+ };
70
+ evalRunId: string;
71
+ source: 'local' | 'server';
72
+ }
73
+ /** One `@decide` record's payload. */
74
+ export interface DecideRecord {
75
+ v: 1;
76
+ /** `<sopId>#<rule id>`. */
77
+ ref: string;
78
+ ruleHash: string;
79
+ /** The paired fallback line's id; null when the rule is stamped shadow and has none. */
80
+ fallbackId: string | null;
81
+ rollout: 'shadow' | 'enforce';
82
+ stampedBy: DecideStamp;
83
+ /** The rule as its `decide:` line carries it. */
84
+ ir: DecideRule;
85
+ sopTitle: string;
86
+ ruleText: string;
87
+ pins: DecidePin[];
88
+ }
89
+ /**
90
+ * Checks a record's rule: the rule's `ruleHash` when it is a valid decide
91
+ * rule, null when it is not. Callers pass `validateDecideRule` then
92
+ * `decideRuleHash`, with their own SHA-256.
93
+ */
94
+ export type DecideRuleCheck = (ir: unknown) => string | null;
95
+ /** `@format<TAB><format>`: 2 ({@link DECIDE_SNAPSHOT_FORMAT}) unless the writer passes 3 for a snapshot with `@rule` records. */
96
+ export declare function encodeSnapshotFormatRecord(format?: number): string;
97
+ /** The format a `@format` line names: a positive integer, or null for any other line or a damaged one. */
98
+ export declare function decodeSnapshotFormatRecord(line: string): number | null;
99
+ /** `ruleRef`: `<sopId>#<rule id>`. */
100
+ export declare function decideRuleRef(sopId: string, ruleId: string): string;
101
+ /** `fallbackId`: `decide-fallback:<rule id>@<first 12 hex of sha256(sopId)>`. `sha256Hex` hashes a string's UTF-8 bytes. */
102
+ export declare function decideFallbackId(ruleId: string, sopId: string, sha256Hex: (text: string) => string): string;
103
+ /** What an older gate says when a fallback line refuses. The writer scrubs and bounds it like any reason. */
104
+ export declare function decideFallbackReason(ruleId: string, sopTitle: string): string;
105
+ /** A SOP body as a record carries it: its first {@link DECIDE_RULE_TEXT_MAX_BYTES} bytes of UTF-8, never splitting a character. */
106
+ export declare function decideRuleText(body: string): string;
107
+ /** A pin as a record carries it, or null. Unknown fields refuse: a pin a reader cannot apply must not be read as a weaker one. */
108
+ export declare function parseDecidePin(value: unknown): DecidePin | null;
109
+ /** The record as one `.rules` line. */
110
+ export declare function encodeDecideRecord(record: DecideRecord): string;
111
+ /**
112
+ * Reads an `@decide` line back. Null, and so the fallback line stays in
113
+ * force, for any other line or when: it does not have five columns; the
114
+ * base64 or JSON is damaged; the payload has a field this reader does not
115
+ * know or lacks one; `check` finds the rule invalid or its hash differs from
116
+ * `ruleHash`; the stamp is inconsistent (only a rule stamped `enforce` by
117
+ * `rule` has a fallback line, and it is named for the rule); or columns 2 to
118
+ * 4 disagree with the payload.
119
+ */
120
+ export declare function decodeDecideRecord(line: string, check: DecideRuleCheck): DecideRecord | null;
121
+ /**
122
+ * The decoders above as JavaScript source, for the hook gates to embed. The
123
+ * gate defines `check` with the embedded `./decide.ts` source and its own
124
+ * SHA-256. A CommonJS build names an exported constant `exports.X` inside a
125
+ * function; the source defines each as a plain `X`, so that prefix is dropped.
126
+ */
127
+ export declare const DECIDE_RECORD_JS_SOURCE: string;
128
+ //# sourceMappingURL=decideRecord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decideRecord.d.ts","sourceRoot":"","sources":["../src/decideRecord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ /**
3
+ * Decide rules in the policy snapshot: the `@format` record, the `@decide`
4
+ * record, and the id and reason of a rule's fallback line.
5
+ *
6
+ * A gate that cannot evaluate a decide rule must refuse what the rule covers,
7
+ * never skip it. So the snapshot writer emits, for each rule a gate is to
8
+ * enforce, two lines inside the digest:
9
+ *
10
+ * - an `@decide` record of five tab-separated columns (the tag, the fallback
11
+ * line's id or `-`, the stamped rollout, the comma-joined tools, and base64
12
+ * of the record's canonical JSON). Every 2.x rule parser needs six columns,
13
+ * so it skips the record;
14
+ * - a fallback line, an ordinary `block` rule on the rule's tools whose id
15
+ * starts `decide-fallback:`, which every gate since 2.0 enforces.
16
+ *
17
+ * A gate that reads decide rules skips a fallback line only when a record
18
+ * with that `fallbackId` decodes ({@link decodeDecideRecord}): a damaged
19
+ * record leaves its fallback line in force, which refuses. The `@format`
20
+ * record names the snapshot format, 2 once any decide line is written; a
21
+ * snapshot without one is byte-identical to a format 1 snapshot.
22
+ *
23
+ * Self-contained (no imports, no module state) so `@intutic/gate` can carry
24
+ * a byte-identical copy (`packages/gate-js/src/decideRecord.ts`) and the
25
+ * JavaScript hook gates can embed the decoder ({@link DECIDE_RECORD_JS_SOURCE}).
26
+ * The rule itself is checked by the caller's {@link DecideRuleCheck}, which
27
+ * runs `validateDecideRule` and `decideRuleHash` from `./decide.ts`. The
28
+ * Python gate's port is `intutic_clawde/gate/decide_record.py`; both run the
29
+ * `record` group of `fixtures/decide-vectors.json`.
30
+ *
31
+ * @module
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.DECIDE_RECORD_JS_SOURCE = exports.DECIDE_SOP_TITLE_MAX_CHARS = exports.DECIDE_RULE_TEXT_MAX_BYTES = exports.DECIDE_FALLBACK_PREFIX = exports.DECIDE_RECORD_TAG = exports.DECIDE_SNAPSHOT_FORMAT = exports.SNAPSHOT_FORMAT = exports.SNAPSHOT_FORMAT_RECORD_TAG = void 0;
35
+ exports.encodeSnapshotFormatRecord = encodeSnapshotFormatRecord;
36
+ exports.decodeSnapshotFormatRecord = decodeSnapshotFormatRecord;
37
+ exports.decideRuleRef = decideRuleRef;
38
+ exports.decideFallbackId = decideFallbackId;
39
+ exports.decideFallbackReason = decideFallbackReason;
40
+ exports.decideRuleText = decideRuleText;
41
+ exports.parseDecidePin = parseDecidePin;
42
+ exports.encodeDecideRecord = encodeDecideRecord;
43
+ exports.decodeDecideRecord = decodeDecideRecord;
44
+ /** First column of the snapshot format record: `@format<TAB><integer>`. */
45
+ exports.SNAPSHOT_FORMAT_RECORD_TAG = '@format';
46
+ /**
47
+ * The newest snapshot format a gate of this generation reads: 3, which adds
48
+ * the `@rule` records of the loops, graphs and gates release (`./gateRuleRecord.ts`). A gate reading a
49
+ * newer one reports `snapshot_format_newer` and enforces the fallback lines
50
+ * of the records it does not know.
51
+ */
52
+ exports.SNAPSHOT_FORMAT = 3;
53
+ /**
54
+ * The format a snapshot carrying decide lines and no `@rule` record is
55
+ * written in, so such a snapshot stays byte-identical to one written before rule records existed. One
56
+ * with any `@rule` record is format 3 (`RULE_SNAPSHOT_FORMAT`).
57
+ */
58
+ exports.DECIDE_SNAPSHOT_FORMAT = 2;
59
+ /** First column of a decide rule's record. */
60
+ exports.DECIDE_RECORD_TAG = '@decide';
61
+ /** The id prefix of a decide rule's fallback line. */
62
+ exports.DECIDE_FALLBACK_PREFIX = 'decide-fallback:';
63
+ /** A record carries the first this many bytes of its SOP's body. */
64
+ exports.DECIDE_RULE_TEXT_MAX_BYTES = 4096;
65
+ /** The longest SOP title a record carries, in code points. */
66
+ exports.DECIDE_SOP_TITLE_MAX_CHARS = 80;
67
+ const DECIDE_STAMPS = ['rule', 'local-disable', 'server-shadow', 'silent-log', 'no-pins'];
68
+ const DECIDE_RECORD_FIELDS = ['fallbackId', 'ir', 'pins', 'ref', 'rollout', 'ruleHash', 'ruleText', 'sopTitle', 'stampedBy', 'v'];
69
+ const DECIDE_PIN_FIELDS = ['band', 'combiner', 'evalRunId', 'modelDigest', 'source', 'temperature'];
70
+ /** `@format<TAB><format>`: 2 ({@link DECIDE_SNAPSHOT_FORMAT}) unless the writer passes 3 for a snapshot with `@rule` records. */
71
+ function encodeSnapshotFormatRecord(format = exports.DECIDE_SNAPSHOT_FORMAT) {
72
+ return `${exports.SNAPSHOT_FORMAT_RECORD_TAG}\t${format}`;
73
+ }
74
+ /** The format a `@format` line names: a positive integer, or null for any other line or a damaged one. */
75
+ function decodeSnapshotFormatRecord(line) {
76
+ const f = line.split('\t');
77
+ if (f.length !== 2 || f[0] !== exports.SNAPSHOT_FORMAT_RECORD_TAG || !/^[1-9][0-9]{0,5}$/.test(f[1]))
78
+ return null;
79
+ return Number(f[1]);
80
+ }
81
+ /** `ruleRef`: `<sopId>#<rule id>`. */
82
+ function decideRuleRef(sopId, ruleId) {
83
+ return `${sopId}#${ruleId}`;
84
+ }
85
+ /** `fallbackId`: `decide-fallback:<rule id>@<first 12 hex of sha256(sopId)>`. `sha256Hex` hashes a string's UTF-8 bytes. */
86
+ function decideFallbackId(ruleId, sopId, sha256Hex) {
87
+ return `${exports.DECIDE_FALLBACK_PREFIX}${ruleId}@${sha256Hex(sopId).slice(0, 12)}`;
88
+ }
89
+ /** What an older gate says when a fallback line refuses. The writer scrubs and bounds it like any reason. */
90
+ function decideFallbackReason(ruleId, sopTitle) {
91
+ return `Decide rule ${ruleId} (${sopTitle}): upgrade to Intutic 3.0 to evaluate this rule`;
92
+ }
93
+ /** A SOP body as a record carries it: its first {@link DECIDE_RULE_TEXT_MAX_BYTES} bytes of UTF-8, never splitting a character. */
94
+ function decideRuleText(body) {
95
+ let bytes = 0;
96
+ let end = 0;
97
+ for (const ch of body) {
98
+ const cp = ch.codePointAt(0);
99
+ const n = cp < 0x80 ? 1 : cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4;
100
+ if (bytes + n > exports.DECIDE_RULE_TEXT_MAX_BYTES)
101
+ break;
102
+ bytes += n;
103
+ end += ch.length;
104
+ }
105
+ return body.slice(0, end);
106
+ }
107
+ function decideIsObject(v) {
108
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
109
+ }
110
+ function decideIsNumber(v) {
111
+ return typeof v === 'number' && isFinite(v);
112
+ }
113
+ /** Every own key is one of `allowed`. */
114
+ function decideOnlyFields(obj, allowed) {
115
+ return Object.keys(obj).every((k) => allowed.indexOf(k) !== -1);
116
+ }
117
+ function decideNumberMap(v) {
118
+ return decideIsObject(v) && Object.keys(v).every((k) => decideIsNumber(v[k]));
119
+ }
120
+ /** JSON with keys sorted at every level and no whitespace, so a record is byte-stable. */
121
+ function decideCanonicalRecordJson(v) {
122
+ if (Array.isArray(v))
123
+ return '[' + v.map(decideCanonicalRecordJson).join(',') + ']';
124
+ if (decideIsObject(v)) {
125
+ return '{' + Object.keys(v).sort().map((k) => JSON.stringify(k) + ':' + decideCanonicalRecordJson(v[k])).join(',') + '}';
126
+ }
127
+ return JSON.stringify(v);
128
+ }
129
+ function decideToBase64(text) {
130
+ let binary = '';
131
+ for (const byte of new TextEncoder().encode(text))
132
+ binary += String.fromCharCode(byte);
133
+ return btoa(binary);
134
+ }
135
+ /** Standard base64 back to UTF-8 text. Throws on anything but the canonical encoding of valid UTF-8. */
136
+ function decideFromBase64(b64) {
137
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));
138
+ if (decideToBase64(text) !== b64)
139
+ throw new Error('not canonical base64');
140
+ return text;
141
+ }
142
+ /** A pin as a record carries it, or null. Unknown fields refuse: a pin a reader cannot apply must not be read as a weaker one. */
143
+ function parseDecidePin(value) {
144
+ if (!decideIsObject(value) || !decideOnlyFields(value, DECIDE_PIN_FIELDS))
145
+ return null;
146
+ const { modelDigest, evalRunId, source, temperature, band, combiner } = value;
147
+ if (typeof modelDigest !== 'string' || modelDigest === '' || typeof evalRunId !== 'string' || evalRunId === '')
148
+ return null;
149
+ if (source !== 'local' && source !== 'server')
150
+ return null;
151
+ if (temperature !== undefined && !decideNumberMap(temperature))
152
+ return null;
153
+ if (band !== undefined) {
154
+ if (!decideIsObject(band) || !decideOnlyFields(band, ['yes_at', 'no_below']))
155
+ return null;
156
+ const { yes_at, no_below } = band;
157
+ if (!decideIsNumber(yes_at) || !decideIsNumber(no_below) || !(yes_at > 0.5 && yes_at < 1 && no_below > 0 && no_below < yes_at))
158
+ return null;
159
+ }
160
+ if (combiner !== undefined) {
161
+ if (!decideIsObject(combiner) || !decideOnlyFields(combiner, ['bias', 'weights']))
162
+ return null;
163
+ if (!decideIsNumber(combiner.bias) || !decideNumberMap(combiner.weights))
164
+ return null;
165
+ }
166
+ return value;
167
+ }
168
+ /** The record's tools column: the rule's tools, distinct and sorted, joined by commas. */
169
+ function decideToolsColumn(ir) {
170
+ return (ir.tools ?? []).filter((t, i, all) => all.indexOf(t) === i).sort().join(',');
171
+ }
172
+ /** The record as one `.rules` line. */
173
+ function encodeDecideRecord(record) {
174
+ return [
175
+ exports.DECIDE_RECORD_TAG,
176
+ record.fallbackId ?? '-',
177
+ record.rollout,
178
+ decideToolsColumn(record.ir),
179
+ decideToBase64(decideCanonicalRecordJson(record)),
180
+ ].join('\t');
181
+ }
182
+ /**
183
+ * Reads an `@decide` line back. Null, and so the fallback line stays in
184
+ * force, for any other line or when: it does not have five columns; the
185
+ * base64 or JSON is damaged; the payload has a field this reader does not
186
+ * know or lacks one; `check` finds the rule invalid or its hash differs from
187
+ * `ruleHash`; the stamp is inconsistent (only a rule stamped `enforce` by
188
+ * `rule` has a fallback line, and it is named for the rule); or columns 2 to
189
+ * 4 disagree with the payload.
190
+ */
191
+ function decodeDecideRecord(line, check) {
192
+ const f = line.split('\t');
193
+ if (f.length !== 5 || f[0] !== exports.DECIDE_RECORD_TAG || !f[4])
194
+ return null;
195
+ let r;
196
+ try {
197
+ r = JSON.parse(decideFromBase64(f[4]));
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ if (!decideIsObject(r) || !decideOnlyFields(r, DECIDE_RECORD_FIELDS))
203
+ return null;
204
+ if (DECIDE_RECORD_FIELDS.some((k) => !Object.prototype.hasOwnProperty.call(r, k)))
205
+ return null;
206
+ const ir = r.ir;
207
+ if (r.v !== 1 || !decideIsObject(ir) || typeof ir.id !== 'string')
208
+ return null;
209
+ if (typeof r.ruleHash !== 'string' || !/^[0-9a-f]{64}$/.test(r.ruleHash) || check(ir) !== r.ruleHash)
210
+ return null;
211
+ if (typeof r.ref !== 'string' || r.ref.length <= ir.id.length + 1 || r.ref.slice(-(ir.id.length + 1)) !== '#' + ir.id)
212
+ return null;
213
+ if (r.rollout !== 'shadow' && r.rollout !== 'enforce')
214
+ return null;
215
+ if (typeof r.stampedBy !== 'string' || DECIDE_STAMPS.indexOf(r.stampedBy) === -1)
216
+ return null;
217
+ if (r.rollout === 'enforce') {
218
+ if (r.stampedBy !== 'rule' || typeof r.fallbackId !== 'string')
219
+ return null;
220
+ const head = exports.DECIDE_FALLBACK_PREFIX + ir.id + '@';
221
+ if (r.fallbackId.slice(0, head.length) !== head || !/^[0-9a-f]{12}$/.test(r.fallbackId.slice(head.length)))
222
+ return null;
223
+ }
224
+ else if (r.fallbackId !== null) {
225
+ return null;
226
+ }
227
+ if (typeof r.sopTitle !== 'string' || /[\t\r\n]/.test(r.sopTitle) || Array.from(r.sopTitle).length > exports.DECIDE_SOP_TITLE_MAX_CHARS)
228
+ return null;
229
+ if (typeof r.ruleText !== 'string' || new TextEncoder().encode(r.ruleText).length > exports.DECIDE_RULE_TEXT_MAX_BYTES)
230
+ return null;
231
+ if (!Array.isArray(r.pins) || r.pins.some((p) => parseDecidePin(p) === null))
232
+ return null;
233
+ const record = r;
234
+ if (f[1] !== (record.fallbackId ?? '-') || f[2] !== record.rollout || f[3] !== decideToolsColumn(record.ir))
235
+ return null;
236
+ return record;
237
+ }
238
+ /**
239
+ * The decoders above as JavaScript source, for the hook gates to embed. The
240
+ * gate defines `check` with the embedded `./decide.ts` source and its own
241
+ * SHA-256. A CommonJS build names an exported constant `exports.X` inside a
242
+ * function; the source defines each as a plain `X`, so that prefix is dropped.
243
+ */
244
+ exports.DECIDE_RECORD_JS_SOURCE = [
245
+ `const SNAPSHOT_FORMAT_RECORD_TAG = ${JSON.stringify(exports.SNAPSHOT_FORMAT_RECORD_TAG)};`,
246
+ `const DECIDE_RECORD_TAG = ${JSON.stringify(exports.DECIDE_RECORD_TAG)};`,
247
+ `const DECIDE_FALLBACK_PREFIX = ${JSON.stringify(exports.DECIDE_FALLBACK_PREFIX)};`,
248
+ `const DECIDE_RULE_TEXT_MAX_BYTES = ${exports.DECIDE_RULE_TEXT_MAX_BYTES};`,
249
+ `const DECIDE_SOP_TITLE_MAX_CHARS = ${exports.DECIDE_SOP_TITLE_MAX_CHARS};`,
250
+ `const DECIDE_STAMPS = ${JSON.stringify(DECIDE_STAMPS)};`,
251
+ `const DECIDE_RECORD_FIELDS = ${JSON.stringify(DECIDE_RECORD_FIELDS)};`,
252
+ `const DECIDE_PIN_FIELDS = ${JSON.stringify(DECIDE_PIN_FIELDS)};`,
253
+ ...[
254
+ decodeSnapshotFormatRecord,
255
+ decideIsObject,
256
+ decideIsNumber,
257
+ decideOnlyFields,
258
+ decideNumberMap,
259
+ decideToBase64,
260
+ decideFromBase64,
261
+ parseDecidePin,
262
+ decideToolsColumn,
263
+ decodeDecideRecord,
264
+ ].map((fn) => fn.toString().replace(/\bexports\./g, '')),
265
+ ].join('\n');
266
+ //# sourceMappingURL=decideRecord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decideRecord.js","sourceRoot":"","sources":["../src/decideRecord.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * `decide(state, questions, opts)` for people building their own agents: ask
3
+ * a self-hosted model a few narrow questions about a state, and get each
4
+ * answer's probability, its three-valued reading through a band and, with a
5
+ * `when`, the outcome a decide rule would reach.
6
+ *
7
+ * The questions are written as a decide rule's `questions` and validated
8
+ * the same way, so wording that asks the model for approval is refused. Every
9
+ * call goes through the engine client and its host guard: a hosted decision
10
+ * API is refused before anything is sent, and there is no way to pass an
11
+ * engine object that skips the guard.
12
+ *
13
+ * Errors are {@link DecideEngineError}s and are thrown, so a caller cannot
14
+ * read an unavailable engine as an answer. The one exception is
15
+ * `too_large`: a state over the limit is never truncated, every answer is
16
+ * unknown, and with a `when` the outcome escalates.
17
+ *
18
+ * Self-contained but for the decide modules beside it, so `@intutic/gate` and
19
+ * `@intutic/clawde` carry byte-identical copies; each wraps it in a `decide()`
20
+ * that reads `~/.intutic/decide/engine.json` and the process environment. The
21
+ * Python port is `intutic_clawde/gate/decide_sdk.py`. All three run
22
+ * `fixtures/decide-sdk-vectors.json`.
23
+ *
24
+ * @module
25
+ */
26
+ import { type ChoiceAtom, type DecideBand, type DecideQuestion, type Tri } from './decide.js';
27
+ import { type DecideAdapter, type DecideEngineConfig } from './decideEngine.js';
28
+ /** The budget when neither `budgetMs` nor `INTUTIC_DECIDE_BUDGET_MS` sets one. */
29
+ export declare const DECIDE_SDK_DEFAULT_BUDGET_MS = 7300;
30
+ /** The engine configuration's path under the home directory. */
31
+ export declare const DECIDE_ENGINE_FILE_PATH: readonly [".intutic", "decide", "engine.json"];
32
+ export interface DecideSdkOptions {
33
+ /** A `when` over the questions. With it, the result carries the rule's outcome. */
34
+ when?: string;
35
+ /** The band for every question without its own (default `{yes_at: 0.85, no_below: 0.15}`). */
36
+ band?: DecideBand;
37
+ /** What an unknown `when` does (default `ask`). */
38
+ escalate?: 'ask' | 'review' | 'refuse';
39
+ /** The engine's configuration. Without it, `engineFile` and `env` give it. */
40
+ engine?: DecideEngineConfig;
41
+ /** The parsed `engine.json`, or null when there is none. */
42
+ engineFile?: unknown;
43
+ /** The environment, which overrides `engineFile`. */
44
+ env?: Record<string, string | undefined>;
45
+ /** Milliseconds for the whole call (default `INTUTIC_DECIDE_BUDGET_MS`, else 7,300). */
46
+ budgetMs?: number;
47
+ }
48
+ export type DecideSdkAnswer = {
49
+ type: 'yes_no';
50
+ p: number | null;
51
+ value: Tri;
52
+ } | {
53
+ type: 'choice';
54
+ perOrder: Array<Record<string, number>> | null;
55
+ /** Each option's atom `q == o`: the mean over the orders, their spread, and its value. */
56
+ options: Record<string, ChoiceAtom>;
57
+ /** The option whose atom is T, or null. At most one can be, because `yes_at` is above 0.5. */
58
+ choice: string | null;
59
+ };
60
+ export interface DecideSdkResult {
61
+ answers: Record<string, DecideSdkAnswer>;
62
+ /** `when`'s value, or null without a `when`. */
63
+ value: Tri | null;
64
+ outcome: 'allow' | 'refuse' | 'escalate' | null;
65
+ code: 'DECIDE_REFUSED' | 'DECIDE_ESCALATED' | null;
66
+ via: 'ask' | 'review' | null;
67
+ /** `too_large` when the state was over 16,384 bytes, a tool call's limit, and nothing was asked. */
68
+ error: 'too_large' | null;
69
+ model: string | null;
70
+ modelDigest: string | null;
71
+ digestStrength: 'strong' | 'weak' | null;
72
+ adapter: DecideAdapter;
73
+ latencyMs: number;
74
+ }
75
+ /**
76
+ * `engine.json`'s parsed content through `read`, or null when the file does
77
+ * not exist. A file that does not parse leaves the engine not configured.
78
+ */
79
+ export declare function loadDecideEngineFile(read: (path: string) => string, path: string): unknown;
80
+ /**
81
+ * Asks `questions` about `state`. Throws a `TypeError` for questions (or a
82
+ * `when`) a decide rule would refuse, and a {@link DecideEngineError} when
83
+ * the engine is not configured, its host is refused, or it cannot answer.
84
+ */
85
+ export declare function decideQuestions(state: string, questions: Record<string, DecideQuestion>, opts?: DecideSdkOptions): Promise<DecideSdkResult>;
86
+ //# sourceMappingURL=decideSdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decideSdk.d.ts","sourceRoot":"","sources":["../src/decideSdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAQL,KAAK,UAAU,EAEf,KAAK,UAAU,EACf,KAAK,cAAc,EAEnB,KAAK,GAAG,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAA;AAE1B,kFAAkF;AAClF,eAAO,MAAM,4BAA4B,OAAQ,CAAA;AACjD,gEAAgE;AAChE,eAAO,MAAM,uBAAuB,gDAAiD,CAAA;AAErF,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8FAA8F;IAC9F,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACtC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACxC,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,GAChD;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAA;IAC9C,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACnC,8FAA8F;IAC9F,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAEL,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACxC,gDAAgD;IAChD,KAAK,EAAE,GAAG,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAA;IAC/C,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,IAAI,CAAA;IAClD,GAAG,EAAE,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAA;IAC5B,oGAAoG;IACpG,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,cAAc,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,OAAO,EAAE,aAAa,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AA2BD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAa1F;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EACzC,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,eAAe,CAAC,CA0D1B"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /**
3
+ * `decide(state, questions, opts)` for people building their own agents: ask
4
+ * a self-hosted model a few narrow questions about a state, and get each
5
+ * answer's probability, its three-valued reading through a band and, with a
6
+ * `when`, the outcome a decide rule would reach.
7
+ *
8
+ * The questions are written as a decide rule's `questions` and validated
9
+ * the same way, so wording that asks the model for approval is refused. Every
10
+ * call goes through the engine client and its host guard: a hosted decision
11
+ * API is refused before anything is sent, and there is no way to pass an
12
+ * engine object that skips the guard.
13
+ *
14
+ * Errors are {@link DecideEngineError}s and are thrown, so a caller cannot
15
+ * read an unavailable engine as an answer. The one exception is
16
+ * `too_large`: a state over the limit is never truncated, every answer is
17
+ * unknown, and with a `when` the outcome escalates.
18
+ *
19
+ * Self-contained but for the decide modules beside it, so `@intutic/gate` and
20
+ * `@intutic/clawde` carry byte-identical copies; each wraps it in a `decide()`
21
+ * that reads `~/.intutic/decide/engine.json` and the process environment. The
22
+ * Python port is `intutic_clawde/gate/decide_sdk.py`. All three run
23
+ * `fixtures/decide-sdk-vectors.json`.
24
+ *
25
+ * @module
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.DECIDE_ENGINE_FILE_PATH = exports.DECIDE_SDK_DEFAULT_BUDGET_MS = void 0;
29
+ exports.loadDecideEngineFile = loadDecideEngineFile;
30
+ exports.decideQuestions = decideQuestions;
31
+ const decide_js_1 = require("./decide.js");
32
+ const decideEngine_js_1 = require("./decideEngine.js");
33
+ /** The budget when neither `budgetMs` nor `INTUTIC_DECIDE_BUDGET_MS` sets one. */
34
+ exports.DECIDE_SDK_DEFAULT_BUDGET_MS = 7_300;
35
+ /** The engine configuration's path under the home directory. */
36
+ exports.DECIDE_ENGINE_FILE_PATH = ['.intutic', 'decide', 'engine.json'];
37
+ /**
38
+ * The rule the questions are checked as: an `on: response` rule whose `when`
39
+ * is the caller's, or one naming every question once.
40
+ */
41
+ function sdkRule(questions, opts) {
42
+ const when = opts.when ??
43
+ Object.keys(questions)
44
+ .sort()
45
+ .map((n) => (questions[n]?.type === 'choice' ? `${n} == ${decide_js_1.NONE_OF_THESE}` : n))
46
+ .join(' OR ');
47
+ const rule = { id: 'sdk', on: 'response', questions, when };
48
+ if (opts.band !== undefined)
49
+ rule.band = opts.band;
50
+ if (opts.escalate !== undefined)
51
+ rule.escalate = opts.escalate;
52
+ return rule;
53
+ }
54
+ function budgetFrom(opts, env) {
55
+ if (opts.budgetMs !== undefined)
56
+ return opts.budgetMs;
57
+ const raw = env.INTUTIC_DECIDE_BUDGET_MS?.trim();
58
+ if (!raw)
59
+ return exports.DECIDE_SDK_DEFAULT_BUDGET_MS;
60
+ if (!/^[0-9]+$/.test(raw))
61
+ throw new decideEngine_js_1.DecideEngineError('not_configured', 'INTUTIC_DECIDE_BUDGET_MS must be a whole number');
62
+ return Number(raw);
63
+ }
64
+ /**
65
+ * `engine.json`'s parsed content through `read`, or null when the file does
66
+ * not exist. A file that does not parse leaves the engine not configured.
67
+ */
68
+ function loadDecideEngineFile(read, path) {
69
+ let text;
70
+ try {
71
+ text = read(path);
72
+ }
73
+ catch (err) {
74
+ if (err.code === 'ENOENT')
75
+ return null;
76
+ throw new decideEngine_js_1.DecideEngineError('not_configured', `${path} cannot be read`);
77
+ }
78
+ try {
79
+ return JSON.parse(text);
80
+ }
81
+ catch {
82
+ throw new decideEngine_js_1.DecideEngineError('not_configured', `${path} is not JSON`);
83
+ }
84
+ }
85
+ /**
86
+ * Asks `questions` about `state`. Throws a `TypeError` for questions (or a
87
+ * `when`) a decide rule would refuse, and a {@link DecideEngineError} when
88
+ * the engine is not configured, its host is refused, or it cannot answer.
89
+ */
90
+ async function decideQuestions(state, questions, opts = {}) {
91
+ if (typeof state !== 'string')
92
+ throw new TypeError('decide: state must be a string');
93
+ const v = (0, decide_js_1.validateDecideRule)(sdkRule(questions, opts));
94
+ if (!v.ok)
95
+ throw new TypeError(v.reason);
96
+ const rule = v.rule;
97
+ const env = opts.env ?? {};
98
+ const cfg = opts.engine ?? (0, decideEngine_js_1.resolveDecideEngineConfig)(opts.engineFile ?? null, env);
99
+ const budgetMs = budgetFrom(opts, env);
100
+ const engine = (0, decideEngine_js_1.createDecideEngine)(cfg);
101
+ const names = Object.keys(rule.questions).sort();
102
+ const calls = names.map((name) => {
103
+ const q = rule.questions[name];
104
+ return q.type === 'choice' ? { name, type: 'choice', ask: q.ask, options: (0, decide_js_1.decideOptions)(q) } : { name, type: 'yes_no', ask: q.ask };
105
+ });
106
+ const started = Date.now();
107
+ let res = null;
108
+ // A caller's state is held to the tool-call limit: the engine takes larger
109
+ // ones, up to an `on: response` state's limit, from the state builders only.
110
+ if (new TextEncoder().encode(state).length <= decideEngine_js_1.DECIDE_MAX_STATE_BYTES) {
111
+ try {
112
+ res = await engine.ask(state, calls, started + budgetMs);
113
+ }
114
+ catch (err) {
115
+ if (!(err instanceof decideEngine_js_1.DecideEngineError) || err.code !== 'too_large')
116
+ throw err;
117
+ }
118
+ }
119
+ const inputs = {};
120
+ const answers = {};
121
+ for (const name of names) {
122
+ const q = rule.questions[name];
123
+ const a = res?.answers[name];
124
+ const band = (0, decide_js_1.decideQuestionBand)(rule, name);
125
+ inputs[name] = { p: a?.p, perOrder: a?.perOrder };
126
+ if (q.type === 'yes_no') {
127
+ const p = typeof a?.p === 'number' ? a.p : null;
128
+ answers[name] = { type: 'yes_no', p, value: p === null ? 'U' : (0, decide_js_1.bandValue)(p, band) };
129
+ }
130
+ else {
131
+ const options = (0, decide_js_1.choiceAtoms)((0, decide_js_1.decideOptions)(q), a?.perOrder ?? [], band);
132
+ const choice = Object.keys(options).find((o) => options[o].value === 'T') ?? null;
133
+ answers[name] = { type: 'choice', perOrder: a?.perOrder ?? null, options, choice };
134
+ }
135
+ }
136
+ const ruled = opts.when === undefined ? null : (0, decide_js_1.decideRuleOutcome)(rule, v.expr, inputs);
137
+ return {
138
+ answers,
139
+ value: ruled?.value ?? null,
140
+ outcome: ruled?.outcome ?? null,
141
+ code: ruled?.code ?? null,
142
+ via: ruled?.via ?? null,
143
+ error: res ? null : 'too_large',
144
+ model: res?.model ?? null,
145
+ modelDigest: res?.modelDigest ?? null,
146
+ digestStrength: res?.digestStrength ?? null,
147
+ adapter: cfg.adapter,
148
+ latencyMs: res?.latencyMs ?? Date.now() - started,
149
+ };
150
+ }
151
+ //# sourceMappingURL=decideSdk.js.map