@aitne/shared 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/dist/advisor-models.d.ts +34 -0
  3. package/dist/advisor-models.d.ts.map +1 -0
  4. package/dist/advisor-models.js +39 -0
  5. package/dist/advisor-models.js.map +1 -0
  6. package/dist/agent-identity.d.ts +11 -0
  7. package/dist/agent-identity.d.ts.map +1 -0
  8. package/dist/agent-identity.js +29 -0
  9. package/dist/agent-identity.js.map +1 -0
  10. package/dist/alerts.d.ts +44 -0
  11. package/dist/alerts.d.ts.map +1 -0
  12. package/dist/alerts.js +12 -0
  13. package/dist/alerts.js.map +1 -0
  14. package/dist/backend-api-key-config.d.ts +337 -0
  15. package/dist/backend-api-key-config.d.ts.map +1 -0
  16. package/dist/backend-api-key-config.js +682 -0
  17. package/dist/backend-api-key-config.js.map +1 -0
  18. package/dist/backend.d.ts +93 -0
  19. package/dist/backend.d.ts.map +1 -0
  20. package/dist/backend.js +22 -0
  21. package/dist/backend.js.map +1 -0
  22. package/dist/branding.d.ts +96 -0
  23. package/dist/branding.d.ts.map +1 -0
  24. package/dist/branding.js +102 -0
  25. package/dist/branding.js.map +1 -0
  26. package/dist/chat-session-scope.d.ts +14 -0
  27. package/dist/chat-session-scope.d.ts.map +1 -0
  28. package/dist/chat-session-scope.js +18 -0
  29. package/dist/chat-session-scope.js.map +1 -0
  30. package/dist/date-utils.d.ts +80 -0
  31. package/dist/date-utils.d.ts.map +1 -0
  32. package/dist/date-utils.js +187 -0
  33. package/dist/date-utils.js.map +1 -0
  34. package/dist/docs-frontmatter.d.ts +51 -0
  35. package/dist/docs-frontmatter.d.ts.map +1 -0
  36. package/dist/docs-frontmatter.js +184 -0
  37. package/dist/docs-frontmatter.js.map +1 -0
  38. package/dist/docs-schema.d.ts +79 -0
  39. package/dist/docs-schema.d.ts.map +1 -0
  40. package/dist/docs-schema.js +135 -0
  41. package/dist/docs-schema.js.map +1 -0
  42. package/dist/editable-config-keys.d.ts +14 -0
  43. package/dist/editable-config-keys.d.ts.map +1 -0
  44. package/dist/editable-config-keys.js +157 -0
  45. package/dist/editable-config-keys.js.map +1 -0
  46. package/dist/exec-with-stdin.d.ts +14 -0
  47. package/dist/exec-with-stdin.d.ts.map +1 -0
  48. package/dist/exec-with-stdin.js +35 -0
  49. package/dist/exec-with-stdin.js.map +1 -0
  50. package/dist/index.d.ts +37 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +49 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/integrations-snapshot.d.ts +183 -0
  55. package/dist/integrations-snapshot.d.ts.map +1 -0
  56. package/dist/integrations-snapshot.js +757 -0
  57. package/dist/integrations-snapshot.js.map +1 -0
  58. package/dist/integrations.d.ts +675 -0
  59. package/dist/integrations.d.ts.map +1 -0
  60. package/dist/integrations.js +1656 -0
  61. package/dist/integrations.js.map +1 -0
  62. package/dist/keychain-helper-client.d.ts +31 -0
  63. package/dist/keychain-helper-client.d.ts.map +1 -0
  64. package/dist/keychain-helper-client.js +105 -0
  65. package/dist/keychain-helper-client.js.map +1 -0
  66. package/dist/log-entry.d.ts +14 -0
  67. package/dist/log-entry.d.ts.map +1 -0
  68. package/dist/log-entry.js +2 -0
  69. package/dist/log-entry.js.map +1 -0
  70. package/dist/management-domains.d.ts +369 -0
  71. package/dist/management-domains.d.ts.map +1 -0
  72. package/dist/management-domains.js +499 -0
  73. package/dist/management-domains.js.map +1 -0
  74. package/dist/process-key.d.ts +67 -0
  75. package/dist/process-key.d.ts.map +1 -0
  76. package/dist/process-key.js +366 -0
  77. package/dist/process-key.js.map +1 -0
  78. package/dist/schemas.d.ts +267 -0
  79. package/dist/schemas.d.ts.map +1 -0
  80. package/dist/schemas.js +271 -0
  81. package/dist/schemas.js.map +1 -0
  82. package/dist/secret-client-factory.d.ts +16 -0
  83. package/dist/secret-client-factory.d.ts.map +1 -0
  84. package/dist/secret-client-factory.js +111 -0
  85. package/dist/secret-client-factory.js.map +1 -0
  86. package/dist/secret-client-file.d.ts +51 -0
  87. package/dist/secret-client-file.d.ts.map +1 -0
  88. package/dist/secret-client-file.js +160 -0
  89. package/dist/secret-client-file.js.map +1 -0
  90. package/dist/secret-client-linux.d.ts +26 -0
  91. package/dist/secret-client-linux.d.ts.map +1 -0
  92. package/dist/secret-client-linux.js +63 -0
  93. package/dist/secret-client-linux.js.map +1 -0
  94. package/dist/secret-client-windows.d.ts +37 -0
  95. package/dist/secret-client-windows.d.ts.map +1 -0
  96. package/dist/secret-client-windows.js +82 -0
  97. package/dist/secret-client-windows.js.map +1 -0
  98. package/dist/secret-redaction.d.ts +3 -0
  99. package/dist/secret-redaction.d.ts.map +1 -0
  100. package/dist/secret-redaction.js +31 -0
  101. package/dist/secret-redaction.js.map +1 -0
  102. package/dist/skill-curation/decision-language.d.ts +6 -0
  103. package/dist/skill-curation/decision-language.d.ts.map +1 -0
  104. package/dist/skill-curation/decision-language.js +38 -0
  105. package/dist/skill-curation/decision-language.js.map +1 -0
  106. package/dist/skill-curation/schemas.d.ts +461 -0
  107. package/dist/skill-curation/schemas.d.ts.map +1 -0
  108. package/dist/skill-curation/schemas.js +211 -0
  109. package/dist/skill-curation/schemas.js.map +1 -0
  110. package/dist/types.d.ts +204 -0
  111. package/dist/types.d.ts.map +1 -0
  112. package/dist/types.js +54 -0
  113. package/dist/types.js.map +1 -0
  114. package/package.json +50 -0
