@tealbrick/kit 0.2.6 → 0.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/BOOTSTRAP.md CHANGED
@@ -1,13 +1,12 @@
1
- > Eve 0.63.0 is a candidate; the approved baseline remains Eve 0.58.1. Suite 0.2.4 is an unpublished compatibility candidate.
1
+ > Prerelease 0.3.0-rc.1 targets fresh Eve 0.70 installations. Use the explicit candidate version or `next` tag after publication. Stable `latest` remains 0.2.7 for existing Eve 0.66 installations; durable cross-version session migration is not validated.
2
2
 
3
3
  # Deployment guide: Eve agent + Teal Brick kit
4
4
 
5
5
  This guide installs the seven implemented packages through one kit and activates
6
- only the capabilities you select. The supported harness line is **Eve 0.63.x**;
7
- **0.63.0** and **AI SDK 7.0.105** are the current tested versions. Use Node **24**.
6
+ only the capabilities you select. The supported harness line is **Eve 0.70.x**;
7
+ **0.70.0** and **AI SDK 7.0.127** are the current tested versions. Use Node **24**.
8
8
 
9
- **Release status:** `@tealbrick/kit@0.2.4` is the unpublished Eve 0.63 compatibility candidate for the
10
- complete suite. Use the npm installation in step 3. Do not substitute an unrelated unscoped `tealbrick` package.
9
+ **Release status:** this prerelease is prepared for the `next` tag. Until publication, use the reviewed local tarballs. The versioned npm commands apply after publication. Native onboarding additionally requires the matching Portal runtime setup endpoints.
11
10
 
12
11
  ## What installs what
13
12
 
@@ -19,12 +18,12 @@ Eve runs the agent and needs its own working primary-model authentication.
19
18
  For a fresh machine, the sequence is:
20
19
 
21
20
  1. Install Node 24 and npm.
22
- 2. Create an Eve 0.63.0 project and configure its primary model credentials.
23
- 3. Install `@tealbrick/kit@0.2.4` inside that project.
21
+ 2. Create an Eve 0.70.0 project and configure its primary model credentials.
22
+ 3. Install `@tealbrick/kit@0.3.0-rc.1` inside that project.
24
23
  4. Run `tealbrick setup` to sign in, select capabilities and register the card.
25
24
  5. Build and start Eve, then check a real chat from desktop.
26
25
 
27
- For an **existing Eve 0.63.0 project**, start at step 3 from its project directory.
26
+ For an **existing Eve 0.70.0 project**, start at step 3 from its project directory.
28
27
  Do not run `eve init` over an existing agent. Preserve its model configuration and
29
28
  review any existing Eve channel before setup, as explained under Portal below.
30
29
 
@@ -40,14 +39,14 @@ npm --version
40
39
 
41
40
  The first command should report `v24.x`. Run the remaining commands as the account
42
41
  that will own the new agent project. For our deployment this account runs on
43
- Nucbox/Fedora N150. This package guide does not provision a VM or install AVMM.
42
+ an AVMM guest on Neuu after the September23 migration. This package guide does not provision a VM or install AVMM.
44
43
 
45
44
  ## 2. Create a fresh Eve project
46
45
 
47
46
  Choose an unused project directory, outside any existing live agent:
48
47
 
49
48
  ```bash
50
- npx --yes eve@0.63.0 init tealbrick-agent --model openai/gpt-5.6-terra
49
+ npx --yes eve@0.70.0 init tealbrick-agent --model openai/gpt-5.6-terra
51
50
  ```
52
51
 
53
52
  Finish scaffolding and return to the terminal before continuing. If Eve starts
@@ -55,12 +54,12 @@ Finish scaffolding and return to the terminal before continuing. If Eve starts
55
54
 
56
55
  ```bash
57
56
  cd tealbrick-agent
58
- npm install --save-exact eve@0.63.0 ai@7.0.105
57
+ npm install --save-exact eve@0.70.0 ai@7.0.127
59
58
  ```
60
59
 
61
60
  Keep `package-lock.json` under version control.
62
61
  The exact Eve pin is intentional: the compatibility workflow validates patches
63
- before advancing it and will not cross into 0.59 automatically.
62
+ before advancing it. No production pin changes automatically.
64
63
 
65
64
  The example model uses the Vercel AI Gateway. To supply its key for this terminal
66
65
  without putting the key in shell history, in Bash run:
@@ -82,7 +81,7 @@ Run this inside the Eve project created in step 2 (or your existing compatible
82
81
  project), not in an empty directory or as a global install:
83
82
 
84
83
  ```bash
85
- npm install --save-exact @tealbrick/kit@0.2.4
84
+ npm install --save-exact @tealbrick/kit@0.3.0-rc.1
86
85
  ```
87
86
 
88
87
  This installs Portal, AVM, Voice, Vision, Deliver and the shared provider transport
@@ -245,3 +244,15 @@ Restart the running Eve process after the rebuild. Deselecting a capability remo
245
244
  its kit-owned mounts. Setup refuses to overwrite hand-edited mounts. Saved settings
246
245
  and optional local secrets live under ignored `.tealbrick/`; secrets are not embedded
247
246
  in generated source. Disabling a capability does not revoke or delete its credentials.
