Repository navigation
Expand file tree
/
Copy pathlinkOrigin.ts
More file actions
58 lines (51 loc) · 2.24 KB
/
Copy pathlinkOrigin.ts
File metadata and controls
58 lines (51 loc) · 2.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import {
linkOriginIsFenced,
OrchestratorMcpFailure,
type OrchestrationV2LinkOrigin,
type OrchestrationV2ThreadShell,
} from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import type { Caller } from "./threadAccess.ts";
/**
* Below full access, a link may mutate only work carrying its session and may not use our links.
* Reads remain unfenced. This confines T3 tools, not the operating-system user running the agent.
*/
/** The link a caller acts for: its own session, or the link that started its thread. */
export const callerLinkOrigin = (
caller: Pick<Caller, "scope" | "caller">,
): OrchestrationV2LinkOrigin | undefined =>
caller.caller?.linkOrigin ??
(caller.scope.client?.linked === true
? {
sessionId: caller.scope.client.sessionId,
label: caller.scope.client.label,
access: caller.scope.client.access,
}
: undefined);
/** The link that fences a caller, if any: one paired below full access. */
const fencingLinkOrigin = (caller: Pick<Caller, "scope" | "caller">) => {
const origin = callerLinkOrigin(caller);
return origin !== undefined && linkOriginIsFenced(origin) ? origin : undefined;
};
const outsideLink = (origin: OrchestrationV2LinkOrigin, message: string) =>
new OrchestratorMcpFailure({
code: "capability_denied",
message: `This work was started from ${origin.label} through a linked environment, so ${message} A link paired with full access is not limited this way.`,
});
/** A thread a fenced caller may change: one its own link started. */
export const assertSameLink = (
caller: Pick<Caller, "scope" | "caller">,
target: Pick<OrchestrationV2ThreadShell, "id" | "linkOrigin">,
) => {
const origin = fencingLinkOrigin(caller);
return origin === undefined || target.linkOrigin?.sessionId === origin.sessionId
? Effect.void
: Effect.fail(outsideLink(origin, `it may change only threads that link started.`));
};
/** Fenced work never changes what the environment already has: projects, settings, scheduled runs. */
export const assertNotLinked = (caller: Pick<Caller, "scope" | "caller">, what: string) => {
const origin = fencingLinkOrigin(caller);
return origin === undefined
? Effect.void
: Effect.fail(outsideLink(origin, `it cannot ${what}.`));
};