@nullsquare/agent-authority 0.4.3 → 0.4.4
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/README.md +10 -4
- package/ROADMAP.md +10 -6
- package/docs/npm-release.md +8 -3
- package/docs/transport-invariance.md +117 -0
- package/package.json +1 -1
- package/src/execution.js +34 -3
- package/src/mcp-gateway.js +35 -12
- package/src/mcp-remote.js +5 -1
- package/src/mcp-server.js +3 -0
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
**Agent Authority turns a human-approved task into temporary execution authority, then keeps that authority bounded as the agent discovers resources, crosses tools, and performs side effects.**
|
|
10
10
|
|
|
11
|
-
[Task Leases](docs/task-leases.md) · [
|
|
11
|
+
[Task Leases](docs/task-leases.md) · [Executable evidence](docs/evidence.md) · [Extractor conformance](docs/authority-extractor-conformance.md) · [Google proof](docs/live-google-validation.md) · [Integration contract](docs/integration-contract.md) · [CLI](docs/cli.md) · [Architecture](docs/architecture.md) · [Roadmap](ROADMAP.md) · [Contributing](CONTRIBUTING.md)
|
|
12
12
|
|
|
13
|
-
> **Status: public pre-alpha / v0.4.
|
|
13
|
+
> **Status: public pre-alpha / v0.4.3 Developer Preview.** Published on npm as `@nullsquare/agent-authority`. The repository has a working policy runtime, protocol-neutral guard, Task Lease prototype, execution-bound derived facts, reviewed Google and GitHub authority extractors, two-provider conformance tests, approvals, revocation, idempotency, credential isolation, MCP v2 gateway, live GitHub proofs, CI and CodeQL. It is not production-ready yet.
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -158,7 +158,9 @@ The demo performs this flow without provider credentials:
|
|
|
158
158
|
|
|
159
159
|
The side-effect callbacks for blocked actions never run.
|
|
160
160
|
|
|
161
|
-
The repository also includes a real Gmail → Calendar validation path and a reusable Google provider adapter. The
|
|
161
|
+
The repository also includes a real Gmail → Calendar validation path and a reusable Google provider adapter. The strict path binds the derived sender to the exact guarded output before it becomes authority. See [Live Gmail → Calendar validation](docs/live-google-validation.md) and [Executable Evidence](docs/evidence.md).
|
|
162
|
+
|
|
163
|
+
v0.4.3 applies the **same primitive to GitHub**: a root-bound repository + fixture marker are used by the reviewed GitHub adapter to select one issue from a real `issue.list` response; `deriveFromEvidence()` establishes that exact issue number as downstream authority; one real comment mutation succeeds; unrelated and post-completion issue mutations never reach the provider. Google and GitHub are now exercised by the same [authority extractor conformance contract](docs/authority-extractor-conformance.md).
|
|
162
164
|
|
|
163
165
|
## Minimal developer API
|
|
164
166
|
|
|
@@ -264,6 +266,8 @@ The long-term validation target is the **same Task Lease and authority lineage a
|
|
|
264
266
|
- execution evidence binding an allowed receipt, request and exact output hash
|
|
265
267
|
- strict `deriveFromEvidence()` path where the caller cannot provide the authority value
|
|
266
268
|
- reviewed Gmail sender authority extractor bound to `gmail:thread.read`
|
|
269
|
+
- reviewed GitHub selected-issue-number extractor bound to marker-scoped `github:issue.list`
|
|
270
|
+
- shared Google/GitHub authority-extractor conformance suite
|
|
267
271
|
- legacy host-trusted `derive()` compatibility path
|
|
268
272
|
- required parent lineage and extraction selector
|
|
269
273
|
- exact context-field bindings
|
|
@@ -288,6 +292,7 @@ The long-term validation target is the **same Task Lease and authority lineage a
|
|
|
288
292
|
- AES-256-GCM local encrypted secret store
|
|
289
293
|
- safe reconnect cleanup
|
|
290
294
|
- GitHub brokered execution without returning the token to the agent
|
|
295
|
+
- GitHub REST mappings for repository access plus evidence-derived `issue.list` / `issue.comment`
|
|
291
296
|
- Google REST provider mappings for Gmail thread reads and Calendar event mutations
|
|
292
297
|
- short-lived signed local agent-instance tokens
|
|
293
298
|
- local CLI/daemon
|
|
@@ -296,11 +301,12 @@ The long-term validation target is the **same Task Lease and authority lineage a
|
|
|
296
301
|
|
|
297
302
|
- adversarial authorization tests
|
|
298
303
|
- execution-evidence substitution, tampering, replay, cross-lease and selector tests
|
|
304
|
+
- the same provider-derived-authority conformance attacks against Google and GitHub
|
|
299
305
|
- Node 20 and Node 22 CI
|
|
300
306
|
- coverage run
|
|
301
307
|
- package checks
|
|
302
308
|
- clean-consumer npm registry verification
|
|
303
|
-
- live GitHub read and mutation proofs
|
|
309
|
+
- live GitHub read and evidence-derived mutation proofs
|
|
304
310
|
- CodeQL
|
|
305
311
|
|
|
306
312
|
## What is different from OAuth, IAM and MCP authorization?
|
package/ROADMAP.md
CHANGED
|
@@ -97,17 +97,21 @@ Do **not** build a general semantic policy language unless real integrations req
|
|
|
97
97
|
|
|
98
98
|
**Success criterion:** provider-derived authority cannot be established through the strict path unless the exact guarded output, ALLOW receipt and reviewed extractor contract agree on the selected value. This behavior is now exercised across two provider mappings. Stronger provider attestation and source invalidation remain separate follow-on problems.
|
|
99
99
|
|
|
100
|
-
## M4 — Same task, multiple transports
|
|
100
|
+
## M4 — Same task, multiple transports — first proof established
|
|
101
101
|
|
|
102
102
|
Prove Agent Authority is not an MCP product or SDK wrapper.
|
|
103
103
|
|
|
104
|
-
- [
|
|
105
|
-
- [
|
|
106
|
-
- [
|
|
104
|
+
- [x] same Task Lease through ordinary `guard.run()` SDK call
|
|
105
|
+
- [x] same Task Lease through MCP gateway
|
|
106
|
+
- [x] same Task Lease through brokered provider execution
|
|
107
107
|
- [ ] at least one non-bypassable harness/tool-middleware integration
|
|
108
|
-
- [
|
|
108
|
+
- [x] interoperability test vectors across transports
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
`test/transport-invariance.test.js` establishes one `execution-evidence-v1` derived fact from brokered execution, then reuses that exact Task Lease and fact through direct SDK, MCP and brokered execution. The three paths produce the same `allow`, `authority_delta_required` and `task_lease_completed` outcomes, and blocked attempts execute zero host callbacks, MCP upstream calls or brokered provider operations.
|
|
111
|
+
|
|
112
|
+
Brokered Task Lease execution deliberately does not consume mission-level one-time approval to override a lease-level authority delta. Updating a live Task Lease after explicit approval remains separate M2 work.
|
|
113
|
+
|
|
114
|
+
**Success criterion:** changing transport or harness does not expand the task's authority. The SDK/MCP/broker portion is now demonstrated in-process; an external non-bypassable harness/tool-middleware integration remains the final M4 proof.
|
|
111
115
|
|
|
112
116
|
## M5 — Production credential and approval UX
|
|
113
117
|
|
package/docs/npm-release.md
CHANGED
|
@@ -13,19 +13,24 @@ Before any publication:
|
|
|
13
13
|
After publication, verify from a fresh project with:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm install @nullsquare/agent-authority@0.4.
|
|
16
|
+
npm install @nullsquare/agent-authority@0.4.3
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Then run the same consumer smoke flow through the registry-installed package. Registry verification is part of the release gate; a successful `npm publish` command alone is not sufficient.
|
|
20
20
|
|
|
21
|
-
The repository also includes `.github/workflows/verify-npm-registry.yml`, which verifies registry visibility and a clean consumer install. For v0.4.
|
|
21
|
+
The repository also includes `.github/workflows/verify-npm-registry.yml`, which verifies registry visibility and a clean consumer install. For v0.4.3 it additionally verifies the public `@nullsquare/agent-authority/authority-evidence` API plus both reviewed provider authority extractors:
|
|
22
|
+
|
|
23
|
+
- `gmailThreadSenderAuthorityExtractor` from `@nullsquare/agent-authority/providers/google`;
|
|
24
|
+
- `githubIssueListSelectedNumberAuthorityExtractor` from `@nullsquare/agent-authority/providers/github`.
|
|
25
|
+
|
|
26
|
+
This makes the registry artifact verification cover the same two-provider execution-evidence surface exercised by the repository conformance suite.
|
|
22
27
|
|
|
23
28
|
## npm vs GitHub release surfaces
|
|
24
29
|
|
|
25
30
|
Publishing to the public npm registry does not automatically create either a GitHub Release or a GitHub Packages entry.
|
|
26
31
|
|
|
27
32
|
- **npm registry** — `npm publish --access public` publishes `@nullsquare/agent-authority` to `registry.npmjs.org` / npmjs.com. This is the package users install with `npm install`.
|
|
28
|
-
- **GitHub Releases** — a separate GitHub object, normally backed by a Git tag such as `v0.4.
|
|
33
|
+
- **GitHub Releases** — a separate GitHub object, normally backed by a Git tag such as `v0.4.3`. A release must be created explicitly or by release automation.
|
|
29
34
|
- **GitHub Packages** — a separate package registry. It only appears when the package is published to GitHub's npm registry (`npm.pkg.github.com`); publishing to npmjs.com does not populate it.
|
|
30
35
|
|
|
31
36
|
Agent Authority currently uses npmjs.com as its public package registry. Therefore an empty GitHub **Packages** section is expected unless the project intentionally adopts dual publication. A GitHub **Release** is still useful for source-release discoverability and should track published versions, but it is independent from npm publication.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Task Lease transport invariance
|
|
2
|
+
|
|
3
|
+
Agent Authority treats SDK calls, MCP calls and brokered provider execution as execution paths, not separate authority models.
|
|
4
|
+
|
|
5
|
+
The property under test is:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
same Task Lease + same established authority fact
|
|
9
|
+
|
|
|
10
|
+
+----------+----------+
|
|
11
|
+
| | |
|
|
12
|
+
direct MCP brokered
|
|
13
|
+
guard.run() gateway provider
|
|
14
|
+
| | |
|
|
15
|
+
+----------+----------+
|
|
16
|
+
|
|
|
17
|
+
same authority
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Changing the transport must not broaden task authority.
|
|
21
|
+
|
|
22
|
+
## Executable proof
|
|
23
|
+
|
|
24
|
+
`test/transport-invariance.test.js` creates one Task Lease and one derived authority fact.
|
|
25
|
+
|
|
26
|
+
The fact is first established from a brokered provider result using the strict evidence path:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
Task Lease root
|
|
30
|
+
|
|
|
31
|
+
v
|
|
32
|
+
brokered item.discover
|
|
33
|
+
|
|
|
34
|
+
+--> Task-Lease ALLOW receipt
|
|
35
|
+
+--> exact output hash evidence
|
|
36
|
+
|
|
|
37
|
+
v
|
|
38
|
+
reviewed test extractor
|
|
39
|
+
|
|
|
40
|
+
v
|
|
41
|
+
deriveFromEvidence()
|
|
42
|
+
|
|
|
43
|
+
v
|
|
44
|
+
fact:selected-item = alpha
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That exact Task Lease and fact are then used through three execution paths:
|
|
48
|
+
|
|
49
|
+
1. ordinary in-process `guard.run()`;
|
|
50
|
+
2. `MissionMcpGateway` configured with the Task Lease;
|
|
51
|
+
3. `ExecutingAuthorityRuntime.executeTaskLease()` with a brokered provider adapter.
|
|
52
|
+
|
|
53
|
+
For `item = alpha`, every path allows execution.
|
|
54
|
+
|
|
55
|
+
For `item = beta`, every path returns the same task-level authority delta:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
authority_delta_required
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The blocked direct callback, MCP upstream call and brokered provider operation all remain unexecuted.
|
|
62
|
+
|
|
63
|
+
After the Task Lease is completed, all three paths return:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
task_lease_completed
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Again, no blocked execution reaches the host callback or provider boundary.
|
|
70
|
+
|
|
71
|
+
## Broker behavior
|
|
72
|
+
|
|
73
|
+
`ExecutingAuthorityRuntime.executeTaskLease()` evaluates the Task Lease before adapter readiness or provider execution.
|
|
74
|
+
|
|
75
|
+
A lease-level `require_approval` result is returned as-is. Brokered execution does not consume a mission-level one-time approval to bypass the narrower Task Lease. Applying an explicitly approved authority delta back into a live Task Lease is separate roadmap work.
|
|
76
|
+
|
|
77
|
+
Successful brokered Task Lease execution returns execution evidence bound to the Task-Lease receipt and exact provider output, so strict derived authority can originate from brokered execution as well as from `guard.run()`.
|
|
78
|
+
|
|
79
|
+
## MCP behavior
|
|
80
|
+
|
|
81
|
+
`MissionMcpGateway` remains backward-compatible with Mission-only use, but now accepts exactly one authority source:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
mission OR lease
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
When configured with a Task Lease, each tool call is evaluated through that lease before `upstream.callTool()` can run.
|
|
88
|
+
|
|
89
|
+
MCP result metadata includes:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
io.nullsquare.agent-authority/decision
|
|
93
|
+
io.nullsquare.agent-authority/code
|
|
94
|
+
io.nullsquare.agent-authority/receipt_hash
|
|
95
|
+
io.nullsquare.agent-authority/task_lease_id
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The remote MCP handler and loopback proxy can pass the same Task Lease into the gateway.
|
|
99
|
+
|
|
100
|
+
## What this proves
|
|
101
|
+
|
|
102
|
+
- Task-Lease narrowing is no longer specific to the direct SDK guard;
|
|
103
|
+
- MCP cannot silently fall back to Mission-only authority when explicitly configured with a Task Lease;
|
|
104
|
+
- brokered provider execution can enforce the same Task Lease before credential-backed execution;
|
|
105
|
+
- one derived fact can constrain all three execution paths;
|
|
106
|
+
- task completion invalidates the same authority across all three paths;
|
|
107
|
+
- broker credentials may remain connected after task authority disappears.
|
|
108
|
+
|
|
109
|
+
## What this does not prove yet
|
|
110
|
+
|
|
111
|
+
- a hostile harness cannot bypass Agent Authority through an entirely separate unguarded tool path;
|
|
112
|
+
- Task Lease state survives process restart;
|
|
113
|
+
- the same lease is serialized and recovered across separate processes or hosts;
|
|
114
|
+
- an approved authority delta is durably applied back into a running lease;
|
|
115
|
+
- provider outputs are cryptographically attested by providers.
|
|
116
|
+
|
|
117
|
+
The remaining M4 target is at least one real harness/tool-middleware integration where executable tool calls cannot bypass the Task Lease boundary.
|
package/package.json
CHANGED
package/src/execution.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthorityRuntime, createReceipt } from './index.js';
|
|
2
|
+
import { createExecutionEvidence } from './authority-evidence.js';
|
|
2
3
|
|
|
3
4
|
function executionFailure(mission, request, code, reason, extra = {}) {
|
|
4
5
|
const result = { decision: 'deny', code, reason, ...extra };
|
|
@@ -115,10 +116,39 @@ export class ExecutingAuthorityRuntime extends AuthorityRuntime {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Execute through the broker while preserving Task Lease narrowing.
|
|
121
|
+
*
|
|
122
|
+
* A lease-level REQUIRE_APPROVAL is returned before adapter readiness or
|
|
123
|
+
* provider execution. It is not converted into a mission-level one-time
|
|
124
|
+
* approval because applying an authority delta back into a live lease is a
|
|
125
|
+
* separate, not-yet-implemented capability.
|
|
126
|
+
*/
|
|
127
|
+
async executeTaskLease(lease, request) {
|
|
128
|
+
if (!lease || typeof lease.evaluate !== 'function' || !lease.mission) {
|
|
129
|
+
throw new Error('task lease with mission and evaluate() is required');
|
|
130
|
+
}
|
|
131
|
+
return this.execute(lease.mission, request, { lease });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async execute(missionInput, request, { lease = null } = {}) {
|
|
135
|
+
if (lease && lease.mission?.mission_id !== missionInput?.mission_id) {
|
|
136
|
+
throw new Error('task lease mission does not match execution mission');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let evaluation = lease
|
|
140
|
+
? lease.evaluate(this, request)
|
|
141
|
+
: this.evaluate(missionInput, request);
|
|
142
|
+
|
|
120
143
|
if (evaluation.result.decision === 'deny') return { ...evaluation, output: null };
|
|
121
144
|
|
|
145
|
+
// A Task Lease is the narrowest authority object. Do not let brokered
|
|
146
|
+
// execution reinterpret a lease-level authority delta as a broader mission
|
|
147
|
+
// approval. This keeps broker behavior aligned with guard.run() and MCP.
|
|
148
|
+
if (lease && evaluation.result.decision !== 'allow') {
|
|
149
|
+
return { ...evaluation, output: null };
|
|
150
|
+
}
|
|
151
|
+
|
|
122
152
|
const adapter = this.adapters.resolve(request.service);
|
|
123
153
|
if (!adapter) {
|
|
124
154
|
return executionFailure(missionInput, request, 'adapter_unavailable', `no adapter is registered for ${request.service}`);
|
|
@@ -148,6 +178,7 @@ export class ExecutingAuthorityRuntime extends AuthorityRuntime {
|
|
|
148
178
|
|
|
149
179
|
try {
|
|
150
180
|
const output = await adapter.execute({ mission: missionInput, request });
|
|
181
|
+
const evidence = createExecutionEvidence({ receipt: evaluation.receipt, output });
|
|
151
182
|
let usage = null;
|
|
152
183
|
if (budgetCheck && !budgetCheck.result) {
|
|
153
184
|
const spent = this.usage.record(missionInput.mission_id, budgetCheck.currency, budgetCheck.amount);
|
|
@@ -160,7 +191,7 @@ export class ExecutingAuthorityRuntime extends AuthorityRuntime {
|
|
|
160
191
|
if (executionRecord && this.executions) {
|
|
161
192
|
this.executions.complete({ mission: missionInput, request, receipt_id: evaluation.receipt?.receipt_id || null });
|
|
162
193
|
}
|
|
163
|
-
return { ...evaluation, output, usage, execution: executionRecord || null };
|
|
194
|
+
return { ...evaluation, output, evidence, usage, execution: executionRecord || null };
|
|
164
195
|
} catch (error) {
|
|
165
196
|
if (executionRecord && this.executions) {
|
|
166
197
|
this.executions.uncertain({ mission: missionInput, request, error_code: error.code || 'provider_error' });
|
package/src/mcp-gateway.js
CHANGED
|
@@ -31,6 +31,15 @@ export function isDeclaredReadOnlyTool(tool) {
|
|
|
31
31
|
return tool?.annotations?.readOnlyHint === true;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function authorityMeta(evaluation = {}) {
|
|
35
|
+
return {
|
|
36
|
+
'io.nullsquare.agent-authority/decision': evaluation.result?.decision || 'deny',
|
|
37
|
+
'io.nullsquare.agent-authority/code': evaluation.result?.code || null,
|
|
38
|
+
'io.nullsquare.agent-authority/receipt_hash': evaluation.receipt?.receipt_hash || null,
|
|
39
|
+
'io.nullsquare.agent-authority/task_lease_id': evaluation.receipt?.task_lease_id || null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
function deniedToolResult(result, extra = {}) {
|
|
35
44
|
return {
|
|
36
45
|
content: [{
|
|
@@ -47,32 +56,43 @@ function deniedToolResult(result, extra = {}) {
|
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
/**
|
|
50
|
-
* Small
|
|
59
|
+
* Small policy gateway for MCP tools.
|
|
51
60
|
*
|
|
52
61
|
* The upstream object only needs two methods:
|
|
53
62
|
* listTools(params?) -> { tools: [...] }
|
|
54
63
|
* callTool(params) -> MCP CallToolResult
|
|
55
64
|
*
|
|
56
|
-
*
|
|
65
|
+
* The gateway accepts either a Mission or a Task Lease. When a Task Lease is
|
|
66
|
+
* supplied, every MCP tool call is evaluated through that exact lease before
|
|
67
|
+
* the upstream callback can run. This keeps transport changes from bypassing
|
|
68
|
+
* task-level narrowing.
|
|
69
|
+
*
|
|
70
|
+
* The gateway still defaults to read-only enforcement. A tool is considered
|
|
57
71
|
* read-only only when its MCP annotations explicitly set readOnlyHint=true.
|
|
58
|
-
* Write support
|
|
59
|
-
* is intentionally not inferred from tool names.
|
|
72
|
+
* Write support must be enabled deliberately; it is never inferred from names.
|
|
60
73
|
*/
|
|
61
74
|
export class MissionMcpGateway {
|
|
62
75
|
constructor({
|
|
63
76
|
mission,
|
|
77
|
+
lease,
|
|
64
78
|
runtime,
|
|
65
79
|
upstream,
|
|
66
80
|
service = 'mcp:upstream',
|
|
67
81
|
readOnly = true,
|
|
68
82
|
contextMapper = contextFromToolArguments
|
|
69
83
|
} = {}) {
|
|
70
|
-
if (
|
|
84
|
+
if ((mission && lease) || (!mission && !lease)) {
|
|
85
|
+
throw new Error('provide exactly one of mission or lease');
|
|
86
|
+
}
|
|
87
|
+
if (lease && (typeof lease.evaluate !== 'function' || !lease.mission)) {
|
|
88
|
+
throw new Error('lease must provide mission and evaluate(runtime, request)');
|
|
89
|
+
}
|
|
71
90
|
if (!runtime || typeof runtime.evaluate !== 'function') throw new Error('authority runtime is required');
|
|
72
91
|
if (!upstream || typeof upstream.listTools !== 'function' || typeof upstream.callTool !== 'function') {
|
|
73
92
|
throw new Error('upstream MCP client must implement listTools() and callTool()');
|
|
74
93
|
}
|
|
75
|
-
this.mission = mission;
|
|
94
|
+
this.mission = mission || lease.mission;
|
|
95
|
+
this.lease = lease || null;
|
|
76
96
|
this.runtime = runtime;
|
|
77
97
|
this.upstream = upstream;
|
|
78
98
|
this.service = service;
|
|
@@ -81,6 +101,12 @@ export class MissionMcpGateway {
|
|
|
81
101
|
this.tools = new Map();
|
|
82
102
|
}
|
|
83
103
|
|
|
104
|
+
evaluate(request) {
|
|
105
|
+
return this.lease
|
|
106
|
+
? this.lease.evaluate(this.runtime, request)
|
|
107
|
+
: this.runtime.evaluate(this.mission, request);
|
|
108
|
+
}
|
|
109
|
+
|
|
84
110
|
async refreshTools(params = undefined) {
|
|
85
111
|
const listed = await this.upstream.listTools(params);
|
|
86
112
|
for (const tool of listed.tools || []) this.tools.set(tool.name, tool);
|
|
@@ -122,11 +148,9 @@ export class MissionMcpGateway {
|
|
|
122
148
|
action: mcpToolAction(toolName),
|
|
123
149
|
context
|
|
124
150
|
};
|
|
125
|
-
const evaluation = this.
|
|
151
|
+
const evaluation = this.evaluate(request);
|
|
126
152
|
if (evaluation.result.decision !== 'allow') {
|
|
127
|
-
return deniedToolResult(evaluation.result,
|
|
128
|
-
'io.nullsquare.agent-authority/receipt_hash': evaluation.receipt?.receipt_hash || null
|
|
129
|
-
});
|
|
153
|
+
return deniedToolResult(evaluation.result, authorityMeta(evaluation));
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
const output = await this.upstream.callTool(params);
|
|
@@ -134,8 +158,7 @@ export class MissionMcpGateway {
|
|
|
134
158
|
...output,
|
|
135
159
|
_meta: {
|
|
136
160
|
...(output?._meta || {}),
|
|
137
|
-
|
|
138
|
-
'io.nullsquare.agent-authority/receipt_hash': evaluation.receipt?.receipt_hash || null
|
|
161
|
+
...authorityMeta(evaluation)
|
|
139
162
|
}
|
|
140
163
|
};
|
|
141
164
|
}
|
package/src/mcp-remote.js
CHANGED
|
@@ -54,6 +54,7 @@ export class RemoteMcpUpstream {
|
|
|
54
54
|
|
|
55
55
|
export function createMcpGatewayHandler({
|
|
56
56
|
mission,
|
|
57
|
+
lease,
|
|
57
58
|
runtime,
|
|
58
59
|
upstream,
|
|
59
60
|
upstreamUrl,
|
|
@@ -63,6 +64,7 @@ export function createMcpGatewayHandler({
|
|
|
63
64
|
const resolvedUpstream = upstream || new RemoteMcpUpstream({ url: upstreamUrl });
|
|
64
65
|
const gateway = new MissionMcpGateway({
|
|
65
66
|
mission,
|
|
67
|
+
lease,
|
|
66
68
|
runtime,
|
|
67
69
|
upstream: resolvedUpstream,
|
|
68
70
|
service,
|
|
@@ -74,7 +76,9 @@ export function createMcpGatewayHandler({
|
|
|
74
76
|
{
|
|
75
77
|
name: 'agent-authority-gateway',
|
|
76
78
|
version: '0.3.0',
|
|
77
|
-
description:
|
|
79
|
+
description: lease
|
|
80
|
+
? 'Task-Lease-aware policy gateway for MCP tools'
|
|
81
|
+
: 'Mission-aware policy gateway for MCP tools'
|
|
78
82
|
},
|
|
79
83
|
{ capabilities: { tools: {} } }
|
|
80
84
|
);
|
package/src/mcp-server.js
CHANGED
|
@@ -30,6 +30,7 @@ function sendJson(res, status, value) {
|
|
|
30
30
|
*/
|
|
31
31
|
export function createMcpProxyServer({
|
|
32
32
|
mission,
|
|
33
|
+
lease,
|
|
33
34
|
runtime,
|
|
34
35
|
upstream,
|
|
35
36
|
upstreamUrl,
|
|
@@ -43,6 +44,7 @@ export function createMcpProxyServer({
|
|
|
43
44
|
|
|
44
45
|
const gateway = createMcpGatewayHandler({
|
|
45
46
|
mission,
|
|
47
|
+
lease,
|
|
46
48
|
runtime,
|
|
47
49
|
upstream,
|
|
48
50
|
upstreamUrl,
|
|
@@ -64,6 +66,7 @@ export function createMcpProxyServer({
|
|
|
64
66
|
ok: true,
|
|
65
67
|
service: 'agent-authority-mcp-gateway',
|
|
66
68
|
mode: 'read-only',
|
|
69
|
+
authority: lease ? 'task-lease' : 'mission',
|
|
67
70
|
upstream: upstreamUrl || 'injected'
|
|
68
71
|
});
|
|
69
72
|
}
|