Skip to content

v0.9.0: serverless chat core (seepient-core package split) — release-gated - #22

Merged
hashangit merged 32 commits into
mainfrom
027-serverless-chat-core
Oct 12, 2026
Merged

hashangit merged 32 commits into
mainfrom
027-serverless-chat-core

Conversation

@hashangit

Copy link
Copy Markdown
Owner

Spec 027. Gate: PASS at seal 199cadf — receipt Reviews/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.

…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.
…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.
…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.
…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)
Copilot AI balanced review requested due to automatic review settings October 12, 2026 07:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hashangit
hashangit merged commit 7c4b204 into main Oct 12, 2026
24 of 25 checks passed
@hashangit
hashangit deleted the 027-serverless-chat-core branch October 12, 2026 09:40
hashangit added a commit that referenced this pull request Oct 17, 2026
v0.9.0: serverless chat core (seepient-core package split) — release-gated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants