@tealbrick/kit 0.3.0-rc.1 → 0.3.0-rc.10

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 (43) hide show
  1. package/BOOTSTRAP.md +9 -10
  2. package/BUZZ.md +146 -0
  3. package/NATIVE.md +10 -4
  4. package/README.md +263 -2
  5. package/RUNTIME.md +1 -1
  6. package/assets/buzz/buzz-backend-provider.py +99 -0
  7. package/assets/buzz/buzz-plist.py +22 -0
  8. package/assets/buzz/buzz-run.sh +39 -0
  9. package/assets/buzz/tb-provider-apply.py +97 -0
  10. package/dist/buzz.d.ts +63 -0
  11. package/dist/buzz.js +262 -0
  12. package/dist/cli.js +9 -4
  13. package/dist/index.d.ts +15 -14
  14. package/dist/index.js +1 -1
  15. package/dist/native-acp.d.ts +40 -0
  16. package/dist/native-acp.js +224 -0
  17. package/dist/native-app-capabilities.d.ts +33 -0
  18. package/dist/native-app-capabilities.js +37 -0
  19. package/dist/native-buzz.d.ts +161 -0
  20. package/dist/native-buzz.js +649 -0
  21. package/dist/native-child-guard.js +7 -2
  22. package/dist/native-claude.d.ts +99 -0
  23. package/dist/native-claude.js +584 -0
  24. package/dist/native-enroll.d.ts +22 -0
  25. package/dist/native-enroll.js +85 -0
  26. package/dist/native-runtime-sync.d.ts +49 -0
  27. package/dist/native-runtime-sync.js +101 -0
  28. package/dist/native-selection.js +19 -4
  29. package/dist/native-serve-config.d.ts +286 -0
  30. package/dist/native-serve-config.js +296 -0
  31. package/dist/native-serve.d.ts +76 -0
  32. package/dist/native-serve.js +145 -0
  33. package/dist/native-setup.js +38 -15
  34. package/dist/native-standing-grants.d.ts +105 -0
  35. package/dist/native-standing-grants.js +226 -0
  36. package/dist/native.d.ts +3 -0
  37. package/dist/native.js +94 -5
  38. package/dist/onboarding.d.ts +8 -0
  39. package/dist/onboarding.js +9 -2
  40. package/dist/runtime-onboarding.js +2 -2
  41. package/dist/secrets.d.ts +2 -1
  42. package/dist/secrets.js +4 -2
  43. package/package.json +9 -7