247
+
248
+ ## Eve 0.66 deployment additions (suite 0.2.7)
249
+
250
+ `tealbrick setup` now includes Persistent routines and Production self-evolution.
251
+ Routines require an enrolled host bridge, scope and runtime token; setup mounts
252
+ CRUD, production dispatch and desktop delivery together. Self-evolution requires
253
+ an existing `deployment/*.ts` native Eve policy module outside the writable agent
254
+ tree. Native proposals require the deployment's existing governed activation path.
255
+
256
+ AVMM/guests migrated to Neuu on September23. Verify current canonical migration
257
+ records before targeting either host. Do not create new canary agents. Committed
258
+ source, npm publication and installed deployment are separate states.
package/NATIVE.md ADDED
@@ -0,0 +1,77 @@
1
+ # Native agent setup — Stage A candidate
2
+
3
+ Prerelease suite **0.3.0-rc.1** targets the `next` tag. Stable npm latest remains 0.2.7, which lacks this flow. The `tealbrick` facade forwards the existing kit CLI; it is not another SDK. Matching Portal runtime setup endpoints must be deployed before native onboarding can succeed.
4
+
5
+ ## Install and connect
6
+
7
+ Install the review tarballs with the supplied checksum-verifying `install-candidate.mjs`. Once the release is actually published, the intended installation is `npm install --save-exact tealbrick@0.3.0-rc.1`. Today that registry command is not a tested installation path.
8
+
9
+ From your native agent workspace:
10
+
11
+ ```sh
12
+ npx --no-install tealbrick setup
13
+ ```
14
+
15
+ Choose Claude Agent SDK, Codex TypeScript SDK, LangChain or the existing Eve setup. Native setup then:
16
+
17
+ 1. Opens the existing Portal device-consent flow for your account.
18
+ 2. Lets you choose or create an owned workspace.
19
+ 3. Lets you create an accurately labelled agent or select an owned existing one.
20
+ 4. Creates/reuses its stable card through Portal, then enrolls its runtime connection.
21
+ 5. Saves ordinary SDK configuration at `.tealbrick/native-sdk.json`.
22
+
23
+ No pre-created card, endpoint, Fleet licence, or UUID lookup is required. Names that match multiple identities require an explicit selection. Creation does not grant apps. Eve identities cannot be silently converted to native identities.
24
+
25
+ Named selections also work:
26
+
27
+ ```sh
28
+ npx --no-install tealbrick setup --harness claude --create-workspace Community --create-agent Researcher
29
+ npx --no-install tealbrick setup --harness langchain --workspace-name Community --agent-name Researcher
30
+ ```
31
+
32
+ The second example requires an existing **LangChain** identity. `--workspace ID --agent ID [--node ID]` remain advanced selections. Setup requires the Portal candidate's authenticated `GET/POST /api/runtime/setup`; a public gateway must expose that reviewed route. It then uses the unchanged `/api/runtime/connections` credential exchange.
33
+
34
+ Repeat setup in the same workspace to reconnect. `.tealbrick/native-setup.json` retains the account-bound identity intent and idempotency key, without credentials. A lost response or enrollment failure reuses that key on retry. The local setup lock prevents concurrent installers. If the installer is killed, verify that the PID in `.tealbrick/native-setup.lock` is no longer running before removing **only that lock** and retrying; retain the journal. A different account/harness or an attempt to replace an already-bound identity fails closed.
35
+
36
+ ## Native SDK configuration
37
+
38
+ Load `.tealbrick/native-sdk.json` into the SDK's ordinary options. No tokens are present; the installed CLI path and workspace root are absolute. Keep the package at that path. Use native SDK session persistence and continuation.
39
+
40
+ ```js
41
+ const config = JSON.parse(await readFile('.tealbrick/native-sdk.json', 'utf8'));
42
+ // Claude Agent SDK 0.3.287: preserve the generated settings hook when merging.
43
+ const stream = query({prompt, options: {...config, model: selectedModel,
44
+ ...(savedSessionId ? {resume: savedSessionId} : {})}});
45
+ // Codex TypeScript SDK 0.160.0 (not OpenAI Agents SDK)
46
+ const codex = new Codex(config);
47
+ const thread = savedThreadId ? codex.resumeThread(savedThreadId, options) : codex.startThread(options);
48
+ // LangChain 1.5.15 and MCP adapters 2.0.0
49
+ const adapter = new MCPAdapter(config);
50
+ const agent = createAgent({model: configuredModel, tools: await adapter.listTools(), checkpointer});
51
+ ```
52
+
53
+ Configure provider authentication on the native host through its supported SDK. Kit does not copy login caches, choose providers or change billing. No Eve daemon, AVMM or inbound bridge is required. Native mode emits no Eve mounts and uses the existing kit state and owner-only credential file.
54
+
55
+ ## Children and host authority
56
+
57
+ - **Claude:** generated `settings.hooks.PreToolUse` executes the packaged guard for `mcp__tealbrick__*`. It denies requests with SDK-authored `agent_id`; parent calls receive no permission override. It preserves the host's `Agent` tool, independently scoped agents and non-Tealbrick tools. Merge this hook with existing settings instead of dropping it. Unit/process tests verify the hook; live delegated execution is still unverified.
58
+ - **Codex:** no verified per-child credential boundary exists in this attachment. Setup explicitly asks before generating a profile with `multi_agent` and `multi_agent_v2` disabled. For scripted selection use `--delegation disabled`; the same flag is required for `native config`. It never edits the host's existing Codex config. Do not merge this profile into a delegation-enabled agent and claim isolation.
59
+ - **LangChain:** `createAgent` receives only the two Tealbrick tools; no child tool is introduced. Do not pass this adapter into another agent. Child grants are unsupported.
60
+
61
+ These controls do not isolate processes sharing an OS user. A child with unrestricted filesystem/shell access can read parent files. Keep the credential directory outside child filesystem authority; independently scoped child runtimes must receive their own grants, not the parent root or credential. No full delegated-access acceptance is claimed.
62
+
63
+ ## Knowledge and revocation
64
+
65
+ In the selected workspace, licence/configure Knowledge, verify its instance using the existing `tealbrick app claim` flow, and save explicit CRUD tethers. The instance admin approves the claim; runtime uses a separate scoped service-principal token. Nothing automatically grants all products.
66
+
67
+ ```sh
68
+ npx --no-install tealbrick app claim --workspace WORKSPACE_ID --node KNOWLEDGE_NODE_ID --binding community --endpoint https://YOUR_KNOWLEDGE_ORIGIN --company YOUR_PARTITION --issuer https://portal.tealbrick.com
69
+ npx --no-install tealbrick native disable
70
+ npx --no-install tealbrick native enable
71
+ ```
72
+
73
+ Each request reloads local activation and fetches signed Portal authorization. Revoke or unavailable authorization blocks the next app request. In-flight side effects are not undone; cancellation reaches the app request but does not prove a write rolled back. No automatic write retry is added. Re-enrollment rotates the runtime credential and keeps the logical agent. Portal logout does not revoke that connection.
74
+
75
+ ## Acceptance boundary
76
+
77
+ Disposable installed-artifact tests cover empty-account creation, named selection, permission/ambiguity denial, lost-response retry, signed config, scoped Knowledge-fixture read, revoke/reconnect, deactivation and MCP cancellation. They are not real browser consent or real Knowledge product proof. Root's separate Foxy receipt proves the earlier packed candidate's live Claude MCP inactive denial, exact-session process restart and compaction. Codex and LangChain have independent narrower fixture evidence, not live-provider parity. Marketplace, voice/vision and delegated Tealbrick grants are outside this Stage A claim.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- > Eve 0.63.0 is a candidate; the approved baseline remains Eve 0.58.1. Suite 0.2.4 is an unpublished compatibility candidate.
1
+ > Prerelease 0.3.0-rc.1 targets fresh Eve 0.70 installations. Use the explicit candidate version or `next` tag after publication. Stable `latest` remains 0.2.7 for existing Eve 0.66 installations; durable cross-version session migration is not validated.
2
2
 
3
3
  # @tealbrick/kit
4
4
 
@@ -6,16 +6,16 @@ One installation, explicitly selected capabilities. The kit includes the current
6
6
  implemented Portal, Voice, Vision, Deliver and AVM packages. Dependencies are code
7
7
  availability, not agent permissions. Nothing is mounted by installing the kit.
8
8
 
9
- Install inside an existing Eve 0.63.0 project:
9
+ Install inside an existing Eve 0.70.0 project:
10
10
 
11
11
  ```sh
12
- npm install --save-exact @tealbrick/kit@0.2.4
12
+ npm install --save-exact @tealbrick/kit@0.3.0-rc.1
13
13
  npx --no-install tealbrick setup
14
14
  ```
15
15
 
16
16
  The suite is published on npm. The kit does not install, create or start Eve.
17
17
  Create the Eve project first and configure its primary-model authentication.
18
- Eve 0.63.0 and Node 24 are the validated host versions; VM provisioning remains
18
+ Eve 0.70.0 and Node 24 are the validated host versions; VM provisioning remains
19
19
  host setup. After Teal Brick setup, build and start Eve to make the agent usable.
20
20
 
21
21
  See [BOOTSTRAP.md](BOOTSTRAP.md) for the complete deployment guide, including fresh-machine and existing-agent paths, provider prompts and verification.
package/dist/cli.js CHANGED
@@ -4,9 +4,22 @@ import { readFile } from 'node:fs/promises';
4
4
  import { loginPortal, registerPortalCard, portalOrigin, agentOrigin } from './onboarding.js';
5
5
  import { collectSetup } from '@tealbrick/deliver/setup';
6
6
  import { applyKit, planKit, readKit } from './index.js';
7
+ import { nativeCommand } from './native.js';
7
8
  import { runtimeOnboarding } from './runtime-onboarding.js';
8
9
  async function main() {
9
10
  const command = process.argv[2], root = process.cwd();
11
+ if (command === 'setup') {
12
+ const args = process.argv.slice(3), index = args.indexOf('--harness');
13
+ const harness = index >= 0 ? args[index + 1] : await select({ message: 'Agent harness', choices: [{ name: 'Claude Agent SDK', value: 'claude' }, { name: 'Codex TypeScript SDK', value: 'codex' }, { name: 'LangChain', value: 'langchain' }, { name: 'Eve', value: 'eve' }] });
14
+ if (harness !== 'eve') {
15
+ await nativeCommand(['setup', ...args, ...(index < 0 ? ['--harness', harness] : [])], root);
16
+ return;
17
+ }
18
+ }
19
+ if (command === 'native') {
20
+ await nativeCommand(process.argv.slice(3), root);
21
+ return;
22
+ }
10
23
  if (command === 'connect') {
11
24
  await runtimeOnboarding('connect', process.argv.slice(3), root);
12
25
  return;
@@ -37,6 +50,8 @@ async function main() {
37
50
  { name: 'Voice: live-session / STT / TTS endpoints', value: 'voice', checked: !!old.voice },
38
51
  { name: 'Vision API (native tool requires a trusted turn-auth adapter)', value: 'vision', checked: !!old.vision },
39
52
  { name: 'Deliver files to chat', value: 'deliver', checked: old.deliver !== undefined },
53
+ { name: 'Production self-evolution (requires deployment policy)', value: 'selfEvolution', checked: !!old.selfEvolution },
54
+ { name: 'Persistent routines, desktop management and AVMM wake', value: 'routines', checked: !!old.routines },
40
55
  { name: 'AVM inspection over SSH', value: 'avm', checked: !!old.avm },
41
56
  ] });
42
57
  const config = { version: 1 }, credentials = {};
@@ -95,6 +110,15 @@ async function main() {
95
110
  delete config.avm.identityTokenFile;
96
111
  }
97
112
  }
113
+ if (selected.includes('selfEvolution'))
114
+ config.selfEvolution = { policyModule: await input({ message: 'Deployment policy module outside agent/ (deployment/name.ts)', default: old.selfEvolution?.policyModule }) };
115
+ if (selected.includes('routines')) {
116
+ const credentialRef = 'TEALBRICK_ROUTINE_BRIDGE_TOKEN';
117
+ const token = await password({ message: 'Host routine bridge token (blank keeps existing runtime secret)', mask: '*' });
118
+ if (token)
119
+ credentials[credentialRef] = token;
120
+ config.routines = { url: await input({ message: 'Enrolled host routine bridge URL including /routines', default: old.routines?.url }), credentialRef, scope: { org: login.org, workspace: workspaceId, agent: name, owner: await input({ message: 'Portal managing user ID (must match the host enrollment)', default: old.routines?.scope.owner }) }, buildCommand: JSON.parse(await input({ message: 'Build command JSON argv', default: JSON.stringify(old.routines?.buildCommand ?? ['npm', 'run', 'build']) })), restartCommand: JSON.parse(await input({ message: 'Operator-configured restart command JSON argv', default: old.routines ? JSON.stringify(old.routines.restartCommand) : undefined })) };
121
+ }
98
122
  console.log(JSON.stringify({ enabled: planKit(config).enabled, mounts: Object.keys(planKit(config).files) }, null, 2));
99
123
  if (!await confirm({ message: 'Apply settings and register this agent in the selected Portal workspace? Rebuild/restart Eve afterward.', default: false }))
100
124
  return;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,29 @@
1
1
  import { z } from 'zod';
