@claudexor/orchestrator 3.6.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-resolution.d.ts +3 -0
- package/dist/account-resolution.d.ts.map +1 -1
- package/dist/account-resolution.js +23 -0
- package/dist/account-resolution.js.map +1 -1
- package/dist/candidateEvidence.d.ts +3 -3
- package/dist/candidateEvidence.d.ts.map +1 -1
- package/dist/candidateOutputs.d.ts +2 -2
- package/dist/candidateOutputs.d.ts.map +1 -1
- package/dist/candidateOutputs.js +11 -6
- package/dist/candidateOutputs.js.map +1 -1
- package/dist/contract-gates.d.ts +4 -4
- package/dist/contract-gates.d.ts.map +1 -1
- package/dist/contract-gates.js +2 -0
- package/dist/contract-gates.js.map +1 -1
- package/dist/credential-profiles.d.ts +12 -1
- package/dist/credential-profiles.d.ts.map +1 -1
- package/dist/credential-profiles.js +13 -3
- package/dist/credential-profiles.js.map +1 -1
- package/dist/deepScanReducer.d.ts.map +1 -1
- package/dist/deepScanReducer.js +2 -1
- package/dist/deepScanReducer.js.map +1 -1
- package/dist/delegatedHome.d.ts +21 -69
- package/dist/delegatedHome.d.ts.map +1 -1
- package/dist/delegatedHome.js +29 -85
- package/dist/delegatedHome.js.map +1 -1
- package/dist/modelGovernance.d.ts +35 -4
- package/dist/modelGovernance.d.ts.map +1 -1
- package/dist/modelGovernance.js +71 -27
- package/dist/modelGovernance.js.map +1 -1
- package/dist/orchestrator-credentials.d.ts.map +1 -1
- package/dist/orchestrator-credentials.js +7 -1
- package/dist/orchestrator-credentials.js.map +1 -1
- package/dist/orchestrator.d.ts +5 -6
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +210 -222
- package/dist/orchestrator.js.map +1 -1
- package/dist/planRun.d.ts +5 -5
- package/dist/planRun.d.ts.map +1 -1
- package/dist/planRun.js.map +1 -1
- package/dist/plannerAttempt.d.ts +2 -2
- package/dist/plannerAttempt.d.ts.map +1 -1
- package/dist/plannerAttempt.js +2 -1
- package/dist/plannerAttempt.js.map +1 -1
- package/dist/requestRequirements.d.ts +3 -15
- package/dist/requestRequirements.d.ts.map +1 -1
- package/dist/requestRequirements.js +11 -21
- package/dist/requestRequirements.js.map +1 -1
- package/dist/routeContext.d.ts +4 -1
- package/dist/routeContext.d.ts.map +1 -1
- package/dist/routeContext.js +8 -3
- package/dist/routeContext.js.map +1 -1
- package/dist/runAdmission.d.ts +29 -0
- package/dist/runAdmission.d.ts.map +1 -0
- package/dist/runAdmission.js +63 -0
- package/dist/runAdmission.js.map +1 -0
- package/dist/task-contract-builder.d.ts +2 -2
- package/dist/task-contract-builder.d.ts.map +1 -1
- package/dist/task-contract-builder.js +5 -6
- package/dist/task-contract-builder.js.map +1 -1
- package/package.json +17 -17
package/dist/delegatedHome.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { AccessProfile, HarnessConfinement, WorkspaceEnvelope } from "@claudexor/schema";
|
|
1
|
+
import type { AccessProfile, RecordedAppliedAttemptFacts as HistoricalAppliedAttemptFacts, WorkspaceEnvelope } from "@claudexor/schema";
|
|
3
2
|
import type { WorkspaceManager } from "@claudexor/workspace";
|
|
4
3
|
/** What one attempt's harness HOME actually resolved to, and whether it is scoped. */
|
|
5
4
|
export interface ScopedHarnessHome {
|
|
@@ -9,78 +8,35 @@ export interface ScopedHarnessHome {
|
|
|
9
8
|
isolated: boolean;
|
|
10
9
|
/** The scoped home directory, or null when the attempt inherits the operator's. */
|
|
11
10
|
homeDir: string | null;
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Why this attempt got NO boundary, when it asked for one and the host had
|
|
16
|
-
* none. Null both when a boundary WAS applied and when none was ever asked
|
|
17
|
-
* for — an ordinary run is not "unconfined", it is out of scope, and the
|
|
18
|
-
* terminal gate below depends on being able to tell those apart.
|
|
19
|
-
*/
|
|
20
|
-
confinementUnavailableReason: string | null;
|
|
11
|
+
/** Why no additional outer boundary applies; null for ordinary/non-mutating runs. */
|
|
12
|
+
outerBoundaryUnavailableReason: string | null;
|
|
21
13
|
}
|
|
22
14
|
/** Access profiles under which the harness can modify the filesystem. */
|
|
23
15
|
export declare function isMutatingAccess(access: AccessProfile): boolean;
|
|
24
16
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* Only a delegated run asks for an engine-provided boundary, and only a host
|
|
28
|
-
* that can actually provide one earns the trade. Telling a harness to drop its
|
|
29
|
-
* native sandbox where Claudexor will NOT replace it is a pure loss — strictly
|
|
30
|
-
* worse than never having asked — so the two conditions are one expression
|
|
31
|
-
* rather than a flag the caller passes and a check nobody makes.
|
|
32
|
-
*/
|
|
33
|
-
export declare function externallyConfinedLane(delegated: boolean, host?: ConfinementHost): boolean;
|
|
34
|
-
/** Roots the confinement is drawn against; overridable so tests never touch the real home. */
|
|
35
|
-
export interface ConfinementRoots {
|
|
36
|
-
operatorHome: string;
|
|
37
|
-
runtimeRoot: string;
|
|
38
|
-
nativeStateRoot: string;
|
|
39
|
-
}
|
|
40
|
-
export declare function defaultConfinementRoots(): ConfinementRoots;
|
|
41
|
-
/**
|
|
42
|
-
* Decide the harness HOME and the OS boundary one attempt runs under.
|
|
17
|
+
* Decide the harness HOME one attempt runs under.
|
|
43
18
|
*
|
|
44
19
|
* Unchanged for ordinary runs: an ISOLATED envelope gets the envelope's scoped
|
|
45
20
|
* home, while an IN-PLACE attempt deliberately inherits the operator's native
|
|
46
21
|
* environment so a harness whose native session store hangs off `$HOME` can
|
|
47
22
|
* resume its vendor conversation.
|
|
48
23
|
*
|
|
49
|
-
* A DELEGATED run (`execution.delegated`) is scoped either way
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* all `~/.claudexor/v3/daemon/token`, which grants the entire control API.
|
|
55
|
-
*
|
|
56
|
-
* THE SCOPED HOME IS NOT THE BOUNDARY. It redirects `~`-relative lookups and
|
|
57
|
-
* nothing more; the token above is one absolute path away, and a live probe
|
|
58
|
-
* read it out of an ordinary `workspace_write` agent run. So a delegated
|
|
59
|
-
* MUTATING attempt additionally gets an OS-ENFORCED boundary
|
|
60
|
-
* (`applyConfinement`), verified against a path it denies before the harness is
|
|
61
|
-
* spawned. A delegated READ-ONLY attempt keeps the harness's own read-only
|
|
62
|
-
* enforcement instead: confinement stands that enforcement down (macOS refuses
|
|
63
|
-
* nested sandboxes), which would be a net loss where there is no shell to
|
|
64
|
-
* confine.
|
|
65
|
-
*
|
|
66
|
-
* WHERE THE HOST HAS NO BOUNDARY the attempt still runs, and says so: the
|
|
67
|
-
* reason travels onto the attempt record, into the child's prompt and into the
|
|
68
|
-
* caller's result. A delegated run that refused on every platform Claudexor has
|
|
69
|
-
* not implemented a mechanism for would be a macOS-only feature wearing a
|
|
70
|
-
* general name.
|
|
24
|
+
* A DELEGATED run (`execution.delegated`) is scoped either way. The scoped HOME
|
|
25
|
+
* redirects native state and profile selection; it is deliberately not called
|
|
26
|
+
* a filesystem boundary. Mutating delegated attempts disclose that Claudexor
|
|
27
|
+
* adds no outer OS boundary and leave the selected harness's native access mode
|
|
28
|
+
* in effect.
|
|
71
29
|
*
|
|
72
30
|
* THE COST of the scoped home, stated plainly: an in-place delegated attempt
|
|
73
31
|
* CANNOT resume a native vendor session whose store lives under the real `$HOME`
|
|
74
32
|
* — cursor (`~/.cursor`) and opencode (XDG data under `$HOME`) lose resume.
|
|
75
33
|
* codex and claude keep it: their native session stores are Claudexor-owned
|
|
76
|
-
* directories that the adapter re-points on top of this env and
|
|
77
|
-
* confinement carves back out, and the macOS login Keychain is reached through
|
|
34
|
+
* directories that the adapter re-points on top of this env, and the macOS login Keychain is reached through
|
|
78
35
|
* the declared scoped-home bridge (INV-067), so subscription auth is unaffected.
|
|
79
36
|
*
|
|
80
|
-
* Never degrades silently: a delegated attempt whose scoped home is not on disk
|
|
81
|
-
* or whose boundary cannot be applied and proven, refuses.
|
|
37
|
+
* Never degrades silently: a delegated attempt whose scoped home is not on disk refuses.
|
|
82
38
|
*/
|
|
83
|
-
export declare function scopedHarnessHome(wsm: WorkspaceManager, envelope: WorkspaceEnvelope, inPlaceEnvelope: boolean, delegated: boolean, access?: AccessProfile
|
|
39
|
+
export declare function scopedHarnessHome(wsm: WorkspaceManager, envelope: WorkspaceEnvelope, inPlaceEnvelope: boolean, delegated: boolean, access?: AccessProfile): ScopedHarnessHome;
|
|
84
40
|
/**
|
|
85
41
|
* What the CHILD is told, when it is running without an OS boundary.
|
|
86
42
|
*
|
|
@@ -90,19 +46,19 @@ export declare function scopedHarnessHome(wsm: WorkspaceManager, envelope: Works
|
|
|
90
46
|
* fact, so it is stated to the model in plain words rather than left in an
|
|
91
47
|
* artifact it never reads.
|
|
92
48
|
*/
|
|
93
|
-
export declare function
|
|
49
|
+
export declare function outerBoundaryNotice(home: ScopedHarnessHome | null | undefined): string | null;
|
|
94
50
|
/**
|
|
95
51
|
* What an attempt's harness process ACTUALLY ran under.
|
|
96
52
|
*
|
|
97
53
|
* One shape, written by the success path and the failure path alike. An attempt
|
|
98
54
|
* that spawned a harness and then died still ran a process, and the caller of a
|
|
99
55
|
* delegated run still needs to know what that process could reach — "it failed"
|
|
100
|
-
* is not an answer to "
|
|
56
|
+
* is not an answer to "what outer-boundary evidence applied".
|
|
101
57
|
*/
|
|
102
58
|
export interface AppliedAttemptFacts {
|
|
103
59
|
/**
|
|
104
60
|
* ABSENT when the attempt died before its home was decided (`wsm.create` or
|
|
105
|
-
*
|
|
61
|
+
* home selection failed). A literal `false` is a POSITIVE claim — this
|
|
106
62
|
* child ran in the operator's own HOME — and an attempt that never reached
|
|
107
63
|
* the decision made no such claim. Absence keeps it a gap in the record,
|
|
108
64
|
* which is what `appliedEvidenceComplete` already refuses on.
|
|
@@ -134,28 +90,24 @@ export interface AppliedAttemptFacts {
|
|
|
134
90
|
*/
|
|
135
91
|
export declare function appliedAttemptFacts(home: ScopedHarnessHome | null | undefined, access: AccessProfile, credentialProfileId: string | null): AppliedAttemptFacts;
|
|
136
92
|
/**
|
|
137
|
-
* Whether an attempt's record is auditable for a delegated
|
|
93
|
+
* Whether an attempt's record is auditable for a delegated mutating run.
|
|
138
94
|
*
|
|
139
95
|
* The distinction this predicate exists to draw: evidence that is MISSING is
|
|
140
96
|
* still a reason to refuse — an attempt that cannot say what it ran under is
|
|
141
97
|
* indistinguishable from one that ran unconfined behind a green terminal.
|
|
142
|
-
* Evidence that honestly says "no boundary
|
|
143
|
-
* the absence of a boundary is a fact about the host, not a gap in the record,
|
|
144
|
-
* and refusing on it would make a delegated mutating run impossible wherever
|
|
145
|
-
* Claudexor has not implemented a mechanism.
|
|
98
|
+
* Evidence that honestly says "no additional boundary, by design" is complete.
|
|
146
99
|
*
|
|
147
100
|
* A named mechanism is a claim, and only `verified_denied_path` discharges it —
|
|
148
101
|
* so a record carrying the name without the proof is NOT complete, and neither
|
|
149
102
|
* is one that claims a boundary and a reason for its absence at the same time.
|
|
150
103
|
*/
|
|
151
|
-
export declare function appliedEvidenceComplete(facts: AppliedAttemptFacts | null | undefined): boolean;
|
|
104
|
+
export declare function appliedEvidenceComplete(facts: AppliedAttemptFacts | HistoricalAppliedAttemptFacts | null | undefined): boolean;
|
|
152
105
|
/**
|
|
153
|
-
* The terminal gate, shared by every lane that assembles attempts.
|
|
106
|
+
* The terminal evidence gate, shared by every lane that assembles attempts.
|
|
154
107
|
*
|
|
155
108
|
* A delegated MUTATING run may only reach a terminal when EVERY attempt it ran
|
|
156
|
-
* can state what it ran under
|
|
157
|
-
*
|
|
158
|
-
* and both callers spend it.
|
|
109
|
+
* can state what it ran under, including the deliberate absence of an outer
|
|
110
|
+
* boundary, so the rule lives here and both callers spend it.
|
|
159
111
|
*/
|
|
160
112
|
export declare function assertDelegatedEvidence(delegated: boolean, access: AccessProfile, attempts: readonly {
|
|
161
113
|
attemptId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegatedHome.d.ts","sourceRoot":"","sources":["../src/delegatedHome.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delegatedHome.d.ts","sourceRoot":"","sources":["../src/delegatedHome.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,2BAA2B,IAAI,6BAA6B,EAC5D,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,sGAAsG;IACtG,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,EAAE,OAAO,CAAC;IAClB,mFAAmF;IACnF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qFAAqF;IACrF,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAE/D;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,EAAE,OAAO,EACxB,SAAS,EAAE,OAAO,EAClB,MAAM,GAAE,aAAiC,GACxC,iBAAiB,CAsBnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ7F;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,aAAa,CAAC;IAC9B,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,gCAAgC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,8BAA8B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,EAC1C,MAAM,EAAE,aAAa,EACrB,mBAAmB,EAAE,MAAM,GAAG,IAAI,GACjC,mBAAmB,CAWrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,mBAAmB,GAAG,6BAA6B,GAAG,IAAI,GAAG,SAAS,GAC5E,OAAO,CAMT;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,SAAS;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAE,EAAE,GACxE,IAAI,CAON"}
|
package/dist/delegatedHome.js
CHANGED
|
@@ -1,101 +1,51 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import { confinementBoundaryProven } from "@claudexor/schema";
|
|
4
|
-
import { claudexorOwnedRoot, nativeHarnessStateRoot, userHomeDir } from "@claudexor/util";
|
|
2
|
+
import { DelegatedEvidenceIncompleteError, DelegatedHomeUnavailableError } from "@claudexor/core";
|
|
3
|
+
import { confinementBoundaryProven, DELIBERATE_NO_OUTER_BOUNDARY_REASON } from "@claudexor/schema";
|
|
5
4
|
/** Access profiles under which the harness can modify the filesystem. */
|
|
6
5
|
export function isMutatingAccess(access) {
|
|
7
6
|
return access !== "readonly";
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Only a delegated run asks for an engine-provided boundary, and only a host
|
|
13
|
-
* that can actually provide one earns the trade. Telling a harness to drop its
|
|
14
|
-
* native sandbox where Claudexor will NOT replace it is a pure loss — strictly
|
|
15
|
-
* worse than never having asked — so the two conditions are one expression
|
|
16
|
-
* rather than a flag the caller passes and a check nobody makes.
|
|
17
|
-
*/
|
|
18
|
-
export function externallyConfinedLane(delegated, host) {
|
|
19
|
-
return delegated && confinementBoundaryAvailable(host).available;
|
|
20
|
-
}
|
|
21
|
-
export function defaultConfinementRoots() {
|
|
22
|
-
return {
|
|
23
|
-
operatorHome: userHomeDir(),
|
|
24
|
-
runtimeRoot: claudexorOwnedRoot(),
|
|
25
|
-
nativeStateRoot: nativeHarnessStateRoot(),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Decide the harness HOME and the OS boundary one attempt runs under.
|
|
9
|
+
* Decide the harness HOME one attempt runs under.
|
|
30
10
|
*
|
|
31
11
|
* Unchanged for ordinary runs: an ISOLATED envelope gets the envelope's scoped
|
|
32
12
|
* home, while an IN-PLACE attempt deliberately inherits the operator's native
|
|
33
13
|
* environment so a harness whose native session store hangs off `$HOME` can
|
|
34
14
|
* resume its vendor conversation.
|
|
35
15
|
*
|
|
36
|
-
* A DELEGATED run (`execution.delegated`) is scoped either way
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* all `~/.claudexor/v3/daemon/token`, which grants the entire control API.
|
|
42
|
-
*
|
|
43
|
-
* THE SCOPED HOME IS NOT THE BOUNDARY. It redirects `~`-relative lookups and
|
|
44
|
-
* nothing more; the token above is one absolute path away, and a live probe
|
|
45
|
-
* read it out of an ordinary `workspace_write` agent run. So a delegated
|
|
46
|
-
* MUTATING attempt additionally gets an OS-ENFORCED boundary
|
|
47
|
-
* (`applyConfinement`), verified against a path it denies before the harness is
|
|
48
|
-
* spawned. A delegated READ-ONLY attempt keeps the harness's own read-only
|
|
49
|
-
* enforcement instead: confinement stands that enforcement down (macOS refuses
|
|
50
|
-
* nested sandboxes), which would be a net loss where there is no shell to
|
|
51
|
-
* confine.
|
|
52
|
-
*
|
|
53
|
-
* WHERE THE HOST HAS NO BOUNDARY the attempt still runs, and says so: the
|
|
54
|
-
* reason travels onto the attempt record, into the child's prompt and into the
|
|
55
|
-
* caller's result. A delegated run that refused on every platform Claudexor has
|
|
56
|
-
* not implemented a mechanism for would be a macOS-only feature wearing a
|
|
57
|
-
* general name.
|
|
16
|
+
* A DELEGATED run (`execution.delegated`) is scoped either way. The scoped HOME
|
|
17
|
+
* redirects native state and profile selection; it is deliberately not called
|
|
18
|
+
* a filesystem boundary. Mutating delegated attempts disclose that Claudexor
|
|
19
|
+
* adds no outer OS boundary and leave the selected harness's native access mode
|
|
20
|
+
* in effect.
|
|
58
21
|
*
|
|
59
22
|
* THE COST of the scoped home, stated plainly: an in-place delegated attempt
|
|
60
23
|
* CANNOT resume a native vendor session whose store lives under the real `$HOME`
|
|
61
24
|
* — cursor (`~/.cursor`) and opencode (XDG data under `$HOME`) lose resume.
|
|
62
25
|
* codex and claude keep it: their native session stores are Claudexor-owned
|
|
63
|
-
* directories that the adapter re-points on top of this env and
|
|
64
|
-
* confinement carves back out, and the macOS login Keychain is reached through
|
|
26
|
+
* directories that the adapter re-points on top of this env, and the macOS login Keychain is reached through
|
|
65
27
|
* the declared scoped-home bridge (INV-067), so subscription auth is unaffected.
|
|
66
28
|
*
|
|
67
|
-
* Never degrades silently: a delegated attempt whose scoped home is not on disk
|
|
68
|
-
* or whose boundary cannot be applied and proven, refuses.
|
|
29
|
+
* Never degrades silently: a delegated attempt whose scoped home is not on disk refuses.
|
|
69
30
|
*/
|
|
70
|
-
export function scopedHarnessHome(wsm, envelope, inPlaceEnvelope, delegated, access = "workspace_write"
|
|
31
|
+
export function scopedHarnessHome(wsm, envelope, inPlaceEnvelope, delegated, access = "workspace_write") {
|
|
71
32
|
if (inPlaceEnvelope && !delegated) {
|
|
72
33
|
return {
|
|
73
34
|
isolated: false,
|
|
74
35
|
homeDir: null,
|
|
75
|
-
|
|
76
|
-
confinementUnavailableReason: null,
|
|
36
|
+
outerBoundaryUnavailableReason: null,
|
|
77
37
|
};
|
|
78
38
|
}
|
|
79
39
|
const env = wsm.envFor(envelope);
|
|
80
40
|
const homeDir = env["HOME"];
|
|
81
41
|
if (delegated && (!homeDir || !existsSync(homeDir))) {
|
|
82
|
-
throw new DelegatedHomeUnavailableError(`delegated run cannot
|
|
42
|
+
throw new DelegatedHomeUnavailableError(`delegated run cannot start: the scoped harness home for attempt ${envelope.attempt_id} is missing (${homeDir || "unset"})`);
|
|
83
43
|
}
|
|
84
|
-
const outcome = delegated && isMutatingAccess(access)
|
|
85
|
-
? applyConfinement({
|
|
86
|
-
operatorHome: roots.operatorHome,
|
|
87
|
-
runtimeRoot: roots.runtimeRoot,
|
|
88
|
-
nativeStateRoot: roots.nativeStateRoot,
|
|
89
|
-
scopedHome: homeDir,
|
|
90
|
-
worktree: envelope.worktree_path,
|
|
91
|
-
}, host)
|
|
92
|
-
: { confinement: null, unavailableReason: null };
|
|
93
44
|
return {
|
|
94
45
|
env,
|
|
95
46
|
isolated: true,
|
|
96
47
|
homeDir: homeDir ?? null,
|
|
97
|
-
|
|
98
|
-
confinementUnavailableReason: outcome.unavailableReason,
|
|
48
|
+
outerBoundaryUnavailableReason: delegated && isMutatingAccess(access) ? DELIBERATE_NO_OUTER_BOUNDARY_REASON : null,
|
|
99
49
|
};
|
|
100
50
|
}
|
|
101
51
|
/**
|
|
@@ -107,16 +57,14 @@ export function scopedHarnessHome(wsm, envelope, inPlaceEnvelope, delegated, acc
|
|
|
107
57
|
* fact, so it is stated to the model in plain words rather than left in an
|
|
108
58
|
* artifact it never reads.
|
|
109
59
|
*/
|
|
110
|
-
export function
|
|
111
|
-
const reason = home?.
|
|
60
|
+
export function outerBoundaryNotice(home) {
|
|
61
|
+
const reason = home?.outerBoundaryUnavailableReason;
|
|
112
62
|
if (!reason)
|
|
113
63
|
return null;
|
|
114
64
|
return [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
"can reach. Confine yourself to the worktree you were given and the HOME you were given,",
|
|
119
|
-
"and do not read the operator's credential stores or Claudexor's own runtime directory.",
|
|
65
|
+
`Engine disclosure: ${reason}`,
|
|
66
|
+
"The scoped HOME selects native state and credentials; it is not containment. Stay within",
|
|
67
|
+
"the execution workspace and do not access unrelated operator or Claudexor state.",
|
|
120
68
|
].join("\n");
|
|
121
69
|
}
|
|
122
70
|
/**
|
|
@@ -132,22 +80,19 @@ export function appliedAttemptFacts(home, access, credentialProfileId) {
|
|
|
132
80
|
harness_home_dir: home?.homeDir ?? null,
|
|
133
81
|
access_applied: access,
|
|
134
82
|
credential_profile_applied: credentialProfileId,
|
|
135
|
-
confinement_mechanism:
|
|
136
|
-
confinement_profile_digest:
|
|
137
|
-
confinement_verified_denied_path:
|
|
138
|
-
confinement_unavailable_reason: home?.
|
|
83
|
+
confinement_mechanism: null,
|
|
84
|
+
confinement_profile_digest: null,
|
|
85
|
+
confinement_verified_denied_path: null,
|
|
86
|
+
confinement_unavailable_reason: home?.outerBoundaryUnavailableReason ?? null,
|
|
139
87
|
};
|
|
140
88
|
}
|
|
141
89
|
/**
|
|
142
|
-
* Whether an attempt's record is auditable for a delegated
|
|
90
|
+
* Whether an attempt's record is auditable for a delegated mutating run.
|
|
143
91
|
*
|
|
144
92
|
* The distinction this predicate exists to draw: evidence that is MISSING is
|
|
145
93
|
* still a reason to refuse — an attempt that cannot say what it ran under is
|
|
146
94
|
* indistinguishable from one that ran unconfined behind a green terminal.
|
|
147
|
-
* Evidence that honestly says "no boundary
|
|
148
|
-
* the absence of a boundary is a fact about the host, not a gap in the record,
|
|
149
|
-
* and refusing on it would make a delegated mutating run impossible wherever
|
|
150
|
-
* Claudexor has not implemented a mechanism.
|
|
95
|
+
* Evidence that honestly says "no additional boundary, by design" is complete.
|
|
151
96
|
*
|
|
152
97
|
* A named mechanism is a claim, and only `verified_denied_path` discharges it —
|
|
153
98
|
* so a record carrying the name without the proof is NOT complete, and neither
|
|
@@ -162,12 +107,11 @@ export function appliedEvidenceComplete(facts) {
|
|
|
162
107
|
return Boolean(facts.confinement_unavailable_reason);
|
|
163
108
|
}
|
|
164
109
|
/**
|
|
165
|
-
* The terminal gate, shared by every lane that assembles attempts.
|
|
110
|
+
* The terminal evidence gate, shared by every lane that assembles attempts.
|
|
166
111
|
*
|
|
167
112
|
* A delegated MUTATING run may only reach a terminal when EVERY attempt it ran
|
|
168
|
-
* can state what it ran under
|
|
169
|
-
*
|
|
170
|
-
* and both callers spend it.
|
|
113
|
+
* can state what it ran under, including the deliberate absence of an outer
|
|
114
|
+
* boundary, so the rule lives here and both callers spend it.
|
|
171
115
|
*/
|
|
172
116
|
export function assertDelegatedEvidence(delegated, access, attempts) {
|
|
173
117
|
if (!delegated || !isMutatingAccess(access))
|
|
@@ -175,6 +119,6 @@ export function assertDelegatedEvidence(delegated, access, attempts) {
|
|
|
175
119
|
const unauditable = attempts.filter((attempt) => !appliedEvidenceComplete(attempt.applied));
|
|
176
120
|
if (unauditable.length === 0)
|
|
177
121
|
return;
|
|
178
|
-
throw new DelegatedEvidenceIncompleteError(`delegated mutating run cannot terminalize: ${unauditable.length} attempt(s) state neither a proven
|
|
122
|
+
throw new DelegatedEvidenceIncompleteError(`delegated mutating run cannot terminalize: ${unauditable.length} attempt(s) state neither a historical proven boundary nor the deliberate no-outer-boundary reason (${unauditable.map((attempt) => attempt.attemptId).join(", ")})`);
|
|
179
123
|
}
|
|
180
124
|
//# sourceMappingURL=delegatedHome.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delegatedHome.js","sourceRoot":"","sources":["../src/delegatedHome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"delegatedHome.js","sourceRoot":"","sources":["../src/delegatedHome.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,11 +5,24 @@
|
|
|
5
5
|
* adapter has one, else the manifest `known_models` list. A violation throws
|
|
6
6
|
* a typed error BEFORE any vendor CLI spawns; the orchestrator surfaces it
|
|
7
7
|
* through the routing-failure path, so failure.yaml names harness, model, and
|
|
8
|
-
* truth source.
|
|
9
|
-
*
|
|
8
|
+
* truth source.
|
|
9
|
+
*
|
|
10
|
+
* Two rules keep the gate honest about WHICH account answered:
|
|
11
|
+
* - The inventory is asked of the account the run will actually use. A
|
|
12
|
+
* profile-pinned route derives its auth route from that profile's credential
|
|
13
|
+
* kind, and the query carries the profile's own state HOME, so model truth
|
|
14
|
+
* can never come from the default credential store.
|
|
15
|
+
* - Nothing about the spawned spec is rewritten to make the two agree. A
|
|
16
|
+
* profile-less `auto` run is enumerated with the same `auto` preference the
|
|
17
|
+
* adapter will resolve, so preflight and spawn share one resolution instead
|
|
18
|
+
* of the gate freezing a route the adapter would then be unable to disclose.
|
|
19
|
+
*
|
|
20
|
+
* A fallback model is checked by the authoritative per-spawn gate only after
|
|
21
|
+
* its own quota/profile preflight; checking it against the primary profile
|
|
22
|
+
* here would reject a valid cross-profile fallback.
|
|
10
23
|
*/
|
|
11
24
|
import type { HarnessAdapter } from "@claudexor/core";
|
|
12
|
-
import { type KnownModelEntry } from "@claudexor/schema";
|
|
25
|
+
import { type CredentialProfile, type HarnessEvent, type HarnessRunSpec, type KnownModelEntry } from "@claudexor/schema";
|
|
13
26
|
export interface ModelGovernedRoute {
|
|
14
27
|
adapter: HarnessAdapter;
|
|
15
28
|
/** Manifest model truth source (used when the adapter has no live models()). */
|
|
@@ -18,10 +31,28 @@ export interface ModelGovernedRoute {
|
|
|
18
31
|
* filtered by it, and stay EXCLUDED when it is null (fail-closed — a
|
|
19
32
|
* route-scoped model never passes the gate on an undecidable route). */
|
|
20
33
|
authRouteEstimate: "local_session" | "api_key" | null;
|
|
34
|
+
/** Effective account after profile preflight/rotation. Model truth must come
|
|
35
|
+
* from this same identity, never the default credential store. */
|
|
36
|
+
quotaAdmission: {
|
|
37
|
+
profile: CredentialProfile | null;
|
|
38
|
+
};
|
|
21
39
|
settings: {
|
|
22
40
|
defaultModel: string | null;
|
|
23
41
|
fallbackModel: string | null;
|
|
24
42
|
} | null;
|
|
25
43
|
}
|
|
26
|
-
export declare function assertRouteModelsAllowed(routes: readonly ModelGovernedRoute[], models: Record<string, string> | undefined, cwd: string
|
|
44
|
+
export declare function assertRouteModelsAllowed(routes: readonly ModelGovernedRoute[], models: Record<string, string> | undefined, cwd: string,
|
|
45
|
+
/** The mutable state HOME the eventual spawn will receive for this harness,
|
|
46
|
+
* so an account-scoped inventory is read from that same home. */
|
|
47
|
+
routeStateEnvFor?: (harnessId: string) => Record<string, string> | undefined): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Authoritative per-spawn model gate. Admission validates the initially routed
|
|
50
|
+
* account early; this guard rebinds the same strict truth contract to the
|
|
51
|
+
* profile, auth preference, cwd, and mutable state HOME the vendor process will
|
|
52
|
+
* actually receive. Every routed spawn funnels through this generator, so a
|
|
53
|
+
* quota rotation or fallback-model preflight cannot reuse another account's
|
|
54
|
+
* inventory. The spec is passed to the adapter byte-identical: the gate reads
|
|
55
|
+
* the run's identity, it never rewrites it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function runModelGovernedRoute(routed: ModelGovernedRoute, spec: HarnessRunSpec): AsyncIterable<HarnessEvent>;
|
|
27
58
|
//# sourceMappingURL=modelGovernance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelGovernance.d.ts","sourceRoot":"","sources":["../src/modelGovernance.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"modelGovernance.d.ts","sourceRoot":"","sources":["../src/modelGovernance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,gFAAgF;IAChF,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC;;4EAEwE;IACxE,iBAAiB,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC;IACtD;sEACkE;IAClE,cAAc,EAAE;QAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAA;KAAE,CAAC;IACtD,QAAQ,EAAE;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAChF;AA8DD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC1C,GAAG,EAAE,MAAM;AACX;iEACiE;AACjE,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAC3E,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;;;;;;;GAQG;AACH,wBAAuB,qBAAqB,CAC1C,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,cAAc,GACnB,aAAa,CAAC,YAAY,CAAC,CAa7B"}
|
package/dist/modelGovernance.js
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { HarnessUnavailableError, validateModel } from "@claudexor/core";
|
|
2
|
-
import { knownModelIdsForRoute } from "@claudexor/schema";
|
|
3
|
-
|
|
2
|
+
import { knownModelIdsForRoute, } from "@claudexor/schema";
|
|
3
|
+
/** A pinned profile decides the route by its credential kind; without one the
|
|
4
|
+
* pre-spawn estimate stands (and stays fail-closed when undecidable). */
|
|
5
|
+
function authRouteForProfile(profile, estimate) {
|
|
6
|
+
if (!profile)
|
|
7
|
+
return estimate;
|
|
8
|
+
return profile.credential_kind === "api_key" ? "api_key" : "local_session";
|
|
9
|
+
}
|
|
10
|
+
async function modelTruthForRoute(routed, query) {
|
|
11
|
+
const route = authRouteForProfile(query.profile, routed.authRouteEstimate);
|
|
12
|
+
if (typeof routed.adapter.models === "function") {
|
|
13
|
+
const inventory = await routed.adapter.models({
|
|
14
|
+
cwd: query.cwd,
|
|
15
|
+
...(query.env ? { env: query.env } : {}),
|
|
16
|
+
...(query.authPreference ? { authPreference: query.authPreference } : {}),
|
|
17
|
+
...(query.profile ? { credentialProfile: query.profile } : {}),
|
|
18
|
+
});
|
|
19
|
+
return { list: inventory.map((model) => model.id), source: "api", route };
|
|
20
|
+
}
|
|
21
|
+
return { list: knownModelIdsForRoute(routed.knownModels, route), source: "manifest", route };
|
|
22
|
+
}
|
|
23
|
+
function assertModelsAllowed(routed, candidates, truth, profile) {
|
|
24
|
+
for (const { role, model } of candidates) {
|
|
25
|
+
const check = validateModel(model, truth.list, truth.source);
|
|
26
|
+
if (check.status === "ok")
|
|
27
|
+
continue;
|
|
28
|
+
// A pinned profile OWNS this inventory: sending the operator to the
|
|
29
|
+
// profile-less `claudexor models` would print a different account's list.
|
|
30
|
+
const remedy = profile
|
|
31
|
+
? `the selected credential profile '${profile.profile_id}' supplied this inventory; verify or re-authenticate that profile, then inspect its live vendor model list`
|
|
32
|
+
: `run \`claudexor models --harness ${routed.adapter.id}\``;
|
|
33
|
+
throw new HarnessUnavailableError(`harness '${routed.adapter.id}' refused ${role} '${model}' (truth source: ${truth.source}${truth.source === "manifest" ? `, route: ${truth.route ?? "undecided"}` : ""}): ${check.message}; ` +
|
|
34
|
+
remedy);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function assertRouteModelsAllowed(routes, models, cwd,
|
|
38
|
+
/** The mutable state HOME the eventual spawn will receive for this harness,
|
|
39
|
+
* so an account-scoped inventory is read from that same home. */
|
|
40
|
+
routeStateEnvFor) {
|
|
4
41
|
const checked = new Set();
|
|
5
42
|
for (const routed of routes) {
|
|
6
43
|
const id = routed.adapter.id;
|
|
@@ -8,32 +45,39 @@ export async function assertRouteModelsAllowed(routes, models, cwd) {
|
|
|
8
45
|
continue;
|
|
9
46
|
checked.add(id);
|
|
10
47
|
const resolved = models?.[id] ?? routed.settings?.defaultModel ?? null;
|
|
11
|
-
|
|
12
|
-
{ role: "model", model: resolved },
|
|
13
|
-
{ role: "fallback_model", model: routed.settings?.fallbackModel ?? null },
|
|
14
|
-
].filter((c) => Boolean(c.model));
|
|
15
|
-
if (candidates.length === 0)
|
|
48
|
+
if (!resolved)
|
|
16
49
|
continue;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
const profile = routed.quotaAdmission.profile;
|
|
51
|
+
const routeStateEnv = routeStateEnvFor?.(id);
|
|
52
|
+
const truth = await modelTruthForRoute(routed, {
|
|
53
|
+
cwd,
|
|
54
|
+
...(routeStateEnv ? { env: routeStateEnv } : {}),
|
|
55
|
+
profile,
|
|
56
|
+
});
|
|
57
|
+
assertModelsAllowed(routed, [{ role: "model", model: resolved }], truth, profile);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Authoritative per-spawn model gate. Admission validates the initially routed
|
|
62
|
+
* account early; this guard rebinds the same strict truth contract to the
|
|
63
|
+
* profile, auth preference, cwd, and mutable state HOME the vendor process will
|
|
64
|
+
* actually receive. Every routed spawn funnels through this generator, so a
|
|
65
|
+
* quota rotation or fallback-model preflight cannot reuse another account's
|
|
66
|
+
* inventory. The spec is passed to the adapter byte-identical: the gate reads
|
|
67
|
+
* the run's identity, it never rewrites it.
|
|
68
|
+
*/
|
|
69
|
+
export async function* runModelGovernedRoute(routed, spec) {
|
|
70
|
+
const model = spec.model_hint?.trim();
|
|
71
|
+
if (model) {
|
|
72
|
+
const profile = spec.credential_profile ?? null;
|
|
73
|
+
const truth = await modelTruthForRoute(routed, {
|
|
74
|
+
cwd: spec.cwd,
|
|
75
|
+
env: spec.env,
|
|
76
|
+
authPreference: spec.auth_preference,
|
|
77
|
+
profile,
|
|
78
|
+
});
|
|
79
|
+
assertModelsAllowed(routed, [{ role: "model", model }], truth, profile);
|
|
37
80
|
}
|
|
81
|
+
yield* routed.adapter.run(spec);
|
|
38
82
|
}
|
|
39
83
|
//# sourceMappingURL=modelGovernance.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelGovernance.js","sourceRoot":"","sources":["../src/modelGovernance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"modelGovernance.js","sourceRoot":"","sources":["../src/modelGovernance.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator-credentials.d.ts","sourceRoot":"","sources":["../src/orchestrator-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,6BAA6B,EAC7B,cAAc,EACd,YAAY,EACZ,aAAa,EACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator-credentials.d.ts","sourceRoot":"","sources":["../src/orchestrator-credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { credentialProfilePolicyProblem, credentialProfilePolicyState } from "@claudexor/core";
|
|
1
2
|
import { PoolRouteFlags, resolveAccountForRun } from "./account-resolution.js";
|
|
2
3
|
import { currentSubjectProber, readyProfilesForRotation } from "./credential-differential.js";
|
|
3
4
|
import { resolveCredentialProfile, } from "./credential-profiles.js";
|
|
@@ -133,10 +134,15 @@ export class OrchestratorCredentials {
|
|
|
133
134
|
/** Thin wrapper over the ONE account-resolution owner (account-resolution.ts). */
|
|
134
135
|
async preflightProfile(input, harnessId, model, log, defaultRoute) {
|
|
135
136
|
const adapter = this.host.registry().get(harnessId);
|
|
137
|
+
const registry = this.host.config(input.repoRoot)?.global.credential_profiles ?? [];
|
|
138
|
+
const profileCardinality = credentialProfilePolicyState({ adapter, registry });
|
|
139
|
+
if (profileCardinality.ambiguous)
|
|
140
|
+
throw credentialProfilePolicyProblem(profileCardinality, "credential_profile_ambiguous");
|
|
136
141
|
return resolveAccountForRun({
|
|
137
142
|
harnessId,
|
|
138
|
-
registry
|
|
143
|
+
registry,
|
|
139
144
|
policy: this.profilePolicy(input.repoRoot, harnessId),
|
|
145
|
+
profileCardinality,
|
|
140
146
|
snapshots: this.host.quotaSnapshots(),
|
|
141
147
|
quota: this.vendorQuotaObservations(),
|
|
142
148
|
unusable: this.host.credentialUnusable(),
|