Include merged webhook URL warnings in the Leaf staging build

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* origin/master:
  chore(db): keep only the newest 5 drizzle snapshots and stop shipping them (#13687)
  Warn when routine webhook URLs may not be publicly reachable (#13684)
This commit is contained in:
DottaandPaperclip committed 2026-09-19 12:52:12 -05:00
commit 4da456feec
151 files changed
+174 -3679064

No files matched your search

+1
View File
@@ -190,6 +190,7 @@ When authoring migrations or one-time backfills:
- Create every migration with `pnpm --filter @paperclipai/db generate`. Do not hand-write a snapshot.
- Do not hand-edit a snapshot to resolve a merge conflict. Renumber your migration and run `generate` again, as `packages/db/.gitattributes` describes.
- The repo keeps only the newest 5 snapshots. `generate` runs `prune:snapshots` afterwards to delete older ones. Drizzle only reads the newest snapshot, and each snapshot is a full copy of the schema (over 1 MB each). Older snapshots are still in git history.
- `packages/db/src/migration-snapshot-drift.test.ts` is the enforcement backstop. It repeats the diff that `generate` performs and fails when the newest snapshot no longer matches `packages/db/src/schema/`.
## Cloud runtime identity singleton
+2
View File
@@ -428,3 +428,5 @@ Independently addressable examples live under `Connections/In-task connections`
## Shared setup wizard (2026-09-19)
`ui/src/components/SetupWizard.tsx` extracts the Slack setup navigation into reusable numbered steps, a portal sidebar, an optional section-menu takeover outlet, and a single-row footer. `SetupWizardSidebarProvider` owns the portal target and takeover lifecycle. Chat exports retain their existing names and defaults for compatibility. The Design Guide demonstrates the components. The production routine trigger wizard and its Storybook previews share the sidebar takeover, navigation, and footer. `routine-triggers/TriggerWizard.tsx`, `TriggerCard.tsx`, and `WebhookFields.tsx` provide the shared trigger setup, compact editable cards, copyable credentials, and agent instructions.
`routine-triggers/WebhookUrlWarning.tsx` uses `InlineBanner` for non-blocking localhost, private-network, Tailscale, and HTTP guidance. Setup and saved webhook editors share it; the Design Guide shows each warning. URL classification is heuristic, not a public reachability test.
+2
View File
@@ -298,3 +298,5 @@ Finish setup with `PATCH /api/routine-triggers/{id}` and `{ "setupPending": fals
For compatibility, API-created triggers without `setupPending: true` are immediately live. Completed triggers cannot be returned to setup mode. Checking a previously enabled webhook observes real deliveries and can start the routine; the management UI explains this difference.
Trigger cards support removal with Undo. `PATCH` with `{ "archived": true }` excludes a trigger from routine detail and scheduling, and rejects its webhook deliveries. Setting `archived` back to `false` restores the same URL and credentials. `DELETE` remains the permanent deletion API.
The webhook wizard and saved trigger editor warn about localhost, private-network addresses, Tailscale hostnames, and HTTP URLs without blocking setup. HTTPS does not imply public access: Tailscale Serve is private, while Funnel can expose the same hostname publicly. These warnings inspect the URL only; they do not resolve DNS or test internet reachability. Use [the HTTPS setup guide](https://docs.paperclip.ing/reference/deploy/https/) to configure public access when the sender is outside your network.
+3 -2
View File
@@ -39,10 +39,11 @@
],
"scripts": {
"check:migrations": "tsx src/check-migration-numbering.ts && tsx src/check-migration-safety.ts",
"build": "pnpm run check:migrations && tsc && cp -r src/migrations dist/migrations",
"build": "pnpm run check:migrations && tsc && rm -rf dist/migrations && mkdir -p dist/migrations/meta && cp src/migrations/*.sql dist/migrations/ && cp src/migrations/meta/_journal.json dist/migrations/meta/",
"clean": "rm -rf dist",
"typecheck": "pnpm run check:migrations && tsc --noEmit",
"generate": "pnpm run check:migrations && pnpm run clean && tsc -p tsconfig.json && drizzle-kit generate",
"generate": "pnpm run check:migrations && pnpm run clean && tsc -p tsconfig.json && drizzle-kit generate && pnpm run prune:snapshots",
"prune:snapshots": "ls src/migrations/meta/*_snapshot.json | sort -r | tail -n +6 | xargs rm -f",
"migrate": "pnpm run check:migrations && tsx src/migrate.ts",
"seed": "tsx src/seed.ts"
},
@@ -134,7 +134,7 @@ describe("chat and execution identity migration reconciliation", () => {
}
});
it("keeps canonical identity history in every regenerated chat checkpoint", async () => {
it("keeps the identity and chat migrations in their reconciled journal order", async () => {
const journal = JSON.parse(
await readFile(
new URL("./migrations/meta/_journal.json", import.meta.url),
@@ -155,108 +155,6 @@ describe("chat and execution identity migration reconciliation", () => {
)
.map((entry: { tag: string }) => entry.tag),
).toEqual(chatMigrations.map(([tag]) => tag));
let previous = JSON.parse(
await readFile(
new URL("./migrations/meta/0254_snapshot.json", import.meta.url),
"utf8",
),
);
for (let step = 0; step < chatMigrations.length; step++) {
const index = String(255 + step).padStart(4, "0");
const current = JSON.parse(
await readFile(
new URL(`./migrations/meta/${index}_snapshot.json`, import.meta.url),
"utf8",
),
);
expect(current.prevId, index).toBe(previous.id);
expect(current.tables["public.run_identity_contexts"], index).toEqual(
previous.tables["public.run_identity_contexts"],
);
expect(
current.tables["public.heartbeat_runs"].columns
.active_identity_context_id,
index,
).toBeDefined();
expect(
current.tables["public.issues"].columns.origin_identity_context_id,
index,
).toBeDefined();
expect(
current.tables["public.issues"].columns
.continuation_identity_context_id,
index,
).toBeDefined();
expect(
current.tables["public.issue_thread_interactions"].columns
.source_identity_context_id,
index,
).toBeDefined();
expect(
Boolean(
current.tables["public.chat_conversations"].columns
.session_generation,
),
index,
).toBe(step >= 1);
expect(
Boolean(
current.tables["public.chat_endpoints"].indexes
.chat_endpoints_live_bot_external_uq,
),
index,
).toBe(step >= 2);
expect(
current.tables[
"public.chat_publications"
].checkConstraints.chat_publications_state_check.value.includes(
"delivery_unknown",
),
index,
).toBe(step >= 3);
expect(
current.tables["public.chat_endpoints"].columns.allow_group_chats
.default,
index,
).toBe(step < 4);
expect(
current.tables[
"public.agent_wakeup_requests"
].indexes.agent_wakeup_requests_question_response_delivery_idempotency_uq.where.includes(
"interaction:%",
),
index,
).toBe(step >= 5);
expect(
current.tables[
"public.chat_endpoints"
].checkConstraints.chat_endpoints_provider_check.value.includes(
"discord",
),
index,
).toBe(step >= 6);
expect(
Boolean(
current.tables["public.chat_endpoints"].indexes
.chat_endpoints_live_discord_bot_external_uq,
),
index,
).toBe(step >= 7);
expect(
Boolean(
current.tables["public.chat_endpoints"].indexes
.chat_endpoints_live_global_app_bot_external_uq,
),
index,
).toBe(step >= 8);
expect(
Boolean(
current.tables["public.issue_attachments"].columns.originating_run_id,
),
index,
).toBe(step >= 9);
previous = current;
}
});
});
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff. Show more