instar 1.3.441 → 1.3.443

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 (33) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +58 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/coordination/MandateStore.d.ts +30 -2
  5. package/dist/coordination/MandateStore.d.ts.map +1 -1
  6. package/dist/coordination/MandateStore.js +89 -3
  7. package/dist/coordination/MandateStore.js.map +1 -1
  8. package/dist/coordination/types.d.ts +35 -0
  9. package/dist/coordination/types.d.ts.map +1 -1
  10. package/dist/permissions/LlmIntentClassifier.d.ts +77 -0
  11. package/dist/permissions/LlmIntentClassifier.d.ts.map +1 -0
  12. package/dist/permissions/LlmIntentClassifier.js +225 -0
  13. package/dist/permissions/LlmIntentClassifier.js.map +1 -0
  14. package/dist/permissions/MandateBackedGrantStore.d.ts +40 -0
  15. package/dist/permissions/MandateBackedGrantStore.d.ts.map +1 -0
  16. package/dist/permissions/MandateBackedGrantStore.js +72 -0
  17. package/dist/permissions/MandateBackedGrantStore.js.map +1 -0
  18. package/dist/permissions/index.d.ts +2 -0
  19. package/dist/permissions/index.d.ts.map +1 -1
  20. package/dist/permissions/index.js +2 -0
  21. package/dist/permissions/index.js.map +1 -1
  22. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  23. package/dist/server/CapabilityIndex.js +1 -0
  24. package/dist/server/CapabilityIndex.js.map +1 -1
  25. package/dist/server/routes.d.ts.map +1 -1
  26. package/dist/server/routes.js +57 -0
  27. package/dist/server/routes.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/data/builtin-manifest.json +46 -46
  30. package/upgrades/1.3.442.md +28 -0
  31. package/upgrades/1.3.443.md +25 -0
  32. package/upgrades/side-effects/mandate-user-grants.md +78 -0
  33. package/upgrades/side-effects/slack-llm-intent-classifier.md +67 -0
@@ -16,8 +16,18 @@
16
16
  * (threat-model T12, out of scope) — the proof stops a forged/edited AUTHORED mandate;
17
17
  * local-file tamper of server-managed state is the baseline trust boundary.
18
18
  */
19
- import type { Authority, CoordinationMandate } from './types.js';
20
- /** Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded). */
19
+ import type { Authority, CoordinationMandate, UserAuthorityGrant } from './types.js';
20
+ /**
21
+ * Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded).
22
+ *
23
+ * BACKWARD-COMPAT (non-negotiable): `grants` is appended to the byte sequence ONLY
24
+ * when it is present AND non-empty. A mandate with no grants (the pre-extension shape,
25
+ * `grants` undefined OR `[]`) therefore canonicalizes to the EXACT same bytes as before
26
+ * this extension existed — so every previously-signed mandate's stored `authProof`
27
+ * keeps verifying. When grants ARE present, they extend the canonical bytes (each grant
28
+ * as a field-ordered tuple), so the proof covers them and a grant cannot be forged or
29
+ * added without re-signing through the PIN-gated path.
30
+ */
21
31
  export declare function canonicalMandate(m: Omit<CoordinationMandate, 'authProof' | 'revoked'>): string;
