@nullsquare/agent-authority 0.4.6 → 0.4.8
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 +108 -5
- package/ROADMAP.md +30 -5
- package/docs/connected-execution-api.md +60 -0
- package/docs/connected-github.md +140 -0
- package/docs/npm-release.md +30 -10
- package/docs/product-proof.md +134 -8
- package/docs/quickstart.md +147 -0
- package/docs/release-v0.4.7.md +38 -0
- package/examples/live-github-derived-mutation.js +47 -54
- package/examples/quickstart-github-connected.mjs +77 -0
- package/examples/quickstart-github-live.mjs +84 -0
- package/examples/quickstart.mjs +101 -0
- package/examples/task-first-coding.js +238 -0
- package/examples/task-first-finance.js +223 -0
- package/examples/task-first-support.js +127 -0
- package/package.json +7 -3
- package/src/cli.js +3 -3
- package/src/connections.js +19 -4
- package/src/providers/github-coding.js +101 -0
- package/src/providers/github.js +178 -9
- package/src/task.js +45 -4
package/docs/product-proof.md
CHANGED
|
@@ -71,13 +71,137 @@ Do not prioritize another deep authorization subsystem until the following are d
|
|
|
71
71
|
|
|
72
72
|
- [ ] a new developer can run a meaningful task-first example in under 10 minutes;
|
|
73
73
|
- [ ] at least three real workflow examples exist: coding, support/communications, and operations/finance;
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
78
|
-
- [
|
|
74
|
+
- [x] the same task-first API works in-memory and with durable local state;
|
|
75
|
+
- [x] useful-task completion stays high under the deterministic product benchmark;
|
|
76
|
+
- [x] normal fixture task actions do not trigger unnecessary approvals;
|
|
77
|
+
- [x] unrelated-resource effects execute zero provider callbacks in the deterministic fixture, live GitHub proof, support/communications proof, and operations/finance proof;
|
|
78
|
+
- [x] approval/step-up output explains the established authority and requested delta clearly;
|
|
79
79
|
- [ ] at least one external developer uses the package without project-author assistance.
|
|
80
80
|
|
|
81
|
+
The checked utility items are evidence about the current deterministic fixtures and live GitHub proof, not a claim that arbitrary real-world agent workloads have already met the same rates.
|
|
82
|
+
|
|
83
|
+
## First live provider product proof
|
|
84
|
+
|
|
85
|
+
The existing GitHub Actions mutation validation now runs through the public task-first API rather than hand-assembling Mission + Task Lease + Guard.
|
|
86
|
+
|
|
87
|
+
The live workflow uses:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
createTask()
|
|
91
|
+
|
|
|
92
|
+
v
|
|
93
|
+
task.run(issue.list)
|
|
94
|
+
|
|
|
95
|
+
v
|
|
96
|
+
task.authorityFrom(reviewed GitHub output)
|
|
97
|
+
|
|
|
98
|
+
v
|
|
99
|
+
issue #9 becomes downstream task authority
|
|
100
|
+
|
|
|
101
|
+
+--> task.run(issue.comment #9) -> real GitHub mutation
|
|
102
|
+
+--> task.run(issue.comment #1) -> STEP-UP, zero provider mutation
|
|
103
|
+
|
|
|
104
|
+
v
|
|
105
|
+
task.complete()
|
|
106
|
+
|
|
|
107
|
+
v
|
|
108
|
+
issue.comment #9 -> DENY, zero provider mutation
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Passing CI evidence from the live run:
|
|
112
|
+
|
|
113
|
+
- repository root: `Null-Square/agent-authority`;
|
|
114
|
+
- reviewed fixture selection: issue `#9`;
|
|
115
|
+
- `task.authorityFrom()` established issue `#9` as downstream authority;
|
|
116
|
+
- exactly one real GitHub comment mutation executed;
|
|
117
|
+
- unrelated issue `#1` produced `authority_delta_required` before provider mutation;
|
|
118
|
+
- `task.explain()` reported established authority `9` vs requested value `1`;
|
|
119
|
+
- the same issue was denied after `task.complete()`;
|
|
120
|
+
- provider calls before cleanup: `reads=1`, `task_mutations=1`;
|
|
121
|
+
- the temporary validation comment was deleted outside the authority proof.
|
|
122
|
+
|
|
123
|
+
This establishes a real-provider product proof for the facade, but it is **not yet** the full coding-agent product workflow. Branch creation, file edits and PR creation still need to be chained under task-derived authority while merge/deploy remain outside the task.
|
|
124
|
+
|
|
125
|
+
## Cross-provider support/communications proof
|
|
126
|
+
|
|
127
|
+
`examples/task-first-support.js` exercises the same public facade across two service boundaries using the exact field names and normalized sender shape used by the Google adapter:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
Task: handle one customer email and schedule the requested meeting
|
|
131
|
+
|
|
132
|
+
origin thread authority
|
|
133
|
+
|
|
|
134
|
+
v
|
|
135
|
+
task.run(gmail:thread.read)
|
|
136
|
+
|
|
|
137
|
+
v
|
|
138
|
+
reviewed Gmail sender extractor
|
|
139
|
+
|
|
|
140
|
+
v
|
|
141
|
+
customer@example.com
|
|
142
|
+
|
|
|
143
|
+
v
|
|
144
|
+
task.bind(calendar:event.create.attendee_email)
|
|
145
|
+
|
|
|
146
|
+
+--> exact customer meeting -> ALLOW
|
|
147
|
+
+--> unrelated attendee -> STEP-UP, zero Calendar callbacks
|
|
148
|
+
|
|
|
149
|
+
v
|
|
150
|
+
task.complete() -> same meeting authority no longer usable
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The task also binds the Gmail `thread_id` and Calendar `calendar_id` to explicit task-entry roots. A different Gmail thread is stopped before its callback runs.
|
|
154
|
+
|
|
155
|
+
The example and `test/task-product-support.test.js` run on Node 20 and Node 22 CI. The support proof demonstrates that the task-first model crosses Gmail -> Calendar without a second authorization abstraction.
|
|
156
|
+
|
|
157
|
+
This is intentionally a **self-contained product proof**. It mirrors the real Google provider contract but does not close the separate public Google Actions evidence gate; that gate still requires repository OAuth secrets.
|
|
158
|
+
|
|
159
|
+
## Operations / finance lineage proof
|
|
160
|
+
|
|
161
|
+
`examples/task-first-finance.js` exercises a longer evidence-derived authority chain without adding another provider, policy DSL, or numeric relation language:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
Task: resolve one support ticket by refunding only its payment
|
|
165
|
+
|
|
166
|
+
ticket:481
|
|
167
|
+
|
|
|
168
|
+
v
|
|
169
|
+
task.run(helpdesk:ticket.read)
|
|
170
|
+
|
|
|
171
|
+
v
|
|
172
|
+
order:991
|
|
173
|
+
|
|
|
174
|
+
v
|
|
175
|
+
task.run(orders:order.read)
|
|
176
|
+
|
|
|
177
|
+
v
|
|
178
|
+
payment:abc123
|
|
179
|
+
|
|
|
180
|
+
v
|
|
181
|
+
task.run(payments:payment.read)
|
|
182
|
+
|
|
|
183
|
+
+--> amount = 12500 minor units
|
|
184
|
+
+--> currency = USD
|
|
185
|
+
|
|
|
186
|
+
v
|
|
187
|
+
task.bind(refund payment_id + amount + currency)
|
|
188
|
+
|
|
|
189
|
+
+--> exact full refund -> ALLOW
|
|
190
|
+
+--> another payment -> STEP-UP, zero refund callbacks
|
|
191
|
+
+--> over-refund -> STEP-UP, zero refund callbacks
|
|
192
|
+
+--> wrong currency -> STEP-UP, zero refund callbacks
|
|
193
|
+
+--> partial refund -> STEP-UP under current exact binding model
|
|
194
|
+
|
|
|
195
|
+
v
|
|
196
|
+
task.complete() -> same refund authority no longer usable
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The task cannot read an arbitrary order before the ticket establishes the order fact: the unresolved binding fails closed before the order callback runs. The same pattern continues through payment and refund.
|
|
200
|
+
|
|
201
|
+
The example and `test/task-product-finance.test.js` run on Node 20 and Node 22 CI. On the passing fixture, provider-shaped callbacks are exactly one ticket read, one order read, one payment read and one refund.
|
|
202
|
+
|
|
203
|
+
This proof intentionally exposes a **product limitation rather than hiding it behind new policy machinery**: Task Lease bindings currently require exact equality. Therefore the payment amount `12500` can authorize an exact `12500` refund, but a legitimate partial refund such as `5000` also produces `authority_delta_required`. A future narrow `requested amount <= evidence-derived payment amount` relation may be justified if real provider/adoption evidence shows partial refunds are needed. Until then, the repository should keep this limitation visible instead of adding a general expression language speculatively.
|
|
204
|
+
|
|
81
205
|
## Utility metrics
|
|
82
206
|
|
|
83
207
|
Security tests remain required, but product work should additionally track:
|
|
@@ -102,7 +226,7 @@ true authority-delta step-up rate = 100%
|
|
|
102
226
|
unauthorized effect rate = 0%
|
|
103
227
|
```
|
|
104
228
|
|
|
105
|
-
Real provider/harness benchmarks should replace or supplement
|
|
229
|
+
The current fixture run contains 40 normal tasks and 10 unrelated-resource attempts. Real provider/harness benchmarks should replace or supplement it as the product matures.
|
|
106
230
|
|
|
107
231
|
## Three product proofs
|
|
108
232
|
|
|
@@ -120,6 +244,8 @@ repository -> issue -> task branch -> changed files -> pull request
|
|
|
120
244
|
|
|
121
245
|
Unrelated repositories, issues, merge and deploy remain outside the task.
|
|
122
246
|
|
|
247
|
+
The live issue-discovery -> exact-issue-comment proof is the first slice of this direction; it does not complete the branch/files/PR lineage yet.
|
|
248
|
+
|
|
123
249
|
### Support / communications agent
|
|
124
250
|
|
|
125
251
|
Task:
|
|
@@ -132,7 +258,7 @@ Desired authority lineage:
|
|
|
132
258
|
email thread -> customer -> meeting / CRM record / reply target
|
|
133
259
|
```
|
|
134
260
|
|
|
135
|
-
The
|
|
261
|
+
The self-contained Gmail-thread -> exact Calendar-attendee slice is now established. The next value proof should connect the same customer authority to another useful downstream action (for example reply/CRM) or rerun the task-first flow with the real Google Actions fixture once repository OAuth secrets are available.
|
|
136
262
|
|
|
137
263
|
### Operations / finance agent
|
|
138
264
|
|
|
@@ -146,7 +272,7 @@ Desired authority lineage:
|
|
|
146
272
|
ticket -> customer -> order -> payment -> refund <= original payment
|
|
147
273
|
```
|
|
148
274
|
|
|
149
|
-
|
|
275
|
+
The self-contained ticket -> order -> payment -> **exact full refund** lineage is now established. The remaining product question is narrower than the original workflow: whether partial refunds are important enough to justify one evidence-derived numeric ceiling relation. Current exact bindings intentionally step up for a smaller amount rather than guessing that relation into the core.
|
|
150
276
|
|
|
151
277
|
## Freeze list
|
|
152
278
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Fresh-install quickstart
|
|
2
|
+
|
|
3
|
+
This quickstart is for a developer who wants to understand Agent Authority before connecting an account or learning Mission/Task Lease internals.
|
|
4
|
+
|
|
5
|
+
It uses the real public task-first API and the reviewed GitHub issue-number authority extractor from the published npm package. The first provider callback is a local provider-shaped fixture, so **no GitHub token, OAuth setup, repository checkout, or custom extractor is required**.
|
|
6
|
+
|
|
7
|
+
## 1. Create a blank project
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
mkdir agent-authority-quickstart
|
|
11
|
+
cd agent-authority-quickstart
|
|
12
|
+
npm init -y
|
|
13
|
+
npm install @nullsquare/agent-authority
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires Node.js 20+.
|
|
17
|
+
|
|
18
|
+
## 2. Get the credential-free fixture quickstart
|
|
19
|
+
|
|
20
|
+
Download or copy `examples/quickstart.mjs` from this repository into the blank project as `quickstart.mjs`.
|
|
21
|
+
|
|
22
|
+
For example on macOS/Linux:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
curl -fsSL https://raw.githubusercontent.com/Null-Square/agent-authority/main/examples/quickstart.mjs -o quickstart.mjs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The file imports only published package exports:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import { createTask } from '@nullsquare/agent-authority/task';
|
|
32
|
+
import { AuthorityApprovalRequiredError } from '@nullsquare/agent-authority/guard';
|
|
33
|
+
import { githubIssueListSelectedNumberAuthorityExtractor } from '@nullsquare/agent-authority/providers/github';
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 3. Run it
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node quickstart.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Expected shape:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
ALLOW -> task discovered issue #42 and the exact comment effect ran
|
|
46
|
+
STEP-UP -> The task established authority for 42 but this action requested 7.
|
|
47
|
+
PASS -> useful task work ran; unrelated standing permission did not become task authority
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## What happened
|
|
51
|
+
|
|
52
|
+
The task starts with authority over one repository and one task-selection marker:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
repository + marker
|
|
56
|
+
|
|
|
57
|
+
v
|
|
58
|
+
authorized issue discovery
|
|
59
|
+
|
|
|
60
|
+
v
|
|
61
|
+
reviewed extractor + execution evidence
|
|
62
|
+
|
|
|
63
|
+
v
|
|
64
|
+
issue #42 becomes downstream task authority
|
|
65
|
+
|
|
|
66
|
+
+--> comment on #42 -> ALLOW
|
|
67
|
+
+--> comment on #7 -> STEP-UP before callback
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The important point is not the fixture itself. It is that the callback which represents the provider effect executes for the task-derived issue and does **not** execute for the unrelated issue even though the Mission-level GitHub permission includes `issue.comment`.
|
|
71
|
+
|
|
72
|
+
The quickstart counts callbacks and fails if the unrelated effect executes.
|
|
73
|
+
|
|
74
|
+
## 4. Next step: call real GitHub with no credential
|
|
75
|
+
|
|
76
|
+
The second quickstart uses the same published package in the same blank project, but the callback now makes a real network request to GitHub's public API.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
curl -fsSL https://raw.githubusercontent.com/Null-Square/agent-authority/main/examples/quickstart-github-live.mjs -o quickstart-github-live.mjs
|
|
80
|
+
node quickstart-github-live.mjs
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Default behavior:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
Standing GitHub permission -> repo.read
|
|
87
|
+
Task authority -> Null-Square/agent-authority
|
|
88
|
+
GitHub mode -> public API; no credential required
|
|
89
|
+
ALLOW -> real GitHub returned Null-Square/agent-authority
|
|
90
|
+
STEP-UP -> The task established authority for "Null-Square/agent-authority" but this action requested "octocat/Hello-World".
|
|
91
|
+
PASS -> broader standing repo.read permission could not reach an unrelated repository for this task
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This example deliberately models the **standing capability as broader than the task**. Mission-level `github:repo.read` is allowed without a repository constraint. The Task authority root then binds `repo.read` to exactly `Null-Square/agent-authority`.
|
|
95
|
+
|
|
96
|
+
The allowed request performs one real `fetch()` to GitHub. The unrelated repository request reaches the Task authority check, becomes `authority_delta_required`, and does not execute a second `fetch()`.
|
|
97
|
+
|
|
98
|
+
You can inspect another public repository by passing it as the first argument:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
node quickstart-github-live.mjs owner/repository
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
An optional `GITHUB_TOKEN` may be supplied for authenticated GitHub API access, but no token is required for the default public-repository path.
|
|
105
|
+
|
|
106
|
+
## Replace the fixture with your provider call
|
|
107
|
+
|
|
108
|
+
The first quickstart's discovery callback is the only intentionally fake provider piece:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
const discovery = await task.run(request, async () => {
|
|
112
|
+
return providerShapedOutput;
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
In an application, keep the Agent Authority request and replace the callback with the SDK/provider call you already use. For the built-in GitHub extractor, use the normalized output produced by the Agent Authority GitHub adapter. If your provider/output shape is different, use a reviewed extractor for that mapping rather than trusting arbitrary model-selected values.
|
|
117
|
+
|
|
118
|
+
The live GitHub quickstart shows the even simpler direct-boundary case: an application can put its existing `fetch()` or SDK call inside `task.run()` while Task authority remains narrower than the standing account/app capability.
|
|
119
|
+
|
|
120
|
+
## Evidence boundaries
|
|
121
|
+
|
|
122
|
+
The credential-free fixture is an **adoption quickstart**, not a live-provider security proof. The live GitHub quickstart is a real-provider onboarding proof, but it is read-only and uses a public repository by default.
|
|
123
|
+
|
|
124
|
+
Separate repository evidence already covers:
|
|
125
|
+
|
|
126
|
+
- a real GitHub issue discovery -> exact issue comment mutation through the task-first API;
|
|
127
|
+
- Gmail sender -> Calendar attendee authority;
|
|
128
|
+
- SDK, MCP and broker transport invariance;
|
|
129
|
+
- durable local Task Lease recovery/session behavior;
|
|
130
|
+
- adversarial execution-evidence tests.
|
|
131
|
+
|
|
132
|
+
The public Gmail -> Calendar GitHub Actions proof remains separately gated on repository Google OAuth secrets. Authenticated/private-repository onboarding and production OAuth/KMS UX also remain separate product work.
|
|
133
|
+
|
|
134
|
+
## Automated fresh-install gates
|
|
135
|
+
|
|
136
|
+
`.github/workflows/verify-quickstart.yml` repeats the fixture developer path in a blank temporary project:
|
|
137
|
+
|
|
138
|
+
1. resolve the latest public `@nullsquare/agent-authority` version from npm;
|
|
139
|
+
2. create a new empty npm project;
|
|
140
|
+
3. install only that registry package;
|
|
141
|
+
4. copy the quickstart file;
|
|
142
|
+
5. confirm the optional AI SDK was not installed;
|
|
143
|
+
6. run `node quickstart.mjs`.
|
|
144
|
+
|
|
145
|
+
`.github/workflows/verify-live-quickstart.yml` repeats the real-provider path from another blank Node 20 project and requires exactly one live GitHub request before the unrelated repository is blocked.
|
|
146
|
+
|
|
147
|
+
Both gates have passed against `@nullsquare/agent-authority@0.4.6`. They catch documentation/example drift against the actually published package. They do **not** substitute for timing a first-time external developer, so the roadmap's under-10-minute human adoption gate remains open until that evidence exists.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# v0.4.7 connected execution
|
|
2
|
+
|
|
3
|
+
v0.4.7 closes a concrete adoption gap between the task-first API and the existing credential broker/provider runtime.
|
|
4
|
+
|
|
5
|
+
## What shipped
|
|
6
|
+
|
|
7
|
+
- `task.execute(request)` executes through an `ExecutingAuthorityRuntime` while preserving the current Task Lease as the narrowest authority object.
|
|
8
|
+
- connected deny/step-up outcomes use the same public error classes as `task.run()`.
|
|
9
|
+
- successful connected execution returns sanitized provider output, receipt and execution evidence that can feed `task.authorityFrom()`.
|
|
10
|
+
- `@nullsquare/agent-authority/runtime-env` exposes the existing local encrypted runtime composition for developer onboarding.
|
|
11
|
+
- a sole active provider account can be resolved when requests omit `account_id`; multiple active accounts remain ambiguous and fail closed.
|
|
12
|
+
- default disconnect can remove that sole connection without requiring the caller to know an auto-detected provider account ID.
|
|
13
|
+
- the connected GitHub quickstart proves the local encrypted vault + credential broker + live provider path without copying credentials into task/model context.
|
|
14
|
+
- the CLI version is now derived from `package.json` instead of a stale hard-coded constant.
|
|
15
|
+
|
|
16
|
+
## Security boundary
|
|
17
|
+
|
|
18
|
+
This release does not create a GitHub token, OAuth flow, GitHub App, KMS, or new identity format. Provider-side least privilege still comes from GitHub. Agent Authority adds a task boundary underneath that connected account authority.
|
|
19
|
+
|
|
20
|
+
The local encrypted vault remains a trusted-local-host developer reference backend.
|
|
21
|
+
|
|
22
|
+
## Release evidence
|
|
23
|
+
|
|
24
|
+
The exact v0.4.7 candidate passed:
|
|
25
|
+
|
|
26
|
+
- Node 20 and Node 22 test lanes;
|
|
27
|
+
- coverage;
|
|
28
|
+
- packed-package consumer smoke;
|
|
29
|
+
- connected-execution packed consumer smoke;
|
|
30
|
+
- Vercel AI SDK integration;
|
|
31
|
+
- live GitHub read proof;
|
|
32
|
+
- live evidence-derived GitHub mutation proof;
|
|
33
|
+
- encrypted connected-GitHub onboarding proof;
|
|
34
|
+
- CodeQL.
|
|
35
|
+
|
|
36
|
+
After merge, the independent `Verify npm registry` workflow resolved version `0.4.7`, confirmed it was visible on npm, installed that exact registry artifact into a fresh Node 20 consumer, confirmed the optional AI SDK was absent, and successfully ran both the ordinary public package smoke and the connected-execution smoke.
|
|
37
|
+
|
|
38
|
+
That registry proof is the basis for marking v0.4.7 published.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { CredentialBroker } from '../src/connections.js';
|
|
2
|
-
import { AuthorityRuntime } from '../src/index.js';
|
|
3
2
|
import {
|
|
4
3
|
AuthorityApprovalRequiredError,
|
|
5
|
-
AuthorityDeniedError
|
|
6
|
-
createTaskLeaseGuard
|
|
4
|
+
AuthorityDeniedError
|
|
7
5
|
} from '../src/guard.js';
|
|
8
6
|
import { createGitHubProviderAdapter } from '../src/providers/github.js';
|
|
9
|
-
import {
|
|
7
|
+
import { createTask } from '../src/task.js';
|
|
10
8
|
|
|
11
9
|
const repository = process.env.AA_VALIDATION_REPOSITORY || 'Null-Square/agent-authority';
|
|
12
10
|
const marker = process.env.AA_VALIDATION_MARKER || 'agent-authority-live-fixture-v1';
|
|
@@ -19,69 +17,55 @@ if (!token) {
|
|
|
19
17
|
const [owner, repo] = repository.split('/');
|
|
20
18
|
if (!owner || !repo) throw new Error(`invalid repository: ${repository}`);
|
|
21
19
|
|
|
22
|
-
const
|
|
23
|
-
version: '0.1',
|
|
20
|
+
const task = createTask({
|
|
24
21
|
mission_id: 'mission:live-derived-github-mutation',
|
|
25
|
-
principal:
|
|
26
|
-
agent:
|
|
22
|
+
principal: 'user:validation',
|
|
23
|
+
agent: 'agent:github-actions-validation',
|
|
24
|
+
request: 'Find the Agent Authority live validation fixture and leave one validation comment',
|
|
27
25
|
objective: 'Discover the validation issue and comment only on that issue',
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
service: 'github',
|
|
26
|
+
permissions: {
|
|
27
|
+
github: {
|
|
31
28
|
allow: ['issue.list', 'issue.comment'],
|
|
32
29
|
deny: ['issue.close', 'issue.delete', 'repo.write', 'repo.delete'],
|
|
33
30
|
constraints: { repository: [repository] }
|
|
34
31
|
}
|
|
35
|
-
|
|
36
|
-
constraints: { expires_at: '2099-01-01T00:00:00Z' }
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const lease = createTaskLease({
|
|
40
|
-
mission,
|
|
41
|
-
request: 'Find the Agent Authority live validation fixture and leave one validation comment',
|
|
42
|
-
roots: [
|
|
43
|
-
{
|
|
44
|
-
fact_id: 'fact:repository',
|
|
32
|
+
},
|
|
33
|
+
constraints: { expires_at: '2099-01-01T00:00:00Z' },
|
|
34
|
+
authority: {
|
|
35
|
+
repository: {
|
|
45
36
|
kind: 'github.repository',
|
|
46
37
|
value: repository,
|
|
47
38
|
source: 'validation-task'
|
|
48
39
|
},
|
|
49
|
-
{
|
|
50
|
-
fact_id: 'fact:fixture-marker',
|
|
40
|
+
fixtureMarker: {
|
|
51
41
|
kind: 'github.issue.marker',
|
|
52
42
|
value: marker,
|
|
53
43
|
source: 'validation-task'
|
|
54
44
|
}
|
|
55
|
-
|
|
45
|
+
},
|
|
56
46
|
bindings: [
|
|
57
47
|
{
|
|
58
48
|
service: 'github',
|
|
59
49
|
action: 'issue.list',
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
field: 'repository',
|
|
51
|
+
authority: 'repository'
|
|
62
52
|
},
|
|
63
53
|
{
|
|
64
54
|
service: 'github',
|
|
65
55
|
action: 'issue.list',
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
field: 'fixture_marker',
|
|
57
|
+
authority: 'fixtureMarker'
|
|
68
58
|
},
|
|
69
59
|
{
|
|
70
60
|
service: 'github',
|
|
71
61
|
action: 'issue.comment',
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
service: 'github',
|
|
77
|
-
action: 'issue.comment',
|
|
78
|
-
context_field: 'issue_number',
|
|
79
|
-
fact_id: 'fact:discovered-issue-number'
|
|
62
|
+
field: 'repository',
|
|
63
|
+
authority: 'repository'
|
|
80
64
|
}
|
|
81
65
|
]
|
|
82
66
|
});
|
|
83
67
|
|
|
84
|
-
const
|
|
68
|
+
const mission = task.mission;
|
|
85
69
|
const broker = new CredentialBroker();
|
|
86
70
|
broker.connect({
|
|
87
71
|
principal_id: mission.principal.id,
|
|
@@ -95,7 +79,7 @@ const adapter = createGitHubProviderAdapter({ broker });
|
|
|
95
79
|
const cleanupHeaders = {
|
|
96
80
|
accept: 'application/vnd.github+json',
|
|
97
81
|
authorization: `Bearer ${token}`,
|
|
98
|
-
'user-agent': 'agent-authority-derived-mutation-validation-cleanup',
|
|
82
|
+
'user-agent': 'agent-authority-task-first-derived-mutation-validation-cleanup',
|
|
99
83
|
'x-github-api-version': '2022-11-28'
|
|
100
84
|
};
|
|
101
85
|
|
|
@@ -119,7 +103,7 @@ function discoveryRequest() {
|
|
|
119
103
|
|
|
120
104
|
async function discoverFixtureIssue() {
|
|
121
105
|
const request = discoveryRequest();
|
|
122
|
-
return
|
|
106
|
+
return task.run(request, async () => {
|
|
123
107
|
providerReadCalls += 1;
|
|
124
108
|
return adapter.execute({ mission, request });
|
|
125
109
|
});
|
|
@@ -132,7 +116,7 @@ async function commentOnIssue(issueNumber, body) {
|
|
|
132
116
|
context: { repository, issue_number: issueNumber, body }
|
|
133
117
|
};
|
|
134
118
|
|
|
135
|
-
return
|
|
119
|
+
return task.run(request, async () => {
|
|
136
120
|
providerMutationCalls += 1;
|
|
137
121
|
return adapter.execute({ mission, request });
|
|
138
122
|
});
|
|
@@ -151,9 +135,10 @@ async function cleanupComment(commentId) {
|
|
|
151
135
|
}
|
|
152
136
|
|
|
153
137
|
try {
|
|
154
|
-
console.log(
|
|
155
|
-
console.log(`Task root
|
|
156
|
-
console.log(
|
|
138
|
+
console.log('Task-first live workflow: discover one issue and comment only on that issue');
|
|
139
|
+
console.log(`Task root repository: ${task.authority('repository').value}`);
|
|
140
|
+
console.log(`Task root fixture marker: ${task.authority('fixtureMarker').value}`);
|
|
141
|
+
console.log('1. Discover fixture through task.run() and the reviewed GitHub provider adapter');
|
|
157
142
|
const discovered = await discoverFixtureIssue();
|
|
158
143
|
|
|
159
144
|
if (discovered.output.selected_issue_match_count !== 1) {
|
|
@@ -164,18 +149,21 @@ try {
|
|
|
164
149
|
const extractor = adapter.authorityExtractor(discoveryRequest(), 'github.issue.number');
|
|
165
150
|
if (!extractor) throw new Error('GitHub provider did not advertise the issue-number authority extractor');
|
|
166
151
|
|
|
167
|
-
const issueFact =
|
|
168
|
-
|
|
152
|
+
const issueFact = task.authorityFrom(discovered, {
|
|
153
|
+
name: 'discoveredIssue',
|
|
169
154
|
kind: 'github.issue.number',
|
|
170
|
-
from: ['
|
|
171
|
-
receipt: discovered.receipt,
|
|
172
|
-
evidence: discovered.evidence,
|
|
173
|
-
output: discovered.output,
|
|
155
|
+
from: ['repository', 'fixtureMarker'],
|
|
174
156
|
extractor
|
|
175
157
|
});
|
|
176
|
-
|
|
158
|
+
task.bind({
|
|
159
|
+
service: 'github',
|
|
160
|
+
action: 'issue.comment',
|
|
161
|
+
field: 'issue_number',
|
|
162
|
+
authority: 'discoveredIssue'
|
|
163
|
+
});
|
|
164
|
+
console.log(`2. task.authorityFrom() -> issue #${issueFact.value}`);
|
|
177
165
|
|
|
178
|
-
const validationBody = `Agent Authority live
|
|
166
|
+
const validationBody = `Agent Authority live task-first authorization validation (${new Date().toISOString()}). Temporary comment; CI removes it after the proof.`;
|
|
179
167
|
const allowedMutation = await commentOnIssue(issueFact.value, validationBody);
|
|
180
168
|
createdCommentId = allowedMutation.output.comment_id;
|
|
181
169
|
console.log(`3. ALLOW -> real GitHub comment mutation executed (comment ${createdCommentId})`);
|
|
@@ -188,14 +176,19 @@ try {
|
|
|
188
176
|
if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') {
|
|
189
177
|
throw error;
|
|
190
178
|
}
|
|
179
|
+
const explanation = task.explain(error);
|
|
191
180
|
console.log(`4. STEP-UP -> unrelated issue #${unrelatedIssue} blocked before provider mutation`);
|
|
181
|
+
console.log(` ${explanation.summary}`);
|
|
182
|
+
if (explanation.established_authority?.value !== issueFact.value) {
|
|
183
|
+
throw new Error('task-first authority-delta explanation lost the established issue authority');
|
|
184
|
+
}
|
|
192
185
|
}
|
|
193
186
|
|
|
194
187
|
if (providerMutationCalls !== 1) {
|
|
195
188
|
throw new Error(`expected exactly one task-side provider mutation before completion, got ${providerMutationCalls}`);
|
|
196
189
|
}
|
|
197
190
|
|
|
198
|
-
|
|
191
|
+
task.complete('live task-first evidence-derived mutation validation complete');
|
|
199
192
|
try {
|
|
200
193
|
await commentOnIssue(issueFact.value, 'THIS MUST NOT RUN AFTER TASK COMPLETION');
|
|
201
194
|
throw new Error('post-completion mutation unexpectedly executed');
|
|
@@ -213,8 +206,8 @@ try {
|
|
|
213
206
|
throw new Error(`expected exactly one provider mutation after blocked attempts, got ${providerMutationCalls}`);
|
|
214
207
|
}
|
|
215
208
|
|
|
216
|
-
console.log('PASS -> GitHub provider output became downstream authority
|
|
217
|
-
console.log('PASS -> unrelated and post-completion
|
|
209
|
+
console.log('PASS -> real GitHub provider output became task-first downstream authority through execution evidence and the reviewed extractor');
|
|
210
|
+
console.log('PASS -> unrelated and post-completion task-first calls produced zero additional provider mutations');
|
|
218
211
|
} finally {
|
|
219
212
|
if (createdCommentId) {
|
|
220
213
|
await cleanupComment(createdCommentId);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createTask } from '@nullsquare/agent-authority/task';
|
|
2
|
+
import { AuthorityApprovalRequiredError } from '@nullsquare/agent-authority/guard';
|
|
3
|
+
import { createRuntimeEnvironment } from '@nullsquare/agent-authority/runtime-env';
|
|
4
|
+
|
|
5
|
+
const repository = process.argv[2] || process.env.GITHUB_REPOSITORY || 'Null-Square/agent-authority';
|
|
6
|
+
const unrelatedRepository = process.argv[3] || 'octocat/Hello-World';
|
|
7
|
+
const env = createRuntimeEnvironment({ home: process.env.AGENT_AUTHORITY_HOME });
|
|
8
|
+
|
|
9
|
+
const connection = env.broker.getConnection({
|
|
10
|
+
principal_id: env.config.principal_id,
|
|
11
|
+
service: 'github'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!connection) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
'No unambiguous GitHub connection. Run: printf %s "$GITHUB_TOKEN" | agent-authority connect github --token-stdin'
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const task = createTask({
|
|
21
|
+
principal: env.config.principal_id,
|
|
22
|
+
agent: 'agent:connected-quickstart',
|
|
23
|
+
request: `Inspect only ${repository} through the connected GitHub account`,
|
|
24
|
+
permissions: {
|
|
25
|
+
github: {
|
|
26
|
+
allow: ['repo.read'],
|
|
27
|
+
deny: ['repo.write', 'repo.delete'],
|
|
28
|
+
constraints: {}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
authority: {
|
|
32
|
+
repository: { kind: 'github.repository', value: repository }
|
|
33
|
+
},
|
|
34
|
+
bindings: [
|
|
35
|
+
{ service: 'github', action: 'repo.read', field: 'repository', authority: 'repository' }
|
|
36
|
+
],
|
|
37
|
+
runtime: env.runtime
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(`Connected account -> ${connection.metadata?.login || connection.account_id}`);
|
|
41
|
+
console.log('Credential location -> Agent Authority broker/vault (not task context)');
|
|
42
|
+
console.log('Standing GitHub permission -> repo.read');
|
|
43
|
+
console.log(`Task authority -> ${repository}`);
|
|
44
|
+
|
|
45
|
+
const allowed = await task.execute({
|
|
46
|
+
service: 'github',
|
|
47
|
+
action: 'repo.read',
|
|
48
|
+
context: { repository }
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log(`ALLOW -> connected GitHub returned ${allowed.output.body.full_name}`);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await task.execute({
|
|
55
|
+
service: 'github',
|
|
56
|
+
action: 'repo.read',
|
|
57
|
+
context: { repository: unrelatedRepository }
|
|
58
|
+
});
|
|
59
|
+
throw new Error('unrelated repository unexpectedly executed');
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') {
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
console.log(`STEP-UP -> ${task.explain(error).summary}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const visibleState = JSON.stringify({
|
|
68
|
+
mission: task.mission,
|
|
69
|
+
authorities: task.authorities(),
|
|
70
|
+
connection: env.broker.listConnections(env.config.principal_id),
|
|
71
|
+
allowed_output: allowed.output
|
|
72
|
+
});
|
|
73
|
+
if (/github_pat_|gh[pousr]_|Bearer\s/i.test(visibleState)) {
|
|
74
|
+
throw new Error('credential-like value leaked into public task state');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('PASS -> connected credential stayed broker-internal and unrelated repository stayed outside task authority');
|