Skip to content
Merged
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .release/core-v0.5.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"summary": "feat(core): port the first upstream batch — six items from the desktop application tree, each already running there and each replacing something this tree hand-rolls or lacks: backends/secret lands the keychain secret store (one 0600 file per secret under a 0700 directory, AES-256-GCM under a machine-local key with the account name bound as GCM additional data, exposed through the secret.Store/keychain factory) as a non-release-managed module joining go.work, the module lists, the deploy guide and the config skill; core/tool/middleware gains the recoverable Truncate middleware, which persists an oversized tool result whole while the context gets a head plus tail excerpt and a pointer, keeps JSON answers parseable, runs inside ResultLimit, and degrades to the excerpt when the spill fails; core/inference/inferencetest ships a scripted OpenAI-compatible wire provider with recorded requests and headers, an open-ended last reply and hold/release, whose driver/openai pilot lands with the release that ships the provider plus the pin bump; core/utils gains fsatomic (atomic write, publish, directory fsync) and pathsafe (RelRef, RealWithin), with core call sites moved onto them; core/workspace gains Capped bounded reads, adopted by the bindings bridge_fs; and core/utils gains httpprobe plus the httpkit transport-assertion regression fix; the review follow-ups land on the branch (Windows lane fixes, keychain fail-closed key and index handling with atomic publishes, the Truncate non-UTF-8 panic and spill-name escaping, unique scripted tool-call ids, the transport fallback warning, the workdir predicate through pathsafe.RealWithin, a deploy-level keychain test and doc fixes), deliberately leaving the PR-size split, the fragmented tool_calls delta mode and the deploy-level omitted-id fallback to their own change; feat(core/sandbox): add the escalation front end — WithApproval decides whether a command may run and deliberately never widens the policy it runs under, so a command the approver allowed and seatbelt, bwrap or Windows then refused used to fail like any other command; escalation adds the second, explicit question (may this one command leave the confine?) and the front end that acts on the answer: refusal detection (Denied and DenialExcerpt over the three confinement layers' spellings, quiet on 126/127 and plain EACCES), RuleFor unwrapping shell wrappers like the allowlist path, the Escalator, EscalationRules and EscalationGate seams (with Func), and the Escalation front end itself — confined attempt, refusal detection, gate, one prompt, one approved retry through the unconfined chain with byte-identical ExecOptions — where a remembered rule skips the doomed confined attempt but never a false gate, and a failed prompt keeps the confined result (fail closed); it is not a Runner decorator because the one-shot Exec is a derived view over Start, so only the Exec level sees the completed result a refusal is recognized in; the review follow-ups make Command a POSIX-quoted rendering that reads back, carry the refused attempt (a cloned Exec) on the DTO, treat EscalationGateFunc(nil) as a nil gate, snapshot the ExecRequest in the rules store and the escalator, search both captured streams, cut the excerpt on a rune boundary and pin the marker table to real backends instead of fixtures; tests cover the scripted-runner and session paths, the refusal and rendering tables, and real backends on seatbelt, bwrap and Windows, and docs/guides/sandbox.md documents escalation with Unconfined as a chain of its own; feat(core/media): add the media payload layer — the media normalization item of the second upstream batch, landed beside core/memory rather than under core/message/media because that package is the schema and nothing else in it knows about agents, while the prepare hook is an agent.Preparer and imageutil is payload handling: core/utils/filetype classifies content-first (OfPath and OfData answer Kind{MediaType, Text} from the leading 512 bytes, a media signature wins when the payload is binary or the name agrees on the family, a payload with no NUL byte is text, and everything else takes the name's type when the platform table has one, so the .ts case keeps a TypeScript source beside a recording open as source); core/media/imageutil normalizes (NormalizeToJPEG with EXIF orientation, alpha flattened onto white and JPEG q90; DownscaleToJPEG with a longest-edge and byte budget where the quality ladder runs first and resolution only when quality runs out, and the encode always precedes the resize so the dimensions describe the bytes returned; NormalizeFileToJPEG checking the pixel budget from the header so a decompression bomb is rejected before decoding; JPEGUpright probing EXIF so an upright file is persisted byte-for-byte and a stream it cannot follow errs towards needing a transform; budgets of a 10 MiB inline cap, 40 M decode pixels, a 1568 px edge and 786000 raw bytes on the prompt path); core/media/hook registers the media.attachments prepare hook, which inlines URL-sourced image, audio and video parts that point at local files, flattens the non-image kinds a deployment does not pass through into text lines naming their path, and leaves images and text alone, with a fresh board per call on every path; the named decisions: github.com/disintegration/imaging v1.6.2 (MIT, pure Go, no cgo, already the desktop tree's library, with x/image pinned and TIFF/BMP decode tests so the transitive registration cannot disappear unnoticed), passthrough_kinds replacing video_models because the hook runs before routing, where a part's survival is a deployment-level declaration, English markers overridable per kind, attachment size never deciding whether a turn runs (an over-budget file is flattened to a path line, and a source with no path to name is described rather than dropped in silence), two budgets with one owner each (hook.DefaultMaxInlineBytes and imageutil.MaxInlineImageBytes pinned against each other by a test), and the trust boundary (part URLs on the main channel are host-attested, and work_dir renders paths rather than confining them); two review rounds folded in Windows pathsafe.Rel separators, a JPEGUpright that agrees with imaging on long-typed and big-endian orientations, DownscaleToJPEG dimensions that describe the bytes returned, silent flattening of non-URL sources, a whole-run failure on an over-budget attachment, a malformed part failing late, a read bound that wrapped negative at the int64 ceiling, and the media: and media.attachments: error prefixes, with the EXIF tests running against imaging as an oracle; nothing in core wires these packages yet — hosts register the hook and call the normalization entry points, the desktop application first — and wiring the examples, the desktop switch-over and the model-facing image tools follow in their own change; docs/guides/media.md, docs/index.md and the flowcraft-config skill's resource reference are updated; feat(core): extract the shared BM25 kernel and move tool_search onto it, changing the model-visible ranking and hit set with this tag — not an equivalent refactor: prefix expansion is new while the catalog's two-rune ASCII floor stays; core/utils/bm25 is one kernel (a two-field Doc with Name weighted 3x over Text, prefix hits, CJK as single characters plus adjacent bigrams, SearchTerms for callers that persist their own terms, posting tables sized from their own distinct counts, duplicate IDs rejected and nil options ignored); core/tool deletes its hand-rolled scorer and tokenizer and indexes documents by name so tie-breaks stay deterministic, and tool_search gains name weighting, prefix hits (webf reaching webfetch) and CJK queries while keeping the two-rune floor and capping limit at the discovery pool size; the review follow-ups fixed prefix expansion to contribute each term's best match per field instead of summing over its expansions (the exact hit wins, 1.113 against 0.919, and scores are unchanged for a caller that disables prefix matching), restored the two-rune floor so a query like read a file no longer pulls webfetch in through its single-letter a, moved views/summary onto the kernel tokenizer (a CJK query scored 0 on that lane and dropped every candidate, and now matches), dropped entity values that tokenize to nothing, and stopped sorting the caller's slice; tests freeze a hand-computed golden score table (nameBoost, the prefix discount, the idf form, (k1+1)), add the catalog contract tests core/tool never had, and cover duplicate IDs, nil options, the selector-key coupling matrix, a real similarity window in maintain and a deleted-document assertion; the memory backend's lexical lane re-ran LoCoMo identically (hit_rate 0.4605, 70/152, evidence 85/202) and its retrieval side shares the same splitter with algorithm versions naming it (okapi-bm25-v2, deterministic-entity-v3, bm25-query-sigmoid-v2, maintain-v2), while backends/memory stays untagged; detecting a stale lane and the maintain overlay's invalidation are tracked in #604 and per-hit payload bounds in #605; docs/guides/tool.md records the ranking contract, and the memory README's entity-compatibility promise and CJK min_score consequence are corrected; feat(core/skill): add the read-only Agent Skills engine — the agentskills.io convention's read-only half, built on the BM25 kernel: parse decodes SKILL.md frontmatter under the agentskills.io limits (name up to 64 characters, description up to 1024, file up to 256 KiB), falls back to the slugified directory name with a warning when the name is missing or invalid, and decodes leniently so vendor extension fields (license, version, metadata.*) do not fail the parse; Discover(ctx, roots) BFS-walks caller-supplied roots, skipping hidden entries, following symlinks only while their target stays inside the roots, collecting parse failures as non-fatal Diagnostics and sorting results by name then path; the service publishes an immutable snapshot under an atomic pointer with Reload and lock-free read paths (List, ByName, Diagnostics and Roots, plus ReadFull and ReadByPath, which serve only snapshot paths and re-check symlink containment on every read); rank runs BM25 over the name (weighted 3x) and the description through core/utils/bm25, so skills rank exactly like tool catalogs (Rank, RankScored, and Mentioned for standalone $name mentions); render produces the per-turn ## Skills metadata section and never inlines bodies; installation, authoring, usage accounting, retirement and built-in shadowing stay deliberately out of scope (caller policy), because roots are parameters, nothing is scanned implicitly, duplicate names across roots are all listed and rankable, and ByName and Mentioned resolve the first path in canonical (name, path) order; the named decisions: canonical de-duplication (a skill reachable through several roots is collected once, since the kernel rejects duplicate document IDs and double collection would double-inject via top-N, at the cost of Path being the symlink-resolved path), a json-tag frontmatter decode through sigs.k8s.io/yaml so the non-standard metadata.short-description actually decodes, and rune-counted length limits so CJK descriptions are not over-truncated; the review follow-up bounded the walk — walked directories are tracked by canonical path, so an alias pointing back into the walked tree is skipped instead of re-descended (measured on the unfixed code: one alias descended silently to the kernel's ELOOP limit, two doubled the frontier per level) — and made a leading UTF-8 BOM stop making a SKILL.md unreadable, turned os.Stat failures in the walk into diagnostics rather than silence, stopped filterDisabled mutating the caller's slice, and kept a nameless skill in a fully non-ASCII directory an error diagnostic rather than inventing a synthetic name; the suites cover parse, discover, service, rank and render plus a frontmatter fuzzer, docs/guides/skill.md and the navigation entries are updated, and the tool pairing (skill_search and skill_read) with per-turn injection stays on the application side as the next step; fix(core/inference): name the response validation checks — the failed response validation check is preserved as a stable, redacted detail in Error.Detail for unary and terminal streaming generate responses, with modality-specific labels where applicable, while the existing error-kind classification (including UndefinedTool), the tool-call data and the error chain are kept; table-driven coverage exercises every validation check through the real provider and terminal stream paths, and logging the leaf cause and retry-policy differentiation stay open on #584.",
"releases": [
{
"module": "core",
"bump": "minor"
}
]
}
Loading