Skip to content
Merged
Show file tree
Hide file tree
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
8 changes: 6 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,15 @@ export default [
// OpenCode client-config writing against a throwaway XDG dir —
// including the not-installed branch, which no live run reaches.
"test/continuous-test-suite-proxy.ts",
// Background task system with no public surface at all.
// ResearchWorker's branch, state-file and accept/revert behaviour
// is only reproducible by calling its tools with scripted inputs
// against a fixture repo; a live model cannot be made to emit
// them. Only those groups need the exception; the TaskManager
// group drives nl.tasks on a dist NeuroLink in an isolated child.
"test/continuous-test-suite-autoresearch.ts",
// HandlerRegistry<THandler> is internal composition plumbing
// never exported from any package entry point — no public
// surface at all (same reasoning as autoresearch above).
// surface at all.
"test/continuous-test-suite-handler-registry.ts",
// TelemetryService (src/lib/telemetry/telemetryService.ts) is not
// re-exported from any package entry point, and whether a resolved
Expand Down
17 changes: 17 additions & 0 deletions test/continuous-test-suite-acceptance-gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,23 @@ async function main(): Promise<void> {
ceilingServer.requestCount() >= ceiling + 1,
`cell8 budget ceiling: the dedicated server did not observe the second call's request`,
);
// The throw above proves only that something failed; a
// network error or a provider validation error would pass
// it. The server's own rejection log proves the ceiling was
// what refused the call.
const rejections = ceilingServer.budgetRejections();
assert(
rejections.length >= 1,
`cell8 budget ceiling: the failure was not the server's ceiling rejection`,
);
assert(
rejections.some(
(r) =>
r.path.endsWith("/chat/completions") ||
r.path.endsWith("/messages"),
),
`cell8 budget ceiling: no generation request was rejected, only discovery routes`,
);
});
} finally {
await ceilingServer.close();
Expand Down
6 changes: 6 additions & 0 deletions test/continuous-test-suite-adjust-body-after-400.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ import "dotenv/config";
*
* No external API keys — points NVIDIA_NIM_BASE_URL at a local test server.
*
* Requires `pnpm run build` first (imports ../dist/index.js).
*
* Run: npx tsx test/continuous-test-suite-adjust-body-after-400.ts
* pnpm run test:adjust-body-after-400
*/

import { createServer, type IncomingMessage } from "node:http";
import { defineSuite, assert } from "./helpers/harness.js";
import { assertDistFresh } from "./helpers/distFreshness.js";

// Fail loudly rather than silently testing a stale build (see distFreshness.ts).
assertDistFresh();

const { test, runSuite, section } = defineSuite(
"adjustBodyAfter400 composition fix",
Expand Down
61 changes: 42 additions & 19 deletions test/continuous-test-suite-anthropic-loop-characterization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import "dotenv/config";

import { createServer, type Server, type ServerResponse } from "node:http";
import { z } from "zod";
import { trace } from "@opentelemetry/api";
import type { ReadableSpan } from "@opentelemetry/sdk-trace-base";
import {
InMemorySpanExporter,
Expand Down Expand Up @@ -562,21 +563,34 @@ await test("a native loop turn records the provider attempt count on the active
const restore = withAnthropicEnv(server.port);
const counter = { calls: 0 };
spanExporter.reset();
// The turn runs under a span of the caller's own, so the assertions below
// can tell this turn's `neurolink.stream` span from any other span that
// happens to share the name.
let callerSpanId = "";
try {
const nl = new NeuroLink();
const result = await nl.stream({
input: { text: "look something up" },
provider: "anthropic",
disableInternalFallback: true,
model: MODEL,
maxTokens: 32,
maxSteps: 3,
disableTools: false,
tools: customTool(counter),
});
for await (const chunk of result.stream) {
void chunk;
}
await trace
.getTracer("anthropic-loop-characterization")
.startActiveSpan("caller-turn", async (callerSpan) => {
callerSpanId = callerSpan.spanContext().spanId;
try {
const result = await nl.stream({
input: { text: "look something up" },
provider: "anthropic",
disableInternalFallback: true,
model: MODEL,
maxTokens: 32,
maxSteps: 3,
disableTools: false,
tools: customTool(counter),
});
for await (const chunk of result.stream) {
void chunk;
}
} finally {
callerSpan.end();
}
});
} finally {
restore();
await server.close();
Expand Down Expand Up @@ -604,15 +618,20 @@ await test("a native loop turn records the provider attempt count on the active
// to the loop. Pinning the name is what stops this passing on an attribute
// some unrelated provider path wrote on a different span.
//
// It cannot be pinned by wrapping this call in the test's own
// `startActiveSpan` and asserting on that wrapper: the SDK opens
// `neurolink.stream` beneath it, so the attribute lands on the child and the
// wrapper stays bare. Verified by printing the carrying span's name rather
// than assumed.
// The call is wrapped in the test's own `startActiveSpan`, but the attribute
// does not land on that wrapper: the SDK opens `neurolink.stream` beneath it
// and the loop is handed that child, so the wrapper stays bare. Parentage is
// therefore what ties the carrying span to THIS turn: it must be the one
// `neurolink.stream` span whose parent is the caller's span, not merely any
// span of that name.
assert(
carrying.every((span: ReadableSpan) => span.name === "neurolink.stream"),
carrying.length === 1 && carrying[0].name === "neurolink.stream",
"the attempt count was recorded on some span other than the turn's own",
);
assert(
carrying[0].parentSpanContext?.spanId === callerSpanId,
"the span carrying the attempt count is not this caller's turn span",
);
// Every step of a clean turn succeeds first try, so each recorded count is 1.
// Asserting the VALUE and not merely the key's presence keeps a broken
// attempt counter from passing.
Expand Down Expand Up @@ -1119,6 +1138,10 @@ await test("a turn that outlives turnTimeoutMs is reported as a time limit, not
finishReason === "other",
`a timed-out turn must report finishReason "other", reported ${String(finishReason)}`,
);
assert(
finishes.length === 1 && finishes[0] === "other",
"the provider span did not record exactly one finish reason of other for the timed-out turn",
);
});

await test("a turn the model ended itself still reports a plain stop", async () => {
Expand Down
Loading
Loading