@kohala/devkit 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @kohala/devkit
2
2
 
3
+ ## 0.1.2 (unreleased)
4
+
5
+ - **`kohala validate` now rejects tool ids not in the bundled platform
6
+ catalog snapshot** (BUG-033). A typo'd tool id (e.g. `htpp.geet`) used to
7
+ pass validation and only fail at deploy/run time; it now fails validation
8
+ offline with the offending id(s) named. Pass `--allow-unknown-tools` to
9
+ bypass when the snapshot lags newly added platform tools — the deploy
10
+ endpoint still verifies against the live catalog authoritatively.
11
+
12
+ - **Docs: LLM model-override env vars and `memory serve` agent scoping**
13
+ (BUG-047, BUG-035). The README now documents `KOHALA_LLM_MODEL`,
14
+ `ANTHROPIC_MODEL`, and `GEMINI_MODEL`, and clarifies that
15
+ `kohala memory serve` is always scoped to one agent (run it inside an
16
+ agent directory or pass `--agent <name>`).
17
+
18
+ - **Repo hygiene: removed Replit workspace scaffolding from the repository**
19
+ (BUG-016). `.replit`, `replit.md`, `attached_assets/`, and `artifacts/`
20
+ are no longer tracked in the public repo.
21
+
22
+ ## 0.1.1
23
+
24
+ Bug fixes.
25
+
26
+ - **Default models updated to match the hosted platform** (BUG-040).
27
+ Local llm-mode now defaults to `claude-sonnet-4-6` (Anthropic) and
28
+ `gemini-flash-latest` (Gemini) — the same models the platform runs — so
29
+ cap tuning and output behaviour carry over after `kohala deploy`. Override
30
+ locally with `ANTHROPIC_MODEL`, `GEMINI_MODEL`, or `KOHALA_LLM_MODEL`.
31
+
32
+ - **Default Gemini model changed from `gemini-2.0-flash` to
33
+ `gemini-flash-latest`** (BUG-041). The previous default hit quota limits
34
+ on new free-tier Gemini keys; the new default works on fresh keys.
35
+ Override with `GEMINI_MODEL` or `KOHALA_LLM_MODEL`.
36
+
37
+ - **`kohala doctor` now correctly distinguishes llm-mode availability from
38
+ `llm.complete` availability** (BUG-042). `GEMINI_API_KEY` enables
39
+ `llm.complete` in wrap-mode skills but not the llm-mode tool-use loop
40
+ (which requires `ANTHROPIC_API_KEY`). The doctor now reports each case
41
+ separately so the output matches what actually works.
42
+
43
+ - **`kohala run` error message is now specific when `runtimeMode: "llm"` is
44
+ used without an Anthropic key** (BUG-042). The previous message suggested
45
+ that `GEMINI_API_KEY` would work; the new message explains the distinction.
46
+
47
+ - **`docs/` and `examples/` included in the published tarball** (BUG-034).
48
+ The README links to both; they now ship inside the package so installed
49
+ users can read them without cloning the repository.
50
+
51
+ - **`kohala memory serve` documentation clarified** (BUG-035). The command
52
+ requires either `--agent <name>` or to be run from inside an agent
53
+ directory. The docs now show both forms.
54
+
3
55
  ## 0.1.0
4
56
 
5
57
  Initial release.
@@ -13,3 +65,9 @@ Initial release.
13
65
  - Stdlib-only Python script SDK over a loopback RPC boundary
14
66
  - Deploy client for the kohala.ai REST API (idempotent, additive,
15
67
  `--dry-run`)