@@ -0,0 +1,85 @@
1
+ import { realpath } from 'node:fs/promises';
2
+ import { PortalHttpError } from '@tealbrick/portal';
3
+ import { readKit } from './index.js';
4
+ import { harnessLabels } from './native-selection.js';
5
+ import { normalizeOrigin, readServeConfig, writeServeConfig } from './native-serve-config.js';
6
+ /** Register this kit root's public chat endpoint on its EXISTING native Portal agent.
7
+ * Never creates or relabels an identity: the URL goes through Portal's owned-connection
8
+ * upsert, which preserves the agent's harness runtime (the Eve registration route would
9
+ * rewrite it to "eve"). The user token stays in memory. */
10
+ export async function enrollNative(root, input, login, options = {}) {
11
+ root = await realpath(root);
12
+ const kit = await readKit(root);
13
+ if (!kit.native || !kit.runtime)
14
+ throw Error('kit_native_setup_required');
15
+ const { runtime } = kit, label = harnessLabels[kit.native.harness];
16
+ if (login.issuer !== runtime.issuer || login.org !== runtime.org)
17
+ throw Error('kit_runtime_existing_binding_mismatch');
18
+ const url = normalizeOrigin(input.url), old = await readServeConfig(root), port = input.port ?? old?.port ?? 5331;
19
+ if (!Number.isInteger(port) || port < 1024 || port > 65535)
20
+ throw Error('kit_native_serve_invalid_port');
21
+ const path = `/api/workspace?workspaceId=${encodeURIComponent(runtime.workspaceId)}`;
22
+ const owned = (snapshot) => {
23
+ const agent = snapshot.agents?.find(a => a.id === runtime.agentId && a.bindable);
24
+ const nodes = snapshot.draft?.nodes?.filter(n => n.data?.kind === 'agent' && n.data.agentId === runtime.agentId) ?? [];
25
+ if (snapshot.workspace?.id !== runtime.workspaceId || !agent || nodes.length !== 1)
26
+ throw Error('kit_owned_agent_card_required');
27
+ if (agent.runtime !== label || nodes[0].data.harness !== label)
28
+ throw Error('kit_native_portal_harness_mismatch');
29
+ return { agent, node: nodes[0] };
30
+ };
31
+ const { agent } = owned(await login.client.request(path));
32
+ let registered;
33
+ try {
34
+ registered = await login.client.request('/api/connections', { name: agent.name, runtime: label, url });
35
+ }
36
+ catch (error) {
37
+ if (error instanceof PortalHttpError && [404, 405].includes(error.status))
38
+ throw Error('kit_portal_connections_unsupported');
39
+ throw error;
40
+ }
41
+ const c = registered.connection;
42
+ if (c?.id !== agent.id || c.url !== url || c.runtime !== label)
43
+ throw Error('kit_portal_registration_mismatch');
44
+ await writeServeConfig(root, { ...(old ?? { version: 1, port }), version: 1, port, publicUrl: url, agent: { id: agent.id, name: agent.name, ownerId: login.userId } });
45
+ // Re-read after registration: preserve concurrent canvas changes and revision checks.
46
+ for (let attempt = 0;; attempt++) {
47
+ const fresh = await login.client.request(path), { node } = owned(fresh);
48
+ if (node.data.endpoint === url && node.data.authMethod === 'portal-pairing')
49
+ break;
50
+ const nodes = fresh.draft.nodes.map(n => n.id === node.id ? { ...n, data: { ...n.data, endpoint: url, authMethod: 'portal-pairing' } } : n);
51
+ try {
52
+ await login.client.request('/api/workspace/draft', { workspaceId: runtime.workspaceId, draftRevision: fresh.draftRevision, draft: { ...fresh.draft, nodes } });
53
+ break;
54
+ }
55
+ catch (error) {
56
+ if (error instanceof PortalHttpError && error.code === 'draft_changed_reload' && attempt < 2)
57
+ continue;
58
+ throw error;
59
+ }
60
+ }
61
+ const directory = await login.client.request(`/api/me/agents?workspaceId=${encodeURIComponent(runtime.workspaceId)}`);
62
+ if (directory.workspaceId !== runtime.workspaceId || !directory.agents?.some(a => a.id === agent.id && a.url === url))
63
+ throw Error('kit_portal_sidebar_verification_failed');
64
+ const endpoint = { healthy: false, authenticated: false, reason: undefined };
65
+ if (options.verifyEndpoint !== false) {
66
+ const f = options.fetch ?? fetch, get = (route, headers = {}) => f(url + route, { headers, redirect: 'error', signal: AbortSignal.timeout(10000) });
67
+ try {
68
+ const health = await get('/healthz'), body = health.ok ? await health.json() : undefined;
69
+ endpoint.healthy = body?.harness === kit.native.harness;
70
+ if (!endpoint.healthy)
71
+ endpoint.reason = 'health_check_failed';
72
+ else if (login.identity) {
73
+ const info = await get('/tealbrick/v1/info', { authorization: `Bearer ${login.identity.token}`, 'x-tealbrick-bundle': login.identity.bundle });
74
+ const value = info.ok ? await info.json() : undefined;
75
+ endpoint.authenticated = value?.harness === kit.native.harness && value.name === agent.name;
76
+ if (!endpoint.authenticated)
77
+ endpoint.reason = 'authenticated_info_failed_' + info.status;
78
+ }
79
+ }
80
+ catch {
81
+ endpoint.reason = 'endpoint_unreachable';
82
+ }
83
+ }
84
+ return { enrolled: true, agentId: agent.id, name: agent.name, harness: label, endpoint: url, workspaceId: runtime.workspaceId, portalVerified: true, endpointVerified: endpoint.healthy && endpoint.authenticated, ...(endpoint.reason ? { endpointReason: endpoint.reason } : {}) };
85
+ }
@@ -0,0 +1,49 @@
1
+ import { RuntimeConnector } from '@tealbrick/portal/runtime';
2
+ import type { JWTVerifyGetKey } from 'jose';
3
+ export type RuntimeCapabilities = Awaited<ReturnType<RuntimeConnector['capabilities']>>;
4
+ export type RuntimeSyncStatus = 'starting' | 'synced' | 'retrying' | 'inactive' | 'stopped';
5
+ export interface RuntimeSyncSnapshot {
6
+ status: RuntimeSyncStatus;
7
+ revision?: string;
8
+ expiresAt?: number;
9
+ lastSyncAt?: number;
10
+ failures: number;
11
+ error?: string;
12
+ capabilities?: RuntimeCapabilities;
13
+ }
14
+ export interface RuntimeSyncOptions {
15
+ fetch?: typeof fetch;
16
+ keys?: JWTVerifyGetKey;
17
+ log?: (event: Record<string, unknown>) => void;
18
+ /** Base delay between successful syncs. Portal's signed config lives 300s, so this must stay well below it. */
19
+ intervalMs?: number;
20
+ jitterMs?: number;
21
+ /** First retry delay after a failure; doubles per consecutive failure up to maxRetryMs. */
22
+ retryMs?: number;
23
+ maxRetryMs?: number;
24
+ random?: () => number;
25
+ now?: () => number;
26
+ }
27
+ /**
28
+ * Keeps this customer runtime's Portal configuration acknowledged while `native serve` is idle.
29
+ * The per-turn Teal Brick MCP child still fetches a fresh authorization for every call; this loop
30
+ * only fetches the signed config, acknowledges it (Portal derives runtime sync freshness from that)
31
+ * and holds the latest capability snapshot in memory. App credentials never leave the kit root.
32
+ */
33
+ export declare function startRuntimeSync(root: string, options?: RuntimeSyncOptions): {
34
+ snapshot: () => RuntimeSyncSnapshot;
35
+ capabilities: () => {
36
+ revision: string;
37
+ expiresAt: number;
38
+ apps: {
39
+ registrationId: string;
40
+ appId: "knowledge";
41
+ actions: import("@tealbrick/portal/runtime").CrudAction[];
42
+ available: boolean;
43
+ operations: import("@tealbrick/portal/runtime").KnowledgeOperation[];
44
+ }[];
45
+ } | undefined;
46
+ syncNow: () => Promise<void>;
47
+ stop(): Promise<void>;
48
+ };
49
+ export type RuntimeSync = ReturnType<typeof startRuntimeSync>;
@@ -0,0 +1,101 @@
1
+ import { RuntimeConnector } from '@tealbrick/portal/runtime';
2
+ import { readKit } from './index.js';
3
+ import { runtimeSecret } from './secrets.js';
4
+ const defaults = { intervalMs: 90_000, jitterMs: 30_000, retryMs: 15_000, maxRetryMs: 240_000 };
5
+ const code = (v) => typeof v === 'string' && /^[a-z][a-z0-9_]{0,80}$/.test(v) ? v : undefined;
6
+ /**
7
+ * Keeps this customer runtime's Portal configuration acknowledged while `native serve` is idle.
8
+ * The per-turn Teal Brick MCP child still fetches a fresh authorization for every call; this loop
9
+ * only fetches the signed config, acknowledges it (Portal derives runtime sync freshness from that)
10
+ * and holds the latest capability snapshot in memory. App credentials never leave the kit root.
11
+ */
12
+ export function startRuntimeSync(root, options = {}) {
13
+ const o = { ...defaults, ...options }, random = o.random ?? Math.random, now = o.now ?? Date.now;
14
+ const log = o.log ?? ((event) => process.stderr.write(JSON.stringify(event) + '\n'));
15
+ const base = o.fetch ?? fetch;
16
+ let state = { status: 'starting', failures: 0 };
17
+ let timer, stopped = false, running;
18
+ let connector, connectorKey;
19
+ let observed = {};
20
+ const observe = async (input, init) => {
21
+ const response = await base(input, init);
22
+ const path = new URL(input instanceof Request ? input.url : String(input)).pathname;
23
+ if (path === '/api/runtime/config' || path === '/api/runtime/ack') {
24
+ observed[path === '/api/runtime/config' ? 'config' : 'ack'] = response.status;
25
+ if (!response.ok)
26
+ observed.code = code(await response.clone().json().then((b) => b?.error ?? b?.code, () => undefined)) ?? observed.code;
27
+ }
28
+ return response;
29
+ };
30
+ const jitter = (ms, spread) => Math.max(1, Math.round(ms + (random() * 2 - 1) * spread));
31
+ function schedule(ms) { if (stopped)
32
+ return; clearTimeout(timer); timer = setTimeout(() => { void syncNow(); }, ms); timer.unref?.(); }
33
+ async function cycle() {
34
+ let kit;
35
+ try {
36
+ kit = await readKit(root);
37
+ }
38
+ catch {
39
+ kit = undefined;
40
+ }
41
+ if (!kit?.native?.active || !kit.runtime) {
42
+ if (state.status !== 'inactive')
43
+ log({ event: 'native.runtime.sync.inactive', message: 'Teal Brick runtime is not active in this kit root; Portal sync is paused.' });
44
+ connector = undefined;
45
+ connectorKey = undefined;
46
+ state = { status: 'inactive', failures: 0 };
47
+ schedule(jitter(o.intervalMs, o.jitterMs));
48
+ return;
49
+ }
50
+ const { credentialRef, ...runtime } = kit.runtime, key = JSON.stringify(kit.runtime);
51
+ if (!connector || key !== connectorKey) {
52
+ connector = new RuntimeConnector({ ...runtime, credential: () => runtimeSecret(credentialRef, root), resolveCredential: name => runtimeSecret(name, root), fetch: observe, ...(o.keys ? { keys: o.keys } : {}) });
53
+ connectorKey = key;
54
+ }
55
+ observed = {};
56
+ let capabilities;
57
+ try {
58
+ capabilities = await connector.capabilities(true);
59
+ }
60
+ catch (error) {
61
+ const reason = code(error?.message) ?? 'runtime_sync_failed';
62
+ if (observed.config === 401) {
63
+ const why = observed.code ?? 'runtime_credential_invalid';
64
+ stopped = true;
65
+ clearTimeout(timer);
66
+ state = { ...state, status: 'stopped', error: why };
67
+ log({ event: 'native.runtime.sync.stopped', reason: 'credential_revoked', code: why, message: `Portal rejected this runtime's credential (${why}); background runtime sync has stopped and Portal will show the runtime as expired. Re-enroll with \`tealbrick native setup\`, then restart native serve.` });
68
+ return;
69
+ }
70
+ const failures = state.failures + 1, delay = jitter(Math.min(o.maxRetryMs, o.retryMs * 2 ** (failures - 1)), Math.min(o.maxRetryMs, o.retryMs * 2 ** (failures - 1)) * 0.2);
71
+ state = { ...state, status: 'retrying', failures, error: observed.code ?? reason };
72
+ log({ event: 'native.runtime.sync.failed', code: state.error, ...(observed.config !== undefined ? { status: observed.config } : {}), failures, retryInMs: delay });
73
+ schedule(delay);
74
+ return;
75
+ }
76
+ const changed = state.status !== 'synced' || state.revision !== capabilities.revision;
77
+ const ackFailed = observed.ack !== undefined && !(observed.ack >= 200 && observed.ack < 300);
78
+ state = { status: ackFailed ? 'retrying' : 'synced', revision: capabilities.revision, expiresAt: capabilities.expiresAt, lastSyncAt: now(), failures: ackFailed ? state.failures + 1 : 0, capabilities, ...(ackFailed ? { error: observed.code ?? 'runtime_ack_failed' } : {}) };
79
+ if (ackFailed)
80
+ log({ event: 'native.runtime.ack.failed', code: state.error, status: observed.ack, failures: state.failures });
81
+ else if (changed)
82
+ log({ event: 'native.runtime.synced', revision: capabilities.revision.slice(0, 12), apps: capabilities.apps.length, available: capabilities.apps.filter(a => a.available).length, expiresAt: new Date(capabilities.expiresAt).toISOString() });
83
+ // Never let the acknowledged lease lapse between syncs; a failed ack retries on the short schedule.
84
+ const next = ackFailed ? jitter(o.retryMs, o.retryMs * 0.2) : jitter(o.intervalMs, o.jitterMs);
85
+ schedule(Math.max(1, Math.min(next, capabilities.expiresAt - now() - 30_000)));
86
+ }
87
+ async function syncNow() {
88
+ if (stopped)
89
+ return;
90
+ running ??= cycle().catch(error => { log({ event: 'native.runtime.sync.failed', code: code(error?.message) ?? 'runtime_sync_failed' }); schedule(o.maxRetryMs); }).finally(() => { running = undefined; });
91
+ return running;
92
+ }
93
+ void syncNow();
94
+ return {
95
+ snapshot: () => ({ ...state }),
96
+ capabilities: () => state.capabilities,
97
+ syncNow,
98
+ async stop() { stopped = true; clearTimeout(timer); await running; if (state.status !== 'stopped')
99
+ state = { ...state, status: 'stopped' }; },
100
+ };
101
+ }
@@ -37,6 +37,8 @@ export async function selectNativeIdentity(root, flags, login, prompts) {
37
37
  if (workspaceName && flags.node)
38
38
  throw Error('kit_native_existing_node_requires_workspace');
39
39
  const cards = directory.agents.filter(a => a.harness === harness).map(agent => ({ agent }));
40
+ const unavailable = directory.agents.filter(a => a.harness !== harness).map(a => `${a.name} (${a.harness})`);
41
+ const context = unavailable.length ? ` Unavailable for ${harness}: ${unavailable.join(', ')}. Existing cards cannot be converted.` : '';
40
42
  let agentId = flags.agent;
41
43
  if (flags['agent-name']) {
42
44
  const matches = cards.filter((c) => c.agent.name === flags['agent-name']);
@@ -48,9 +50,9 @@ export async function selectNativeIdentity(root, flags, login, prompts) {
48
50
  agentId = old.runtime.agentId;
49
51
  let name = flags['create-agent'];
50
52
  if (!agentId && !name) {
51
- const choice = cards.length ? await prompts.choose('Choose an agent to attach', [...cards.map((c) => ({ name: `${c.agent.name} (${c.agent.id})`, value: c.agent.id })), { name: 'Create an agent', value: '__create__' }]) : '__create__';
53
+ const choice = cards.length ? await prompts.choose('Choose an agent to attach.' + context, [...cards.map((c) => ({ name: `${c.agent.name} — ${c.agent.harness} (${c.agent.id})`, value: c.agent.id })), { name: 'Create an agent', value: '__create__' }]) : '__create__';
52
54
  if (choice === '__create__')
53
- name = await prompts.name('New agent name');
55
+ name = await prompts.name('New agent name.' + context);
54
56
  else {
55
57
  const card = cards.find((c) => c.agent.id === choice);
56
58
  if (!card)
@@ -58,8 +60,21 @@ export async function selectNativeIdentity(root, flags, login, prompts) {
58
60
  agentId = card.agent.id;
59
61
  }
60
62
  }
61
- if (name)
62
- return { workspace, agent: { name: validName(name) } };
63
+ while (name) {
64
+ name = validName(name);
65
+ const conflicts = directory.agents.filter(a => a.name === name);
66
+ if (!conflicts.length)
67
+ return { workspace, agent: { name } };
68
+ const choice = await prompts.choose(`The name "${name}" already belongs to ${conflicts.map(a => `${a.name} (${a.harness}, ${a.id})`).join(', ')}. Select a compatible ${harness} card or choose another name.`, [...cards.map(c => ({ name: `${c.agent.name} — ${c.agent.harness} (${c.agent.id})`, value: c.agent.id })), { name: 'Choose another name', value: '__create__' }]);
69
+ if (choice === '__create__') {
70
+ name = await prompts.name('Different agent name.' + context);
71
+ continue;
72
+ }
73
+ if (!cards.some(c => c.agent.id === choice))
74
+ throw Error('kit_owned_agent_card_required');
75
+ agentId = choice;
76
+ name = undefined;
77
+ }
63
78
  const matches = cards.filter((c) => c.agent.id === agentId);
64
79
  if (matches.length !== 1)
65
80
  throw Error('kit_owned_agent_card_required');
@@ -0,0 +1,286 @@
1
+ import { z } from 'zod';
2
+ export declare const hostPattern: RegExp;
3
+ declare const stdio: z.ZodObject<{
4
+ type: z.ZodOptional<z.ZodLiteral<"stdio">>;
5
+ command: z.ZodString;
6
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
7
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
8
+ }, z.core.$strict>;
9
+ export declare const tealbrickCallGrant: z.ZodObject<{
10
+ registrationId: z.ZodString;
11
+ operation: z.ZodString;
12
+ inputEquals: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
13
+ }, z.core.$strict>;
14
+ declare const effort: z.ZodEnum<{
15
+ low: "low";
16
+ high: "high";
17
+ medium: "medium";
18
+ xhigh: "xhigh";
19
+ max: "max";
20
+ }>;
21
+ /** Tools an agent may add to its default (Restricted) session access: research and expert delegation, never shell or writes. */
22
+ export declare const defaultAccessTools: readonly ["WebSearch", "WebFetch", "Agent", "TodoWrite"];
23
+ export declare const expertContractSchema: z.ZodObject<{
24
+ schema: z.ZodLiteral<"tealbrick.expert-contract/v1">;
25
+ id: z.ZodString;
26
+ version: z.ZodString;
27
+ purpose: z.ZodString;
28
+ instructions: z.ZodString;
29
+ input: z.ZodObject<{
30
+ type: z.ZodLiteral<"object">;
31
+ }, z.core.$loose>;
32
+ output: z.ZodObject<{
33
+ type: z.ZodLiteral<"object">;
34
+ }, z.core.$loose>;
35
+ capabilities: z.ZodObject<{
36
+ builtinTools: z.ZodArray<z.ZodEnum<{
37
+ WebSearch: "WebSearch";
38
+ WebFetch: "WebFetch";
39
+ Read: "Read";
40
+ Glob: "Glob";
41
+ Grep: "Grep";
42
+ }>>;
43
+ apps: z.ZodArray<z.ZodObject<{
44
+ appId: z.ZodString;
45
+ operations: z.ZodArray<z.ZodString>;
46
+ }, z.core.$strict>>;
47
+ marketplace: z.ZodArray<z.ZodObject<{
48
+ plugin: z.ZodString;
49
+ actions: z.ZodArray<z.ZodString>;
50
+ }, z.core.$strict>>;
51
+ }, z.core.$strict>;
52
+ approval: z.ZodObject<{
53
+ outwardWrites: z.ZodEnum<{
54
+ always: "always";
55
+ "never-allowed": "never-allowed";
56
+ }>;
57
+ operations: z.ZodArray<z.ZodString>;
58
+ }, z.core.$strict>;
59
+ model: z.ZodEnum<{
60
+ haiku: "haiku";
61
+ inherit: "inherit";
62
+ opus: "opus";
63
+ sonnet: "sonnet";
64
+ }>;
65
+ effort: z.ZodOptional<z.ZodEnum<{
66
+ low: "low";
67
+ high: "high";
68
+ medium: "medium";
69
+ }>>;
70
+ budget: z.ZodObject<{
71
+ maxTurns: z.ZodNumber;
72
+ }, z.core.$strict>;
73
+ runtime: z.ZodEnum<{
74
+ subagent: "subagent";
75
+ "desktop-operator": "desktop-operator";
76
+ }>;
77
+ changelog: z.ZodOptional<z.ZodArray<z.ZodString>>;
78
+ }, z.core.$strict>;
79
+ export type ExpertContract = z.infer<typeof expertContractSchema>;
80
+ /**
81
+ * Owner-defined standing grant: a bounded class of calls that would otherwise pause for payload approval
82
+ * and that the kit hook approves without a prompt, within caps, until it expires. Never a wider scope than
83
+ * decideTool already allows; it only replaces the per-payload prompt.
84
+ */
85
+ export declare const standingGrantSchema: z.ZodObject<{
86
+ id: z.ZodString;
87
+ description: z.ZodString;
88
+ operation: z.ZodDefault<z.ZodString>;
89
+ match: z.ZodObject<{
90
+ registrationId: z.ZodOptional<z.ZodString>;
91
+ plugin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
92
+ action: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
93
+ }, z.core.$strict>;
94
+ experts: z.ZodOptional<z.ZodArray<z.ZodString>>;
95
+ caps: z.ZodObject<{
96
+ perDay: z.ZodNumber;
97
+ perHour: z.ZodOptional<z.ZodNumber>;
98
+ minIntervalSeconds: z.ZodOptional<z.ZodNumber>;
99
+ }, z.core.$strict>;
100
+ expires: z.ZodISODateTime;
101
+ deny: z.ZodOptional<z.ZodObject<{
102
+ inputPattern: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ }, z.core.$strict>>;
104
+ }, z.core.$strict>;
105
+ export type StandingGrant = z.infer<typeof standingGrantSchema>;
106
+ export declare const serveConfigSchema: z.ZodObject<{
107
+ version: z.ZodLiteral<1>;
108
+ port: z.ZodNumber;
109
+ publicUrl: z.ZodOptional<z.ZodString>;
110
+ allowedHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
111
+ maxActiveTurns: z.ZodOptional<z.ZodNumber>;
112
+ agent: z.ZodOptional<z.ZodObject<{
113
+ id: z.ZodString;
114
+ name: z.ZodString;
115
+ ownerId: z.ZodString;
116
+ }, z.core.$strict>>;
117
+ claude: z.ZodOptional<z.ZodObject<{
118
+ sdkModule: z.ZodOptional<z.ZodString>;
119
+ cwd: z.ZodString;
120
+ instructionsFile: z.ZodOptional<z.ZodString>;
121
+ recipe: z.ZodOptional<z.ZodObject<{
122
+ path: z.ZodString;
123
+ agent: z.ZodString;
124
+ }, z.core.$strict>>;
125
+ model: z.ZodOptional<z.ZodString>;
126
+ effort: z.ZodOptional<z.ZodEnum<{
127
+ low: "low";
128
+ high: "high";
129
+ medium: "medium";
130
+ xhigh: "xhigh";
131
+ max: "max";
132
+ }>>;
133
+ maxTurns: z.ZodOptional<z.ZodNumber>;
134
+ maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
135
+ timeoutSeconds: z.ZodOptional<z.ZodNumber>;
136
+ defaultTools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
137
+ WebSearch: "WebSearch";
138
+ WebFetch: "WebFetch";
139
+ Agent: "Agent";
140
+ TodoWrite: "TodoWrite";
141
+ }>>>;
142
+ experts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
143
+ promptFile: z.ZodString;
144
+ description: z.ZodOptional<z.ZodString>;
145
+ tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
146
+ WebSearch: "WebSearch";
147
+ WebFetch: "WebFetch";
148
+ Read: "Read";
149
+ Glob: "Glob";
150
+ Grep: "Grep";
151
+ }>>>;
152
+ maxTurns: z.ZodOptional<z.ZodNumber>;
153
+ effort: z.ZodOptional<z.ZodEnum<{
154
+ low: "low";
155
+ high: "high";
156
+ medium: "medium";
157
+ xhigh: "xhigh";
158
+ max: "max";
159
+ }>>;
160
+ }, z.core.$strict>>>;
161
+ contracts: z.ZodOptional<z.ZodArray<z.ZodString>>;
162
+ approvalOperations: z.ZodOptional<z.ZodArray<z.ZodString>>;
163
+ standingGrants: z.ZodOptional<z.ZodArray<z.ZodObject<{
164
+ id: z.ZodString;
165
+ description: z.ZodString;
166
+ operation: z.ZodDefault<z.ZodString>;
167
+ match: z.ZodObject<{
168
+ registrationId: z.ZodOptional<z.ZodString>;
169
+ plugin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
170
+ action: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
171
+ }, z.core.$strict>;
172
+ experts: z.ZodOptional<z.ZodArray<z.ZodString>>;
173
+ caps: z.ZodObject<{
174
+ perDay: z.ZodNumber;
175
+ perHour: z.ZodOptional<z.ZodNumber>;
176
+ minIntervalSeconds: z.ZodOptional<z.ZodNumber>;
177
+ }, z.core.$strict>;
178
+ expires: z.ZodISODateTime;
179
+ deny: z.ZodOptional<z.ZodObject<{
180
+ inputPattern: z.ZodOptional<z.ZodArray<z.ZodString>>;
181
+ }, z.core.$strict>>;
182
+ }, z.core.$strict>>>;
183
+ nativeTools: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodString]>>>;
184
+ readTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
185
+ mcpServers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
186
+ type: z.ZodOptional<z.ZodLiteral<"stdio">>;
187
+ command: z.ZodString;
188
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
189
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
190
+ }, z.core.$strict>>>;
191
+ tealbrickCalls: z.ZodOptional<z.ZodArray<z.ZodObject<{
192
+ registrationId: z.ZodString;
193
+ operation: z.ZodString;
194
+ inputEquals: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
195
+ }, z.core.$strict>>>;
196
+ requireSubscription: z.ZodOptional<z.ZodBoolean>;
197
+ workspace: z.ZodOptional<z.ZodObject<{
198
+ dir: z.ZodString;
199
+ network: z.ZodOptional<z.ZodObject<{
200
+ allowedDomains: z.ZodArray<z.ZodString>;
201
+ }, z.core.$strict>>;
202
+ envAllow: z.ZodOptional<z.ZodArray<z.ZodString>>;
203
+ }, z.core.$strict>>;
204
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
205
+ }, z.core.$strict>>;
206
+ codex: z.ZodOptional<z.ZodObject<{
207
+ codexBin: z.ZodString;
208
+ cwd: z.ZodString;
209
+ }, z.core.$strict>>;
210
+ }, z.core.$strict>;
211
+ export type ServeConfig = z.infer<typeof serveConfigSchema>;
212
+ export interface ClaudeProfile {
213
+ cwd: string;
214
+ sdkModule?: string;
215
+ instructions?: string;
216
+ model?: string;
217
+ effort?: z.infer<typeof effort>;
218
+ maxTurns: number;
219
+ maxBudgetUsd?: number;
220
+ timeoutSeconds: number;
221
+ defaultTools: string[];
222
+ experts: Record<string, ClaudeExpert>;
223
+ contracts: Record<string, ExpertContract>;
224
+ approvalOperations: string[];
225
+ standingGrants: StandingGrant[];
226
+ nativeTools: string[];
227
+ readTools: string[];
228
+ mcpServers: Record<string, z.infer<typeof stdio>>;
229
+ tealbrickCalls: Array<z.infer<typeof tealbrickCallGrant>>;
230
+ requireSubscription: boolean;
231
+ envPassthrough: string[];
232
+ /** Present only when `claude.workspace` is configured; `cwd` is then the workspace directory. */
233
+ workspace?: ClaudeWorkspace;
234
+ }
235
+ export interface ClaudeWorkspace {
236
+ dir: string;
237
+ root: string;
238
+ allowedDomains: string[];
239
+ /** envPassthrough names explicitly marked non-secret (still visible to sandboxed shell commands). */
240
+ envAllow: string[];
241
+ }
242
+ export interface ClaudeExpert {
243
+ description: string;
244
+ prompt: string;
245
+ tools: string[];
246
+ maxTurns: number;
247
+ effort?: z.infer<typeof effort>;
248
+ }
249
+ export declare const servePaths: (root: string) => {
250
+ config: string;
251
+ state: string;
252
+ sdk: string;
253
+ /** Kit-owned attachment inbox: written only by the kit process, readable (never writable) by the sandboxed workspace session. */
254
+ inbox: string;
255
+ };
256
+ export declare function normalizeOrigin(value: string): string;
257
+ /** Host header values a TLS-terminating proxy for `publicUrl` may present. */
258
+ export declare function publicHosts(config: ServeConfig): string[];
259
+ export declare function readServeConfig(root: string): Promise<ServeConfig | undefined>;
260
+ export declare function writeServeConfig(root: string, config: ServeConfig): Promise<void>;
261
+ /** The kit-generated Claude attachment: tealbrick MCP server plus the child guard hook. */
262
+ declare const generatedClaude: z.ZodObject<{
263
+ settings: z.ZodObject<{
264
+ hooks: z.ZodObject<{
265
+ PreToolUse: z.ZodArray<z.ZodObject<{
266
+ matcher: z.ZodLiteral<"mcp__tealbrick__.*">;
267
+ hooks: z.ZodArray<z.ZodObject<{
268
+ type: z.ZodLiteral<"command">;
269
+ command: z.ZodString;
270
+ args: z.ZodArray<z.ZodString>;
271
+ }, z.core.$strict>>;
272
+ }, z.core.$strict>>;
273
+ }, z.core.$strict>;
274
+ }, z.core.$strict>;
275
+ mcpServers: z.ZodObject<{
276
+ tealbrick: z.ZodObject<{
277
+ command: z.ZodString;
278
+ args: z.ZodArray<z.ZodString>;
279
+ }, z.core.$strict>;
280
+ }, z.core.$strict>;
281
+ }, z.core.$strict>;
282
+ export type ClaudeAttachment = z.infer<typeof generatedClaude>;
283
+ export declare function readClaudeAttachment(root: string): Promise<ClaudeAttachment>;
284
+ /** Resolve the effective Claude profile. Explicit serve fields override the imported recipe. */
285
+ export declare function resolveClaudeProfile(root: string, config: ServeConfig): Promise<ClaudeProfile>;
286
+ export {};