Skip to content

Commit 1d4c928

Browse files
Merge branch 'main' into posthog-code/fix-zoom-resize-consistency
2 parents 37eed4a + 437c331 commit 1d4c928

114 files changed

Lines changed: 3741 additions & 2085 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/quill-code/SKILL.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
name: quill-code
3+
description: Edit the @posthog/quill design system locally and consume the change in this repo (posthog-code) before it is published to npm. Use when changing quill components/primitives/tokens, when a quill change must be tested inside the Code app, or when the user mentions quill, the design system, the .local-quill tarball, or the @posthog/quill pnpm override.
4+
---
5+
6+
# quill-code
7+
8+
`@posthog/quill` is **not** in this repo. It is a published catalog dependency whose
9+
source lives in the main PostHog monorepo at `../posthog/packages/quill`. To test an
10+
unpublished quill change inside this repo (posthog-code), you build quill, pack it to a
11+
tarball, and point a pnpm `overrides` entry at that tarball. This is a **temporary
12+
local-dev** state — revert before merging (see below).
13+
14+
## Quill layout (where to edit)
15+
16+
`../posthog/packages/quill` is the **workspace** (`@posthog/quill-workspace`). It
17+
contains sub-packages, each a layer of the design system:
18+
19+
- `packages/primitives/src` — base components (Card, Badge, Button, Progress, …)
20+
- `packages/components/src` — composed components (DataTable, DateTimePicker, Metric)
21+
- `packages/blocks/src`**product-level blocks** (e.g. `ExperimentCard`). Add the
22+
file here and export it from `packages/blocks/src/index.ts`.
23+
- `packages/quill/src` — the **aggregate** that re-exports all layers as `@posthog/quill`.
24+
25+
A new export in any sub-package flows to `@posthog/quill` automatically on build.
26+
27+
## The loop (every quill change)
28+
29+
1. Edit/add the component in the right sub-package (above) and export it from that
30+
package's `src/index.ts`.
31+
2. Re-sync into this repo manually — build → pack → point the override → reinstall:
32+
33+
```bash
34+
QUILL_DIR="${QUILL_DIR:-../posthog/packages/quill/packages/quill}"
35+
36+
# a. Build the WHOLE quill workspace (two levels up from the aggregate), so the
37+
# sub-packages rebuild BEFORE the aggregate bundles them.
38+
( cd "$QUILL_DIR/../.." && pnpm build )
39+
40+
# b. Pack into .local-quill/ under a UNIQUE filename. pnpm pins a tarball by
41+
# integrity, so a stable name caches stale across re-syncs — drop old local
42+
# tarballs first, then rename the packed file to a unique local name.
43+
rm -f .local-quill/posthog-quill-local-*.tgz
44+
( cd "$QUILL_DIR" && npm pack --pack-destination "$(git rev-parse --show-toplevel)/.local-quill" )
45+
mv .local-quill/posthog-quill-[0-9]*.tgz ".local-quill/posthog-quill-local-$(git rev-parse --short HEAD)-$$.tgz"
46+
47+
# c. Point the override at the new tarball, then reinstall.
48+
# Edit pnpm-workspace.yaml so overrides['@posthog/quill'] = file:./.local-quill/<new file>
49+
pnpm install
50+
```
51+
52+
> Building only the aggregate (`packages/quill/packages/quill`) re-bundles the
53+
> sub-packages' **stale** `dist/`, so edits to primitives/components/blocks are
54+
> silently dropped. Always build at the workspace root.
55+
3. Verify in the Code app (`pnpm dev`, or the `test-electron-app` skill). Repeat from 1.
56+
57+
After every quill edit you **must** re-run the sync — the app consumes the tarball, not
58+
the quill source, so unsynced edits are invisible here.
59+
60+
If quill lives elsewhere, set `QUILL_DIR=/abs/path/to/posthog/packages/quill/packages/quill`.
61+
62+
## Why a tarball, not `link:`
63+
64+
`link:` symlinks into the mono's `node_modules` and drags in its **React 18** types,
65+
colliding with this repo's **React 19** (dual-React → broken typecheck +
66+
invalid-hook-call at runtime). The tarball is copied into this repo's store and deduped
67+
against React 19. The filename is **content-hashed** because pnpm pins a tarball by
68+
integrity, so a stable filename gets cached stale across re-syncs.
69+
70+
## The override (what the script rewrites)
71+
72+
In `pnpm-workspace.yaml`, under `overrides:`:
73+
74+
```yaml
75+
'@posthog/quill': file:./.local-quill/posthog-quill-local-<hash>.tgz
76+
```
77+
78+
There is also a permanent pin you should leave alone:
79+
80+
```yaml
81+
'@posthog/quill>@base-ui/react': ^1.3.0 # quill ships a broken catalog: dep; do not remove
82+
```
83+
84+
## Reverting (before merge)
85+
86+
The override is local-dev only. Once the quill change is published to npm:
87+
88+
1. Bump the catalog version in `pnpm-workspace.yaml` (`'@posthog/quill': 0.3.0-beta.x`)
89+
to the published version.
90+
2. Restore the override line to point back at the catalog, or remove the `file:` override.
91+
3. `pnpm install`.
92+
93+
Do not commit a `file:./.local-quill/...` override or the `.local-quill/` tarballs.