68
+
69
+ ### SDK compatibility note (BUG-015)
70
+
71
+ 0.1.0 was released against `@kohala/sdk` 0.1.x. The SDK published 0.2.0 on
72
+ 2026-07-20 with updated method signatures. The emulator and deploy client
73
+ work against any SDK version; this note tracks awareness of the divergence.
@@ -0,0 +1,37 @@
1
+ # Code of Conduct
2
+
3
+ ## Our pledge
4
+
5
+ We as members, contributors, and maintainers pledge to make participation in
6
+ the Kohala Devkit project a harassment-free experience for everyone,
7
+ regardless of age, body size, visible or invisible disability, ethnicity, sex
8
+ characteristics, gender identity and expression, level of experience,
9
+ education, socio-economic status, nationality, personal appearance, race,
10
+ religion, or sexual identity and orientation.
11
+
12
+ ## Our standards
13
+
14
+ Examples of behavior that contributes to a positive environment:
15
+
16
+ - Being respectful of differing opinions, viewpoints, and experiences
17
+ - Giving and gracefully accepting constructive feedback
18
+ - Focusing on what is best for the community and the project
19
+
20
+ Examples of unacceptable behavior:
21
+
22
+ - Harassment, trolling, insulting or derogatory comments, personal attacks
23
+ - Publishing others' private information without explicit permission
24
+ - Other conduct which could reasonably be considered inappropriate in a
25
+ professional setting
26
+
27
+ ## Enforcement
28
+
29
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
30
+ reported to the maintainers at conduct@kohala.ai. All complaints will be
31
+ reviewed and investigated promptly and fairly. Maintainers are obligated to
32
+ respect the privacy and security of the reporter of any incident.
33
+
34
+ ## Attribution
35
+
36
+ This Code of Conduct is adapted from the
37
+ [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
@@ -0,0 +1,53 @@
1
+ # Contributing to the Kohala Devkit
2
+
3
+ Thanks for helping! The devkit is deliberately small and boring — please keep
4
+ it that way.
5
+
6
+ ## Setup
7
+
8
+ ```bash
9
+ git clone https://github.com/kohala-ai/devkit
10
+ cd devkit
11
+ npm install # or pnpm install
12
+ npm run build
13
+ node dist/cli/index.js --version
14
+ ```
15
+
16
+ Python 3 must be on your PATH to run the end-to-end tests.
17
+
18
+ ## Development loop
19
+
20
+ ```bash
21
+ npm run typecheck # strict tsc, no emit
22
+ npm run lint # eslint
23
+ npm test # vitest (unit + CLI e2e)
24
+ npm run build # tsup -> dist/
25
+ ```
26
+
27
+ ## Ground rules
28
+
29
+ - **Errors fail loudly.** No silent fallbacks, no mock LLM responses, no
30
+ swallowed exceptions. If something can't work, say so with an actionable
31
+ message.
32
+ - **Platform parity is the product.** Tool names, manifest fields, cap codes
33
+ (`PER_RUN_TOKEN_CAP`, `PER_DAY_TOKEN_CAP`, `TOOL_DENIED`), and enforcement
34
+ order must match the hosted platform. Don't rename things casually.
35
+ - **Dependency budget.** Runtime deps are limited to: commander, zod,
36
+ @modelcontextprotocol/sdk, execa, picocolors, ora. `pg` stays an optional
37
+ peer. New runtime dependencies need a strong justification in the PR.
38
+ - **The Python SDK stays stdlib-only.** Scaffolded agents must run without a
39
+ single `pip install`.
40
+ - Keep CLI command files thin; put behavior in the library modules where it
41
+ can be unit-tested.
42
+
43
+ ## Submitting changes
44
+
45
+ 1. Fork, branch, make the change, add/adjust tests.
46
+ 2. Add a changeset: `npx changeset` (pick patch/minor and write one line).
47
+ 3. Open a PR. CI runs typecheck, lint, tests, and build on Node 20 and 22.
48
+
49
+ ## Releases
50
+
51
+ Releases are automated with [changesets](https://github.com/changesets/changesets):
52
+ merged changesets accumulate, and the release workflow versions, tags, and
53
+ publishes to npm.
package/README.md CHANGED
@@ -55,9 +55,11 @@ skill scripts).
55
55
  ```
56
56
 
57
57
  - `runtimeMode: "wrap"` executes your script directly and validates its
58
- output (stdout).
59
- - `runtimeMode: "llm"` runs a real tool-use loop against **your own**
60
- `ANTHROPIC_API_KEY` the devkit never mocks completions.
58
+ output (stdout). Use `llm.complete` from the script SDK to call an LLM
59
+ mid-script (needs `ANTHROPIC_API_KEY` or `GEMINI_API_KEY`).
60
+ - `runtimeMode: "llm"` runs a real Anthropic tool-use loop — requires
61
+ `ANTHROPIC_API_KEY`. `GEMINI_API_KEY` works for `llm.complete` in wrap
62
+ mode but not for this loop. The devkit never mocks completions.
61
63
 
62
64
  ## Docs
63
65
 
@@ -91,3 +93,24 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and PRs welcome.
91
93
  ## License
92
94
 
93
95
  [MIT](LICENSE)
96
+
97
+ ### LLM model overrides (local emulator)
98
+
99
+ `kohala run` in LLM mode picks its model from environment variables:
100
+
101
+ | Variable | Effect |
102
+ | --- | --- |
103
+ | `KOHALA_LLM_MODEL` | Provider-agnostic override — takes precedence over the two below. |
104
+ | `ANTHROPIC_MODEL` | Anthropic model id (e.g. `claude-3-5-haiku-latest`). |
105
+ | `GEMINI_MODEL` | Gemini model id (e.g. `gemini-2.0-flash`). |
106
+
107
+ Unset, the emulator uses its built-in defaults.
108
+
109
+ ### Memory server scoping
110
+
111
+ `kohala memory serve` is always scoped to ONE agent. Either run it from inside
112
+ an agent directory (one containing `kohala.json`) or pass `--agent <name>`:
113
+
114
+ ```bash
115
+ kohala memory serve --agent my-agent --backend file
116
+ ```
package/SECURITY.md ADDED
@@ -0,0 +1,28 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Only the latest published version of `@kohala/devkit` receives security
6
+ fixes.
7
+
8
+ ## Reporting a vulnerability
9
+
10
+ Please email **security@kohala.ai** with a description of the issue, steps to
11
+ reproduce, and the version affected. Do **not** open a public GitHub issue
12
+ for security reports.
13
+
14
+ We aim to acknowledge reports within 3 business days.
15
+
16
+ ## Scope notes
17
+
18
+ - The devkit runs agent scripts **you** wrote on **your** machine, with your
19
+ own credentials. It does not sandbox skill scripts — treat third-party
20
+ agent code like any other code you execute locally.
21
+ - The `http.post_json` tool refuses private/internal targets: it validates
22
+ the hostname, every IP it resolves to (IPv4 and IPv6, including
23
+ IPv4-mapped, link-local, ULA, and CGNAT ranges), and re-validates each
24
+ redirect hop. A determined attacker with DNS rebinding at request time may
25
+ still find gaps — do not treat it as a hardened network boundary.
26
+ - `kohala login` stores your API key at `~/.kohala/credentials.json` with
27
+ mode 600. The `KOHALA_API_KEY` environment variable always takes
28
+ precedence and is the recommended mechanism in CI.
package/dist/cli/index.js CHANGED
@@ -190,12 +190,240 @@ function loadManifest(agentDir) {
190
190
  return result.data;
191
191
  }
192
192
 
193
+ // src/manifest/known-tools.ts
194
+ var KNOWN_TOOL_IDS = /* @__PURE__ */ new Set([
195
+ "http.get",
196
+ "http.brightdata",
197
+ "run_python_script",
198
+ "web.search",
199
+ "image.generate",
200
+ "code.run",
201
+ "s3.put",
202
+ "koan.publish",
203
+ "report.publish",
204
+ "email.send",
205
+ "send_email",
206
+ "email.user_send",
207
+ "email.user_inbox",
208
+ "calendar.user_events",
209
+ "calendar.user_create_event",
210
+ "whatsapp.user_send",
211
+ "whatsapp.user_inbox",
212
+ "linkedin.user_post",
213
+ "linkedin.user_profile",
214
+ "linkedin.org_posts",
215
+ "linkedin.org_stats",
216
+ "validate_output",
217
+ "html.parse",
218
+ "json.parse",
219
+ "csv.parse",
220
+ "canvas.message",
221
+ "canvas.request_approval",
222
+ "odoo.search_read",
223
+ "odoo.read",
224
+ "odoo.read_group",
225
+ "odoo.create",
226
+ "odoo.write",
227
+ "odoo.unlink",
228
+ "odoo.call_method",
229
+ "odoo.message_post",
230
+ "odoo.attachments.upload",
231
+ "aftership.create_tracking",
232
+ "aftership.get_tracking",
233
+ "aftership.list_trackings",
234
+ "aftership.update_tracking",
235
+ "aftership.delete_tracking",
236
+ "aftership.detect_courier",
237
+ "fedex.track",
238
+ "ups.track",
239
+ "era5.point_history",
240
+ "noaa.forecast",
241
+ "noaa.alerts",
242
+ "airlabs.flight",
243
+ "airlabs.schedules",
244
+ "adsb.aircraft_near",
245
+ "adsb.by_callsign",
246
+ "aftership.list_couriers",
247
+ "ga.run_report",
248
+ "ga.realtime_report",
249
+ "ga.get_metadata",
250
+ "ga.list_properties",
251
+ "epicor.query",
252
+ "epicor.list",
253
+ "epicor.get",
254
+ "epicor.create",
255
+ "epicor.update",
256
+ "epicor.call_method",
257
+ "ideogram.generate",
258
+ "ideogram.edit",
259
+ "ideogram.remix",
260
+ "ideogram.reframe",
261
+ "ideogram.replace_background",
262
+ "ideogram.upscale",
263
+ "ideogram.describe",
264
+ "elevenlabs.text_to_speech",
265
+ "elevenlabs.speech_to_text",
266
+ "elevenlabs.speech_to_speech",
267
+ "elevenlabs.sound_effects",
268
+ "elevenlabs.voice_isolator",
269
+ "elevenlabs.music",
270
+ "elevenlabs.dubbing",
271
+ "elevenlabs.dubbing_result",
272
+ "elevenlabs.voice_design",
273
+ "elevenlabs.list_voices",
274
+ "video.generate_social",
275
+ "video.generate_cinematic",
276
+ "video.generate_avatar",
277
+ "video.status",
278
+ "extract.fields",
279
+ "extract.receipt",
280
+ "extract.financial",
281
+ "extract.legal",
282
+ "extract.product_spec",
283
+ "extract.menu",
284
+ "extract.evaluate",
285
+ "memory.search",
286
+ "memory.remember",
287
+ "http.post_json",
288
+ "s3.list",
289
+ "s3.get",
290
+ "s3.delete",
291
+ "feed.fetch",
292
+ "text.diff",
293
+ "data.diff",
294
+ "pinecone.upsert",
295
+ "pinecone.query",
296
+ "pinecone.fetch",
297
+ "pinecone.delete",
298
+ "llm.complete",
299
+ "pdf.extract",
300
+ "geocode",
301
+ "slack.message",
302
+ "discord.message",
303
+ "schedule.next_run",
304
+ "schedule.last_run",
305
+ "metrics.record",
306
+ "vector.embed",
307
+ "sms.send",
308
+ "push.send",
309
+ "webhook.post",
310
+ "notify.send",
311
+ "agent.handoff",
312
+ "bus.publish",
313
+ "bus.subscribe",
314
+ "bus.list_subscriptions",
315
+ "bus.unsubscribe",
316
+ "invoice.record",
317
+ "invoice.generate",
318
+ "invoice.modify",
319
+ "invoice.list",
320
+ "invoice.publish_koan",
321
+ "invoice.ap_run",
322
+ "invoice.ap_sweep",
323
+ "invoice.ar_sweep",
324
+ "invoice.ar_draft",
325
+ "invoice.send",
326
+ "invoice.review",
327
+ "invoice.calibrate",
328
+ "invoice.settings",
329
+ "invoice.status",
330
+ "sales_agreement.record",
331
+ "sales_agreement.review",
332
+ "sales_agreement.list",
333
+ "sales_agreement.publish_koan",
334
+ "customers.list",
335
+ "customers.add",
336
+ "customers.get_brand_profile",
337
+ "secrets.get",
338
+ "secrets.set",
339
+ "secrets.list",
340
+ "secrets.delete",
341
+ "aws.ec2_list",
342
+ "aws.ec2_start",
343
+ "aws.ec2_stop",
344
+ "aws.s3_list",
345
+ "aws.lambda_list",
346
+ "aws.lambda_invoke",
347
+ "aws.cloudwatch_alarms",
348
+ "aws.cost_summary",
349
+ "aws.rds_list",
350
+ "aws.bedrock_models",
351
+ "aws.grafana_list",
352
+ "googleads.list_accounts",
353
+ "googleads.search",
354
+ "googleads.campaign_performance",
355
+ "googleads.ad_group_performance",
356
+ "googleads.keyword_performance",
357
+ "googleads.budget_summary",
358
+ "calendar.generate",
359
+ "content.analyze",
360
+ "finance.ar.summary",
361
+ "finance.ar_aging",
362
+ "finance.ar_pastdue_report",
363
+ "finance.ar_overdue_notify",
364
+ "finance.ap.summary",
365
+ "finance.ap_aging",
366
+ "finance.reporting.summary",
367
+ "finance.trial_balance",
368
+ "sales.pipeline.summary",
369
+ "sales.pipeline_read",
370
+ "inventory.stock.summary",
371
+ "inventory.procurement_read",
372
+ "slack.list_channels",
373
+ "slack.post_message",
374
+ "notion.search",
375
+ "notion.append_page",
376
+ "hubspot.list_engagements",
377
+ "hubspot.log_call",
378
+ "hubspot.update_deal",
379
+ "linear.create_issue",
380
+ "asana.create_task",
381
+ "airtable.list_records",
382
+ "airtable.create_record",
383
+ "pandadoc.create_document",
384
+ "canva.create_design",
385
+ "canva.autofill_deck",
386
+ "zapier.trigger_zap",
387
+ "commitments.recall",
388
+ "workspacesync.fanout",
389
+ "dealdesk.draft",
390
+ "pricing.catalog_list",
391
+ "pricing.assumptions_list",
392
+ "pricing.benchmark_list",
393
+ "pricing.single_use_list",
394
+ "pricing.reusable_tco_list",
395
+ "pricing.monitoring_list",
396
+ "pricing.simulate_save"
397
+ ]);
398
+ function unknownToolIds(tools) {
399
+ return tools.filter((t) => !KNOWN_TOOL_IDS.has(t));
400
+ }
401
+
193
402
  // src/cli/validate.ts
194
403
  function registerValidateCommand(program2) {
195
- program2.command("validate").argument("<agent>", "agent directory (containing kohala.json)").description("Validate an agent's kohala.json and print precise errors").action((agent) => {
404
+ program2.command("validate").argument("<agent>", "agent directory (containing kohala.json)").option(
405
+ "--allow-unknown-tools",
406
+ "do not fail on tool ids missing from the bundled catalog snapshot"
407
+ ).description("Validate an agent's kohala.json and print precise errors").action((agent, options) => {
196
408
  const agentDir = path3.resolve(process.cwd(), agent);
197
409
  try {
198
410
  const manifest = loadManifest(agentDir);
411
+ const unknown = unknownToolIds(manifest.toolAllowlist);
412
+ if (unknown.length > 0) {
413
+ const msg = `toolAllowlist contains tool id(s) not in the Kohala tool catalog: ` + unknown.join(", ");
414
+ if (options.allowUnknownTools) {
415
+ console.warn(pc2.yellow(`warning: ${msg}`));
416
+ } else {
417
+ console.error(pc2.red(msg));
418
+ console.error(
419
+ pc2.dim(
420
+ " If these are new platform tools the bundled snapshot doesn't know yet, re-run with --allow-unknown-tools (the deploy endpoint will still verify against the live catalog)."
421
+ )
422
+ );
423
+ process.exitCode = 1;
424
+ return;
425
+ }
426
+ }
199
427
  console.log(pc2.green(`kohala.json for "${manifest.name}" is valid.`));
200
428
  console.log(
201
429
  pc2.dim(
@@ -725,8 +953,8 @@ var NoLlmKeyError = class extends Error {
725
953
  this.name = "NoLlmKeyError";
726
954
  }
727
955
  };
728
- var DEFAULT_ANTHROPIC_MODEL = "claude-3-5-haiku-latest";
729
- var DEFAULT_GEMINI_MODEL = "gemini-2.0-flash";
956
+ var DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-6";
957
+ var DEFAULT_GEMINI_MODEL = "gemini-flash-latest";
730
958
  function detectLlmProvider(env = process.env) {
731
959
  if (env.ANTHROPIC_API_KEY) return "anthropic";
732
960
  if (env.GEMINI_API_KEY) return "gemini";
@@ -735,10 +963,12 @@ function detectLlmProvider(env = process.env) {
735
963
  async function completeText(prompt, model, maxOutputTokens = 1024, env = process.env) {
736
964
  const provider = detectLlmProvider(env);
737
965
  if (provider === "anthropic") {
738
- return completeAnthropic(prompt, model ?? DEFAULT_ANTHROPIC_MODEL, maxOutputTokens, env);
966
+ const resolvedModel = model ?? env.KOHALA_LLM_MODEL ?? env.ANTHROPIC_MODEL ?? DEFAULT_ANTHROPIC_MODEL;
967
+ return completeAnthropic(prompt, resolvedModel, maxOutputTokens, env);
739
968
  }
740
969
  if (provider === "gemini") {
741
- return completeGemini(prompt, model ?? DEFAULT_GEMINI_MODEL, maxOutputTokens, env);
970
+ const resolvedModel = model ?? env.KOHALA_LLM_MODEL ?? env.GEMINI_MODEL ?? DEFAULT_GEMINI_MODEL;
971
+ return completeGemini(prompt, resolvedModel, maxOutputTokens, env);
742
972
  }
743
973
  throw new NoLlmKeyError();
744
974
  }
@@ -1211,9 +1441,11 @@ var TOOL_DEFINITIONS = {
1211
1441
  async function runLlmShift(context, skillName, scriptPath, repairFeedback) {
1212
1442
  const apiKey = process.env.ANTHROPIC_API_KEY;
1213
1443
  if (!apiKey) {
1214
- throw new NoLlmKeyError();
1444
+ throw new Error(
1445
+ 'NO_LLM_KEY: llm mode requires ANTHROPIC_API_KEY. GEMINI_API_KEY enables llm.complete calls inside wrap-mode skills, but the llm-mode tool-use loop only supports Anthropic. Set ANTHROPIC_API_KEY in your environment to use runtimeMode: "llm".'
1446
+ );
1215
1447
  }
1216
- const model = process.env.KOHALA_LLM_MODEL ?? "claude-3-5-haiku-latest";
1448
+ const model = process.env.KOHALA_LLM_MODEL ?? process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-6";
1217
1449
  const dispatcher = new ToolDispatcher(context);
1218
1450
  const { manifest, meter, trace, runId } = context;
1219
1451
  const tools = Object.entries(TOOL_DEFINITIONS).filter(([name]) => manifest.toolAllowlist.includes(name)).map(([name, definition]) => ({ name: toApiToolName(name), ...definition }));
@@ -2198,8 +2430,15 @@ function registerDoctorCommand(program2) {
2198
2430
  warn("python 3 not found", "wrap-mode `kohala run --local` will not work until installed");
2199
2431
  }
2200
2432
  const provider = detectLlmProvider();
2201
- if (provider) {
2202
- ok(`LLM key (${provider})`, "llm.complete and llm mode available");
2433
+ const hasAnthropic = Boolean(process.env.ANTHROPIC_API_KEY);
2434
+ if (hasAnthropic) {
2435
+ ok("LLM key (anthropic)", "llm.complete and llm mode both available");
2436
+ } else if (provider === "gemini") {
2437
+ ok("LLM key (gemini)", "llm.complete available in wrap mode");
2438
+ warn(
2439
+ "no ANTHROPIC_API_KEY",
2440
+ 'llm mode (runtimeMode: "llm") requires Anthropic \u2014 GEMINI_API_KEY covers llm.complete only'
2441
+ );
2203
2442
  } else {
2204
2443
  warn(
2205
2444
  "no ANTHROPIC_API_KEY / GEMINI_API_KEY",