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
1 change: 0 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"@corbits/inference-settings",
"@corbits/jimmy-agent",
"@corbits/longevity-sim",
"@corbits/memory-hub",
"@corbits/migration-runner",
"@corbits/mocks",
"@corbits/notify",
Expand Down
1 change: 0 additions & 1 deletion apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"@corbits/mailbox": "github:corbitsdev/corbits-mailbox#bc964040fa42ffe4e5d5004f87edaab6486e5d60",
"@corbits/mcp-tools": "workspace:*",
"@corbits/memory": "github:corbitsdev/corbits-memory#9e6f213fa2c002b531d3f6af1aa0abd737b8afe3",
"@corbits/memory-hub": "workspace:*",
"@corbits/morning-brief-workflow": "workspace:*",
"@corbits/notify": "workspace:*",
"@corbits/pain-point-collateral-workflow": "workspace:*",
Expand Down
23 changes: 23 additions & 0 deletions apps/hub/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import {
createMailboxPersist,
mountMailbox,
} from "@corbits/mailbox";
import { createMemory, loadMemoryConfig } from "@corbits/memory";
import {
createHubMailboxAuthorizeSender,
hubMailboxResolveRefs,
Expand Down Expand Up @@ -1576,6 +1577,28 @@ export async function createHub(config: HubConfig) {
app.route(`${TENANT_PREFIX}/mailbox`, mailboxApp);
}

{
// Firm memory (CL-8186): `@corbits/memory` registers its own
// absolute `/api/tenants/:tenantId/memory/*` routes on whatever
// Hono app it's given (see its `routes/add.ts`, etc.) rather than
// composing under a mount prefix, so it gets its own `Hono<TenantEnv>`
// — matching `mailboxApp` above — routed at "/" rather than
// `TENANT_PREFIX`, which would double the tenant-route prefix.
// Guarded by the hub's own `requireGrant`: the same
// `chatGrantStore`/`grantConditionRegistry` every other tenant-scoped
// mount above uses. `loadMemoryConfig` reads
// `DATABASE_URL`/`EMBED_BASE_URL`/`EMBED_MODEL` (and friends) from
// env itself; nothing else in this file constructs the memory plane.
const memoryApp = new Hono<TenantEnv>();
createMemory({
app: memoryApp,
config: loadMemoryConfig(),
grantStore: chatGrantStore,
conditionRegistry: grantConditionRegistry,
});
app.route("/", memoryApp);
}

// The hub-side deploy seam the on-demand catalog-block route below
// drives — the SAME `prepareProvisionedDeployment` the native `POST
// /workflows/deployments` route drives. Inference sources ride as
Expand Down
28 changes: 14 additions & 14 deletions apps/hub/src/tool-grants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ function assetRow(id: string, direct: boolean): AssetWithOrigin {
} as AssetWithOrigin;
}

const MEMORY_DIR = new URL("../../../packages/memory-tools", import.meta.url)
.pathname;
const CAPABILITY_DIR = new URL(
"../../../packages/capability-tools",
import.meta.url,
).pathname;
const MANUS_DIR = new URL("../../../packages/manus-tools", import.meta.url)
.pathname;

