mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 13:24:55 +01:00
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:
1 parent
63fbb714e6
commit
e6dcc6b095
72 files changed
+9775
-1021
No files matched your search
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user