Connect personal Muse through secure native execution and Vault OAuth (#16448)

Add company-scoped Muse native control, OAuth, wake receiver and recovery.

Recheck current authority after waits and before effects or receipt replay. Preserve task-read scope, assignment expiry, independent holds, nonblocking reassignment and truthful cleanup provenance.

Retain source trust on progress comments and verify current Muse V8 authority through real signed-JWT HTTP requests. Preserve the upstream trimmed ordinary authentication lookup.

Refs #16448.

Preserve captured canonical queue recovery and concurrent environment preparation while retaining the scoped Muse V8 exclusion. Normal migration 0354 and existing Muse SQL remain unchanged. Fresh current-head Greptile review is 5/5 with the project-write finding addressed. Required CI and independent technical review gate the merge; maintainer override is reserved for formal approval rules.

Recheck current Muse project-write authority after all receipt/task/run/admission waits, before creation or replay. Real PostgreSQL regressions reproduce four late effects/replays on the old route; the repaired three-file treatment passes 354 cases with no skips, and the scoped server typecheck passes. Ordinary/Dot behavior is preserved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-10-17 22:44:36 -05:00
1 parent 63fbb714e6
commit e6dcc6b095
72 files changed
+9775 -1021

No files matched your search

+25 -1
View File
@@ -1,6 +1,6 @@
import { createHash, timingSafeEqual } from "node:crypto";
import type { Request, RequestHandler } from "express";
import { and, eq, isNull } from "drizzle-orm";
import { and, eq, isNull, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import {
activityLog,
@@ -429,6 +429,29 @@ export function actorMiddleware(db: Db, opts: ActorMiddlewareOptions): RequestHa
identityRun.activeIdentityContextId = captured.context?.id ?? null;
identityRun.responsibleUserId = captured.context?.responsibleUserId ?? null;
}
if (claims.authorizing_user_id) {
// This signed projection exists only for internal native tools. Recheck
// the admitted run and current immutable binding on every API call.
try {
const { parseNativeExecutionInput } = await import("../vendor/paperclip-runner/index.js");
const { museRunnerBroker } = await import("../services/muse-runner-broker.js");
const [nativeRun] = await db.select({ status: heartbeatRuns.status, runtimeMode: heartbeatRuns.runtimeMode,
nativeExecutionInput: sql<unknown>`${heartbeatRuns.runnerProfileJson}->'nativeExecutionInput'`,
}).from(heartbeatRuns).where(and(
eq(heartbeatRuns.id, claims.run_id), eq(heartbeatRuns.companyId, claims.company_id), eq(heartbeatRuns.agentId, claims.sub),
));
const execution = parseNativeExecutionInput(nativeRun?.nativeExecutionInput);
if (nativeRun?.status !== "running" || nativeRun.runtimeMode !== "native" || execution.schema !== "paperclip.native-execution-input.v8"
|| execution.binding.runId !== claims.run_id || execution.binding.agentId !== claims.sub || execution.binding.companyId !== claims.company_id
|| await museRunnerBroker(db).authorizingUserIdForBinding(execution.provider.binding) !== claims.authorizing_user_id) {
throw new Error("Muse internal tool authority mismatch");
}
await museRunnerBroker(db).assertRunAuthority(execution);
} catch {
_res.status(403).json({ error: "Muse native tool authority is unavailable" });
return;
}
}
const onBehalfOfUserId = identityRun?.activeIdentityContextId
? identityRun.responsibleUserId
: claims.responsible_user_id !== undefined
@@ -451,6 +474,7 @@ export function actorMiddleware(db: Db, opts: ActorMiddlewareOptions): RequestHa
keyScope: normalizeAgentApiKeyScope(claims.key_scope),
runId: claims.run_id,
onBehalfOfUserId,
...(claims.authorizing_user_id ? { authorizingUserId: claims.authorizing_user_id } : {}),
identityContextId: identityRun?.activeIdentityContextId ?? null,
onBehalfOfMemberships,
source: "agent_jwt",