Expand All @@ -71,17 +73,15 @@ async function grantsFor(
describe("createToolGrantsForPins", () => {
test("mints tool:<qualifiedId>/invoke for every tool the installed manifest declares", async () => {
const assetService = await fakeRegistryAssetService([
await packToolPackageTarball(MEMORY_DIR),
await packToolPackageTarball(CAPABILITY_DIR),
]);
const grants = await grantsFor(
assetService,
[assetRow("asset_1", true)],
[{ name: "@corbits/memory-tools", version: "^1" }],
[{ name: "@corbits/capability-tools", version: "^1" }],
);
expect(grants.map((grant) => grant.resource)).toEqual([
"tool:@corbits/memory-tools/memory:memory_search",
"tool:@corbits/memory-tools/memory:memory_add",
"tool:@corbits/memory-tools/memory:memory_list",
"tool:@corbits/capability-tools/cap:request_capability",
]);
expect(grants.every((grant) => grant.action === "invoke")).toBe(true);
});
Expand Down Expand Up @@ -109,20 +109,20 @@ describe("createToolGrantsForPins", () => {

test("unions grants across every pinned package and skips unknown pins", async () => {
const assetService = await fakeRegistryAssetService([
await packToolPackageTarball(MEMORY_DIR),
await packToolPackageTarball(CAPABILITY_DIR),
await packToolPackageTarball(MANUS_DIR),
]);
const grants = await grantsFor(
assetService,
[assetRow("asset_1", true)],
[
{ name: "@corbits/memory-tools", version: "^1" },
{ name: "@corbits/capability-tools", version: "^1" },
{ name: "@corbits/manus-tools", version: "*" },
{ name: "@corbits/unknown-tools", version: "^1" },
],
);
expect(
grants.some((grant) => grant.resource.includes("memory-tools")),
grants.some((grant) => grant.resource.includes("capability-tools")),
).toBe(true);
expect(grants.some((grant) => grant.resource.includes("manus-tools"))).toBe(
true,
Expand All @@ -134,12 +134,12 @@ describe("createToolGrantsForPins", () => {

test("resolves an inherited registry, not only a direct one", async () => {
const assetService = await fakeRegistryAssetService([
await packToolPackageTarball(MEMORY_DIR),
await packToolPackageTarball(CAPABILITY_DIR),
]);
const grants = await grantsFor(
assetService,
[assetRow("asset_root", false)],
[{ name: "@corbits/memory-tools", version: "^1" }],
[{ name: "@corbits/capability-tools", version: "^1" }],
);
expect(grants.length).toBeGreaterThan(0);
});
Expand All @@ -149,14 +149,14 @@ describe("createToolGrantsForPins", () => {
const grants = await grantsFor(
assetService,
[],
[{ name: "@corbits/memory-tools", version: "^1" }],
[{ name: "@corbits/capability-tools", version: "^1" }],
);
expect(grants).toEqual([]);
});

test("no pins yields no grants", async () => {
const assetService = await fakeRegistryAssetService([
await packToolPackageTarball(MEMORY_DIR),
await packToolPackageTarball(CAPABILITY_DIR),
]);
expect(
await grantsFor(assetService, [assetRow("asset_1", true)], []),
Expand Down
6 changes: 3 additions & 3 deletions apps/sidecar/src/workflow-substrate-factory/compactors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
// Both folds are deliberately lossy -- a bounded recap of what was
// active in the folded turns, not a durable record. Anything a channel
// agent needs to recall past the working window is firm memory's job
// (`@corbits/memory-tools`'s `memory_search`/`memory_add`, already
// pinned for the assistant workflow), not this recap's.
// (`@corbits/memory/tools`'s `memory_search`/`memory_add`, already
// wired for the assistant workflow), not this recap's.

import type {
Compactor,
Expand Down Expand Up @@ -270,7 +270,7 @@ export function createSummarizeOlderTurnsCompactor(
* deliberately lossy -- it is a bounded reminder of what was active in
* the folded turns, not a durable record. Anything a channel agent
* needs to recall past the working window belongs in firm memory
* (`@corbits/memory-tools`'s `memory_search`/`memory_add`), not in this
* (`@corbits/memory/tools`'s `memory_search`/`memory_add`), not in this
* recap.
*/
function foldOlderTurns(
Expand Down
13 changes: 7 additions & 6 deletions apps/sidecar/src/workflow-substrate-factory/step-env.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Proves the CL-6086 binding: `createSidecarStepBuildEnv` threads the
// deploying definition's own `definitionId` (and the derived
// `hubCapabilitiesUrl`) onto the built step env, following exactly the
// binding pattern `hubMemoryUrl`/`hubSkillsUrl`/`sidecarToken`/`address`
// already use — see this file's header comment for the memory-tools
// precedent this mirrors.
// binding pattern `hubSkillsUrl`/`sidecarToken`/`address` already use.
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
Expand Down Expand Up @@ -87,14 +85,17 @@ test("the built step env carries the deploying definition's own definitionId and
expect(
(env as unknown as { hubCapabilitiesUrl: string }).hubCapabilitiesUrl,
).toBe("https://hub.example.com");
// Same hub origin as the sibling artifacts/memory/skills bindings —
// one origin, one name per tool-bundle surface, never overloaded.
// Same hub origin as the sibling artifacts/skills bindings — one
// origin, one name per tool-bundle surface, never overloaded.
expect((env as unknown as { hubArtifactsUrl: string }).hubArtifactsUrl).toBe(
"https://hub.example.com",
);
expect((env as unknown as { hubMemoryUrl: string }).hubMemoryUrl).toBe(
expect((env as unknown as { memoryBaseUrl: string }).memoryBaseUrl).toBe(
"https://hub.example.com",
);
expect((env as unknown as { memoryTenantId: string }).memoryTenantId).toBe(
"ten_test",
);
expect((env as unknown as { address: string }).address).toBe(
"run_1@example.com",
);
Expand Down
45 changes: 32 additions & 13 deletions apps/sidecar/src/workflow-substrate-factory/step-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ export interface SidecarStepBuildEnvDeps {
* (`@corbits/artifacts-hub`'s `createWorkflowArtifactRoutes`, CL-6000)
* without ever holding a database handle. `address` is already on
* `env` via `mailboxAddress` below; these two widen the same surface.
* The same origin is re-exposed on the step env as `hubMemoryUrl` for
* `@corbits/memory-tools` (`requires: ["hubMemoryUrl", "sidecarToken",
* "address"]`), which calls the sibling workflow-memory HTTP surface
* (`@corbits/memory-hub`'s `createWorkflowMemoryRoutes`, CL-5852), and
* again as `hubSkillsUrl` for `@corbits/tools-skills`, which calls
* The same origin is re-exposed on the step env as `memoryBaseUrl` for
* `@corbits/memory/tools` (CL-8186), which calls the STOCK
* `/api/tenants/:tenantId/memory/*` routes `@corbits/memory` mounts on
* the hub directly (see `memoryFetch` below), and again as
* `hubSkillsUrl` for `@corbits/tools-skills`, which calls
* `@corbits/skills`' `createWorkflowSkillRoutes`.
*/
hubArtifactsUrl: string;
Expand Down Expand Up @@ -372,7 +372,13 @@ export function createSidecarStepBuildEnv(
transport: MessageTransport;
address: string;
hubArtifactsUrl: string;
hubMemoryUrl: string;
memoryBaseUrl: string;
memoryTenantId: string;
memoryAuthToken: string;
memoryFetch: (
input: Parameters<typeof fetch>[0],
init?: Parameters<typeof fetch>[1],
) => ReturnType<typeof fetch>;
hubSkillsUrl: string;
hubCapabilitiesUrl: string;
hubRoutinesUrl: string;
Expand Down Expand Up @@ -420,13 +426,26 @@ export function createSidecarStepBuildEnv(
transport,
address: deps.mailboxAddress,
hubArtifactsUrl: deps.hubArtifactsUrl,
// Same hub HTTP origin as `hubArtifactsUrl` above, under the key
// `@corbits/memory-tools` declares (`requires: ["hubMemoryUrl",
// "sidecarToken", "address"]`) — one hub origin, two accurately
// named env keys per tool-bundle surface, matching the artifact
// bundle's own precedent rather than overloading its name for an
// unrelated surface.
hubMemoryUrl: deps.hubArtifactsUrl,
// `@corbits/memory/tools` (CL-8186) calls the STOCK
// `/api/tenants/:tenantId/memory/*` routes `@corbits/memory` mounts
// on the hub, the same run-bearer scheme `@corbits/connections-tools`
// and friends use: sidecar token as the bearer, `tenantId` as the
// path segment. Its own HTTP client sends only an `Authorization`
// header, so `memoryFetch` (the client's documented fetch override,
// never part of `requires`) is what actually attaches the
// `x-workflow-run-address` header the hub's `withWorkflowRunTenantAuth`
// needs to resolve the bearer to this run's principal.
memoryBaseUrl: deps.hubArtifactsUrl,
memoryTenantId: deps.tenantId,
memoryAuthToken: deps.sidecarToken,
memoryFetch: (input, init) =>
fetch(input, {
...init,
headers: {
...init?.headers,
"x-workflow-run-address": deps.mailboxAddress,
},
}),
// And once more under the key `@corbits/tools-skills` declares
// (`requires: ["hubSkillsUrl", "sidecarToken", "address"]`) for the
// skill registry's own run-authenticated surface.
Expand Down
2 changes: 1 addition & 1 deletion apps/sidecar/test/deploy-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ test("a single-step deploy writes the self-anchored run's grants before spawning
const runId = `run_${"a1".repeat(16)}`;
const grant = {
id: "grant_1",
resource: "tool:@corbits/memory-tools:memory_search",
resource: "tool:@corbits/catalog-tools:models_list",
action: "invoke",
effect: "allow" as const,
origin: "system" as const,
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-directory/src/capability-inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type CapabilityModelEntry = { readonly canonicalName: string };
*/
export const BASELINE_AGENT_TOOL_PINS = [
"@corbits/mcp-tools",
"@corbits/memory-tools",
"@corbits/memory",
"@corbits/interaction-tools",
] as const;

Expand Down
14 changes: 7 additions & 7 deletions packages/agent-directory/test/workflow-create-routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const RUN_ADDRESS = `${RUN_ID}@example.com`;
const fakeCapabilityInventory: CapabilityInventoryProvider = {
resolve: () =>
Promise.resolve({
toolPackages: [{ name: "@corbits/memory-tools" }],
toolPackages: [{ name: "@corbits/memory" }],
skills: [{ name: "research" }],
models: [{ canonicalName: "anthropic/claude-sonnet" }],
}),
Expand All @@ -53,8 +53,8 @@ function fakeAssetService(overrides: Partial<AssetService> = {}): AssetService {
throw new Error("not used in these tests");
},
// `resolvePinnedVersion`'s tarball listing — every create test in
// this file pins (or gets baseline-pinned) `@corbits/memory-tools`.
listAssetBlobs: () => Promise.resolve(["corbits-memory-tools-1.4.0.tgz"]),
// this file pins (or gets baseline-pinned) `@corbits/memory`.
listAssetBlobs: () => Promise.resolve(["corbits-memory-1.4.0.tgz"]),
...overrides,
} as unknown as AssetService;
}
Expand Down Expand Up @@ -296,12 +296,12 @@ test("a toolPackagePins entry the tenant's inventory offers is pinned onto the c
name: "Research Buddy",
handle: "research-buddy",
systemPrompt: "You are a careful research assistant.",
toolPackagePins: ["@corbits/memory-tools"],
toolPackagePins: ["@corbits/memory"],
}),
});
expect(response.status).toBe(201);
const written = definitionFrom(writtenFiles);
expect(written).toContain("@corbits/memory-tools");
expect(written).toContain("@corbits/memory");
});

test("a create naming no pins gets the baseline set the inventory offers — a specialist is never toolless (CL-6206)", async () => {
Expand All @@ -325,10 +325,10 @@ test("a create naming no pins gets the baseline set the inventory offers — a s
});
expect(response.status).toBe(201);
const written = definitionFrom(writtenFiles);
// The fake inventory offers memory-tools (see buildApp); mcp-tools and
// The fake inventory offers memory (see buildApp); mcp-tools and
// interaction-tools are not offered, so only the resolvable baseline
// member is pinned — never a pin that would fail at launch.
expect(written).toContain("@corbits/memory-tools");
expect(written).toContain("@corbits/memory");
expect(written).not.toContain("@corbits/mcp-tools");
});

Expand Down
1 change: 0 additions & 1 deletion packages/evals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"@corbits/connections-tools": "workspace:*",
"@corbits/github-tools": "workspace:*",
"@corbits/mcp-tools": "workspace:*",
"@corbits/memory-tools": "workspace:*",
"@types/bun": "catalog:",
"typescript": "catalog:"
}
Expand Down
5 changes: 3 additions & 2 deletions packages/evals/src/scorers/tool-names.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expect, test } from "bun:test";

import { CREATE_AGENT_TOOL as REAL_CREATE_AGENT_TOOL } from "@corbits/agent-directory-tools";
import { MEMORY_ADD_TOOL as REAL_MEMORY_ADD_TOOL } from "@corbits/memory-tools";
import { LIST_CONNECTIONS_TOOL as REAL_LIST_CONNECTIONS_TOOL } from "@corbits/connections-tools";
import {
GITHUB_PULL_REQUEST_DIFF_TOOL as REAL_GITHUB_PULL_REQUEST_DIFF_TOOL,
Expand All @@ -19,7 +18,9 @@ import {

test("eval tool-name constants match the real manager-tools bundles", () => {
expect(CREATE_AGENT_TOOL).toBe(REAL_CREATE_AGENT_TOOL);
expect(MEMORY_ADD_TOOL).toBe(REAL_MEMORY_ADD_TOOL);
// `@corbits/memory` exports no name constant (CL-8186) — its
// `memoryAdd` tool factory names itself "memory_add" inline.
expect(MEMORY_ADD_TOOL).toBe("memory_add");
expect(LIST_CONNECTIONS_TOOL).toBe(REAL_LIST_CONNECTIONS_TOOL);
});

Expand Down
7 changes: 2 additions & 5 deletions packages/evals/src/scorers/world-scorers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ describe("agentHasTools", () => {
id: "def-1",
name: "AI Daily researcher",
displayName: null,
toolPackagePins: [
"@corbits/web-search-tools",
"@corbits/memory-tools",
],
toolPackagePins: ["@corbits/web-search-tools", "@corbits/memory"],
skills: [],
model: null,
},
Expand All @@ -52,7 +49,7 @@ describe("agentHasTools", () => {
id: "def-1",
name: "AI Daily researcher",
displayName: null,
toolPackagePins: ["@corbits/memory-tools"],
toolPackagePins: ["@corbits/memory"],
skills: [],
model: null,
},
Expand Down
Loading
Loading