@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.
@@ -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
- - [ ] the same task-first API works in-memory and with durable local state;
75
- - [ ] useful-task completion stays high under the product benchmark;
76
- - [ ] normal task actions do not trigger unnecessary approvals;
77
- - [ ] unrelated-resource effects execute zero provider callbacks;
78
- - [ ] approval/step-up output explains the established authority and requested delta clearly;
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 the fixture as the product matures.
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 underlying connected account may have broad access; task authority follows only the customer/resource discovered through the authorized thread.
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
- This is the strongest long-term proof because it combines resource lineage with an amount ceiling.
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 { createTaskLease } from '../src/task-lease.js';
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 mission = {
23
- version: '0.1',
20
+ const task = createTask({
24
21
  mission_id: 'mission:live-derived-github-mutation',
25
- principal: { id: 'user:validation' },
26
- agent: { id: 'agent:github-actions-validation' },
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
- resources: [
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
- context_field: 'repository',
61
- fact_id: 'fact:repository'
50
+ field: 'repository',
51
+ authority: 'repository'
62
52
  },
63
53
  {
64
54
  service: 'github',
65
55
  action: 'issue.list',
66
- context_field: 'fixture_marker',
67
- fact_id: 'fact:fixture-marker'
56
+ field: 'fixture_marker',
57
+ authority: 'fixtureMarker'
68
58
  },
69
59
  {
70
60
  service: 'github',
71
61
  action: 'issue.comment',
72
- context_field: 'repository',
73
- fact_id: 'fact:repository'
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 guard = createTaskLeaseGuard({ lease, runtime: new AuthorityRuntime() });
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 guard.run(request, async () => {
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 guard.run(request, async () => {
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(`Task root repository: ${repository}`);
155
- console.log(`Task root fixture marker: ${marker}`);
156
- console.log('1. Discover fixture through the reviewed GitHub provider adapter');
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 = lease.deriveFromEvidence({
168
- fact_id: 'fact:discovered-issue-number',
152
+ const issueFact = task.authorityFrom(discovered, {
153
+ name: 'discoveredIssue',
169
154
  kind: 'github.issue.number',
170
- from: ['fact:repository', 'fact:fixture-marker'],
171
- receipt: discovered.receipt,
172
- evidence: discovered.evidence,
173
- output: discovered.output,
155
+ from: ['repository', 'fixtureMarker'],
174
156
  extractor
175
157
  });
176
- console.log(`2. Evidence-verified authority -> issue #${issueFact.value}`);
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 evidence-derived authorization validation (${new Date().toISOString()}). Temporary comment; CI removes it after the proof.`;
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
- lease.complete('live evidence-derived mutation validation complete');
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 only through execution evidence and a reviewed extractor');
217
- console.log('PASS -> unrelated and post-completion mutations produced zero additional provider mutation calls');
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');