Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0041cfe
Merge pull request #1231 from ChronoAIProject/sync/post-release-v0.17.0
chronoai-shining Aug 7, 2026
649c25d
chore(api): delete dead generation/types duplicates (#1242)
chronoai-shining Sep 16, 2026
f3fa660
refactor(api): split generation route helpers out of routes.ts (#1242)
chronoai-shining Sep 16, 2026
89774f9
refactor(api): extract shared LLM stream loop in generation svc (#1242)
chronoai-shining Sep 16, 2026
b672df0
feat(api): add GenerationMode + references/assets to skill types (#1242)
chronoai-shining Sep 16, 2026
9dea9af
feat(api): mode-aware skill generation prompts (#1242)
chronoai-shining Sep 16, 2026
a9696ee
feat(api): enforce generation mode in the generation service (#1242)
chronoai-shining Sep 16, 2026
49840e1
feat(api): accept mode on POST /skills/generate (#1242)
chronoai-shining Sep 16, 2026
bfb1b60
docs(api): document generation mode on the OpenAPI contract (#1242)
chronoai-shining Sep 16, 2026
660078a
refactor(web): decompose CreateSkillGenerativePage (#1242)
chronoai-shining Sep 16, 2026
2af6a25
feat(web): build references/ and assets/ folders in the preview (#1242)
chronoai-shining Sep 16, 2026
37aecdd
fix(web): show problem+json detail on rejected generate requests (#1242)
chronoai-shining Sep 16, 2026
3aab1d3
feat(web): thread generation mode through stream client + hook (#1242)
chronoai-shining Sep 16, 2026
096c46a
feat(web): Simple | Advanced mode toggle on the generative page (#1242)
chronoai-shining Sep 16, 2026
f1029fb
docs(skills): document generation mode in the agent manuals (#1242)
chronoai-shining Sep 16, 2026
0302e3c
docs: changeset for skill generation modes (#1242)
chronoai-shining Sep 16, 2026
c5267d6
test(api): integration coverage for generation mode end to end (#1242)
chronoai-shining Sep 16, 2026
130f9a6
fix(api): classify simple-mode violations before schema check (#1242)
chronoai-shining Sep 16, 2026
e9e26f8
test(api): pin repeated multipart mode field → invalid_mode (#1242)
chronoai-shining Sep 16, 2026
f2d5719
fix(web): move focus with arrow keys in GenerationModeToggle (#1242)
chronoai-shining Sep 16, 2026
0d72f88
test(web): cover describeHttpFailure title-only + body-read (#1242)
chronoai-shining Sep 16, 2026
ed1c698
docs: correct generation-mode contract text after review (#1242)
chronoai-shining Sep 16, 2026
2157039
chore(deps): override js-yaml, nanoid, browserslist to patched releas…
chronoai-shining Sep 16, 2026
6ad5e23
Merge pull request #1245 from ChronoAIProject/chore/1244-audit-transi…
chronoai-shining Sep 16, 2026
fa3ef7f
Merge remote-tracking branch 'origin/develop' into feature/1242-skill…
chronoai-shining Sep 16, 2026
d1a8589
test(api): give harness-booting mode tests a 30s timeout (#1242)
chronoai-shining Sep 16, 2026
0886483
Merge pull request #1243 from ChronoAIProject/feature/1242-skill-gene…
chronoai-shining Sep 16, 2026
256d7be
docs: release notes for the next release (skill generation modes)
chronoai-shining Sep 17, 2026
53067f4
Merge pull request #1250 from ChronoAIProject/chore/1249-release-note…
chronoai-shining Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/audit-transitive-overrides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/release-notes-20260917.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
6 changes: 6 additions & 0 deletions .changeset/skill-generation-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"ornn-api": minor
"ornn-web": minor
---

Add caller-chosen, server-enforced generation modes to `POST /api/v1/skills/generate` (#1242). `mode: "simple"` asks for a single `SKILL.md`: the model gets a dedicated prompt and the server rejects any answer that is not a plain, file-less skill (one corrective retry, then a terminal `error`), so `generation_complete.raw` in simple mode never carries scripts, references or assets. `mode: "advanced"` — the default, and the pre-existing behaviour — lets the model emit `scripts[]` and, newly, `references[]` and `assets[]` text files. An unknown value fails with 400 `invalid_mode` before the quota reserve. The generative skill builder in ornn-web gains a Simple | Advanced toggle in the composer row (persisted like the model pick, locked while streaming), builds `references/` and `assets/` folders in the package preview, and now surfaces the server's problem+json detail when a generation request is rejected before the stream opens. The OpenAPI spec and the three agent manuals document the new field, the extended `raw` shape and the simple-mode guarantee.
17 changes: 17 additions & 0 deletions .github/release-notes-20260917.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Fixed

- Rejected skill-generation requests now show the server's actual reason
- Few technical bugs fixed

## New Feature

- Choose Simple or Advanced mode when generating a skill
- Simple mode returns one SKILL.md, enforced by the server
- Advanced generation can now include references and assets files
- Agents can request the generation mode through the API

## Changed

- Generative builder gains a mode toggle beside the model picker
- API reference and agent manuals describe generation modes
- Technical enhancement
29 changes: 17 additions & 12 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 5 additions & 15 deletions ornn-api/src/domains/assistant/kb/digest.generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
Produced by ornn-api/scripts/build-assistant-kb.ts (#970).
Re-run: `bun run scripts/build-assistant-kb.ts` from ornn-api/.
budgetTokens: 18000
estimatedTokens: 11360
estimatedTokens: 11364
sources:
- readme: ~2250 tok
- claude-positioning: ~272 tok
- architecture: ~219 tok
- agent-manual-http: ~5486 tok (clipped)
- agent-manual-http: ~5491 tok (clipped)
- conventions: ~2599 tok (clipped)
- design-overview: ~487 tok
-->
Expand Down Expand Up @@ -255,8 +255,8 @@ metadata:
- manual
- skill-lifecycle
- http
version: "1.1"
lastUpdated: 2026-04-29
version: "1.2"
lastUpdated: 2026-09-16
---

# Agent Manual (HTTPS variant)
Expand Down Expand Up @@ -507,7 +507,7 @@ The response is `{ data: { name, description, metadata, files: { "SKILL.md": "..

**Step 5 — If steps 2–3 yielded nothing after 5 search attempts**, you may decide your own way to perform the task. **And if the task is definitive and potentially repeatable, build a skill and upload it back to Ornn so future you (or other agents) can find it.** Build flow:

1. *(Optional)* **Bootstrap with AI generation** — Ornn's LLM can scaffold a skill from a prompt, source code, or an OpenAPI spec via `POST /api/v1/skills/generate*` (SSE). Useful when you need a starter; the generated skill still needs validation + your edits.
1. *(Optional)* **Bootstrap with AI generation** — Ornn's LLM can scaffold a skill from a prompt, source code, or an OpenAPI spec via `POST /api/v1/skills/generate*` (SSE). On the prompt endpoint pass `"mode": "simple"` for a single `SKILL.md` (server-enforced — no scripts / references / assets) or leave the default `"advanced"` to let the model add `scripts/`, `references/` and `assets/`. Useful when you need a starter; the generated skill still needs validation + your edits.

2. **Read the skill format spec** so you write a valid one:

Expand Down Expand Up @@ -600,16 +600,6 @@ curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"isPrivate":true,"sharedWithUsers":["user_abc"],"sharedWithOrgs":["org_xyz"]}' \
"https://ornn.chrono-ai.fun/api/v1/skills/<id>/permissions"
```

**Step 3c — Set to private.**

```bash
curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d

---

Expand Down
64 changes: 64 additions & 0 deletions ornn-api/src/domains/skills/generation/packageContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Turns an uploaded skill package ZIP into the plain-text context block
* the multipart branch of `POST /skills/generate` prepends to the prompt.
*
* Split out of `routes.ts` (#1242). Only `SKILL.md` plus anything under
* `scripts/`, `references/` and `assets/` is read — the same folders the
* upload validator allows at the package root.
*
* @module domains/skills/generation/packageContext
*/

import JSZip from "jszip";
import { resolveZipRoot } from "../../../shared/utils/zip";
import { createLogger } from "../../../shared/logger";

const logger = createLogger("skillGenerationPackageContext");

const RELEVANT_FILES = ["SKILL.md"];
const RELEVANT_DIRS = ["scripts/", "references/", "assets/"];

/**
* Read content from a ZIP package for analysis.
*/
export async function analyzePackageContent(zipBuffer: Uint8Array): Promise<string> {
const zip = await JSZip.loadAsync(zipBuffer);
const allPaths = Object.keys(zip.files);
resolveZipRoot(zip, allPaths);
const parts: string[] = [];

for (const path of allPaths) {
const file = zip.files[path];
// allPaths is `Object.keys(zip.files)`, but noUncheckedIndexedAccess
// (#450) widens the lookup to `T | undefined`. Defensive skip.
if (!file || file.dir) continue;

// Check if this is a relevant file
const segments = path.split("/").filter(Boolean);
let relativePath = path;
if (segments.length > 1) {
const firstEntry = segments[0]!;
const folderEntry = zip.files[firstEntry + "/"];
if (folderEntry && folderEntry.dir) {
relativePath = segments.slice(1).join("/");
}
}

const isRelevant = RELEVANT_FILES.includes(relativePath) ||
RELEVANT_DIRS.some((d) => relativePath.startsWith(d));

if (isRelevant) {
try {
const content = await file.async("string");
parts.push(`--- ${relativePath} ---\n${content}`);
} catch (err) {
// Skip binary or unreadable files. Log so an upload that's
// 100% binary doesn't silently produce an empty generation
// context (#579).
logger.debug({ err, relativePath }, "generation: skipping unreadable file");
}
}
}

return parts.join("\n\n");
}
Loading