Repository navigation
v0.9.0: serverless chat core (seepient-core package split) — release-gated - #22
Merged
Merged
Conversation
…001) Branch base = d3e7e9a (v0.8.2 seal, per spec research tree note). ci.yml push triggers + AGENTS.md SPECKIT block now point at 027.
pnpm-workspace.yaml gains packages/* (overrides/allowBuilds/audit untouched). packages/core manifest per data-model §2.1 (deps: pi-ai 1.0.2 + typebox 1.3.27; files: [dist]; script-free). tsconfig.core.json seeded with the foundations/types.ts root (empty include would fail tsc before T009 populates the spine; emit follows static imports). Scripts: build:core, build/typecheck/test/check chain build:core first (D20); clean removes packages/core/dist. Suite 2219 passed / 11 skipped — unchanged.
(a) zero built-in tool defs without injection + unknown built-in names
seepient; (b) estimateMode=heuristic under a vi.mock'd unresolvable
BPE vendor; (c) unregistered media/image -> typed denial naming
seepient-core; (d) pnpm-lock carries exactly pi-ai's single version
per provider SDK (base-version dedupe over peer-suffixed keys);
(e) stateless createSeepient trusted-host turn completes with
build-local-boundary/effect-broker/sandbox-runtime never loaded
(vi.hoisted hit-recording mocks). All 5 RED on the unsplit tree;
pnpm test is intentionally red until T004-T008 land.
…27 T004-T008)
T004 tools: builtInTools barrel import removed from the engine; default
tool modules arrive via the injection seam (full package registers
FULL_TOOL_MODULES at composition roots; core default = zero built-ins);
use_skill stays loop-side; unknown built-in names fail closed naming
seepient.
T005 tokenizer: gpt-tokenizer lazy-imports with a chars÷4 heuristic
fallback; estimateMode ("exact"|"heuristic") observable via
currentEstimateMode()/whenEstimatorReady, stamped on Usage and
ContextBreakdown output.
T006 media/image: all four construction sites (agent-loop, sdk/seepient,
sdk/index, cli/agent) build the vendor-operation handler via the seam;
google/openai image-raw backends register from capabilities (S-12 clean);
unregistered media/image deny typed (media_not_registered InferenceError
naming seepient-core).
T007 provider dedupe: root pins aligned to pi-ai's exact majors
(openai 7.19.0, @google/genai 2.21.0, @anthropic-ai/sdk 0.129.0,
typebox 1.3.27) — one version of each; dead vendors/openai.ts deleted
(its spike test imports the SDK directly now).
T008 execution pipeline: per-turn pipeline injectable — engine default
is the light boundary (trusted-host/none executors only, typed denial
naming seepient for executor kinds); full package registers
buildLocalBoundary + provider-manager API at composition roots;
legacy-adapter + approval-brokers join core emit (foundations-only
tree, D13 weight rule); discovery-source absence degrades with an
actionable message; injection-seams lives in foundations (structural
types) so every layer can consult it.
New core modules: foundations/injection-seams.ts,
capabilities/execution/{host-executors,light-execution-boundary}.ts,
capabilities/inference/register-image-backends.ts,
transport/sdk/full-registrations.ts. Suite 2225 passed / 11 skipped;
probes 11/11; all five seam gates green.
…(027 T009)
tsconfig.core.json is the single membership source (foundations, skills,
tokenizer, inference adapters, the light execution set, domain minus media,
the SDK core files, pi-ai wrappers, anthropic, egress-check); root tsconfig
excludes mirror it. Root surfaces import the engine via the
'seepient-core/dist/*' specifier (swept ~98 files); root dist is engine-free
except the generated dist/foundations/types.{js,d.ts} forwarding shim
(exports["./types"] path preserved, D16).
New core entry files: sdk/core.ts (curated contract surface),
sdk/ask.ts (askSeepient moved out of the full entry), sdk/chat.ts
(createChat — the one deliberate new API: send/stream/messages/abort/close
over the stateless agent).
Missed core→full edges closed during the emit: digestAction/digestArgs
moved to foundations/action-digest.ts; broker-connector evaluation and
discovery sources became registered lazy loaders (thunks live full-side,
S-12-clean); built-in analyzers register via the seam and ALL_ANALYZERS
became the live allAnalyzers() accessor; the trusted-host fallback analyzer
moved to domain with its full digest/display shape intact; the
semantic scorer moved to domain/middleware (26 lines, zero deps, D10);
model-egress-gate joined the core emit; dead domain comm-analyzers shim
deleted.
vitest.config.ts: test-only alias resolves seepient-core/dist/* back to
src — one engine instance per test process (seam registrations and vi.mock
stay coherent). Production emit is untouched; the core artifact's
what-ships gate is the T013 tarball e2e.
Tests moved to their modules (semantic-scorer), repointed to the real
barrels, or annotated with the shared full-composition helper
(src/domain/__tests__/full-composition.ts). Suite 2226 passed / 11
skipped; tsc (root + test) clean.
…re type boundary (027 T009 fallout)
…program (027 T009 fallout) tsconfig.test.json gains a paths alias mirroring the vitest runtime alias — one set of engine declarations for tests; production emit untouched.
…ader seam (027 T010, T011) scripts/check-package-boundary.mjs walks the compiled core emit from the entry over static AND dynamic edges with comments stripped; B-1 (no edge leaves the core emit except the explicit absence-tolerant guardlist), B-2 (externals ⊆ pi-ai + typebox + node builtins; BANNED_DEPS mirrors the contract table). Negative self-tests run on every invocation: planted banned import flips RED, planted dynamic full-module edge flips RED, a doc-comment import stays inert. Wired as boundary:check into pnpm test. src/__tests__/core-api-surface.test.ts pins the core entry's runtime exports to the contract list generated from core.ts's actual export sites, with a negative self-test (an unplanned export fails the pin) and the fail-closed registration denial naming seepient. Tokenizer seam reworked to a registered lazy loader (the vendor wrapper was leaking into the core emit via tsc's dynamic-import closure — the tracer caught it): core keeps zero gpt-tokenizer specifiers; the full package registers the thunk from capabilities (S-12 clean); estimate stays exact for full consumers and heuristic for core-only. whenEstimatorReady became lazy so registration precedes resolution.
…027 T012, T013, T019) pack:verify is per-package: root (hook assertion, helper staging, contents, placeholder refusal, no-workspace: specifiers, B-3 no-duplicated-engine with the forwarding-shim whitelist, release.yml invariant greps) and core (script-free manifest, dist-only files, clean-install WEIGHT BUDGET ≤150MB with the measured MB logged, banned-categories assert). The npm pack fallback is deleted — raw npm pack ships literal workspace:^ and fails loudly instead. build:core cleans stale emits before tsc. Measured: seepient-core clean-install closure = 102 MB (budget 150, 53 packages, zero banned categories) vs ~346 MB for the full package. release.yml: explicit 'pnpm --filter seepient-core build && pnpm build' before pack:verify (pnpm -r skips the root), seepient-core published FIRST via pnpm publish --no-git-checks --provenance (id-token: write), root second, both skip-if-exists, tag-re-run recovery note in the workflow. scripts/qs-core-chat.mjs (qs:core-chat): packs the core tarball into a clean temp install and runs the six contract flows with ONLY seepient-core imports — stateless multi-turn, createChat send+stream+owned history, askSeepient one-shot, trustedHostTool roundtrip, approveTool on the light pipeline, unavailable-built-in typed denial naming seepient — plus both absence-tolerant degradations (estimateMode heuristic, actionable refreshModels message). Completion in a full-package-free node_modules is itself the loaded-modules assert.
…r (027 T014) Fresh-clone simulation green: rm -rf packages/core/dist node_modules, pnpm install --frozen-lockfile, pnpm run typecheck, pnpm test.
…the new result shape (027 T014 fallout)
…7 T015-T017) T015 parity: full suite + probes 11/11 + CLI/server binary smokes green; the wrapper pin proves the full entry re-exports the engine by identity (createSeepient/askSeepient/createChat are the same function objects — no re-plumbing wrapper for the v0.8.1 class to bite). T016: seepient-core resolves into packages/core (workspace pin), the tenancy-upgrade journey behaves identically through the package path, and the security-suite inventory baseline (711 matched names) is asserted with a floor. T017: packages/core/README.md (runnable chat-only examples under the docs gate, which now validates seepient-core imports against the core entry surface), stateless-workers.md gains the slim-install section, deployment.md the serverless note, README the which-package table, and the CHANGELOG carries the complete documented breaking surface (removed transitive provider-SDK majors; shrunken root dist with the types forwarding shim; lockstep seepient-core dependency).
…ght, dual bump (027 T018) The target version is computed arithmetically (npm version mutated the manifest BEFORE the dry-run exit — the pre-existing defect, now fixed); --dry-run leaves both manifests byte-identical. The preflight checks the previous release completed for BOTH packages with the first-release bootstrap (core absent from the registry + free tag = ready, diagnosed with a warning) and the half-state case dies NAMING the stalled package with the tag-re-run recovery note. The execute phase bumps BOTH manifests and stages them in the release commit. Release-notes generation chains build:core (the script imports the SDK via tsx). QS-5 verified in a sandbox registry: dry-run byte-identical, bootstrap warning path, half-state refusal naming seepient-core.
…, vocabulary un-bans the now-real seepient/types (027 T021) The seepient/types identifier was banned as a fiction through 0.8.2; the D16 forwarding shim makes it real (pack:verify B-3 pins the shim). The QS-4 security-suite inventory count (711 matched names, recorded) is owned by the quickstart gate command — the in-suite vitest subprocess spawn was contention-flaky. AGENTS.md SPECKIT block reflects the implemented state.
…r build learns the split (027 T021) pnpm audit --prod went red on two advisories published after the v0.8.2 audit: source-map-js (<1.2.2, event-loop DoS via jsdom→cssstyle→css-tree) and @modelcontextprotocol/sdk (GHSA-6qxp-vccf-f47h, <1.31.0 — now also riding pi-ai's nested @google/genai, which the root pin cannot reach). Overrides force the patched versions across the tree; audit is back to the single pre-ignored node-forge entry. The Docker build copies tsconfig.core.json + packages/core/package.json and installs both workspace members — build:core needs the config and the seepient-core workspace link.
…027 T021) The runtime image copied node_modules with a dangling seepient-core symlink (packages/ is not in the image) — every server start crashed with ERR_MODULE_NOT_FOUND before the health check. The built core package now ships at node_modules/seepient-core. Verified locally: build green, /v1/health 200.
…ilter-run (review P0-1) pnpm --filter seepient-core build fails against the deliberately script-free core manifest (ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT) — the first tag would die in publish-npm before publishing anything. release.yml and the pack:verify workflow-invariant regex both move to `pnpm run build:core`; contracts/release-flow.md §3 amended.
…heuristic (review P1-1) registerExactEstimator stored a loader nothing ever invoked — the full package counted chars÷4 forever while claiming exact. Registration now resets the readiness memo and fire-and-forget loads the vendor; a failed load honestly pins heuristic. Gate (b) de-vacuumed (rejecting-loader registration drives the real path; the old vi.mock never executed). Exact-arming pins live in their own file — the seam gates must stay registration-free.
…ct for real (review P1-2) The README flagship died three ways (PRINCIPAL_REQUIRED exploited live on the packed tarball; providers as an array; the demolished inline-key credential grammar). Rewritten to the verified shape: record-form providers, the env/seepient/keychain/externalsecret/none credential grammar, tenancy+stateless explicit. docs-examples now (a) validates seepient-core imports against the core entry (knownCoreExports was dead code), (b) constructs the README options under a mock runtime with a string sync-pin against the published fence. stateless-workers: real option names (auditStore/policyStore/capabilityLedger + runtime in multi), platform truth (Node-capable functions; Workers impossible — node:net/dns/child_process), Model A dissonance resolved.
… wired into CI (review P1-3, P1-4) P1-3: seepient-core single+stateless construction defaults the permission plane to the in-memory store set (audit/policy/ledger) via an ambient- defaults seam the full package registers — Profile A parity kept, multi untouched. The provider-audit log degrades to a warning on unwritable dirs (symlink SECURITY_ERROR refusal preserved). Red gate: construction + turn on a READ-ONLY $HOME with zero writes under it; stateless-workers documents the default. P1-4: qs:core-chat (the only compiled-artifact behavioral gate) runs in CI and in release.yml before pack:verify; the QS-4 security-suite inventory baseline (711 names) is a CI step instead of an unwired comment.
…T019 self-test, migration.md, lazy image backends (027 review) src/vendors/anthropic.ts deleted (zero importers from any side; the banned-name-carrying orphan the tracer's own list flagged) — the tracer gains an orphan mode: emitted-but-unreachable files importing banned/undeclared non-builtin externals fail the gate (147 emitted, 47 deep-import orphans stay load-bearing for the full package, zero violations). GUARDED_DYNAMIC_OK emptied (the loader seam removed the tokenizer specifier); duplicate @InQuirer entry dropped. Size story measured once: post-split full-package clean-install closure is 301 MB (346 before, −45 from the dedupe, core riding in) — README, core README, stateless-workers, and the CHANGELOG now quote one pair of numbers. migration.md gains the v0.9.0 section (the three documented breaking items). publish.sh no longer swallows a build:core failure before notes generation. Image backends resolve lazily at use — a late registration is picked up and the typed denial can no longer pass its guard and still return undefined.
…ls, flake settling, dead-code cuts (027 review) The committed 287KB tarball leak removed + *.tgz gitignored + qs-core-chat cleans its pack in the exit handler; Docker core copy narrowed to manifest + dist. Core tarball ships LICENSE (declared BUSL-1.1, file was absent; files whitelist + pack:verify assert updated). ToolUnavailableError typed (replacing the plain Error with a parseable prefix); the empty 'Available:' dangle dropped. createChat.stream narrowed to the fields the streaming turn actually consumes (ChatStreamOptions). dev/dev:server chain build:core (fresh-clone ERR_MODULE_NOT_FOUND). B-3 fails loudly on a missing dist (fresh-clone false-green). r9-1 matrix cleanup settles before rmSync (floating store persist raced the removal — suite-level flake). examples/worker reaches test-seams via the package specifier. Vault map 027 entries; AGENTS.md claims corrected (18/21 + 3 ratified PARTIALs); T009/T019/T021 ticked with notes; T007's align-not-remove deviation synced into both contract texts; OQ-4 ratifications recorded in the task notes. Ponytail: tracer dead listJsFiles + duplicate @InQuirer entry, pack-verify dead firstCore + unused return, publish.sh duplicated BUMP, qs __overlayReady.
…utbox restored, storefront credential story real (027 post-remediation review) P1-1: 181b601's repair of 773fe50's excess-property type error deleted BOTH the invalid and the valid pass-through — askSeepient lost preferInMemoryDefaults entirely (core-only one-shots wrote ambient state on writable HOME and silently lost audit on read-only). Re-threaded into the buildActionLifecycle inputs; the readonly-home gate now covers the one-shot path (writable + read-only HOME) and clears SEEPIENT_SECURITY_DIR so the real ambient root is exercised — the setup redirect is exactly why the suite never saw this. P1-2: isLocalStore now tests the RESOLVED store — a defaulted LocalAuditStore gets the terminal outbox + crash recovery exactly like an injected one. Pinned by seeding the crash signature (dispatched without terminal successor) and asserting construction-time recovery marks it indeterminate — red under the narrowing, green under the fix. (The review's pending-flush sub-claim rides the factory fallback, which reloads+flushes at construction; the discriminating loss was crash recovery.) P1-3: the README quickstart taught env-kind credentials — refused categorically by the stores (CREDENTIAL_REQUIRED at turn one, reviewer reproduced byte-for-byte). Rewritten to the verified wiring: MemoryCredentialStore.put({kind:'api_key'}) + {kind:'seepient', id} ref; chat.ts's shipped JSDoc sibling fixed too. The docs gate constructs the README's exact options through the REAL credential path — put → resolve → lease secret — and pins the env refusal, instead of mocking the seam the example depends on.
…e, compiled-full exact pin, data-model sync (027 post-remediation review) P2-1: the provider-audit fail-soft now covers open/write EACCES/EROFS/EPERM (a pre-existing unwritable ~/.seepient dir crashed construction with AUDIT_WRITE_FAILED); symlink refusals stay fatal. P2-2 (OQ-B): the ambient provider trail is skipped entirely in the core persona — zero-ambient-write is now unconditional for seepient-core single+stateless; the full package keeps the trail. P2-3: the single-user tenancy notice describes the store set that will actually apply (in-memory for core, ambient for full). P2-5: scripts/qs-dist-exact-mode.mjs imports the BUILT root entry in plain node (no vitest alias) and asserts estimateMode exact — red-verified by disabling the arming; wired into CI after the headless-build check. P2-6: data-model §2.1 synced to the shipped manifest and the anthropic deletion + align-not-remove amendment.
…s, errata, boundary helper (027 post-remediation review) P3: built-in tool unknown-name denial is now ToolRegistrationError (typed); NOT_IMPLEMENTED provider-mutation message names the full seepient package; unconfigured_purpose message names providers + modelAssignments; d.mts matches the void assertNoDuplicatedEngine; stranded errors.ts doc comment removed; ask.ts stale 'armed above' comment corrected; tokenizer readiness guards against out-of-order settles across memo generations; migration.md no longer recommends deep paths the core README reserves; create-seepient.md demolished 'Local/env store' vocabulary replaced + serverless cross-discovery added (ask-seepient.md too); core README documents the process-global persona rule; qs flow (a) label honest; examples/worker stub-app test-seams import switched to the package specifier (the fix 9d69df6 falsely claimed). Ponytail: root pack:verify packs ONCE (contents, workspace: leakage, and B-3 read the same tarball); the boundary-resolution ternary collapsed from three copy-pasted sites into resolveTurnBoundary(); du MiB labeled as MiB. tasks.md: commit-message errata for 773fe50 (typecheck-red as committed, false parity claim) and 9d69df6 (false worker claim, attribution drift); OQ-A/B/C dispositions recorded (credential story, unconditional core zero-write, 102 MB accepted / SC-001 target re-baselined).
…error visibility (027 round-3 review) Round-3 six-lens review register (Reviews/2026-10-08-027-round-3-review.md), red-first — each new gate verified red on the unfixed tree, then green: - P1-1: recordProviderAuditEvent now returns on the IMPLICIT ~/.seepient/audit.log path when ambient defaults are unregistered (the f85c39f 'unconditional core zero-write' was a dead import; the providers record quickstart wrote the trail while the notice claimed otherwise). Explicit destinations (customAuditPath, SEEPIENT_AUDIT_LOG_PATH) stay caller-owned. Credentials notice keyed on the RESOLVED default-store outcome, not the package persona. Gate: writable-HOME providers-record zero-write. - P1-2: the providers option family (providers/modelAssignments/credentials/ overlayFile/adapter) is typed on AskSeepientOptions and forwarded through a shared bootstrap module (bootstrap-provider-runtime.ts, core emit) used by createSeepient/createChat/askSeepient — no rung drops what another teaches. Gate: one-shot with README-shape providers + mock adapter reaches the vendor (was unconfigured_purpose naming the ignored options); the no-tenancy upgrade refusal pinned unchanged. - P1-3: ask-seepient.md credential-resolution prose rewritten post-022-5 — the page taught the demolished env grammar below the correct sentence. - P2: textStream throws the turn error after draining queued deltas (delta-only consumers no longer see silent zero-output completion); test-setup + qs-dist-exact redirect SEEPIENT_AUDIT_LOG_PATH (the suite and the exact pin no longer append to the operator's real audit.log); CREDENTIAL_REQUIRED messages teach the store grammar (4 vendor sites); createChat entered the overview ladder + ask-seepient three-way comparison; stateless-workers zero-write/partial-injection sentences scoped; core README streaming example teaches the throw; readonly-home test 1 de-vacuumed (isolateAmbientPaths everywhere); dead test helpers removed. - Claims: cross-discovery landed for real (ask-seepient.md core coverage); the stale media-factory comment in ask.ts actually corrected this time; OQ-B erratum + round-2 record errata recorded in tasks.md; data-model §2.1 files/amendment contradiction closed. Gates: suite 2246 passed / 11 skipped (333 files), probes 11/11, pack:verify root 417 files + core 102 MiB / 53 packages, boundary 148 emitted / 47 accepted orphans, qs:core-chat all flows, qs:dist-exact exact.
…release invariants (027 round-3 OQs)
Both round-3 owner questions decided and implemented, red-first:
- OQ-4: the release.yml invariants no longer grep raw workflow text —
assertReleaseWorkflowInvariants now checks a parsed step list
(parseWorkflowSteps: comment-stripped run bodies, whitespace-normalized).
Two evasion fixtures red-verified against the old checker and now caught:
build:core satisfied ONLY by a comment inside the run block (the exact
P0-1 class), and a line-split core publish ordered AFTER the root publish
(the marker co-location class — the old single-line findIndex left
coreLine at -1 and stayed green with the order inverted). The T019
order-swap fixture now splices at line boundaries: the old mid-line slice
glued a dangling '- name:' prefix onto the publish-homebrew job key —
malformed YAML the grep never read but a parser correctly flagged.
qs:core-chat-before-pack:verify joins the asserted order.
- OQ-3: docs-vocabulary check 8 bans the demolished env grammar as CONCEPT
prose, not names — need-to-pass-keys sentences; env/keychain items in
credential-resolution sequences (heading-scoped, so general
settings-precedence lists are not false positives); non-negated
reads-keys-from-the-environment claims (truthful negations allowed); the
env credential MODE shape; the varName field. Red-verified against the
round-3 rot text verbatim (four patterns fire) and against a LIVE doc
fiction the name-based sweep could never see.
- Doc truth: types.md taught an AccountInput credential union
{mode: "env"|"keychain", varName} that does not exist — replaced with
the real paste/none/preserve contract (provider-manager-api.ts).
Gates: suite 2249 passed / 11 skipped (333 files, +3 gate tests), probes
11/11, pack:verify root 417 files + core 102 MiB, boundary 148 emitted /
47 accepted orphans, qs:core-chat all flows, qs:dist-exact exact, build
clean.
…cripts (027 release prep)
…derivation + self-check, claims truth, golden surface pin, onError hardening, in-memory default widen (v0.9.0 gate r1)
…cs truth, ambient-composition disclosure, resolved-store warning, async onError + setup-window guards, publish.sh fallback subtraction (v0.9.0 gate r2)
…y the streaming dispatch; claims riders for wire scope + refreshModels + ESM run note (v0.9.0 gate r3, owner-authorized)
hashangit
added a commit
that referenced
this pull request
Oct 17, 2026
v0.9.0: serverless chat core (seepient-core package split) — release-gated
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spec 027. Gate: PASS at seal
199cadf— receiptReviews/2026-10-09-v0.9.0-release-gate.md(4 rounds: FAIL r1 (3 P1) → FAIL r2 (1 P1) → FAIL r3 (repair-introduced P1, owner-authorized repair) → PASS r4; fleet fresh every round; task truth 0 FALSE; CI 8/8 green at the seal). Owner decisions recorded: repair #3 authorized; T020 token-scope = publish-and-find-out.