apps/code/snapshots.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,26 @@ snapshots:
9696
hash: v1.k4693efd2.b76d39ed8142d4449cb5befcde538bb11a6bffcbed66e81bdc3aa8e2746b7e06.GSIfBsgE-3RYuqlLUpluZ87C6-KjBUaTj2eu_a5elZ8
9797
autoresearch-runtime-stats--with-context-usage--light:
9898
hash: v1.k4693efd2.18889b5eda942cd19b0f7be00a87925d632529ca4111fac180d6339f7d34d1ca.X3s9IXrx2-TdQaJEjf1td3hwvNzeIp3Xq3uQh9Gwt5A
99+
billing-usagemeter--exceeded--dark:
100+
hash: v1.k4693efd2.dc026c5cace4e44c4cc713b95654e278d7bf0439a4862e9ceea39a033974815d.m5Be4dAOwwmt-UlEnhbhrKiym1rlGUiGZ4oyUonUoVg
101+
billing-usagemeter--exceeded--light:
102+
hash: v1.k4693efd2.850bf1e451a18981d9e3be653837dd282c6be289ac9c0826ff6a981ac67e1e38.1GwpfJ_4dUlMO7TXp8AOM5YmJuItGGB50ihKkkeIFgg
103+
billing-usagemeter--free-tier--dark:
104+
hash: v1.k4693efd2.164d54cac22cfa05dbbf25fae1a62e77c496b1981dcaff6b8e1ab07fc802f5e0.Gh75uI1vHbGWAgKun41BYoX6LZBYX_FVI7Qu-rFlDV4
105+
billing-usagemeter--free-tier--light:
106+
hash: v1.k4693efd2.5e220fe66fac4c54b4cb9f5ac6ec1054e0b709374fee2c58fdba782f5b2888df.W5la88YA6uOSc1tg30raKaRy12plPGxXl5o7uowL2gc
107+
billing-usagemeter--subscribed-past-included--dark:
108+
hash: v1.k4693efd2.ac987b7184aaa4f8895d9b75c6b7e1177efcb9a21cca0668caf533ee0d5eaab3.2Mq2GLXV83ebM8LKhTQY8csv6BeskWv5yPu8NSAQ20Y
109+
billing-usagemeter--subscribed-past-included--light:
110+
hash: v1.k4693efd2.0c4a37beaceb37baab57e6da98321b236c20127731fd5eaeb61677de4580c204.fZ2eDGlkEijZjHQyatiHtP39EFYT6EO1Vn_f8Tz7b0E
111+
billing-usagemeter--subscribed-with-breakdown--dark:
112+
hash: v1.k4693efd2.f74f8f76e51ee14e427423e1757859e3f001b13e6a43d1e7310b5b0cbbb570b5.eMwK2csA86oBarnAya99W87MfP6Z6PMfdjwbCmnPrw4
113+
billing-usagemeter--subscribed-with-breakdown--light:
114+
hash: v1.k4693efd2.1335dba8bf16980c987882641619e114584343ec50bb69b3c01e63507a13756b._v7_6tsVMmW3WixSeFN-puHQVeIxf9AAGxFZ8Ni97YQ
115+
billing-usagemeter--zero-spend-limit--dark:
116+
hash: v1.k4693efd2.1d199b2c4bba8034cb18fb5b49866b64a6eb3311591add5f58ec288710dce2e9.FCc9egSaO1Onih417yciSYKNROe8zqDZyyS6xuXCZGw
117+
billing-usagemeter--zero-spend-limit--light:
118+
hash: v1.k4693efd2.b50c18736bbde45fc89f4c0ac7fd616286e5c1ecc2a56910fffca13eaa13d8a2.31z4F0JmvMeR4yqk5pjs_OQ3MypnM2miZI9al4wuc8c
99119
components-permissions-permissionselector--create-new-file--dark:
100120
hash: v1.k4693efd2.c54203a4e636b83b3d24d7ed9c4ace8659db87cd8f231d9dc2ecc03320e31646.epDm7LebiLzlp0uuZBrE-Obt_anAn0xsE8bHFnm5vos
101121
components-permissions-permissionselector--create-new-file--light:

apps/code/src/main/di/container.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { readFile as fsReadFile, stat as fsStat } from "node:fs/promises";
44
import { TypedContainer } from "@inversifyjs/strongly-typed";
55
import { DEFAULT_GATEWAY_MODEL } from "@posthog/agent/gateway-models";
66
import {
7-
getGatewayInvalidatePlanCacheUrl,
87
getGatewayUsageUrl,
98
getLlmGatewayUrl,
109
} from "@posthog/agent/posthog-api";
@@ -491,8 +490,6 @@ container.bind(LLM_GATEWAY_HOST).toDynamicValue((ctx) => {
491490
messagesUrl: (apiHost: string) =>
492491
`${getLlmGatewayUrl(apiHost)}/v1/messages`,
493492
usageUrl: (apiHost: string) => getGatewayUsageUrl(apiHost),
494-
invalidatePlanCacheUrl: (apiHost: string) =>
495-
getGatewayInvalidatePlanCacheUrl(apiHost),
496493
defaultModel: DEFAULT_GATEWAY_MODEL,
497494
};
498495
});

apps/code/src/renderer/desktop-contributions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { agentChatCoreModule } from "@posthog/core/agent-chat/agentChat.module";
22
import { autoresearchCoreModule } from "@posthog/core/autoresearch/autoresearch.module";
3-
import { billingCoreModule } from "@posthog/core/billing/billing.module";
3+
import { taskThreadCoreModule } from "@posthog/core/canvas/taskThread.module";
44
import { inboxCoreModule } from "@posthog/core/inbox/inbox.module";
55
import { githubConnectModule } from "@posthog/core/integrations/githubConnect.module";
66
import { localMcpCoreModule } from "@posthog/core/local-mcp/local-mcp.module";
@@ -36,7 +36,7 @@ export function registerDesktopContributions(): void {
3636
authUiModule,
3737
autoresearchCoreModule,
3838
billingUiModule,
39-
billingCoreModule,
39+
taskThreadCoreModule,
4040
browserTabsUiModule,
4141
cloneUiModule,
4242
connectivityUiModule,

apps/web/src/web-container.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "reflect-metadata";
22
import { TypedContainer } from "@inversifyjs/strongly-typed";
3+
import { taskThreadCoreModule } from "@posthog/core/canvas/taskThread.module";
34
import { setRootContainer } from "@posthog/di/container";
45
import { ROOT_LOGGER, type RootLogger } from "@posthog/di/logger";
56
import {
@@ -96,4 +97,6 @@ container.bind(MCP_SANDBOX_PROXY_URL).toConstantValue(() => {
9697
return sandboxProxyUrl;
9798
});
9899

100+
container.load(taskThreadCoreModule);
101+
99102
setRootContainer(container);

packages/agent/src/adapters/claude/session/rtk.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ import { gitSubcommand } from "../git-command";
1515

1616
// Commands RTK compresses faithfully and that have no side effects, so wrapping
1717
// them changes only how much output reaches the model, never what runs.
18-
const RTK_PLAIN_COMMANDS = new Set(["grep", "find", "ls"]);
18+
// Exported so the instruction-level Codex guidance advertises the same set.
19+
export const RTK_PLAIN_COMMANDS = new Set(["grep", "find", "ls"]);
1920

2021
// Git subcommands whose output is worth compressing and that RTK handles
2122
// faithfully. The criterion is compressible output, NOT read-only: RTK never
2223
// changes what runs, so a mutating form (`git tag -d`, `git remote add`,
2324
// `git reflog expire`) still executes its write — its output is just shorter.
2425
// Excludes commit/push: negligible output to compress, and the cloud
2526
// signed-commit guard keys on a leading `git` token that `rtk git …` would hide.
26-
const GIT_COMPRESSIBLE_SUBCOMMANDS = new Set([
27+
// Exported so the instruction-level Codex guidance advertises the same set.
28+
export const GIT_COMPRESSIBLE_SUBCOMMANDS = new Set([
2729
"status",
2830
"diff",
2931
"log",
@@ -44,7 +46,8 @@ const GIT_COMPRESSIBLE_SUBCOMMANDS = new Set([
4446
// wrapping only its head would change the meaning of the rest.
4547
const SHELL_OPERATORS = /[|&;<>`\n]|\$\(/;
4648

47-
function shQuote(value: string): string {
49+
// Exported so the instruction-level Codex guidance quotes the prefix the same way.
50+
export function shQuote(value: string): string {
4851
if (/^[\w./-]+$/.test(value)) return value;
4952
return `'${value.replace(/'/g, `'\\''`)}'`;
5053
}

packages/agent/src/adapters/codex-app-server/codex-app-server-agent.test.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,9 +1961,23 @@ describe("CodexAppServerAgent", () => {
19611961
} as unknown as PromptRequest);
19621962

19631963
// The single turn/completed resolves both the original and the folded prompt.
1964+
stub.emit("thread/tokenUsage/updated", {
1965+
tokenUsage: {
1966+
last: {
1967+
totalTokens: 45,
1968+
inputTokens: 30,
1969+
cachedInputTokens: 5,
1970+
outputTokens: 10,
1971+
},
1972+
},
1973+
});
19641974
stub.emit("turn/completed", { turn: { status: "completed" } });
1965-
expect((await first).stopReason).toBe("end_turn");
1966-
expect((await second).stopReason).toBe("end_turn");
1975+
const [firstResult, secondResult] = await Promise.all([first, second]);
1976+
expect(firstResult).toMatchObject({
1977+
stopReason: "end_turn",
1978+
usage: { totalTokens: 45 },
1979+
});
1980+
expect(secondResult).toEqual({ stopReason: "end_turn" });
19671981

19681982
const steer = stub.requests.find((r) => r.method === "turn/steer");
19691983
expect(steer?.params).toMatchObject({
@@ -2126,7 +2140,19 @@ describe("CodexAppServerAgent", () => {
21262140
},
21272141
});
21282142
stub.emit("turn/completed", { turn: { status: "completed" } });
2129-
await done;
2143+
const result = await done;
2144+
2145+
expect(result).toEqual({
2146+
stopReason: "end_turn",
2147+
usage: {
2148+
inputTokens: 60,
2149+
outputTokens: 30,
2150+
cachedReadTokens: 10,
2151+
cachedWriteTokens: 0,
2152+
thoughtTokens: 5,
2153+
totalTokens: 100,
2154+
},
2155+
});
21302156

21312157
const turnComplete = extNotifications.find(
21322158
(n) => n.method === "_posthog/turn_complete",
@@ -2818,6 +2844,16 @@ describe("CodexAppServerAgent", () => {
28182844
text: "The implementation plan is ready.",
28192845
},
28202846
});
2847+
stub.emit("thread/tokenUsage/updated", {
2848+
tokenUsage: {
2849+
last: {
2850+
totalTokens: 30,
2851+
inputTokens: 20,
2852+
outputTokens: 10,
2853+
reasoningOutputTokens: 2,
2854+
},
2855+
},
2856+
});
28212857
stub.emit("turn/completed", {
28222858
turn: { id: "turn_1", status: "completed" },
28232859
});
@@ -2826,10 +2862,31 @@ describe("CodexAppServerAgent", () => {
28262862
await waitUntil(
28272863
() => stub.requests.filter((r) => r.method === "turn/start").length >= 2,
28282864
);
2865+
stub.emit("thread/tokenUsage/updated", {
2866+
tokenUsage: {
2867+
last: {
2868+
totalTokens: 50,
2869+
inputTokens: 35,
2870+
cachedInputTokens: 5,
2871+
outputTokens: 10,
2872+
reasoningOutputTokens: 3,
2873+
},
2874+
},
2875+
});
28292876
stub.emit("turn/completed", {
28302877
turn: { id: "turn_2", status: "completed" },
28312878
});
2832-
expect((await done).stopReason).toBe("end_turn");
2879+
expect(await done).toEqual({
2880+
stopReason: "end_turn",
2881+
usage: {
2882+
inputTokens: 55,
2883+
outputTokens: 20,
2884+
cachedReadTokens: 5,
2885+
cachedWriteTokens: 0,
2886+
thoughtTokens: 5,
2887+
totalTokens: 80,
2888+
},
2889+
});
28332890

28342891
// The approval renders as the plan-approval UI (switch_mode + the plan text).
28352892
expect(permissionRequests).toHaveLength(1);

0 commit comments

Comments
 (0)