2
2
  declare const schema: z.ZodObject<{
3
3
  version: z.ZodLiteral<1>;
4
+ native: z.ZodOptional<z.ZodObject<{
5
+ harness: z.ZodEnum<{
6
+ claude: "claude";
7
+ codex: "codex";
8
+ langchain: "langchain";
9
+ }>;
10
+ active: z.ZodBoolean;
11
+ }, z.core.$strict>>;
12
+ selfEvolution: z.ZodOptional<z.ZodObject<{
13
+ policyModule: z.ZodString;
14
+ }, z.core.$strict>>;
15
+ routines: z.ZodOptional<z.ZodObject<{
16
+ url: z.ZodString;
17
+ credentialRef: z.ZodString;
18
+ scope: z.ZodObject<{
19
+ org: z.ZodString;
20
+ workspace: z.ZodString;
21
+ agent: z.ZodString;
22
+ owner: z.ZodString;
23
+ }, z.core.$strict>;
24
+ buildCommand: z.ZodTuple<[z.ZodString], z.ZodString>;
25
+ restartCommand: z.ZodTuple<[z.ZodString], z.ZodString>;
26
+ }, z.core.$strict>>;
4
27
  portal: z.ZodOptional<z.ZodObject<{
5
28
  issuer: z.ZodString;
6
29
  org: z.ZodString;
@@ -922,7 +945,7 @@ declare const schema: z.ZodObject<{
922
945
  }>>>;
923
946
  }, z.core.$strict>;
924
947
  export type KitConfig = z.input<typeof schema>;
925
- export declare const capabilities: readonly ["portal", "runtime", "voice", "vision", "deliver", "avm"];
948
+ export declare const capabilities: readonly ["portal", "runtime", "voice", "vision", "deliver", "avm", "routines", "selfEvolution"];
926
949
  export interface MountPlan {
927
950
  enabled: string[];
928
951
  files: Record<string, string>;
package/dist/index.js CHANGED
@@ -10,19 +10,23 @@ const auth = z.object({ issuer: z.string().url().refine(v => new URL(v).protocol
10
10
  const media = z.object({ settings: z.unknown(), authorizationModule: z.string().optional() }).strict();
11
11
  const ref = z.string().regex(/^TEALBRICK_[A-Z0-9_]+$/);
12
12
  const runtime = z.object({ issuer: z.string().url(), org: z.string().min(1), workspaceId: z.string().min(1), agentId: z.string().min(1), connectionId: z.string().min(1), credentialRef: ref, bindings: z.record(z.string(), z.object({ endpoint: z.string().url(), instanceId: z.string().min(1), companyId: z.string().min(1), credentialRef: ref }).strict()) }).strict();
13
- const schema = z.object({ version: z.literal(1), portal: auth.optional(), runtime: runtime.optional(), voice: media.optional(), vision: media.optional(), deliver: z.unknown().optional(), avm: z.custom(v => avmConfigSchema.safeParse(v).success).transform(v => 'target' in v ? { target: v.target, sudo: v.sudo ?? false, ...Object.fromEntries(Object.entries(v).filter(([k]) => k !== 'target' && k !== 'sudo')) } : v).optional() }).strict();
14
- export const capabilities = ['portal', 'runtime', 'voice', 'vision', 'deliver', 'avm'];
13
+ const command = z.tuple([z.string().min(1)]).rest(z.string());
14
+ const routines = z.object({ url: z.string().url(), credentialRef: ref, scope: z.object({ org: z.string().min(1), workspace: z.string().min(1), agent: z.string().min(1), owner: z.string().min(1) }).strict(), buildCommand: command, restartCommand: command }).strict();
15
+ const schema = z.object({ version: z.literal(1), native: z.object({ harness: z.enum(['claude', 'codex', 'langchain']), active: z.boolean() }).strict().optional(), selfEvolution: z.object({ policyModule: z.string().regex(/^deployment\/[a-zA-Z0-9_-]+\.(ts|js|mjs)$/) }).strict().optional(), routines: routines.optional(), portal: auth.optional(), runtime: runtime.optional(), voice: media.optional(), vision: media.optional(), deliver: z.unknown().optional(), avm: z.custom(v => avmConfigSchema.safeParse(v).success).transform(v => 'target' in v ? { target: v.target, sudo: v.sudo ?? false, ...Object.fromEntries(Object.entries(v).filter(([k]) => k !== 'target' && k !== 'sudo')) } : v).optional() }).strict();
16
+ export const capabilities = ['portal', 'runtime', 'voice', 'vision', 'deliver', 'avm', 'routines', 'selfEvolution'];
15
17
  const marker = '// Managed by @tealbrick/kit. Change selection with tealbrick setup.\n';
16
18
  const render = (body) => marker + body + '\n';
17
19
  const hash = (data) => createHash('sha256').update(data).digest('hex');
18
20
  /** Pure mount plan: dependencies alone never contribute capabilities to Eve. */
19
21
  export function planKit(value) {
20
22
  const c = schema.parse(value), files = {};
23
+ if (c.native && (c.portal || c.voice || c.vision || c.deliver !== undefined || c.avm || c.routines || c.selfEvolution))
24
+ throw Error('kit_native_eve_configuration_conflict');
21
25
  if ((c.voice || c.vision) && !c.portal)
22
26
  throw Error('kit_media_requires_portal_binding');
23
27
  if (c.portal)
24
28
  files['agent/channels/eve.ts'] = render(`import {eveChannel} from 'eve/channels/eve';\nimport {portalAuth} from '@tealbrick/portal/eve';\nexport default eveChannel({auth:[portalAuth(${JSON.stringify(c.portal)})]});`);
25
- if (c.runtime) {
29
+ if (c.runtime && !c.native) {
26
30
  files['agent/tools/tealbrick_capabilities.ts'] = render(`import {capabilitiesTool} from '@tealbrick/kit/runtime-eve';\nexport default capabilitiesTool(${JSON.stringify(c.runtime)});`);
27
31
  files['agent/tools/tealbrick_call.ts'] = render(`import {appCallTool} from '@tealbrick/kit/runtime-eve';\nexport default appCallTool(${JSON.stringify(c.runtime)});`);
28
32
  }
@@ -65,6 +69,25 @@ export function planKit(value) {
65
69
  }
66
70
  if (c.avm)
67
71
  files['agent/extensions/tealbrick-avm.ts'] = render(`import avm from '@tealbrick/avm/eve';\nexport default avm(${JSON.stringify(c.avm)});`);
72
+ if (c.selfEvolution) {
73
+ if (!c.portal)
74
+ throw Error('kit_evolution_requires_portal');
75
+ // Native deployed authoring policy lives outside the agent-authored tree.
76
+ const module = '../../' + c.selfEvolution.policyModule;
77
+ files['agent/extensions/tealbrick-self-evolution.ts'] = render(`import selfModification from 'eve/self-modification';\nimport policy from ${JSON.stringify(module)};\nexport default selfModification(policy);`);
78
+ }
79
+ if (c.routines) {
80
+ if (!c.portal || c.routines.scope.org !== c.portal.org || c.routines.scope.agent !== c.portal.agent)
81
+ throw Error('kit_routines_require_matching_portal');
82
+ const r = c.routines;
83
+ const endpoint = new URL(r.url);
84
+ if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username || endpoint.password || endpoint.search || endpoint.hash)
85
+ throw Error('kit_invalid_routine_endpoint');
86
+ files['agent/lib/tealbrick-routines.ts'] = render(`import {createRoutineClient} from '@tealbrick/avm/routines-client';\nimport {runtimeSecret} from '@tealbrick/kit/secrets';\nexport const scope=${JSON.stringify(r.scope)};\nexport const bridge=createRoutineClient({url:${JSON.stringify(r.url)},token:async()=>{const token=await runtimeSecret(${JSON.stringify(r.credentialRef)});if(!token)throw Error('routine_credential_missing');return token;}});`);
87
+ files['agent/tools/tealbrick_routines.ts'] = render(`import {routineTool} from '@tealbrick/avm/routines-eve';\nimport {bridge,scope} from '../lib/tealbrick-routines';\nexport default routineTool({bridge,scope});`);
88
+ files['agent/channels/management.ts'] = render(`import {managementChannel} from '@tealbrick/portal/management/eve';\nimport {bridge,scope} from '../lib/tealbrick-routines';\nexport default managementChannel({auth:${JSON.stringify(c.portal)},appRoot:process.cwd(),routines:{owner:scope.owner,call:(action,input)=>bridge.call(action,input)},buildCommand:${JSON.stringify(r.buildCommand)},restartCommand:${JSON.stringify(r.restartCommand)}});`);
89
+ files['agent/schedules/tealbrick-routines.ts'] = render(`import {routineDispatcher} from '@tealbrick/avm/routines-eve';\nimport {bridge} from '../lib/tealbrick-routines';\nimport delivery from '../channels/management';\nexport default routineDispatcher({bridge,delivery});`);
90
+ }
68
91
  return { enabled: capabilities.filter(k => c[k] !== undefined), files };
69
92
  }
70
93
  async function optional(path) { try {
@@ -128,6 +151,11 @@ export async function applyKit(root, config, credentials = {}) {
128
151
  else if (content !== null)
129
152
  throw Error(`kit_mount_conflict:${name}`);
130
153
  }
154
+ if (config.selfEvolution) {
155
+ const path = await safe(root, config.selfEvolution.policyModule);
156
+ if (await optional(path) === null)
157
+ throw Error('kit_evolution_policy_module_missing');
158
+ }
131
159
  if (config.vision?.authorizationModule) {
132
160
  const path = await safe(root, config.vision.authorizationModule);
133
161
  if (await optional(path) === null)
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ /** Claude SDK command hook: protect this MCP attachment without disabling unrelated experts.
2
+ * agent_id is SDK-authored context, never a model tool argument. This is not OS isolation. */
3
+ let output = { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: 'Tealbrick child authority is not configured' } };
4
+ try {
5
+ let text = '';
6
+ for await (const chunk of process.stdin) {
7
+ text += chunk;
8
+ if (text.length > 65536)
9
+ throw Error('oversized_hook');
10
+ }
11
+ const input = JSON.parse(text);
12
+ if (input.hook_event_name === 'PreToolUse' && typeof input.tool_name === 'string' && input.tool_name.startsWith('mcp__tealbrick__') && input.agent_id === undefined)
13
+ output = {};
14
+ }
15
+ catch { }
16
+ process.stdout.write(JSON.stringify(output) + '\n');
17
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { PortalLogin } from './onboarding.js';
2
+ export interface SetupPrompts {
3
+ choose(message: string, choices: Array<{
4
+ name: string;
5
+ value: string;
6
+ }>): Promise<string>;
7
+ name(message: string): Promise<string>;
8
+ }
9
+ export interface NativeSelection {
10
+ workspace: {
11
+ id: string;
12
+ } | {
13
+ name: string;
14
+ };
15
+ agent: {
16
+ id: string;
17
+ } | {
18
+ name: string;
19
+ };
20
+ }
21
+ export declare const harnessLabels: {
22
+ readonly claude: "Claude Agent SDK";
23
+ readonly codex: "Codex";
24
+ readonly langchain: "LangChain";
25
+ };
26
+ /** Names are display/selection hints only; Portal remains the identity and ownership authority. */
27
+ export declare function selectNativeIdentity(root: string, flags: Record<string, string>, login: PortalLogin, prompts: SetupPrompts): Promise<NativeSelection>;
@@ -0,0 +1,72 @@
1
+ import { readKit } from './index.js';
2
+ export const harnessLabels = { claude: 'Claude Agent SDK', codex: 'Codex', langchain: 'LangChain' };
3
+ const validName = (value) => { const name = value.trim(); if (!name || name.length > 80)
4
+ throw Error('kit_native_name_required'); return name; };
5
+ /** Names are display/selection hints only; Portal remains the identity and ownership authority. */
6
+ export async function selectNativeIdentity(root, flags, login, prompts) {
7
+ const old = await readKit(root), harness = harnessLabels[flags.harness];
8
+ if (!harness)
9
+ throw Error('kit_native_harness_required');
10
+ if ([flags.workspace, flags['workspace-name'], flags['create-workspace']].filter(Boolean).length > 1 || [flags.agent, flags['agent-name'], flags['create-agent']].filter(Boolean).length > 1)
11
+ throw Error('kit_native_conflicting_selection');
12
+ if (flags.node && !flags.agent)
13
+ throw Error('kit_native_agent_required');
14
+ const directory = await login.client.request('/api/runtime/setup');
15
+ let workspaceId = flags.workspace, workspaceName = flags['create-workspace'];
16
+ if (flags['workspace-name']) {
17
+ const matches = directory.workspaces.filter(w => w.name === flags['workspace-name']);
18
+ if (matches.length !== 1)
19
+ throw Error(matches.length ? 'kit_native_ambiguous_workspace_name' : 'kit_native_workspace_not_found');
20
+ workspaceId = matches[0].id;
21
+ }
22
+ if (!workspaceId && !workspaceName && old.runtime?.issuer === login.issuer && old.runtime.org === login.org)
23
+ workspaceId = old.runtime.workspaceId;
24
+ if (workspaceId && !directory.workspaces.some(w => w.id === workspaceId))
25
+ throw Error('kit_portal_workspace_required');
26
+ if (!workspaceId && !workspaceName) {
27
+ const choice = directory.workspaces.length ? await prompts.choose('Choose a Portal workspace', [...directory.workspaces.map(w => ({ name: `${w.name} (${w.id})`, value: w.id })), { name: 'Create a workspace', value: '__create__' }]) : '__create__';
28
+ if (choice === '__create__')
29
+ workspaceName = await prompts.name('New workspace name');
30
+ else {
31
+ if (!directory.workspaces.some(w => w.id === choice))
32
+ throw Error('kit_portal_workspace_required');
33
+ workspaceId = choice;
34
+ }
35
+ }
36
+ const workspace = workspaceName ? { name: validName(workspaceName) } : { id: workspaceId };
37
+ if (workspaceName && flags.node)
38
+ throw Error('kit_native_existing_node_requires_workspace');
39
+ const cards = directory.agents.filter(a => a.harness === harness).map(agent => ({ agent }));
40
+ let agentId = flags.agent;
41
+ if (flags['agent-name']) {
42
+ const matches = cards.filter((c) => c.agent.name === flags['agent-name']);
43
+ if (matches.length !== 1)
44
+ throw Error(matches.length ? 'kit_native_ambiguous_agent_name' : 'kit_native_agent_not_found');
45
+ agentId = matches[0].agent.id;
46
+ }
47
+ if (!agentId && !flags['create-agent'] && old.runtime && old.runtime.workspaceId === workspaceId && old.runtime.org === login.org && old.runtime.issuer === login.issuer)
48
+ agentId = old.runtime.agentId;
49
+ let name = flags['create-agent'];
50
+ 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__';
52
+ if (choice === '__create__')
53
+ name = await prompts.name('New agent name');
54
+ else {
55
+ const card = cards.find((c) => c.agent.id === choice);
56
+ if (!card)
57
+ throw Error('kit_owned_agent_card_required');
58
+ agentId = card.agent.id;
59
+ }
60
+ }
61
+ if (name)
62
+ return { workspace, agent: { name: validName(name) } };
63
+ const matches = cards.filter((c) => c.agent.id === agentId);
64
+ if (matches.length !== 1)
65
+ throw Error('kit_owned_agent_card_required');
66
+ if (flags.node) {
67
+ const snapshot = await login.client.request('/api/workspace?workspaceId=' + encodeURIComponent(workspaceId));
68
+ if (!snapshot.draft?.nodes?.some((n) => n.id === flags.node && n.data?.agentId === agentId && n.data?.harness === harness))
69
+ throw Error('kit_owned_agent_card_required');
70
+ }
71
+ return { workspace, agent: { id: matches[0].agent.id } };
72
+ }
@@ -0,0 +1,63 @@
1
+ import type { PortalLogin } from './onboarding.js';
2
+ import { type SetupPrompts } from './native-selection.js';
3
+ import { type NativeHarness } from './native.js';
4
+ export declare function nativeSdkConfig(harness: NativeHarness, root: string, options?: {
5
+ codexDelegationDisabled?: boolean;
6
+ }): {
7
+ config: {
8
+ features: {
9
+ multi_agent: boolean;
10
+ multi_agent_v2: boolean;
11
+ };
12
+ mcp_servers: {
13
+ tealbrick: {
14
+ command: string;
15
+ args: string[];
16
+ };
17
+ };
18
+ };
19
+ servers?: undefined;
20
+ settings?: undefined;
21
+ mcpServers?: undefined;
22
+ } | {
23
+ servers: {
24
+ tealbrick: {
25
+ command: string;
26
+ args: string[];
27
+ };
28
+ };
29
+ config?: undefined;
30
+ settings?: undefined;
31
+ mcpServers?: undefined;
32
+ } | {
33
+ settings: {
34
+ hooks: {
35
+ PreToolUse: {
36
+ matcher: string;
37
+ hooks: {
38
+ type: string;
39
+ command: string;
40
+ args: string[];
41
+ }[];
42
+ }[];
43
+ };
44
+ };
45
+ mcpServers: {
46
+ tealbrick: {
47
+ command: string;
48
+ args: string[];
49
+ };
50
+ };
51
+ config?: undefined;
52
+ servers?: undefined;
53
+ };
54
+ /** Private setup journal stores only identity intent, never account/runtime credentials. */
55
+ export declare function guidedNativeSetup(root: string, flags: Record<string, string>, login: PortalLogin, prompts: SetupPrompts): Promise<{
56
+ workspaceId: string;
57
+ nodeId: string;
58
+ configPath: string;
59
+ connected: boolean;
60
+ agentId: string;
61
+ connectionId: string;
62
+ harness: "claude" | "codex" | "langchain";
63
+ }>;
@@ -0,0 +1,99 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { join, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { mkdir, lstat, readFile, writeFile, rename, unlink, realpath } from 'node:fs/promises';
5
+ import { readKit } from './index.js';
6
+ import { selectNativeIdentity, harnessLabels } from './native-selection.js';
7
+ import { setupNative } from './native.js';
8
+ export function nativeSdkConfig(harness, root, options = {}) {
9
+ if (harness === 'codex' && !options.codexDelegationDisabled)
10
+ throw Error('kit_native_codex_delegation_opt_in_required');
11
+ const server = { command: process.execPath, args: [fileURLToPath(new URL('./cli.js', import.meta.url)), 'native', 'mcp', '--root', resolve(root)] };
12
+ return harness === 'codex' ? { config: { features: { multi_agent: false, multi_agent_v2: false }, mcp_servers: { tealbrick: server } } } : harness === 'langchain' ? { servers: { tealbrick: server } } : { settings: { hooks: { PreToolUse: [{ matcher: 'mcp__tealbrick__.*', hooks: [{ type: 'command', command: process.execPath, args: [fileURLToPath(new URL('./native-child-guard.js', import.meta.url))] }] }] } }, mcpServers: { tealbrick: server } };
13
+ }
14
+ /** Private setup journal stores only identity intent, never account/runtime credentials. */
15
+ export async function guidedNativeSetup(root, flags, login, prompts) {
16
+ root = await realpath(root);
17
+ const dir = join(root, '.tealbrick');
18
+ try {
19
+ if ((await lstat(dir)).isSymbolicLink())
20
+ throw Error('kit_symlink_path');
21
+ }
22
+ catch (e) {
23
+ if (e.code !== 'ENOENT')
24
+ throw e;
25
+ }
26
+ await mkdir(dir, { recursive: true, mode: 0o700 });
27
+ const paths = ['native-setup.json', 'native-setup.lock', 'native-sdk.json'].map(name => join(dir, name));
28
+ for (const path of paths) {
29
+ try {
30
+ if ((await lstat(path)).isSymbolicLink())
31
+ throw Error('kit_symlink_path');
32
+ }
33
+ catch (e) {
34
+ if (e.code !== 'ENOENT')
35
+ throw e;
36
+ }
37
+ }
38
+ const [journalPath, lockPath, configPath] = paths;
39
+ try {
40
+ await writeFile(lockPath, String(process.pid), { flag: 'wx', mode: 0o600 });
41
+ }
42
+ catch (e) {
43
+ if (e.code === 'EEXIST')
44
+ throw Error('kit_native_setup_locked');
45
+ throw e;
46
+ }
47
+ try {
48
+ const harness = flags.harness;
49
+ if (harness === 'codex' && flags.delegation !== 'disabled')
50
+ throw Error('kit_native_codex_delegation_opt_in_required');
51
+ if (!Object.hasOwn(harnessLabels, harness))
52
+ throw Error('kit_native_harness_required');
53
+ const old = await readKit(root);
54
+ if (old.runtime && (old.runtime.issuer !== login.issuer || old.runtime.org !== login.org) || old.native && old.native.harness !== harness)
55
+ throw Error('kit_runtime_existing_binding_mismatch');
56
+ if (old.portal || old.voice || old.vision || old.deliver !== undefined || old.avm || old.routines || old.selfEvolution || old.runtime && !old.native)
57
+ throw Error('kit_native_eve_configuration_conflict');
58
+ let journal;
59
+ try {
60
+ journal = JSON.parse(await readFile(journalPath, 'utf8'));
61
+ }
62
+ catch (e) {
63
+ if (e.code !== 'ENOENT')
64
+ throw e;
65
+ }
66
+ const stamp = { issuer: login.issuer, org: login.org, userId: login.userId, harness };
67
+ if (journal && JSON.stringify(journal.stamp) !== JSON.stringify(stamp))
68
+ throw Error('kit_native_setup_account_mismatch');
69
+ const explicit = ['workspace', 'workspace-name', 'create-workspace', 'agent', 'node', 'agent-name', 'create-agent'].some(key => flags[key]);
70
+ let request = journal && !explicit ? journal.request : undefined;
71
+ if (!request) {
72
+ const chosen = await selectNativeIdentity(root, flags, login, prompts);
73
+ const agent = 'id' in chosen.agent ? { mode: 'attach', id: chosen.agent.id, harness: harnessLabels[harness] } : { mode: 'create', name: chosen.agent.name, harness: harnessLabels[harness] };
74
+ const selection = { workspace: chosen.workspace, agent };
75
+ if (journal && JSON.stringify({ workspace: journal.request.workspace, agent: journal.request.agent }) === JSON.stringify(selection))
76
+ request = journal.request;
77
+ else {
78
+ if (old.runtime)
79
+ throw Error('kit_runtime_existing_binding_mismatch');
80
+ request = { ...selection, idempotencyKey: randomUUID() };
81
+ const tmp = journalPath + '.' + randomUUID();
82
+ await writeFile(tmp, JSON.stringify({ version: 1, stamp, request }) + '\n', { flag: 'wx', mode: 0o600 });
83
+ await rename(tmp, journalPath);
84
+ }
85
+ }
86
+ const prepared = await login.client.request('/api/runtime/setup', request);
87
+ if (!prepared.workspace?.id || !prepared.agent?.id || !prepared.nodeId || prepared.agent.harness !== harnessLabels[harness])
88
+ throw Error('kit_native_setup_response_invalid');
89
+ const authenticated = { ...login, workspaces: [...login.workspaces.filter(w => w.id !== prepared.workspace.id), prepared.workspace] };
90
+ const result = await setupNative(root, { harness, workspaceId: prepared.workspace.id, nodeId: prepared.nodeId, agentId: prepared.agent.id }, authenticated);
91
+ const tmp = configPath + '.' + randomUUID();
92
+ await writeFile(tmp, JSON.stringify(nativeSdkConfig(harness, root, { codexDelegationDisabled: flags.delegation === 'disabled' }), null, 2) + '\n', { flag: 'wx', mode: 0o600 });
93
+ await rename(tmp, configPath);
94
+ return { ...result, workspaceId: prepared.workspace.id, nodeId: prepared.nodeId, configPath };
95
+ }
96
+ finally {
97
+ await unlink(lockPath);
98
+ }
99
+ }
@@ -0,0 +1,39 @@
1
+ import { guidedNativeSetup, nativeSdkConfig } from './native-setup.js';
2
+ import { type PortalLogin } from './onboarding.js';
3
+ import { type KnowledgeOperation } from '@tealbrick/portal/runtime';
4
+ declare const harnesses: {
5
+ readonly claude: "Claude Agent SDK";
6
+ readonly codex: "Codex";
7
+ readonly langchain: "LangChain";
8
+ };
9
+ export type NativeHarness = keyof typeof harnesses;
10
+ /** Enroll an existing, accurately labelled owned card. Registration belongs to Portal. */
11
+ export declare function setupNative(root: string, input: {
12
+ harness: NativeHarness;
13
+ workspaceId: string;
14
+ nodeId: string;
15
+ agentId: string;
16
+ }, login: PortalLogin): Promise<{
17
+ connected: boolean;
18
+ agentId: string;
19
+ connectionId: string;
20
+ harness: "claude" | "codex" | "langchain";
21
+ }>;
22
+ /** A fresh connector per invocation rechecks Portal and local activation. No authorization cache
23
+ * survives MCP reconnect, local deactivation or a request boundary. No session is created here. */
24
+ export declare function nativeConnector(): {
25
+ capabilities: () => Promise<{
26
+ revision: string;
27
+ expiresAt: number;
28
+ apps: {
29
+ registrationId: string;
30
+ appId: "knowledge";
31
+ actions: import("@tealbrick/portal/runtime").CrudAction[];
32
+ available: boolean;
33
+ operations: KnowledgeOperation[];
34
+ }[];
35
+ }>;
36
+ call: (registrationId: string, operation: KnowledgeOperation, input?: Record<string, unknown>, signal?: AbortSignal) => Promise<any>;
37
+ };
38
+ export declare function nativeCommand(args: string[], root: string): Promise<void>;
39
+ export { guidedNativeSetup, nativeSdkConfig };
package/dist/native.js ADDED
@@ -0,0 +1,104 @@
1
+ import { resolve } from 'node:path';
2
+ import { guidedNativeSetup, nativeSdkConfig } from './native-setup.js';
3
+ import { applyKit, readKit } from './index.js';
4
+ import { loginPortal, portalOrigin } from './onboarding.js';
5
+ import { PortalHttpError } from '@tealbrick/portal';
6
+ import { RuntimeConnector } from '@tealbrick/portal/runtime';
7
+ import { serveMcp } from '@tealbrick/portal/mcp';
8
+ import { runtimeSecret } from './secrets.js';
9
+ const harnesses = { claude: 'Claude Agent SDK', codex: 'Codex', langchain: 'LangChain' };
10
+ /** Enroll an existing, accurately labelled owned card. Registration belongs to Portal. */
11
+ export async function setupNative(root, input, login) {
12
+ if (!Object.hasOwn(harnesses, input.harness))
13
+ throw Error('kit_native_harness_required');
14
+ if (!login.workspaces.some(w => w.id === input.workspaceId))
15
+ throw Error('kit_portal_workspace_required');
16
+ const old = await readKit(root);
17
+ if (old.runtime && (old.runtime.issuer !== login.issuer || old.runtime.org !== login.org || old.runtime.workspaceId !== input.workspaceId || old.runtime.agentId !== input.agentId))
18
+ throw Error('kit_runtime_existing_binding_mismatch');
19
+ if (old.native && old.native.harness !== input.harness)
20
+ throw Error('kit_native_harness_mismatch');
21
+ if (old.portal || old.voice || old.vision || old.deliver !== undefined || old.avm || old.routines || old.selfEvolution || old.runtime && !old.native)
22
+ throw Error('kit_native_eve_configuration_conflict');
23
+ const snapshot = await login.client.request('/api/workspace?workspaceId=' + encodeURIComponent(input.workspaceId));
24
+ const agent = snapshot.agents?.find((a) => a.id === input.agentId && a.bindable);
25
+ const node = snapshot.draft?.nodes?.find((n) => n.id === input.nodeId && n.data?.agentId === input.agentId && n.data.kind === 'agent');
26
+ if (snapshot.workspace?.id !== input.workspaceId || !agent || !node)
27
+ throw Error('kit_owned_agent_card_required');
28
+ if (agent.runtime !== harnesses[input.harness] || node.data.harness !== harnesses[input.harness])
29
+ throw Error('kit_native_portal_harness_mismatch');
30
+ const enrolled = await login.client.request('/api/runtime/connections', { workspaceId: input.workspaceId, nodeId: input.nodeId, agentId: input.agentId });
31
+ if (!enrolled.connectionId || !enrolled.credential || !Number.isFinite(enrolled.expiresAt) || enrolled.expiresAt <= Date.now())
32
+ throw Error('kit_runtime_enrollment_failed');
33
+ await applyKit(root, { version: 1, native: { harness: input.harness, active: true }, runtime: { issuer: login.issuer, org: login.org, workspaceId: input.workspaceId, agentId: input.agentId, connectionId: enrolled.connectionId, credentialRef: 'TEALBRICK_RUNTIME_CREDENTIAL', bindings: old.runtime?.bindings ?? {} } }, { TEALBRICK_RUNTIME_CREDENTIAL: enrolled.credential });
34
+ return { connected: true, agentId: input.agentId, connectionId: enrolled.connectionId, harness: input.harness };
35
+ }
36
+ /** A fresh connector per invocation rechecks Portal and local activation. No authorization cache
37
+ * survives MCP reconnect, local deactivation or a request boundary. No session is created here. */
38
+ export function nativeConnector() {
39
+ async function current() {
40
+ const config = await readKit(process.cwd());
41
+ if (!config.native?.active || !config.runtime)
42
+ throw Error('runtime_product_inactive');
43
+ const { credentialRef, ...runtime } = config.runtime;
44
+ return new RuntimeConnector({ ...runtime, credential: () => runtimeSecret(credentialRef), resolveCredential: runtimeSecret });
45
+ }
46
+ return { capabilities: async () => (await current()).capabilities(true), call: async (registrationId, operation, input = {}, signal) => (await current()).call(registrationId, operation, input, signal) };
47
+ }
48
+ export async function nativeCommand(args, root) {
49
+ const command = args[0], flags = {};
50
+ for (let i = 1; i < args.length; i += 2) {
51
+ if (!/^--(root|harness|workspace|workspace-name|create-workspace|node|agent|agent-name|create-agent|delegation|issuer)$/.test(args[i]) || !args[i + 1] || flags[args[i].slice(2)])
52
+ throw Error('kit_native_invalid_arguments');
53
+ flags[args[i].slice(2)] = args[i + 1];
54
+ }
55
+ if (flags.root) {
56
+ root = resolve(flags.root);
57
+ process.chdir(root);
58
+ }
59
+ if (command === 'setup') {
60
+ if (flags.harness && !Object.hasOwn(harnesses, flags.harness))
61
+ throw Error('kit_native_harness_required');
62
+ if (!flags.harness)
63
+ throw Error('kit_native_harness_required');
64
+ if (flags.harness === 'codex' && flags.delegation !== 'disabled') {
65
+ const { confirm } = await import('@inquirer/prompts');
66
+ if (flags.delegation || !await confirm({ message: 'This Codex Tealbrick profile requires native delegation disabled until scoped child grants are supported. Continue with delegation disabled?', default: false }))
67
+ throw Error('kit_native_codex_delegation_opt_in_required');
68
+ flags.delegation = 'disabled';
69
+ }
70
+ const issuer = portalOrigin(flags.issuer ?? 'https://portal.tealbrick.com');
71
+ const login = await loginPortal(issuer, (url, code) => console.error(`Approve Tealbrick setup: ${url}\nCode: ${code}`));
72
+ const { select, input } = await import('@inquirer/prompts');
73
+ try {
74
+ console.log(JSON.stringify(await guidedNativeSetup(root, flags, login, { choose: (message, choices) => select({ message, choices }), name: message => input({ message }) })));
75
+ }
76
+ catch (error) {
77
+ if (error instanceof PortalHttpError && /^[a-z_]+$/.test(error.code ?? ''))
78
+ throw Error('kit_portal_' + error.code);
79
+ throw error;
80
+ }
81
+ return;
82
+ }
83
+ if (command === 'config') {
84
+ const state = await readKit(root);
85
+ if (!state.native)
86
+ throw Error('kit_native_setup_required');
87
+ console.log(JSON.stringify(nativeSdkConfig(state.native.harness, root, { codexDelegationDisabled: flags.delegation === 'disabled' }), null, 2));
88
+ return;
89
+ }
90
+ if (command === 'mcp') {
91
+ await serveMcp(nativeConnector());
92
+ return;
93
+ }
94
+ if (command === 'disable' || command === 'enable') {
95
+ const old = await readKit(root);
96
+ if (!old.native)
97
+ throw Error('kit_native_setup_required');
98
+ await applyKit(root, { ...old, native: { ...old.native, active: command === 'enable' } });
99
+ console.log(JSON.stringify({ active: command === 'enable' }));
100
+ return;
101
+ }
102
+ throw Error('Usage: tealbrick native setup --harness claude|codex|langchain [--workspace-name NAME | --create-workspace NAME] [--agent-name NAME | --create-agent NAME] [--issuer HTTPS_ORIGIN] | native mcp [--root DIRECTORY] | native enable|disable');
103
+ }
104
+ export { guidedNativeSetup, nativeSdkConfig };
@@ -1,5 +1,6 @@
1
1
  import { PortalClient } from '@tealbrick/portal';
2
2
  export interface PortalLogin {
3
+ issuer: string;
3
4
  client: PortalClient;
4
5
  org: string;
5
6
  userId: string;
@@ -53,7 +53,7 @@ export async function loginPortal(issuer, show, options = {}) {
53
53
  const directory = await authenticated.request('/api/me/workspaces');
54
54
  if (!account.user?.id || !account.user.orgId || directory.accountId !== account.user.id || directory.organizationId !== account.user.orgId || !Array.isArray(directory.workspaces) || directory.workspaces.some(w => !w.id || !w.name))
55
55
  throw Error('kit_portal_account_mismatch');
56
- return { client: authenticated, org: account.user.orgId, userId: account.user.id, workspaces: directory.workspaces };
56
+ return { issuer, client: authenticated, org: account.user.orgId, userId: account.user.id, workspaces: directory.workspaces };
57
57
  }
58
58
  /** Reuse Portal's owned registration + revision-checked canvas APIs, not a parallel card store. */
59
59
  export async function registerPortalCard(login, input) {
@@ -50,7 +50,7 @@ export async function runtimeOnboarding(command, args, root) {
50
50
  const runtime = { issuer, org: login.org, workspaceId, agentId, connectionId: enrolled.connectionId, credentialRef: 'TEALBRICK_RUNTIME_CREDENTIAL', bindings: old.runtime?.bindings ?? {} };
51
51
  await applyKit(root, { ...old, runtime }, { TEALBRICK_RUNTIME_CREDENTIAL: enrolled.credential });
52
52
  console.log(JSON.stringify({ connectionId: enrolled.connectionId, workspaceId, agentId, expiresAt: enrolled.expiresAt, requiresReload: true }));
53
- console.log('Connected. Customer-side tools are mounted; rebuild/restart Eve. Claim a licensed Knowledge instance and set its local binding before calling it.');
53
+ console.log(old.native ? 'Connected. Native MCP will reload the current connection on each call.' : 'Connected. Customer-side tools are mounted; rebuild/restart Eve.');
54
54
  return;
55
55
  }
56
56
  if (!old.runtime || old.runtime.issuer !== issuer || old.runtime.workspaceId !== workspaceId || old.runtime.org !== login.org)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tealbrick/kit",
3
- "version": "0.2.6",
3
+ "version": "0.3.0-rc.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Complete Teal Brick package kit with explicit Eve capability activation",
@@ -13,7 +13,8 @@
13
13
  "LICENSE",
14
14
  "NOTICE",
15
15
  "BOOTSTRAP.md",
16
- "RUNTIME.md"
16
+ "RUNTIME.md",
17
+ "NATIVE.md"
17
18
  ],
18
19
  "bin": {
19
20
  "tealbrick": "dist/cli.js"
@@ -30,23 +31,28 @@
30
31
  "./secrets": {
31
32
  "types": "./dist/secrets.d.ts",
32
33
  "import": "./dist/secrets.js"
33
- }
34
+ },
35
+ "./native": {
36
+ "types": "./dist/native.d.ts",
37
+ "import": "./dist/native.js"
38
+ },
39
+ "./cli": "./dist/cli.js"
34
40
  },
35
41
  "scripts": {
36
42
  "build": "npm run build --workspace @tealbrick/provider-transport --workspace @tealbrick/portal --workspace @tealbrick/voice --workspace @tealbrick/vision --workspace @tealbrick/deliver --workspace @tealbrick/avm && tsc -p tsconfig.json",
37
43
  "test": "node --test test/*.test.mjs"
38
44
  },
39
45
  "dependencies": {
40
- "@tealbrick/portal": "0.2.6",
41
- "@tealbrick/avm": "0.2.6",
42
- "@tealbrick/voice": "0.2.6",
43
- "@tealbrick/vision": "0.2.6",
44
- "@tealbrick/deliver": "0.2.6",
46
+ "@tealbrick/portal": "0.3.0-rc.1",
47
+ "@tealbrick/avm": "0.3.0-rc.1",
48
+ "@tealbrick/voice": "0.3.0-rc.1",
49
+ "@tealbrick/vision": "0.3.0-rc.1",
50
+ "@tealbrick/deliver": "0.3.0-rc.1",
45
51
  "@inquirer/prompts": "8.7.2",
46
52
  "zod": "^4.0.0"
47
53
  },
48
54
  "peerDependencies": {
49
- "eve": ">=0.66.0 <0.67.0"
55
+ "eve": ">=0.70.0 <0.71.0"
50
56
  },
51
57
  "peerDependenciesMeta": {
52
58
  "eve": {
@@ -59,6 +65,7 @@
59
65
  },
60
66
  "publishConfig": {
61
67
  "access": "public",
62
- "registry": "https://registry.npmjs.org"
68
+ "registry": "https://registry.npmjs.org",
69
+ "tag": "next"
63
70
  }
64
71
  }