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
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/agent-address.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

// Deliberately permissive, unlike `@intx/types`'s `parseAgentAddress`: the
// mention rule only needs the substring before "@", not a validated id.
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/chat/wire/blocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md) — apps/web
// must not import @corbits/chat, a server-only package.
// Browser-side wire contract (see docs/chat-wire-contract.md) — apps/web
// must not import server-only packages.

import { type } from "arktype";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/consumer-inference-text.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

// HTTP status, raw provider dumps, and JSON error objects never belong on
// the timeline — DESIGN.md Honesty is one consumer sentence.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/display-name.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

// One derivation for "what does this agent look like to a person", never a
// scattered `description ?? name` reimplemented per call site.
Expand Down
47 changes: 47 additions & 0 deletions apps/web/src/chat/wire/id-leak-guard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, test } from "bun:test";
import { generateId } from "../../../../../vendor/intx/hub-common/src/ids";
import { assertNoLeakedInternalId } from "./id-leak-guard";

const KINDS: Parameters<typeof generateId>[0][] = [
"tenant",
"principal",
"principalKey",
"role",
"grant",
"federationTrust",
"provider",
"oauthClient",
"credential",
"wallet",
"transaction",
"offering",
"model",
"modelProvider",
"modelOffering",
"modelPricing",
"session",
"sessionMail",
"inferenceTurn",
"turnPart",
"asset",
"gitToken",
"workflowRun",
"approval",
"signal",
"workflowDefinition",
"workflowDefinitionVersion",
];

describe("assertNoLeakedInternalId", () => {
test.each(KINDS)("catches a generated %s id", (kind) => {
const id = generateId(kind);
expect(() => assertNoLeakedInternalId(id, "title")).toThrow("internal identifier");
expect(() => assertNoLeakedInternalId(`Open ${id} now`, "title")).toThrow(
"internal identifier",
);
});

test("passes ordinary text", () => {
expect(() => assertNoLeakedInternalId("Weekly digest", "title")).not.toThrow();
});
});
34 changes: 31 additions & 3 deletions apps/web/src/chat/wire/id-leak-guard.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md, which
// also covers this file's rationale).
// Mirrors PREFIXES in vendor/intx/hub-common/src/ids.ts (see
// docs/chat-wire-contract.md, which also covers this file's rationale).

// Matches `_`, space, or `-` as separator so both the raw id and
// `humanizeSlug`'s Title-Cased reading of one ("Run 737a058d…") trip it.
const ID_PREFIX_WORDS = ["run", "wfd", "tnt", "prn", "ast", "gtk"] as const;
const ID_PREFIX_WORDS = [
"tnt",
"prn",
"pky",
"rol",
"grt",
"ftr",
"prv",
"ocl",
"crd",
"wlt",
"txn",
"ofr",
"mdl",
"mpv",
"mof",
"mpr",
"ses",
"sml",
"itn",
"tp",
"ast",
"gtk",
"run",
"apr",
"sig",
"wfd",
"wdv",
] as const;

export const ID_LEAK_PATTERN = new RegExp(
`\\b(?:${ID_PREFIX_WORDS.join("|")})[_\\s-][0-9a-f]{16,}\\b`,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/mentions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

// The composer and the server fan-out both derive mentions from the same
// `ParticipantRecord[]`, so what the composer highlights is always exactly
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/participants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

// Reading tolerates a bare address string (pre-rollout data) and upgrades it
// to a record; writing always produces records, never bare strings.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/parts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

import { type } from "arktype";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/stream-events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md, which
// Browser-side wire contract (see docs/chat-wire-contract.md, which
// also covers this file's organizing rule).
import { type } from "arktype";
import { Part } from "./parts";
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/chat/wire/tools-unsupported.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mirrored from packages/chat/src (see docs/chat-wire-contract.md).
// Browser-side wire contract (see docs/chat-wire-contract.md).

export const TOOLS_UNSUPPORTED_CONSUMER_MESSAGE = "This agent's model can't use tools.";

Expand Down
17 changes: 7 additions & 10 deletions docs/chat-wire-contract.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# Chat wire contract

`apps/web/src/chat/wire/*` is the browser-facing half of the wire contract
also implemented server-side in `packages/chat/src`. `apps/web` cannot import
`@corbits/chat` — a server-only package — so this half is hand-mirrored
rather than shared by import. Once the hub moves onto native mail threads,
`packages/chat`'s copy goes away and this becomes the one source of truth.

Keep both sides in sync by hand until then; a schema or type added to one
side belongs on the other.
`apps/web/src/chat/wire/*` holds the browser-side wire shapes and guards for
chat. `apps/web` cannot import server-only `@intx/*` packages, so anything
it needs from them (for example the `@intx/hub-common` id prefixes) is
mirrored here by hand. There is no server-side copy in this repo to keep in
sync with.

## Internal-id leak guard

Expand All @@ -17,7 +14,7 @@ Title-Cased reading of one, e.g. "Run 737a058d…"). This recurred repeatedly
as one-off display-time patches (insights, a worker reply, a chat title)
before being consolidated here, so a new id-generating prefix is a missed
test run rather than a missed grep. Prefix words mirror `@intx/hub-common`'s
`generateId` (`PREFIXES` in `packages/hub-common/src/ids.ts`).
`generateId` (`PREFIXES` in `vendor/intx/hub-common/src/ids.ts`).

## Approve block (`chat/blocks/approve-block.tsx`)

Expand Down Expand Up @@ -50,7 +47,7 @@ already gave the connecting stream.

By the time a reply reaches the chat timeline it is a plain text part with
no metadata — the failure's category is structured further upstream
(`packages/chat`'s orchestrator reads it directly), but nothing carries it
(the server-side orchestrator reads it directly), but nothing carries it
down to this render layer. So this file matches reply prose against the
exact preambles `@intx/inference`'s `formatInferenceError` writes for
`credential_failure` and `quota_exhausted`, anchored at the start of the
Expand Down
Loading