@@ -0,0 +1,675 @@
1
+ import { z } from "zod";
2
+ import { type BackendId } from "./backend.js";
3
+ /**
4
+ * Integration Delegation Framework — shared types (Phase 1–3).
5
+ *
6
+ * See `GOOGLE_AUTH_DELEGATION_DESIGN.md` v3. Phase 1 ships the registry +
7
+ * per-integration mode config with Gmail + Calendar as the first two keys.
8
+ * Phase 3 adds skill / task-flow variant selection helpers consumed by
9
+ * SkillsCompiler and prompts.ts. Git lifecycle Phase 4 retroactively registers
10
+ * the local Git / GitHub observers under the same mode framework.
11
+ */
12
+ /**
13
+ * Mode-aware integrations — the ones that can flip between `direct` and
14
+ * `delegated` and route through the per-backend probe / connector / skill
15
+ * filter framework. Other surfaces (lifestyle services like
16
+ * receipts/books/travel-bookings, Obsidian) integrate via dedicated routes
17
+ * and observers without participating in this registry.
18
+ */
19
+ export declare const INTEGRATION_KEYS: readonly ["gmail", "google_calendar", "notion", "git", "github", "outlook_mail", "outlook_calendar"];
20
+ export type IntegrationKey = (typeof INTEGRATION_KEYS)[number];
21
+ export declare function isIntegrationKey(value: string): value is IntegrationKey;
22
+ export declare const INTEGRATION_MODES: readonly ["direct", "delegated", "disabled"];
23
+ export type IntegrationMode = (typeof INTEGRATION_MODES)[number];
24
+ export declare function isIntegrationMode(value: string): value is IntegrationMode;
25
+ export interface IntegrationBackendConnector {
26
+ /** Prefix that vendor-shipped MCP tools share, e.g. `mcp__claude_ai_Gmail__`. */
27
+ toolNamespace: string;
28
+ /** Capabilities that must all be present for delegated mode to be offered. */
29
+ requiredCapabilities: readonly string[];
30
+ /** Capabilities the feature matrix renders from (tick/cross UI). */
31
+ optionalCapabilities: readonly string[];
32
+ /**
33
+ * Maps each capability name to the unsuffixed tool names that satisfy it.
34
+ * Used by the probe to translate a live MCP tool list into per-capability
35
+ * presence. Tool names are the part after `toolNamespace` (e.g. for the
36
+ * Claude Gmail connector `toolNamespace = "mcp__claude_ai_Gmail__"`,
37
+ * `"search"` maps to `["search_threads"]`).
38
+ *
39
+ * Every capability listed in `requiredCapabilities` ∪ `optionalCapabilities`
40
+ * MUST have an entry here. An empty array means "no shipped tool satisfies
41
+ * this capability" (always absent in the probe). The registry
42
+ * self-consistency test in `integration-probe.test.ts` enforces this.
43
+ */
44
+ capabilityTools: Readonly<Record<string, readonly string[]>>;
45
+ /**
46
+ * DELEGATED-TASK-MODE-DESIGN.md §7.3 — bare tool names (same form as
47
+ * `capabilityTools` values) whose invocation mutates user-visible state
48
+ * in a way that requires explicit confirmation per the CLAUDE.md
49
+ * "destructive ops require user confirmation" invariant. Concrete tool
50
+ * names rather than capability keys because some capabilities mix reads
51
+ * and writes (Gemini Gmail's `label` covers `listLabels` (read) and
52
+ * `modify` (write)); a tool-level list lets us deny the writes without
53
+ * classifying the read as destructive.
54
+ *
55
+ * Used by:
56
+ * - Task mode `runDelegatedTask`: when `allowDestructive: false`,
57
+ * these are removed from the SDK's `allowedTools` (Claude) /
58
+ * emitted as priority-998 deny rules (Gemini), and the subprocess
59
+ * is instructed to return `{needsConfirmation, confirmationPlan}`.
60
+ * - The `destructive-coverage.test.ts` drift test asserts every entry
61
+ * in `RECOMMENDED_STARTER_DENIED_TOOLS` for this connector appears
62
+ * here (i.e. the recommended starter list is a subset of the
63
+ * descriptor's destructive set).
64
+ *
65
+ * Every entry MUST also appear in some `capabilityTools[k]` array — a
66
+ * destructive tool the descriptor doesn't otherwise know about would be
67
+ * dead weight. Enforced by the same self-consistency test that covers
68
+ * the rest of the descriptor surface.
69
+ */
70
+ destructiveTools: readonly string[];
71
+ }
72
+ export interface IntegrationDirectSetup {
73
+ /** Keychain keys that must be present for direct mode to function. */
74
+ credentialKeys: readonly string[];
75
+ /** Documentation URL shown in the setup wizard and dashboard cards. */
76
+ helpUrl: string;
77
+ }
78
+ export interface IntegrationDescriptor {
79
+ key: IntegrationKey;
80
+ displayName: string;
81
+ supportedModes: readonly IntegrationMode[];
82
+ directSetup?: IntegrationDirectSetup;
83
+ backendConnectors: Partial<Record<BackendId, IntegrationBackendConnector>>;
84
+ /** Skill slugs whose bodies depend on this integration. Declarative only in Phase 1. */
85
+ skillsTouched: readonly string[];
86
+ /** Task-flow keys same. Declarative only in Phase 1. */
87
+ taskFlowsTouched: readonly string[];
88
+ /**
89
+ * Runtime observer names gated by `integration.mode === "direct"`. Each
90
+ * string is the value the observer exposes via its `Observer.name`
91
+ * property — `ObserverManager.has()` / `stopAndUnregister()` look up by
92
+ * that exact key. (The design doc uses class names like `MailPoller`
93
+ * for human readability; the registry uses the runtime name so the
94
+ * lifecycle module can act without a translation table.)
95
+ */
96
+ observersTouched: readonly string[];
97
+ /**
98
+ * Path prefixes the registry middleware 410-gates when delegated. The
99
+ * 410 is **defense-in-depth** in v2 (DELEGATED-MODE-V2-DESIGN.md §6.3):
100
+ * cross-backend skill prose directs the agent at
101
+ * `POST /api/integrations/:key/invoke`, and same-backend skill is not
102
+ * provisioned at all. The gate fires only when the agent invents a
103
+ * call the variant prose did not teach it.
104
+ *
105
+ * Multi-provider routes (e.g. Gmail under `/api/mail/*`) are
106
+ * intentionally NOT listed here — prefix matching would also block
107
+ * other providers (iCloud, Outlook, IMAP). Per-account 410 inside the
108
+ * route handler covers those cases. Single-provider surfaces like
109
+ * `/api/calendar` can be listed safely.
110
+ */
111
+ apiRoutesTouched: readonly string[];
112
+ /**
113
+ * Skill slugs whose `SKILL.md` body should be filtered by this
114
+ * integration's `deniedTools` independently of `skillsTouched` —
115
+ * forward-compat hook for skill bodies that touch the integration but
116
+ * should not trigger variant resolution. Today, `skillsTouched`
117
+ * already covers every body that needs the deny pass; this field
118
+ * remains for symmetry with non-default-variant cases.
119
+ */
120
+ deniedToolsAppliesToSkills?: readonly string[];
121
+ /**
122
+ * Subset of `skillsTouched` whose body the integration's same-backend
123
+ * connector tools fully cover. When `selectSkillVariantFile` resolves
124
+ * to "same-backend", a touched skill is dropped (returns `null`) only
125
+ * if EVERY touching integration declares it here. Skills broader than
126
+ * the integration (e.g. `mail` covers IMAP/Outlook on top of Gmail;
127
+ * `external-services` covers Obsidian/GitHub/scheduling on top of
128
+ * Google Calendar) must NOT appear here — dropping them would orphan
129
+ * the non-delegated functionality. Skills that are pure connector
130
+ * wrappers (e.g. `notion` is solely Notion) belong here.
131
+ */
132
+ sameBackendDropsSkillBody?: readonly string[];
133
+ /**
134
+ * Marks an integration whose delegated mode relies on an MCP server or
135
+ * connector the user installs on the agent backend (Claude Code / Codex /
136
+ * Gemini CLI) themselves. The daemon does not ship a tool inventory for
137
+ * these connectors, so:
138
+ * - `backendConnectors` may be empty (no descriptor-driven feature
139
+ * matrix or capability probe).
140
+ * - The PATCH `/integrations/:key` endpoint accepts any `delegatedBackend`
141
+ * supported by the daemon — capability probing is the user's
142
+ * responsibility on the backend side.
143
+ * - The probe and route-gate surfaces fall back to a generic
144
+ * "user-managed connector" path that does not enforce the daemon's
145
+ * normal tool-inventory checks.
146
+ *
147
+ * Today: `outlook_mail` and `outlook_calendar`. Microsoft does not ship
148
+ * a hosted MCP connector for Claude / Codex / Gemini; users register an
149
+ * Outlook / Microsoft Graph MCP server on their backend (Claude Code
150
+ * Connector, Codex MCP, Gemini extension) and we trust that wiring.
151
+ */
152
+ userManagedConnector?: boolean;
153
+ }
154
+ /**
155
+ * The registry. Single source of truth for which integrations exist, what
156
+ * backends can delegate for them, and which parts of the daemon they touch.
157
+ *
158
+ * `backendConnectors` is a `Partial` record — omitting a backend means
159
+ * delegation through that backend is unsupported.
160
+ *
161
+ * Gemini namespace convention differs from Claude / Codex. Gemini CLI's
162
+ * MCP_TOOL_PREFIX is `mcp_` (single underscore) and the per-server prefix
163
+ * is `mcp_<serverName>_`, so a tool registered as `gmail.search` on the
164
+ * `google-workspace` extension surfaces as `mcp_google-workspace_gmail.search`.
165
+ * Hyphens and dots in the registered name are preserved. The tool-name
166
+ * format was confirmed by stream-event probe (2026-04-26 — see
167
+ * `gemini -p ... --output-format stream-json` `tool_use` events) and is
168
+ * the source of truth for `toolNamespace` and `capabilityTools` below.
169
+ *
170
+ * Gemini connectors require host-side MCP setup the daemon does not
171
+ * manage:
172
+ * - Gmail + Calendar: `~/.gemini/extensions/google-workspace/`
173
+ * (install via `gemini extensions install <url>`).
174
+ * - Notion: register Notion's official MCP server under the server name
175
+ * `notion` (e.g. `gemini mcp add notion <url>`); changing the server
176
+ * name breaks the namespace assumption — see Gemini Notion descriptor
177
+ * notes below.
178
+ */
179
+ export declare const INTEGRATION_DESCRIPTORS: Readonly<Record<IntegrationKey, IntegrationDescriptor>>;
180
+ export declare function getIntegrationDescriptor(key: IntegrationKey): IntegrationDescriptor;
181
+ export declare function listIntegrationDescriptors(): readonly IntegrationDescriptor[];
182
+ /**
183
+ * Per-integration runtime state. Persisted as a single JSON blob in the
184
+ * `settings` table under key `"integrations"`; rendered into
185
+ * `~/.personal-agent/integrations.md` so users can edit by hand.
186
+ */
187
+ export declare const integrationStateSchema: z.ZodObject<{
188
+ mode: z.ZodEnum<{
189
+ direct: "direct";
190
+ delegated: "delegated";
191
+ disabled: "disabled";
192
+ }>;
193
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
194
+ claude: "claude";
195
+ codex: "codex";
196
+ gemini: "gemini";
197
+ }>>>>;
198
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
199
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
200
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
201
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
202
+ lastChangedAt: z.ZodString;
203
+ }, z.core.$strip>;
204
+ export type IntegrationState = z.infer<typeof integrationStateSchema>;
205
+ /**
206
+ * The full integrations map stored in the `settings` table. Every registered
207
+ * integration key has an entry; missing keys are filled with the per-key
208
+ * default before persistence. Gmail / Calendar / Notion stay disabled on
209
+ * fresh installs, while Git / GitHub default to direct mode to preserve the
210
+ * pre-registry observer behaviour and match the Git lifecycle design.
211
+ */
212
+ export declare const integrationsMapSchema: z.ZodObject<{
213
+ git: z.ZodOptional<z.ZodObject<{
214
+ mode: z.ZodEnum<{
215
+ direct: "direct";
216
+ delegated: "delegated";
217
+ disabled: "disabled";
218
+ }>;
219
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
220
+ claude: "claude";
221
+ codex: "codex";
222
+ gemini: "gemini";
223
+ }>>>>;
224
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
225
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
226
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
227
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
228
+ lastChangedAt: z.ZodString;
229
+ }, z.core.$strip>>;
230
+ gmail: z.ZodOptional<z.ZodObject<{
231
+ mode: z.ZodEnum<{
232
+ direct: "direct";
233
+ delegated: "delegated";
234
+ disabled: "disabled";
235
+ }>;
236
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
237
+ claude: "claude";
238
+ codex: "codex";
239
+ gemini: "gemini";
240
+ }>>>>;
241
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
242
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
243
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
244
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
245
+ lastChangedAt: z.ZodString;
246
+ }, z.core.$strip>>;
247
+ google_calendar: z.ZodOptional<z.ZodObject<{
248
+ mode: z.ZodEnum<{
249
+ direct: "direct";
250
+ delegated: "delegated";
251
+ disabled: "disabled";
252
+ }>;
253
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
254
+ claude: "claude";
255
+ codex: "codex";
256
+ gemini: "gemini";
257
+ }>>>>;
258
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
259
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
260
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
261
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
262
+ lastChangedAt: z.ZodString;
263
+ }, z.core.$strip>>;
264
+ notion: z.ZodOptional<z.ZodObject<{
265
+ mode: z.ZodEnum<{
266
+ direct: "direct";
267
+ delegated: "delegated";
268
+ disabled: "disabled";
269
+ }>;
270
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
271
+ claude: "claude";
272
+ codex: "codex";
273
+ gemini: "gemini";
274
+ }>>>>;
275
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
276
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
277
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
278
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
279
+ lastChangedAt: z.ZodString;
280
+ }, z.core.$strip>>;
281
+ github: z.ZodOptional<z.ZodObject<{
282
+ mode: z.ZodEnum<{
283
+ direct: "direct";
284
+ delegated: "delegated";
285
+ disabled: "disabled";
286
+ }>;
287
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
288
+ claude: "claude";
289
+ codex: "codex";
290
+ gemini: "gemini";
291
+ }>>>>;
292
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
293
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
294
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
295
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
296
+ lastChangedAt: z.ZodString;
297
+ }, z.core.$strip>>;
298
+ outlook_mail: z.ZodOptional<z.ZodObject<{
299
+ mode: z.ZodEnum<{
300
+ direct: "direct";
301
+ delegated: "delegated";
302
+ disabled: "disabled";
303
+ }>;
304
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
305
+ claude: "claude";
306
+ codex: "codex";
307
+ gemini: "gemini";
308
+ }>>>>;
309
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
310
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
311
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
312
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
313
+ lastChangedAt: z.ZodString;
314
+ }, z.core.$strip>>;
315
+ outlook_calendar: z.ZodOptional<z.ZodObject<{
316
+ mode: z.ZodEnum<{
317
+ direct: "direct";
318
+ delegated: "delegated";
319
+ disabled: "disabled";
320
+ }>;
321
+ delegatedBackend: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodEnum<{
322
+ claude: "claude";
323
+ codex: "codex";
324
+ gemini: "gemini";
325
+ }>>>>;
326
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
327
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
328
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
329
+ deniedTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
330
+ lastChangedAt: z.ZodString;
331
+ }, z.core.$strip>>;
332
+ }, z.core.$strict>;
333
+ export type IntegrationsMap = Partial<Record<IntegrationKey, IntegrationState>>;
334
+ /** Build the default integration map used for fresh installs. */
335
+ export declare function defaultIntegrationsMap(now?: string): Record<IntegrationKey, IntegrationState>;
336
+ /**
337
+ * Narrow user input (from integrations.md or PATCH /api/integrations/:key) to a
338
+ * valid state. Used by the parser and the API route so both enforce identical
339
+ * rules.
340
+ */
341
+ export declare const integrationPatchSchema: z.ZodObject<{
342
+ mode: z.ZodEnum<{
343
+ direct: "direct";
344
+ delegated: "delegated";
345
+ disabled: "disabled";
346
+ }>;
347
+ delegatedBackend: z.ZodNullable<z.ZodOptional<z.ZodEnum<{
348
+ claude: "claude";
349
+ codex: "codex";
350
+ gemini: "gemini";
351
+ }>>>;
352
+ delegatedModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
353
+ delegatedMaxTurns: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
354
+ delegatedSyncEnabled: z.ZodOptional<z.ZodBoolean>;
355
+ deniedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
356
+ }, z.core.$strip>;
357
+ export type IntegrationPatch = z.infer<typeof integrationPatchSchema>;
358
+ /**
359
+ * DELEGATED-MODE-V2-DESIGN.md §4.1.1 — pick the SKILL.md variant for a given
360
+ * skill, **session backend**, and integration state. Three outcomes:
361
+ *
362
+ * - `"SKILL.md"` — direct/default body. Used when no touched
363
+ * integration is delegated, when the skill is
364
+ * integration-agnostic, OR when every touched
365
+ * integration is same-backend delegated but the
366
+ * skill body covers more than the connector
367
+ * exposes (see `sameBackendDropsSkillBody`).
368
+ * - `null` — do **not** materialize the skill at all. Reached
369
+ * when every touched integration is delegated AND
370
+ * `delegatedBackend === sessionBackend` AND every
371
+ * touching integration declares the skill in its
372
+ * `sameBackendDropsSkillBody` (i.e. the connector
373
+ * covers the entire skill surface). The agent
374
+ * already has the connector's tools natively, so
375
+ * a redundant skill body is omitted.
376
+ * - `"SKILL.delegated.<sessionBackend>.md"` — cross-backend variant. The
377
+ * DM session runs on `sessionBackend` but the
378
+ * connector for at least one touched integration
379
+ * lives on a *different* backend, so the agent
380
+ * must call the daemon's generic invoke endpoint
381
+ * which spawns the delegatedBackend subprocess.
382
+ *
383
+ * Combination rule for skills that touch multiple integrations: cross-backend
384
+ * wins over same-backend (a single skill body cannot reliably span both
385
+ * worlds), and same-backend → null wins over direct only when *every*
386
+ * touched integration resolves to same-backend AND every touched integration
387
+ * declares the skill in `sameBackendDropsSkillBody`. Any other configuration
388
+ * falls back to `"SKILL.md"`.
389
+ *
390
+ * Callers are responsible for existence-checking: if the returned variant
391
+ * file does not exist on disk, fall back to `"SKILL.md"`.
392
+ */
393
+ export declare function selectSkillVariantFile(skillSlug: string, sessionBackend: BackendId, integrations: Partial<Record<IntegrationKey, IntegrationState>>): string | null;
394
+ /**
395
+ * Return the task-flow variant suffix for a given event type, backend, and
396
+ * integration state. Returns `"direct"` when no touched integration is
397
+ * delegated. Returns `"delegated.<backendId>"` otherwise.
398
+ *
399
+ * The caller constructs the filename as `<eventType>.<suffix>.md` and checks
400
+ * for existence before falling back to `<eventType>.md`.
401
+ */
402
+ export declare function selectTaskFlowVariantSuffix(taskFlowKey: string, backendId: BackendId, integrations: Partial<Record<IntegrationKey, IntegrationState>>): string;
403
+ /**
404
+ * Return the delegated integrations whose `taskFlowsTouched` declares a
405
+ * dependency on this process key. Skills are intentionally not consulted —
406
+ * skills load on demand mid-session, so the router cannot predict them from
407
+ * a process key alone.
408
+ *
409
+ * **Excludes proxy-driven integrations** (see {@link PROXY_DRIVEN_INTEGRATIONS}).
410
+ * The router uses this helper only to decide whether to refuse a fallback
411
+ * because the fallback backend lacks the integration's connector. For
412
+ * proxy-driven integrations the connector lives on `delegatedBackend` and
413
+ * is invoked by the daemon — the agent backend never touches it, so the
414
+ * fallback gate is irrelevant. (Gmail/Calendar still appear in
415
+ * `taskFlowsTouched` because their delegated variant is what
416
+ * `selectTaskFlowVariantSuffix` reads to compensate for stopped pollers;
417
+ * the asymmetry is intentional.)
418
+ *
419
+ * Returns an empty array when no native-MCP delegated integration touches
420
+ * the key.
421
+ */
422
+ export declare function delegatedIntegrationsForProcessKey(processKey: string, integrations: Partial<Record<IntegrationKey, IntegrationState>>): IntegrationKey[];
423
+ /**
424
+ * True when the registry declares a connector for `(integrationKey, backendId)`.
425
+ * Descriptor presence is the contract the BackendRouter consults on the
426
+ * fallback path — a missing entry means the backend has no connector for
427
+ * this integration, so routing a delegated-integration process key through
428
+ * it would silently execute with the wrong tool surface.
429
+ *
430
+ * This does NOT consult capability lists or live probe state; `PATCH
431
+ * /api/integrations/:key` already enforces `requiredCapabilities` against
432
+ * the live probe before a delegated flip is accepted, and the router does
433
+ * not re-run that check at dispatch time.
434
+ */
435
+ export declare function backendHasIntegrationConnector(integrationKey: IntegrationKey, backendId: BackendId): boolean;
436
+ /**
437
+ * DELEGATED-MODE-V2-DESIGN.md §4.3.5 — match a deny pattern against a single
438
+ * tool name. Patterns are bare unsuffixed tool names — no `mcp__*` prefix,
439
+ * no leading underscore (the connector's `toolNamespace` already terminates
440
+ * as needed; e.g. Codex Gmail's namespace is `mcp__codex_apps__gmail._`,
441
+ * so the bare match key is `send_email`, not `_send_email`). Optionally
442
+ * suffixed with `*`:
443
+ *
444
+ * `send_email` — exact match
445
+ * `send_*` — prefix match, anything starting with `send_`
446
+ * `*` — matches anything (deny everything; rare)
447
+ *
448
+ * Anchors are implicit; `*` is only honored as a suffix to keep the pattern
449
+ * language single-purpose. `*` mid-string is treated as literal text.
450
+ *
451
+ * Used by `validateDeniedTools` (typo defense at PATCH time),
452
+ * `filterDeniedToolsForBackend` (active/stale partition during
453
+ * materialization), and the `/api/integrations/:key/invoke` chokepoint
454
+ * (per-call deny enforcement).
455
+ */
456
+ export declare function matchToolPattern(pattern: string, tool: string): boolean;
457
+ export type ValidateDeniedToolsResult = {
458
+ ok: true;
459
+ } | {
460
+ ok: false;
461
+ error: "no_connector";
462
+ backendId: BackendId;
463
+ } | {
464
+ ok: false;
465
+ error: "unknown_tool";
466
+ tool: string;
467
+ knownTools: readonly string[];
468
+ } | {
469
+ ok: false;
470
+ error: "denial_breaks_required_capability";
471
+ capability: string;
472
+ remainingTools: readonly string[];
473
+ };
474
+ /**
475
+ * Validate a proposed `deniedTools` list against an integration's
476
+ * descriptor for a given `delegatedBackend`. Returns the first failure;
477
+ * callers map this to the documented 400 shapes.
478
+ *
479
+ * Patterns: each entry is either an exact tool name (e.g. `_send_email`)
480
+ * or a `*`-suffixed glob (e.g. `_delete_*`, `*`). See {@link matchToolPattern}.
481
+ *
482
+ * - **`unknown_tool`**: an exact entry isn't in any of the connector's
483
+ * `capabilityTools` arrays, OR a glob entry matches no known tool
484
+ * (typo defense for both forms). Helps users catch typos and stale
485
+ * Claude / Codex names after a backend swap. (Note: the *materializer*
486
+ * tolerates stale entries silently per §7.7 — but PATCH is strict so
487
+ * the user sees the typo before saving. The dashboard's stale-entries
488
+ * UI handles the soft case after a backend flip.)
489
+ * - **`denial_breaks_required_capability`**: collectively, the proposal
490
+ * drops every tool that satisfies at least one `requiredCapability`.
491
+ * Multi-cap overlap matters — Notion's `notion-update-page` covers
492
+ * `update_properties`, `patch_content`, `archive`, and `apply_template`,
493
+ * so denying it breaks all four required caps at once. The error names
494
+ * the first failing capability and lists what's still in the
495
+ * `capabilityTools[capability]` set after the deny — so the dashboard
496
+ * can show a "you'd need to keep X, Y, or Z" hint without a second call.
497
+ * Globs are expanded against the connector's known tools before the
498
+ * capability-coverage check runs.
499
+ */
500
+ export declare function validateDeniedTools(integrationKey: IntegrationKey, backendId: BackendId, deniedTools: readonly string[]): ValidateDeniedToolsResult;
501
+ /**
502
+ * Filter a `deniedTools` list down to entries that exist in the active
503
+ * backend's capability tools, expanding glob patterns. Used by the
504
+ * materializer to silently drop stale names after a `delegatedBackend`
505
+ * swap (§7.7 mode-flip behavior) and by `collectSessionDeniedTools` to
506
+ * produce concrete namespaced tool names for backend `disallowedTools`.
507
+ *
508
+ * The output `active` list is the **expanded** concrete tool name set
509
+ * (globs replaced by their matches) so callers can feed it directly into
510
+ * an SDK `disallowedTools` array or `applyDeniedTools`. `stale` carries
511
+ * the original user-entered patterns that matched nothing on this
512
+ * backend's tool universe (typo or post-swap leftover).
513
+ *
514
+ * Distinct from `validateDeniedTools` — the API rejects unknown tools at
515
+ * PATCH time, but a user who flips claude → codex carries Claude-namespaced
516
+ * entries that the API didn't see at PATCH time. The materializer ignores
517
+ * them; the dashboard surfaces them as "stale" so the user can clean up.
518
+ */
519
+ export declare function filterDeniedToolsForBackend(integrationKey: IntegrationKey, backendId: BackendId, deniedTools: readonly string[]): {
520
+ active: string[];
521
+ stale: string[];
522
+ };
523
+ /**
524
+ * Lookup the recommended starter denylist for `(integrationKey,
525
+ * backendId)`. Returns a fresh array each call so the caller can store it
526
+ * directly without aliasing the registry constant.
527
+ */
528
+ export declare function recommendedStarterDeniedTools(integrationKey: IntegrationKey, backendId: BackendId): string[];
529
+ /**
530
+ * DELEGATED-TASK-MODE-DESIGN.md §7.3 — destructive tool list for
531
+ * `(integrationKey, backendId)`, returned as **fully-qualified names**
532
+ * (`toolNamespace + bareName`). Used by `runDelegatedTask` to feed into
533
+ * SDK `disallowedTools` (Claude) or admin-policy deny rules (Gemini)
534
+ * when `allowDestructive: false`.
535
+ *
536
+ * Returns `[]` when the backend has no connector for the integration
537
+ * (forward-compat: this never happens today; every backend has a
538
+ * connector for every registered integration).
539
+ */
540
+ export declare function destructiveTaskTools(integrationKey: IntegrationKey, backendId: BackendId): string[];
541
+ /**
542
+ * DELEGATED-TASK-MODE-DESIGN.md §7.3 — bare-name version of
543
+ * {@link destructiveTaskTools}, returned WITHOUT the `toolNamespace`
544
+ * prefix. Used by anti-prompt-injection assertions and by callers that
545
+ * already namespace separately.
546
+ */
547
+ export declare function destructiveTaskToolsBare(integrationKey: IntegrationKey, backendId: BackendId): string[];
548
+ /**
549
+ * DELEGATED-MODE-V2-DESIGN.md §4.3.3 — collect every `deniedTools` entry
550
+ * that applies to the **session backend's own native MCP** (the
551
+ * "same-backend" delegated state). For each integration whose
552
+ * `delegatedBackend === sessionBackend`, expand the user's deny patterns
553
+ * against the connector's known tools and return the namespaced tool names
554
+ * (`mcp__<connector>__<tool>`) ready to drop into:
555
+ *
556
+ * - Claude Code SDK `disallowedTools` array (hard enforcement)
557
+ * - Gemini admin policy `denied_tools` rules (hard enforcement)
558
+ * - Codex agent profile prose block (soft enforcement — accepted gap,
559
+ * §4.3.4 outcome γ)
560
+ *
561
+ * Cross-backend (delegatedBackend !== sessionBackend) integrations are
562
+ * excluded — those calls go through `/api/integrations/:key/invoke` which
563
+ * enforces deny at the daemon chokepoint (§4.3.2). Disabled / direct
564
+ * integrations are excluded — deny only applies to delegated mode.
565
+ *
566
+ * Returns a `Map` keyed by integration so callers can attribute denies
567
+ * per-integration in logs / prose. Empty entries are omitted.
568
+ */
569
+ export declare function collectSessionDeniedTools(integrations: Partial<Record<IntegrationKey, IntegrationState>>, sessionBackend: BackendId): Map<IntegrationKey, string[]>;
570
+ /**
571
+ * Predicates accepted by `applyIntegrationModeFilter`. Each one keeps the
572
+ * wrapped section when its condition holds for the given integration key
573
+ * and session backend.
574
+ */
575
+ export declare const INTEGRATION_MODE_PREDICATES: readonly ["direct", "delegated", "delegated-same", "delegated-cross", "disabled"];
576
+ export type IntegrationModePredicate = (typeof INTEGRATION_MODE_PREDICATES)[number];
577
+ /**
578
+ * Strip mode-conditional sections from a task-flow or skill body based on
579
+ * current integration state and session backend. Mirrors the
580
+ * `<!-- service:* -->` family used by `stripUnconfiguredServices` in
581
+ * skills-compiler.ts.
582
+ *
583
+ * Section syntax (HTML comment delimiters, kept literal so authoring stays
584
+ * markdown-friendly):
585
+ *
586
+ * <!-- mode:<predicate>:<key> -->
587
+ * ...content shown only when the predicate holds...
588
+ * <!-- /mode:<predicate>:<key> -->
589
+ *
590
+ * Predicates and the state they keep content for:
591
+ *
592
+ * direct — `integrations[key].mode === "direct"`
593
+ * delegated — `mode === "delegated"` (regardless of which backend)
594
+ * delegated-same — delegated AND `delegatedBackend === sessionBackend`
595
+ * (the connector is signed in to this same session's
596
+ * backend; the agent uses native MCP tools and skill
597
+ * bodies are typically not materialized)
598
+ * delegated-cross — delegated AND `delegatedBackend !== sessionBackend`
599
+ * (the agent reaches the connector via the daemon
600
+ * proxy at `POST /api/integrations/:key/invoke`)
601
+ * disabled — `mode === "disabled"` OR no state row (treated as
602
+ * disabled by `defaultIntegrationsMap`)
603
+ *
604
+ * Behaviour notes:
605
+ *
606
+ * - **Unknown predicate**: section preserved verbatim. Surfaces the typo in
607
+ * the rendered prompt rather than silently losing or duplicating prose.
608
+ * - **Unknown integration key**: section preserved verbatim. Same rationale —
609
+ * a misspelled key (or registry drift mid-deploy) should remain visible.
610
+ * - **Sections do not nest**: the regex is non-greedy and matches the
611
+ * first close tag. Two sibling blocks at the same level work; nesting
612
+ * a same-key block inside another doesn't and is unsupported.
613
+ * - **Idempotent**: running the filter twice on the same content with the
614
+ * same state produces identical output.
615
+ *
616
+ * Apply this AFTER any whole-file variant selection (e.g. `SKILL.delegated.
617
+ * <backend>.md`) so the variant author can use mode markers freely without
618
+ * worrying about pre-filtering interactions.
619
+ */
620
+ export declare function applyIntegrationModeFilter(content: string, integrations: Partial<Record<IntegrationKey, IntegrationState>>, sessionBackend: BackendId): string;
621
+ /**
622
+ * Validation regex for an `allowedTools` entry on `POST /api/delegated/run`
623
+ * (Phase 2 generic task mode for unregistered MCPs).
624
+ *
625
+ * Spec §4.2 reads: "Specifically rejected: bare `*`, patterns starting with
626
+ * `*`, prefixes shorter than 4 characters before `*` (e.g. `mcp_*`), and
627
+ * anything containing shell metacharacters." The literal example `mcp_*`
628
+ * has a 4-character prefix yet must be rejected — i.e. the spec's prose
629
+ * intent is "≥5 chars before `*`," and the {4,} quantifier in the literal
630
+ * regex contradicts the example. We honor the example: prefix-before-`*`
631
+ * must be ≥5 chars, and bare/leading `*` is rejected outright.
632
+ *
633
+ * Allowed shape:
634
+ * - 5+ chars of `[A-Za-z0-9_-]` to start.
635
+ * - Optional dotted/underscored continuation segments
636
+ * (`([._][A-Za-z0-9_-]+)*`).
637
+ * - Optional trailing `*` for a glob.
638
+ *
639
+ * Examples accepted: `mcp_my-server_*`, `mcp_my-server_subtool.action`,
640
+ * `mcp__custom-srv_doIt`. Examples rejected: `*`, `*foo`, `mcp_*`,
641
+ * `mcp_my-server_*.action`, `srv;rm -rf /`.
642
+ *
643
+ * Shell-metacharacter rejection is doubled-up: even if a pattern slipped
644
+ * through the regex (it can't — the character class excludes them), the
645
+ * dedicated check in {@link validateRunAllowedTool} would reject. This
646
+ * keeps the safety floor obvious in review.
647
+ */
648
+ export declare const MCP_PATTERN_REGEX: RegExp;
649
+ export type ValidateRunAllowedToolsResult = {
650
+ ok: true;
651
+ } | {
652
+ ok: false;
653
+ errorClass: "bad_allowed_tools";
654
+ pattern: string;
655
+ reason: "empty" | "bare_star" | "leading_star" | "prefix_too_short" | "shell_metachar" | "shape_invalid";
656
+ message: string;
657
+ };
658
+ /** Validate a single pattern entry. Exported for unit tests. */
659
+ export declare function validateRunAllowedTool(pattern: unknown): ValidateRunAllowedToolsResult;
660
+ /**
661
+ * Validate every entry in a `/api/delegated/run` `allowedTools` array.
662
+ * Returns the first failing entry (HTTP 400 maps to that), or `{ok: true}`
663
+ * if every pattern is well-formed and the array is non-empty.
664
+ */
665
+ export declare function validateRunAllowedTools(patterns: readonly unknown[]): ValidateRunAllowedToolsResult;
666
+ /**
667
+ * Match an `/api/delegated/run` allowedTools entry against a fully
668
+ * qualified tool name observed in the subprocess stream. Mirrors the
669
+ * `matchToolPattern` semantics used elsewhere — exact equality or
670
+ * `*`-suffix prefix match — but the input space is fully-qualified MCP
671
+ * names, not the bare deny vocabulary, so we expose it as a separate
672
+ * symbol.
673
+ */
674
+ export declare function matchRunAllowedToolPattern(pattern: string, tool: string): boolean;
675
+ //# sourceMappingURL=integrations.d.ts.map