22
32
  export interface MandateStoreDeps {
23
33
  /** Absolute path to the mandates JSON file. */
@@ -38,6 +48,8 @@ export interface IssueMandateInput {
38
48
  expiresAt: string;
39
49
  id?: string;
40
50
  createdAt?: string;
51
+ /** Optional user→agent authority grants signed into the mandate at issuance. */
52
+ grants?: UserAuthorityGrant[];
41
53
  }
42
54
  export declare class MandateStore {
43
55
  private readonly d;
@@ -50,6 +62,22 @@ export declare class MandateStore {
50
62
  * authored bytes, persists, returns the mandate.
51
63
  */
52
64
  issue(input: IssueMandateInput): CoordinationMandate;
65
+ /**
66
+ * Add user→agent authority grant(s) to an existing mandate and RE-SIGN it so the
67
+ * `authProof` covers the new grants (the only path that can sign a grant in — the
68
+ * PIN-gated route is the sole caller). Grants are validated and clamped against the
69
+ * mandate's own expiry: a grant's `expiresAt` may never exceed the mandate's.
70
+ *
71
+ * Returns `{ ok: false }` with a reason for a missing/revoked mandate or an invalid
72
+ * grant; `{ ok: true, mandate }` on success. Never partially applies.
73
+ */
74
+ addGrants(id: string, grants: UserAuthorityGrant[]): {
75
+ ok: true;
76
+ mandate: CoordinationMandate;
77
+ } | {
78
+ ok: false;
79
+ reason: string;
80
+ };
53
81
  /** Verify a mandate's authorship proof (T1/T2). */
54
82
  verifyAuthorship(m: CoordinationMandate): boolean;
55
83
  get(id: string): CoordinationMandate | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"MandateStore.d.ts","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAYjE,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,GAAG,SAAS,CAAC,GAAG,MAAM,CAM9F;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,0DAA0D;IAC1D,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAmB;gBACzB,IAAI,EAAE,gBAAgB;IAIlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,QAAQ;IAKhB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,mBAAmB;IAkBpD,mDAAmD;IACnD,gBAAgB,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO;IAKjD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIhD,IAAI,IAAI,mBAAmB,EAAE;IAI7B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;CAUpE"}
1
+ {"version":3,"file":"MandateStore.d.ts","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAYrF;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,GAAG,SAAS,CAAC,GAAG,MAAM,CAW9F;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,0DAA0D;IAC1D,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAmB;gBACzB,IAAI,EAAE,gBAAgB;IAIlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,QAAQ;IAKhB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,mBAAmB;IAyCpD;;;;;;;;OAQG;IACH,SAAS,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,kBAAkB,EAAE,GAC3B;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,mBAAmB,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAuC7E,mDAAmD;IACnD,gBAAgB,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO;IAKjD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIhD,IAAI,IAAI,mBAAmB,EAAE;IAI7B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;CAUpE"}
@@ -29,13 +29,28 @@ function stableStringify(value) {
29
29
  const keys = Object.keys(obj).sort();
30
30
  return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';
31
31
  }
32
- /** Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded). */
32
+ /**
33
+ * Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded).
34
+ *
35
+ * BACKWARD-COMPAT (non-negotiable): `grants` is appended to the byte sequence ONLY
36
+ * when it is present AND non-empty. A mandate with no grants (the pre-extension shape,
37
+ * `grants` undefined OR `[]`) therefore canonicalizes to the EXACT same bytes as before
38
+ * this extension existed — so every previously-signed mandate's stored `authProof`
39
+ * keeps verifying. When grants ARE present, they extend the canonical bytes (each grant
40
+ * as a field-ordered tuple), so the proof covers them and a grant cannot be forged or
41
+ * added without re-signing through the PIN-gated path.
42
+ */
33
43
  export function canonicalMandate(m) {
34
- return stableStringify([
44
+ const base = [
35
45
  m.id, m.scope, m.agents,
36
46
  m.authorities.map((a) => [a.action, a.bounds, a.requiresCondition ?? '']),
37
47
  m.author, m.createdAt, m.expiresAt,
38
- ]);
48
+ ];
49
+ // Append-only-when-non-empty: no grants → identical bytes to a pre-extension mandate.
50
+ if (Array.isArray(m.grants) && m.grants.length > 0) {
51
+ base.push(m.grants.map((g) => [g.floorAction, g.grantedTo, g.authorizedBy, g.expiresAt, g.bounds ?? {}]));
52
+ }
53
+ return stableStringify(base);
39
54
  }
40
55
  export class MandateStore {
41
56
  d;
@@ -65,9 +80,32 @@ export class MandateStore {
65
80
  issue(input) {
66
81
  const id = input.id ?? (this.d.genId ? this.d.genId() : `mandate-${Math.random().toString(36).slice(2, 10)}`);
67
82
  const createdAt = input.createdAt ?? this.nowIso();
83
+ // Defense-in-depth (second-pass hardening): grants issued WITH the mandate are
84
+ // validated + expiry-clamped the SAME way addGrants() validates them, so a grant
85
+ // can never outlive the mandate that carries it regardless of caller. The HTTP
86
+ // issue route drops grants today; this keeps the library contract uniformly safe
87
+ // for any future caller, not relying solely on the query-side clamp.
88
+ if (input.grants && input.grants.length > 0) {
89
+ const mandateExpiryMs = Date.parse(input.expiresAt);
90
+ for (const g of input.grants) {
91
+ if (!g || typeof g.floorAction !== 'string' || !g.floorAction
92
+ || typeof g.grantedTo !== 'string' || !g.grantedTo
93
+ || typeof g.authorizedBy !== 'string' || !g.authorizedBy
94
+ || typeof g.expiresAt !== 'string' || isNaN(Date.parse(g.expiresAt))) {
95
+ throw new Error('each grant needs a non-empty floorAction, grantedTo, authorizedBy, and a valid ISO expiresAt');
96
+ }
97
+ if (Date.parse(g.expiresAt) > mandateExpiryMs) {
98
+ throw new Error(`grant expiresAt (${g.expiresAt}) must be <= mandate expiresAt (${input.expiresAt})`);
99
+ }
100
+ }
101
+ }
68
102
  const authored = {
69
103
  id, scope: input.scope, agents: input.agents, authorities: input.authorities,
70
104
  author: input.author, createdAt, expiresAt: input.expiresAt,
105
+ // Append-only-when-non-empty: never set `grants` for a no-grant issuance, so the
106
+ // canonical bytes (and thus the proof) are byte-for-byte identical to before this
107
+ // extension existed.
108
+ ...(input.grants && input.grants.length > 0 ? { grants: input.grants } : {}),
71
109
  };
72
110
  const mandate = {
73
111
  ...authored,
@@ -79,6 +117,54 @@ export class MandateStore {
79
117
  this.writeAll(all);
80
118
  return mandate;
81
119
  }
120
+ /**
121
+ * Add user→agent authority grant(s) to an existing mandate and RE-SIGN it so the
122
+ * `authProof` covers the new grants (the only path that can sign a grant in — the
123
+ * PIN-gated route is the sole caller). Grants are validated and clamped against the
124
+ * mandate's own expiry: a grant's `expiresAt` may never exceed the mandate's.
125
+ *
126
+ * Returns `{ ok: false }` with a reason for a missing/revoked mandate or an invalid
127
+ * grant; `{ ok: true, mandate }` on success. Never partially applies.
128
+ */
129
+ addGrants(id, grants) {
130
+ const all = this.readAll();
131
+ const idx = all.findIndex((m) => m.id === id);
132
+ if (idx < 0)
133
+ return { ok: false, reason: 'mandate not found' };
134
+ const existing = all[idx];
135
+ if (existing.revoked)
136
+ return { ok: false, reason: 'mandate is revoked' };
137
+ if (!Array.isArray(grants) || grants.length === 0)
138
+ return { ok: false, reason: 'no grants provided' };
139
+ const mandateExpiry = Date.parse(existing.expiresAt);
140
+ for (const g of grants) {
141
+ if (!g || typeof g.floorAction !== 'string' || !g.floorAction
142
+ || typeof g.grantedTo !== 'string' || !g.grantedTo
143
+ || typeof g.authorizedBy !== 'string' || !g.authorizedBy
144
+ || typeof g.expiresAt !== 'string' || isNaN(Date.parse(g.expiresAt))) {
145
+ return { ok: false, reason: 'each grant needs non-empty floorAction, grantedTo, authorizedBy, and a valid ISO expiresAt' };
146
+ }
147
+ // A grant can NEVER outlive the delegation that carries it.
148
+ if (Date.parse(g.expiresAt) > mandateExpiry) {
149
+ return { ok: false, reason: `grant expiresAt (${g.expiresAt}) must be <= mandate expiresAt (${existing.expiresAt})` };
150
+ }
151
+ }
152
+ const mergedGrants = [...(existing.grants ?? []), ...grants];
153
+ const authored = {
154
+ id: existing.id, scope: existing.scope, agents: existing.agents,
155
+ authorities: existing.authorities, author: existing.author,
156
+ createdAt: existing.createdAt, expiresAt: existing.expiresAt,
157
+ grants: mergedGrants,
158
+ };
159
+ const resigned = {
160
+ ...authored,
161
+ revoked: existing.revoked,
162
+ authProof: this.d.sign(canonicalMandate(authored)),
163
+ };
164
+ all[idx] = resigned;
165
+ this.writeAll(all);
166
+ return { ok: true, mandate: resigned };
167
+ }
82
168
  /** Verify a mandate's authorship proof (T1/T2). */
83
169
  verifyAuthorship(m) {
84
170
  const { authProof, revoked, ...authored } = m;
@@ -1 +1 @@
1
- {"version":3,"file":"MandateStore.js","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MandateStore.js","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,34 @@ export interface Authority {
24
24
  * never from an agent's assertion. */
25
25
  requiresCondition?: string;
26
26
  }
27
+ /**
28
+ * A user→agent authority grant carried INSIDE a signed mandate. This is the
29
+ * extension that lets the human (the mandate author) delegate a Slack FLOOR
30
+ * action (money / prod-deploy / credentials / destructive / external / grant)
31
+ * to a SPECIFIC verified Slack user for a bounded time — the same deny-by-default,
32
+ * signed, expiring, revocable model the mandate already enforces for A2A authority.
33
+ *
34
+ * The grant is covered by the mandate's `authProof` (it is appended to the canonical
35
+ * byte sequence when present), so an agent cannot mint or widen a grant: only the
36
+ * PIN-gated issuance path can sign one in.
37
+ *
38
+ * `expiresAt` is independent of (and MUST be <= ) the mandate's own `expiresAt` —
39
+ * the grant can never outlive the delegation that carries it. The store-side query
40
+ * clamps to the mandate expiry, so a grant is void the moment EITHER clock passes.
41
+ */
42
+ export interface UserAuthorityGrant {
43
+ /** The Slack FLOOR action this grant authorizes (e.g. 'prod-deploy'). Matched
44
+ * against the gate's `scope` (the floorAction) on lookup. */
45
+ floorAction: string;
46
+ /** The VERIFIED Slack user id (U…) this grant is for — never a content name. */
47
+ grantedTo: string;
48
+ /** Who authorized it (the human author / operator). Requester ≠ authorizer. */
49
+ authorizedBy: string;
50
+ /** ISO timestamp after which the grant is void. MUST be <= the mandate's expiresAt. */
51
+ expiresAt: string;
52
+ /** Optional bounds carried for audit/future enforcement (advisory at the floor). */
53
+ bounds?: Record<string, unknown>;
54
+ }
27
55
  /** The human-authored delegation. `authProof` covers all other fields. */
28
56
  export interface CoordinationMandate {
29
57
  id: string;
@@ -41,6 +69,13 @@ export interface CoordinationMandate {
41
69
  at: string;
42
70
  reason: string;
43
71
  } | null;
72
+ /**
73
+ * Optional user→agent authority grants (the Slack-floor delegation extension).
74
+ * Covered by `authProof` ONLY when present and non-empty — a mandate with no
75
+ * grants canonicalizes byte-for-byte identically to a pre-extension mandate, so
76
+ * existing signed mandates keep verifying (backward-compat is non-negotiable).
77
+ */
78
+ grants?: UserAuthorityGrant[];
44
79
  /** Authorship proof — an HMAC over the canonical (proof-excluded) mandate bytes,
45
80
  * produced ONLY by the PIN-gated issuance path. An agent cannot mint or widen its
46
81
  * own mandate without it; a forged/edited mandate fails verification. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/coordination/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,mHAAmH;IACnH,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;2CAEuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C;;8EAE0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/coordination/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,mHAAmH;IACnH,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;2CAEuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,kBAAkB;IACjC;kEAC8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC9B;;8EAE0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;IAC1B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * LlmIntentClassifier — the JUDGMENT-BAND intent classifier (Slack permission gate,
3
+ * Pillar 2, §6.5–6.6 / §6.6 "Layer 1 INTENT + SENSITIVITY").
4
+ *
5
+ * It implements the SAME `IntentClassifier` interface as `HeuristicIntentClassifier`
6
+ * and is interchangeable with it at the gate's `classifier` slot. The difference is
7
+ * the judgment band ABOVE the floor: an LLM call refines the sensitivity tier,
8
+ * directedness, and a non-floor action label more accurately than keyword matching.
9
+ *
10
+ * ── Two invariants this class is built around ──────────────────────────────────
11
+ *
12
+ * 1. THE FLOOR STAYS DETERMINISTIC. This class NEVER lets the LLM decide that a
13
+ * floor action ISN'T one, and it never lets the LLM downgrade a heuristic-detected
14
+ * floor candidate. It runs the deterministic `HeuristicIntentClassifier` FIRST;
15
+ * when the heuristic flags a real floor action (a `floorAction`) OR the
16
+ * ambiguous-deploy low-confidence case (action 'ambiguous', tier 4, low
17
+ * confidence → the gate routes to CLARIFY), that verdict is returned AS-IS and the
18
+ * LLM is never consulted. The LLM can only refine the NON-floor judgment band.
19
+ * (Floor enumeration + role authority remain in RolePolicy / SlackPermissionGate;
20
+ * this class only feeds them an intent and must not be the thing that misses a
21
+ * floor action.)
22
+ *
23
+ * 2. NO SILENT DEGRADATION TO A BRITTLE — OR WIDER — FALLBACK
24
+ * (docs/specs/no-silent-degradation-to-brittle-fallback.md,
25
+ * docs/signal-vs-authority.md). On ANY LLM failure (no provider, throw, timeout,
26
+ * empty/unparseable response, or a response that would WIDEN access beyond the
27
+ * deterministic floor read) we fall back to the `HeuristicIntentClassifier`
28
+ * result — which itself routes ambiguity to a tier-4 low-confidence 'ambiguous'
29
+ * (→ the gate CLARIFIES, a safe non-allow). A classification failure must NEVER
30
+ * return a confident low-tier "allow"-shaped intent on what might be sensitive.
31
+ * The fallback is therefore at-least-as-conservative as the deterministic floor.
32
+ *
33
+ * The LLM is reached through instar's internal `IntelligenceProvider` (the same
34
+ * injected-provider pattern as `TopicIntentExtractor` / `MessagingToneGate`), NOT a
35
+ * raw API call. A `fast` model tier is used, and the call is marked `gating: true`
36
+ * so the IntelligenceRouter will provider-swap on failure before the error
37
+ * propagates (and we then fail closed to the heuristic).
38
+ *
39
+ * Design: docs/specs/SLACK-ORG-INTEGRATION-SPEC.md §6.5–6.6.
40
+ */
41
+ import type { IntelligenceProvider } from '../core/types.js';
42
+ import type { RequestIntent } from './types.js';
43
+ import { type IntentClassifier } from './IntentClassifier.js';
44
+ /** Observability hook reasons for why a classification fell back to the heuristic. */
45
+ export type LlmIntentDegradeReason = 'no-intelligence' | 'error' | 'unparseable' | 'floor-deterministic';
46
+ export interface LlmIntentClassifierDeps {
47
+ /**
48
+ * The internal LLM provider (injected — never a direct framework import). When
49
+ * absent, every classification degrades to the heuristic (fail-closed).
50
+ */
51
+ intelligence?: IntelligenceProvider;
52
+ /**
53
+ * The deterministic floor + fallback classifier. Defaults to a fresh
54
+ * `HeuristicIntentClassifier`. Injectable for tests.
55
+ */
56
+ heuristic?: IntentClassifier;
57
+ /** Per-call timeout for the LLM (ms). Default 8000. */
58
+ timeoutMs?: number;
59
+ /**
60
+ * Optional observability hook — fires on every degrade-to-heuristic path with the
61
+ * reason. Best-effort: it never affects the returned verdict (which is always the
62
+ * safe heuristic result on degrade).
63
+ */
64
+ onDegrade?: (reason: LlmIntentDegradeReason) => void;
65
+ }
66
+ export declare class LlmIntentClassifier implements IntentClassifier {
67
+ private readonly intelligence?;
68
+ private readonly heuristic;
69
+ private readonly timeoutMs;
70
+ private readonly onDegrade?;
71
+ constructor(deps?: LlmIntentClassifierDeps);
72
+ classify(text: string, ctx: {
73
+ directed: boolean;
74
+ }): Promise<RequestIntent>;
75
+ private report;
76
+ }
77
+ //# sourceMappingURL=LlmIntentClassifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmIntentClassifier.d.ts","sourceRoot":"","sources":["../../src/permissions/LlmIntentClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AACjE,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzF,sFAAsF;AACtF,MAAM,MAAM,sBAAsB,GAC9B,iBAAiB,GACjB,OAAO,GACP,aAAa,GACb,qBAAqB,CAAC;AAE1B,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACtD;AAYD,qBAAa,mBAAoB,YAAW,gBAAgB;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAuB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAA2C;gBAE1D,IAAI,GAAE,uBAA4B;IAOxC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IA0DhF,OAAO,CAAC,MAAM;CAOf"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * LlmIntentClassifier — the JUDGMENT-BAND intent classifier (Slack permission gate,
3
+ * Pillar 2, §6.5–6.6 / §6.6 "Layer 1 INTENT + SENSITIVITY").
4
+ *
5
+ * It implements the SAME `IntentClassifier` interface as `HeuristicIntentClassifier`
6
+ * and is interchangeable with it at the gate's `classifier` slot. The difference is
7
+ * the judgment band ABOVE the floor: an LLM call refines the sensitivity tier,
8
+ * directedness, and a non-floor action label more accurately than keyword matching.
9
+ *
10
+ * ── Two invariants this class is built around ──────────────────────────────────
11
+ *
12
+ * 1. THE FLOOR STAYS DETERMINISTIC. This class NEVER lets the LLM decide that a
13
+ * floor action ISN'T one, and it never lets the LLM downgrade a heuristic-detected
14
+ * floor candidate. It runs the deterministic `HeuristicIntentClassifier` FIRST;
15
+ * when the heuristic flags a real floor action (a `floorAction`) OR the
16
+ * ambiguous-deploy low-confidence case (action 'ambiguous', tier 4, low
17
+ * confidence → the gate routes to CLARIFY), that verdict is returned AS-IS and the
18
+ * LLM is never consulted. The LLM can only refine the NON-floor judgment band.
19
+ * (Floor enumeration + role authority remain in RolePolicy / SlackPermissionGate;
20
+ * this class only feeds them an intent and must not be the thing that misses a
21
+ * floor action.)
22
+ *
23
+ * 2. NO SILENT DEGRADATION TO A BRITTLE — OR WIDER — FALLBACK
24
+ * (docs/specs/no-silent-degradation-to-brittle-fallback.md,
25
+ * docs/signal-vs-authority.md). On ANY LLM failure (no provider, throw, timeout,
26
+ * empty/unparseable response, or a response that would WIDEN access beyond the
27
+ * deterministic floor read) we fall back to the `HeuristicIntentClassifier`
28
+ * result — which itself routes ambiguity to a tier-4 low-confidence 'ambiguous'
29
+ * (→ the gate CLARIFIES, a safe non-allow). A classification failure must NEVER
30
+ * return a confident low-tier "allow"-shaped intent on what might be sensitive.
31
+ * The fallback is therefore at-least-as-conservative as the deterministic floor.
32
+ *
33
+ * The LLM is reached through instar's internal `IntelligenceProvider` (the same
34
+ * injected-provider pattern as `TopicIntentExtractor` / `MessagingToneGate`), NOT a
35
+ * raw API call. A `fast` model tier is used, and the call is marked `gating: true`
36
+ * so the IntelligenceRouter will provider-swap on failure before the error
37
+ * propagates (and we then fail closed to the heuristic).
38
+ *
39
+ * Design: docs/specs/SLACK-ORG-INTEGRATION-SPEC.md §6.5–6.6.
40
+ */
41
+ import { HeuristicIntentClassifier } from './IntentClassifier.js';
42
+ const VALID_TIERS = new Set([0, 1, 2, 3, 4]);
43
+ export class LlmIntentClassifier {
44
+ intelligence;
45
+ heuristic;
46
+ timeoutMs;
47
+ onDegrade;
48
+ constructor(deps = {}) {
49
+ this.intelligence = deps.intelligence;
50
+ this.heuristic = deps.heuristic ?? new HeuristicIntentClassifier();
51
+ this.timeoutMs = deps.timeoutMs ?? 8000;
52
+ this.onDegrade = deps.onDegrade;
53
+ }
54
+ async classify(text, ctx) {
55
+ // ── 1. Deterministic floor read FIRST (never delegated to the LLM) ──────────
56
+ // The heuristic is the floor authority. Whatever it returns is the conservative
57
+ // baseline we will never widen past.
58
+ const floorRead = await this.heuristic.classify(text, ctx);
59
+ // If the heuristic flagged a real floor action OR the ambiguous-deploy
60
+ // possibly-floor case (tier 4, no floorAction, low confidence → the gate
61
+ // CLARIFIES), return it AS-IS. The LLM must not get the chance to downgrade a
62
+ // floor candidate into an allow-shaped low tier.
63
+ if (floorRead.floorAction || floorRead.tier >= 4) {
64
+ this.report('floor-deterministic');
65
+ return floorRead;
66
+ }
67
+ // ── 2. No provider → fail closed to the heuristic ───────────────────────────
68
+ if (!this.intelligence) {
69
+ this.report('no-intelligence');
70
+ return floorRead;
71
+ }
72
+ // ── 3. Judgment band: refine the NON-floor intent via the LLM ───────────────
73
+ let raw;
74
+ try {
75
+ const { systemPrompt, userPrompt } = buildIntentPrompt(text, ctx);
76
+ raw = await this.intelligence.evaluate(`${systemPrompt}\n\n${userPrompt}`, {
77
+ model: 'fast',
78
+ temperature: 0,
79
+ maxTokens: 200,
80
+ timeoutMs: this.timeoutMs,
81
+ attribution: {
82
+ component: 'LlmIntentClassifier',
83
+ category: 'gate',
84
+ // SAFETY-GATING: this classification feeds an authority gate, so the
85
+ // router provider-swaps on failure before the error reaches us — and if
86
+ // every provider is down the throw lands in the catch below and we fail
87
+ // CLOSED to the heuristic. We never silently degrade to a wider answer.
88
+ gating: true,
89
+ },
90
+ });
91
+ }
92
+ catch {
93
+ // network/timeout/provider failure / circuit open → fail closed to the
94
+ // deterministic heuristic (which clarifies on ambiguity). NEVER a silent allow.
95
+ this.report('error');
96
+ return floorRead;
97
+ }
98
+ const parsed = parseLlmVerdict(raw);
99
+ if (!parsed) {
100
+ // Unparseable LLM output is a classification FAILURE → fail closed.
101
+ this.report('unparseable');
102
+ return floorRead;
103
+ }
104
+ // ── 4. Reconcile: the LLM may only refine WITHIN the non-floor band ─────────
105
+ return reconcile(floorRead, parsed, ctx);
106
+ }
107
+ report(reason) {
108
+ try {
109
+ this.onDegrade?.(reason);
110
+ }
111
+ catch {
112
+ /* observability is best-effort and must never affect the verdict */
113
+ }
114
+ }
115
+ }
116
+ /**
117
+ * Merge the deterministic floor read with the LLM's judgment-band verdict under a
118
+ * FAIL-CLOSED, never-widen rule:
119
+ *
120
+ * - The floor read is the conservative baseline. If the LLM tries to assert a
121
+ * floor action OR a tier ≥ 4, that is OUT OF ITS LANE — the floor is
122
+ * deterministic, so we DROP the LLM result and return the (non-floor) heuristic
123
+ * read. (A genuine floor would already have short-circuited before the LLM ran.)
124
+ * - Otherwise the LLM may set the tier in [0,3], the action label, and refine
125
+ * directedness. Directedness can only be NARROWED (true→false), never widened:
126
+ * an inbound message the gate already treats as undirected must stay undirected;
127
+ * the LLM cannot promote overheard chatter into a directed command.
128
+ * - floorAction is ALWAYS dropped here (left undefined) — the only floor signal
129
+ * that reaches the gate comes from the deterministic short-circuit above.
130
+ */
131
+ function reconcile(floorRead, llm, ctx) {
132
+ // The LLM tried to claim a floor-level tier — not its lane. Keep the safe
133
+ // deterministic (non-floor) read rather than trusting an LLM floor assertion.
134
+ if (llm.tier >= 4) {
135
+ return floorRead;
136
+ }
137
+ // Directedness: never widen. The caller's ctx.directed is the structural truth
138
+ // (a mention / clear ask was detected upstream). The LLM may only confirm it or
139
+ // narrow it to false (e.g. "this reads as overheard musing, not a command").
140
+ const directed = ctx.directed && llm.directed;
141
+ // Tier: NEVER widen. The LLM may only ESCALATE the heuristic's conservative tier
142
+ // (raise sensitivity), never lower it — a LOWER tier is a WIDER gate verdict (the
143
+ // gate has an unconditional tier-0 allow + role-ceiling checks), so an unbounded
144
+ // llm.tier would let prompt-injected message content downgrade e.g. T3→T0 and widen
145
+ // access (refuse→allow). Clamp to the deterministic floor read, mirroring the
146
+ // one-way `directed` rule above. Letting the LLM CORRECT heuristic over-classification
147
+ // downward is a separate, confidence-gated design decision — not an unbounded side
148
+ // effect of untrusted message content. (Caught by the Phase-5 adversarial review.)
149
+ const tier = Math.max(floorRead.tier, llm.tier);
150
+ return {
151
+ action: llm.action || floorRead.action,
152
+ tier,
153
+ floorAction: undefined, // floor is deterministic-only; never set from the LLM
154
+ confidence: llm.confidence,
155
+ directed,
156
+ };
157
+ }
158
+ /**
159
+ * Validate + clamp the LLM's raw JSON into a safe judgment-band verdict.
160
+ * Returns null when the payload can't be read as a verdict (→ caller fails closed).
161
+ */
162
+ function parseLlmVerdict(raw) {
163
+ if (!raw || typeof raw !== 'string')
164
+ return null;
165
+ // Tolerate prose/markdown around the JSON object (some providers wrap it).
166
+ const start = raw.indexOf('{');
167
+ const end = raw.lastIndexOf('}');
168
+ if (start === -1 || end === -1 || end < start)
169
+ return null;
170
+ let obj;
171
+ try {
172
+ obj = JSON.parse(raw.slice(start, end + 1));
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ const tierNum = typeof obj.tier === 'number' ? obj.tier : Number(obj.tier);
178
+ if (!Number.isFinite(tierNum) || !VALID_TIERS.has(tierNum))
179
+ return null;
180
+ const action = typeof obj.action === 'string' && obj.action.trim() ? obj.action.trim() : '';
181
+ if (!action)
182
+ return null;
183
+ let confidence = typeof obj.confidence === 'number' ? obj.confidence : Number(obj.confidence);
184
+ if (!Number.isFinite(confidence))
185
+ confidence = 0.5;
186
+ confidence = Math.max(0, Math.min(1, confidence));
187
+ const directed = obj.directed === true || obj.directed === 'true';
188
+ return { action, tier: tierNum, directed, confidence };
189
+ }
190
+ /**
191
+ * Build the intent-classification prompt. It is explicit that the FLOOR is handled
192
+ * elsewhere: the model is told NOT to grant/allow anything and to classify only the
193
+ * non-floor sensitivity band, and to use tier 4 ONLY as a "this might be sensitive,
194
+ * please clarify" signal (which the gate treats conservatively).
195
+ */
196
+ function buildIntentPrompt(text, ctx) {
197
+ const systemPrompt = [
198
+ 'You classify a single Slack message into an INTENT for an authorization gate.',
199
+ 'You do NOT decide whether the action is allowed — a separate deterministic gate does that.',
200
+ 'Return ONLY a compact JSON object, no prose, with exactly these keys:',
201
+ ' "action": short verb-phrase label, e.g. "summarize", "post-note", "run-job", "discuss".',
202
+ ' "tier": integer sensitivity 0-4:',
203
+ ' 0 = ambient chatter / reaction, no action requested',
204
+ ' 1 = read/inform (summarize, answer, look up, draft but NOT send)',
205
+ ' 2 = low-write (post a message/doc, file a ticket, create a calendar hold)',
206
+ ' 3 = operational (run a job, modify non-production state, schedule, small spend)',
207
+ ' 4 = potentially-PRIVILEGED — use ONLY when it MIGHT touch money, a production',
208
+ ' deploy, credentials, destructive data ops, an external send, or granting',
209
+ ' authority. When unsure between 3 and 4, choose 4 (safer; the gate clarifies).',
210
+ ' "directed": true if the message is a request aimed AT the agent; false if it is overheard',
211
+ ' chatter or musing not addressed to the agent.',
212
+ ' "confidence": 0.0-1.0, your confidence in this classification. Be honest; low confidence on a',
213
+ ' possibly-sensitive message is the correct, safe answer.',
214
+ 'Never invent authorization. Never output anything but the JSON object.',
215
+ ].join('\n');
216
+ const userPrompt = [
217
+ `directed_hint: ${ctx.directed ? 'the upstream router saw a mention/clear ask' : 'no mention detected'}`,
218
+ 'message:',
219
+ '"""',
220
+ (text || '').slice(0, 2000),
221
+ '"""',
222
+ ].join('\n');
223
+ return { systemPrompt, userPrompt };
224
+ }
225
+ //# sourceMappingURL=LlmIntentClassifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmIntentClassifier.js","sourceRoot":"","sources":["../../src/permissions/LlmIntentClassifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * MandateBackedGrantStore — resolves a Slack FLOOR-action grant for a verified
3
+ * principal from the SIGNED Coordination Mandate(s).
4
+ *
5
+ * This is the production `GrantStore` for `SlackPermissionGate`: instead of an
6
+ * ad-hoc grant table, a floor-action grant is a `UserAuthorityGrant` carried inside
7
+ * a mandate and covered by the mandate's `authProof`. That makes the grant:
8
+ * - deny-by-default (no mandate / no matching grant → undefined → refuse)
9
+ * - signed (an agent cannot mint or widen a grant; only the PIN-gated
10
+ * issuance/grant route can sign one in)
11
+ * - bounded + expiring (the grant has its own expiry, clamped to never outlive the
12
+ * mandate that carries it)
13
+ * - revocable (revoking the mandate voids every grant it carries)
14
+ *
15
+ * `activeGrant` returns a grant ONLY when, for a mandate that is authorship-valid,
16
+ * not expired, and not revoked, there exists a grant whose `grantedTo` equals the
17
+ * verified Slack user id, whose `floorAction` equals the requested scope, and whose
18
+ * effective expiry (min of grant.expiresAt and mandate.expiresAt) is still in the
19
+ * future. Otherwise `undefined` — the gate then falls back to the role floor.
20
+ *
21
+ * Design: docs/specs/SLACK-ORG-INTEGRATION-SPEC.md (Pillar 2, floor grants) +
22
+ * docs/specs/coordination-mandate.md (the signed-delegation model the grant rides on).
23
+ */
24
+ import type { GrantStore } from './SlackPermissionGate.js';
25
+ import type { AuthorityGrant } from './types.js';
26
+ import type { MandateStore } from '../coordination/MandateStore.js';
27
+ export interface MandateBackedGrantStoreDeps {
28
+ /** The signed mandate store the grants live in. */
29
+ store: MandateStore;
30
+ }
31
+ export declare class MandateBackedGrantStore implements GrantStore {
32
+ private readonly store;
33
+ constructor(deps: MandateBackedGrantStoreDeps);
34
+ /**
35
+ * Resolve an active floor-action grant for `slackUserId` + `scope` as of `now` (ms).
36
+ * Returns the FIRST matching grant across all mandates; `undefined` if none.
37
+ */
38
+ activeGrant(slackUserId: string, scope: string, now: number): AuthorityGrant | undefined;
39
+ }
40
+ //# sourceMappingURL=MandateBackedGrantStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateBackedGrantStore.d.ts","sourceRoot":"","sources":["../../src/permissions/MandateBackedGrantStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAgC,MAAM,YAAY,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,MAAM,WAAW,2BAA2B;IAC1C,mDAAmD;IACnD,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,qBAAa,uBAAwB,YAAW,UAAU;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBACzB,IAAI,EAAE,2BAA2B;IAI7C;;;OAGG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;CAkCzF"}