@nullsquare/agent-authority 0.4.5 → 0.4.7

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/ROADMAP.md CHANGED
@@ -1,26 +1,79 @@
1
1
  # Agent Authority Roadmap
2
2
 
3
- Agent Authority is implementation-first. We are not trying to invent a new authentication protocol or policy language.
3
+ Agent Authority is implementation-first, but the current bottleneck is now **product proof**, not another authorization subsystem.
4
4
 
5
- The product thesis we are validating is:
5
+ The product thesis is:
6
6
 
7
7
  > **Give an agent a task, not standing account permissions.**
8
8
 
9
- A human-approved task becomes temporary execution authority. As the agent discovers concrete resources through authorized work, authority may follow those resources through provenance-bound facts, but it may never silently broaden.
9
+ The differentiated mechanism is:
10
10
 
11
- ## Current invariant
11
+ > **Authority may follow exact resources discovered through already-authorized execution, without becoming ambient account authority.**
12
+
13
+ ## Core invariant
12
14
 
13
15
  ```text
14
16
  Task Lease authority <= Mission authority
15
17
  ```
16
18
 
17
- Across delegation and integrations:
19
+ Across delegation, transports and durable state:
18
20
 
19
21
  ```text
20
22
  authority may stay the same or shrink
21
23
  never silently grow
22
24
  ```
23
25
 
26
+ ## P0 — Task-first product proof — current priority
27
+
28
+ The engine has enough depth to test whether developers actually want this layer. Product work now outranks additional distributed/crypto infrastructure unless a real workflow proves the missing infrastructure is blocking adoption or safety.
29
+
30
+ - [x] task-first facade over Mission + Task Lease + Guard
31
+ - [x] explicit service permissions without requiring hand-authored Mission JSON
32
+ - [x] named task authority roots
33
+ - [x] `task.run()` guarded effect boundary
34
+ - [x] `task.authorityFrom()` strict evidence-derived authority
35
+ - [x] task-first binding of named authority to later effects
36
+ - [x] human-readable authority-delta explanation
37
+ - [x] same task-first calls can opt into durable local state by adding a store
38
+ - [x] self-contained GitHub-shaped task-first demo
39
+ - [x] deterministic utility regression benchmark
40
+ - [x] first live provider proof through task-first API: GitHub issue discovery -> exact issue comment
41
+ - [x] self-contained support/communications proof: Gmail thread -> exact Calendar attendee
42
+ - [x] self-contained operations/finance proof: ticket -> order -> payment -> exact full refund
43
+ - [x] automated blank-project quickstart against the current published npm package
44
+ - [x] blank-project real-provider quickstart: broad standing `repo.read` -> one task-authorized public GitHub repository
45
+ - [ ] coding workflow: issue -> branch -> files -> PR, with merge/deploy outside authority
46
+ - [ ] support/communications expansion: customer -> meeting + reply/CRM, or live task-first Google Actions proof
47
+ - [ ] bounded finance refund: derived payment amount can authorize a smaller refund without authorizing an over-refund
48
+ - [ ] first-time developer can complete a meaningful integration in under 10 minutes
49
+ - [ ] at least one external developer adopts the package without project-author assistance
50
+
51
+ The live GitHub task-first proof selected issue #9 through the reviewed provider mapping, established that issue through `task.authorityFrom()`, executed exactly one real comment mutation, blocked unrelated issue #1 with `authority_delta_required`, surfaced the established-vs-requested explanation, denied the same issue after task completion, and observed `reads=1` / `task_mutations=1` before cleanup.
52
+
53
+ The support/communications proof uses the same task-first API across Gmail and Calendar: one authorized `thread_id` establishes one canonical `sender_email` through the reviewed Google extractor; only that attendee can be used for the task-bound Calendar event, while another thread or attendee executes zero provider-shaped callbacks. The example mirrors the real Google adapter contract but does not replace the still-open public Google Actions evidence gate.
54
+
55
+ The operations/finance proof keeps one evidence-derived chain from support ticket -> order -> payment -> exact refund. The exact payment ID, amount in minor units, and currency are all bound before the refund callback can execute. Unrelated payment, over-refund, wrong currency, partial refund under the current equality model, and post-completion refund all execute zero additional refund callbacks. This proof exposed a deliberate product gap: current bindings are exact equality, so a legitimate partial refund also steps up. Do not add a general expression language; add a narrow derived numeric ceiling only when real workflow/adoption evidence shows partial refunds are required.
56
+
57
+ The fixture fresh-install quickstart is independently exercised from a blank Node 20 project against the latest public npm package. The passing proof installed `@nullsquare/agent-authority@0.4.6`, did not install the optional AI SDK, allowed the exact issue #42 effect, stepped up unrelated issue #7, and observed no unrelated callback. This is automated compatibility evidence, not a substitute for the still-open first-time-human under-10-minute test.
58
+
59
+ The live fresh-install quickstart goes one step further from another blank Node 20 project: Mission-level `github:repo.read` remains broader than the task, Task authority binds that action to `Null-Square/agent-authority`, one real public GitHub API call succeeds, and `octocat/Hello-World` produces `authority_delta_required` before a second `fetch()` can run. No credential is required for the default public-repository path. Authenticated/private-repository onboarding remains separate follow-on work.
60
+
61
+ Current utility regression metrics:
62
+
63
+ ```text
64
+ normal task completion rate
65
+ false approval rate
66
+ true authority-delta step-up rate
67
+ unauthorized effect rate
68
+ provider effects per completed task
69
+ ```
70
+
71
+ The deterministic fixture target is 100% normal completion, 0% false approvals, 100% true-delta step-up and 0% unauthorized effects. It is a regression fixture, not a real-world benchmark.
72
+
73
+ See `docs/product-proof.md`.
74
+
75
+ **Product gate:** do not prioritize deeper distributed persistence, provider-attestation protocols, new token formats, broad OAuth platform work, A2A, a policy DSL, another MCP control plane, or connector-count expansion until the product proof moves or a concrete workflow shows one of those items is necessary.
76
+
24
77
  ## M0 — Enforcement foundation — complete
25
78
 
26
79
  - [x] mission validation
@@ -31,7 +84,7 @@ never silently grow
31
84
  - [x] delegation attenuation
32
85
  - [x] mission revocation
33
86
  - [x] action receipts and request hashes
34
- - [x] protocol-neutral `guard.run()` enforcement
87
+ - [x] protocol-neutral guard enforcement
35
88
  - [x] one-time approvals
36
89
  - [x] mutation idempotency
37
90
  - [x] short-lived local agent-instance auth
@@ -41,143 +94,130 @@ never silently grow
41
94
  - [x] harness-managed connector grant proof
42
95
  - [x] Node 20/22 CI, coverage, package checks and CodeQL
43
96
 
44
- ## M1 — Task Lease / derived-authority proof — current
97
+ ## M1 — Task Lease / derived-authority proof — implementation established
45
98
 
46
- - [x] Task Lease object around an existing mission
99
+ - [x] Task Lease around an existing Mission
47
100
  - [x] explicit authority roots
48
- - [x] derived facts anchored to same-mission `ALLOW` receipts
101
+ - [x] derived facts anchored to same-Mission ALLOW receipts
49
102
  - [x] parent-fact lineage
50
103
  - [x] exact context-field bindings
51
104
  - [x] unresolved facts fail closed
52
- - [x] resource mismatch becomes an authority-delta step-up signal
53
- - [x] explicit mission deny remains the ceiling
105
+ - [x] resource mismatch becomes an authority-delta step-up
106
+ - [x] explicit Mission deny remains the ceiling
54
107
  - [x] task completion immediately removes authority
55
- - [x] independent task-lease expiry
56
- - [x] task-lease ID/hash in receipts
57
- - [x] self-contained cross-system demo: Gmail-thread fact -> Calendar attendee
58
- - [x] reusable Google REST adapter for Gmail thread reads and Calendar event mutations
59
- - [x] adversarial Gmail -> Calendar test proving unrelated and post-completion effects never invoke provider callbacks
60
- - [x] real connected-account smoke: Gmail self-test fixture -> exact attendee -> temporary Calendar event -> cleanup
61
- - [ ] rerun the same proof as a public GitHub Actions job after repository Google OAuth secrets are configured
108
+ - [x] independent Task Lease expiry
109
+ - [x] Task Lease ID/hash in receipts
110
+ - [x] self-contained Gmail-thread -> Calendar-attendee demo
111
+ - [x] reusable Google Gmail/Calendar adapter
112
+ - [x] adversarial Gmail -> Calendar zero-provider-call tests
113
+ - [x] connected-account Gmail -> Calendar smoke proof
114
+ - [ ] public GitHub Actions Gmail -> Calendar proof after repository OAuth secrets are configured
62
115
 
63
- **Success criterion:** a real multi-step agent workflow discovers a resource during an authorized read and can use exactly that resource in a later side effect, while an unrelated resource is technically blocked without requiring approval for every normal task step.
116
+ **Success criterion:** an authorized read can establish exactly one later resource as task authority while an unrelated resource is technically blocked without requiring approval for every normal task step.
64
117
 
65
- The core cross-provider behavior is now implemented and exercised against connected Gmail and Calendar accounts. Public Actions reproducibility remains the final M1 evidence gate because GitHub cannot reuse an interactive ChatGPT connector credential; the manual workflow is committed and expects a refresh token stored only as repository secrets.
118
+ The implementation criterion is met. Public Actions reproducibility remains a separate evidence gate because the interactive Google connector credential cannot be reused as repository secrets.
66
119
 
67
- ## M2 — Durable task execution — durable session established
68
-
69
- Build only what the real Task Lease workflows prove necessary.
120
+ ## M2 — Durable task execution — local durable session established
70
121
 
71
122
  - [x] authenticated local Task Lease persistence/recovery
123
+ - [x] exact Mission-hash binding on recovery
72
124
  - [x] atomic authenticated fact/binding/status transaction primitive
73
- - [x] stale-writer compare-and-swap protection for independent recovered worker views
74
- - [x] local per-lease transaction lock that fails closed on overlap
75
- - [x] ordinary running Task Lease mutations can use the durable transaction boundary through `DurableTaskLeaseSession`
76
- - [x] security-critical session evaluation refreshes current durable state before the next authority decision
77
- - [ ] approved authority delta can safely attenuate/update a live lease
78
- - [x] completion state survives process restart
79
- - [ ] durable lineage query: why was this exact action authorized?
80
- - [ ] multi-process stress/recovery tests for multiple agent workers operating under one lease
81
- - [ ] crash-safe coupling between remote provider side effects, receipts and durable Task Lease state
82
-
83
- `JsonFileTaskLeaseStore` writes the complete lease snapshot atomically and authenticates it with a purpose-derived HMAC key from the local Agent Authority master key. Recovery verifies the envelope before authority hydration, binds the snapshot to the exact mission hash, validates status/timestamps and the authority-fact DAG, restores evidence-derived provenance hashes, and verifies the reconstructed Task Lease hash.
84
-
85
- `JsonFileTaskLeaseStore.transact()` is the local durable mutation boundary: acquire one per-lease filesystem lock, reload authenticated current state, compare an optional expected lease hash, apply one synchronous Task Lease mutation, validate the complete authority graph, and atomically replace the authenticated snapshot. Stale writers receive `task_lease_state_conflict`; overlapping local transactions receive `task_lease_state_locked`.
86
-
87
- `DurableTaskLeaseSession` is the normal mutation facade over that primitive. `addRoot()`, legacy `derive()`, strict `deriveFromEvidence()`, `bind()` and `complete()` commit through compare-and-swap and update the session only after durable success. The session exposes detached mission/snapshot/fact views rather than its mutable internal Task Lease. It never auto-replays a stale semantic mutation after a conflict.
125
+ - [x] stale-writer compare-and-swap protection
126
+ - [x] local per-lease transaction lock
127
+ - [x] mission-alias hardening inside transactions
128
+ - [x] automatic durable Task Lease session
129
+ - [x] security-critical session refresh before authority evaluation
130
+ - [x] durable completion/expiry across restart
131
+ - [ ] approved authority delta safely updates a live durable task
132
+ - [ ] durable lineage query for one exact authorization decision
133
+ - [ ] stronger multi-process stress/recovery tooling
134
+ - [ ] crash-safe remote-effect/receipt/state coupling
88
135
 
89
- The session also implements the Task Lease `evaluate(runtime, request)` shape used by guard/MCP/broker paths. Evaluation refreshes authenticated durable state first, so another worker's already-committed completion or narrowing is observed before the next decision. Tests also prove that execution evidence captured at H0 cannot be automatically converted into a derived fact after another worker commits H1; derivation fails on CAS and requires explicit reconsideration.
136
+ The existing local durability layer is sufficient for product proof. The unchecked items remain research/follow-on work unless a real workflow demonstrates that they block useful adoption or safety.
90
137
 
91
- This does **not** yet turn a remote provider side effect and a local Task Lease transition into one distributed transaction. A different worker can still change durable state after an ALLOW decision and before an asynchronous provider effect begins. That TOCTOU/effect-coupling problem remains explicit follow-on M2 work rather than being hidden behind the local filesystem lock. See `docs/durable-task-leases.md`.
92
-
93
- **Success criterion:** a Task Lease survives restart without gaining authority or losing provenance, cooperating local workers cannot silently overwrite newer authority, and ordinary durable mutations use the CAS boundary by default through the session API. Authenticated recovery, local transactions and the durable session are now demonstrated; approved deltas, stronger multi-process stress and remote-effect coupling remain open.
138
+ **Success criterion:** restart and cooperating local workers do not expand authority or silently overwrite newer task state. This is established for the reference local backend.
94
139
 
95
140
  ## M3 — Trustworthy derived facts — two-provider proof established
96
141
 
97
- The first real Gmail -> Calendar integration showed that recording a host-supplied value plus selector was too weak for the strongest derived-authority claim. The compatibility `derive()` path remains host-trusted; new provider work should prefer execution-bound evidence and reviewed adapter extractors.
98
-
99
- - [x] define a small trusted-adapter extractor contract for authority-relevant normalized provider fields
100
- - [x] bind successful guarded outputs to the exact ALLOW receipt, request and output hash
101
- - [x] add `TaskLease.deriveFromEvidence()` so the caller cannot provide the authority value
102
- - [x] migrate Gmail sender -> Calendar attendee derivation to the evidence-verified path
103
- - [x] migrate the real GitHub issue discovery -> comment mutation proof to the same evidence-verified path
104
- - [x] adversarial tests for value substitution, output/evidence tampering, receipt replay, cross-lease reuse, wrong-operation extraction and dangerous selectors
105
- - [x] shared conformance fixtures for reviewed operation -> authority-field mappings across Google and GitHub
106
- - [ ] define provider/result attestation stronger than a trusted host output hash where practical
107
- - [ ] define freshness/invalidation rules when a source resource changes
142
+ - [x] reviewed adapter extractor contract
143
+ - [x] successful guarded output bound to exact ALLOW receipt/request/output hash
144
+ - [x] strict `deriveFromEvidence()` where caller cannot provide the authority value
145
+ - [x] Gmail sender -> Calendar attendee strict derivation
146
+ - [x] GitHub issue discovery -> comment strict derivation
147
+ - [x] substitution/tamper/replay/cross-lease/wrong-operation/dangerous-selector tests
148
+ - [x] shared Google/GitHub authority-extractor conformance fixtures
149
+ - [ ] stronger provider/result attestation where a real provider makes it practical
150
+ - [ ] source freshness/invalidation semantics where a real workflow requires it
108
151
 
109
- The shared contract is documented in `docs/authority-extractor-conformance.md`. Google and GitHub now use the same `guard.run()` -> execution evidence -> reviewed extractor -> `deriveFromEvidence()` primitive, and the same conformance suite attacks both mappings.
152
+ Do **not** build a general semantic policy language around this primitive.
110
153
 
111
- Do **not** build a general semantic policy language unless real integrations require it.
112
-
113
- **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.
154
+ **Success criterion:** strict provider-derived authority requires agreement between the exact guarded output, ALLOW receipt and reviewed extractor. This is demonstrated across Google and GitHub.
114
155
 
115
156
  ## M4 — Same task, multiple transports — complete
116
157
 
117
- Prove Agent Authority is not an MCP product or SDK wrapper.
118
-
119
- - [x] same Task Lease through ordinary `guard.run()` SDK call
158
+ - [x] same Task Lease through direct guard/SDK execution
120
159
  - [x] same Task Lease through MCP gateway
121
160
  - [x] same Task Lease through brokered provider execution
122
- - [x] at least one harness/tool-middleware integration whose configured executable tool path cannot bypass the Task Lease
123
- - [x] interoperability test vectors across transports
161
+ - [x] real Vercel AI SDK protected-tool path
162
+ - [x] interoperability/adversarial vectors across transports
124
163
 
125
- `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.
164
+ Changing transport or configured harness execution path does not expand task authority in the demonstrated paths.
126
165
 
127
- `test/integrations/ai-sdk.integration.mjs` drives the real Vercel AI SDK `ToolLoopAgent` through the protected tool set. An authorized task-bound tool executes once; an unrelated resource, an executable tool with no Agent Authority mapping, and a completed Task Lease are surfaced by the harness as `tool-error` results while the underlying side-effect counters remain zero. This proves the configured AI SDK tool execution path cannot silently bypass the Task Lease.
166
+ ## M5 Adoption UX follows product proof, not infrastructure breadth
128
167
 
129
- The harness claim is intentionally bounded: a malicious host that deliberately gives the model a separate unguarded executable channel remains outside Agent Authority's enforcement boundary.
168
+ Prioritize only the UX needed by successful P0 workflows.
130
169
 
131
- 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.
132
-
133
- **Success criterion:** changing transport or the configured harness execution path does not expand the task's authority. This is now demonstrated across direct SDK, MCP, brokered execution and the Vercel AI SDK `ToolLoopAgent` protected-tool path.
134
-
135
- ## M5 — Production credential and approval UX
136
-
137
- Only after the task-bound enforcement model is validated.
138
-
139
- - [ ] GitHub browser/App onboarding instead of token-stdin
140
- - [ ] reusable OAuth/OIDC connection engine
141
- - [ ] OS keychain / KMS-backed secret backend
142
- - [ ] automatic short-lived agent session bootstrap
170
+ - [x] credential-free fresh-install quickstart validated against the current public npm package
171
+ - [x] one low-friction real provider onboarding path: public GitHub read from a blank npm project
172
+ - [ ] authenticated/private-provider onboarding path
143
173
  - [ ] compact approval UI showing the exact authority delta
144
- - [ ] signed receipt experiment
174
+ - [ ] automatic short-lived agent session bootstrap where needed
175
+ - [ ] framework integration starter focused on task-first API
176
+ - [ ] external-developer quickstart feedback loop
145
177
 
146
- **Success criterion:** an external developer can install Agent Authority, connect one real provider, authorize one task, and complete it without exposing a long-lived credential to the model.
178
+ Items such as reusable OAuth/OIDC engines, KMS backends and signed-receipt experiments remain optional until product usage justifies them.
147
179
 
148
- ## M6 Ecosystem and contribution layer
180
+ **Success criterion:** an external developer can install Agent Authority, connect one real provider, authorize one meaningful task and complete it without exposing a long-lived credential to the model or learning the internal authority machinery first.
149
181
 
150
- - [ ] adapter/conformance starter template
151
- - [ ] framework integration examples
152
- - [ ] `good first issue` tasks based on real mappings/tests
153
- - [ ] independent contributor implementation of one adapter
154
- - [x] release packaging and npm publication
155
- - [ ] documentation site only when README/docs become too large
182
+ ## M6 Ecosystem only after repeatable adoption
156
183
 
157
- ## M7 Standards interoperability
184
+ - [ ] adapter/conformance starter template
185
+ - [ ] framework examples driven by real user requests
186
+ - [ ] good-first-issue tasks based on proven workflows
187
+ - [ ] independent contributor implementation of one mapping/adapter
188
+ - [x] npm release packaging and registry verification
189
+ - [ ] documentation site only when the current README/docs become genuinely limiting
158
190
 
159
- Only after operational evidence.
191
+ ## M7 — Standards interoperability only after operational evidence
160
192
 
161
193
  - [ ] map Task Lease concepts to emerging task/intent authorization work
162
194
  - [ ] accept external authorization envelopes where useful
163
195
  - [ ] avoid creating a competing identity/token standard
164
- - [ ] publish stable test vectors for non-amplification and authority lineage
165
- - [ ] evaluate an appropriate standards venue only if multiple independent implementations exist
166
-
167
- ## What we are not prioritizing
168
-
169
- - another agent harness
170
- - a new OAuth replacement
171
- - an MCP replacement
172
- - a giant connector marketplace
173
- - a proprietary universal policy DSL
174
- - a dashboard-first enterprise product
175
- - A2A support before task-bound tool execution is validated
196
+ - [ ] publish stable non-amplification/authority-lineage test vectors
197
+ - [ ] evaluate standards participation only after independent implementations/users exist
198
+
199
+ ## Freeze list
200
+
201
+ Unless a real workflow proves one is necessary now:
202
+
203
+ - distributed Task Lease databases
204
+ - generic storage abstraction layers
205
+ - provider-signed attestation protocol design
206
+ - new token or identity formats
207
+ - a general delegation protocol
208
+ - proprietary universal policy DSL
209
+ - broad OAuth platform work
210
+ - another MCP control plane
211
+ - A2A protocol implementation
212
+ - connector-count expansion for its own sake
213
+ - full distributed transactions across arbitrary remote providers
214
+ - dashboard-first enterprise product work
176
215
 
177
216
  ## Research questions
178
217
 
179
- 1. What provider-side or transport-side evidence can strengthen output integrity without turning Agent Authority into an attestation protocol?
180
- 2. How should an approved authority delta update a running task without opening a broader wildcard permission?
181
- 3. How should source-data changes invalidate downstream derived authority?
182
- 4. What provider/tool metadata is required to map operations to resource context reliably?
183
- 5. How should a local ALLOW decision be coupled to an asynchronous remote effect without holding a filesystem lease lock across network I/O?
218
+ 1. Can a first-time developer understand and integrate the task-first model in under 10 minutes?
219
+ 2. Which real workflows benefit enough from derived authority that an `if` statement is no longer sufficient?
220
+ 3. Where does Agent Authority create unnecessary approval friction or reduce useful task completion?
221
+ 4. How should an explicitly approved authority delta update a running task without opening wildcard authority?
222
+ 5. Which source-data changes actually require downstream authority invalidation in real workflows?
223
+ 6. What remote-effect coupling is necessary in practice, and which providers already offer idempotency/transaction primitives we can reuse instead of inventing our own protocol?
@@ -0,0 +1,130 @@
1
+ import { createTask } from '../src/task.js';
2
+ import { AuthorityApprovalRequiredError } from '../src/guard.js';
3
+
4
+ const NORMAL_TASKS = 40;
5
+ const DELTA_ATTACKS = 10;
6
+
7
+ function selectedItemExtractor({ receipt, output } = {}) {
8
+ if (receipt?.service !== 'demo' || receipt?.action !== 'item.discover') {
9
+ const error = new Error('wrong operation');
10
+ error.code = 'trusted_extractor_operation_mismatch';
11
+ throw error;
12
+ }
13
+ if (typeof output?.selected_item !== 'string') {
14
+ const error = new Error('invalid selected item');
15
+ error.code = 'trusted_extractor_output_invalid';
16
+ throw error;
17
+ }
18
+ return { extractor_id: 'benchmark.selected-item.v1', selector: 'output.selected_item' };
19
+ }
20
+
21
+ function newTask(index) {
22
+ const catalog = `catalog:${index}`;
23
+ return {
24
+ catalog,
25
+ task: createTask({
26
+ principal: 'user:benchmark',
27
+ agent: 'agent:benchmark',
28
+ request: `Discover and update the selected item for benchmark task ${index}`,
29
+ permissions: {
30
+ demo: {
31
+ allow: ['item.discover', 'item.update'],
32
+ deny: ['item.delete'],
33
+ constraints: { catalog: [catalog] }
34
+ }
35
+ },
36
+ authority: {
37
+ catalog: { kind: 'demo.catalog', value: catalog }
38
+ },
39
+ bindings: [
40
+ { service: 'demo', action: 'item.discover', field: 'catalog', authority: 'catalog' },
41
+ { service: 'demo', action: 'item.update', field: 'catalog', authority: 'catalog' }
42
+ ]
43
+ })
44
+ };
45
+ }
46
+
47
+ const metrics = {
48
+ normal_tasks: NORMAL_TASKS,
49
+ completed_normal_tasks: 0,
50
+ normal_approval_interruptions: 0,
51
+ delta_attacks: DELTA_ATTACKS,
52
+ delta_step_ups: 0,
53
+ unauthorized_effects: 0,
54
+ provider_effects: 0
55
+ };
56
+
57
+ for (let index = 0; index < NORMAL_TASKS; index += 1) {
58
+ const { task, catalog } = newTask(index);
59
+ const item = `item:${index}`;
60
+
61
+ const discovery = await task.run({
62
+ service: 'demo',
63
+ action: 'item.discover',
64
+ context: { catalog }
65
+ }, async () => {
66
+ metrics.provider_effects += 1;
67
+ return { selected_item: item };
68
+ });
69
+
70
+ task.authorityFrom(discovery, {
71
+ name: 'item',
72
+ kind: 'demo.item',
73
+ from: 'catalog',
74
+ extractor: selectedItemExtractor
75
+ });
76
+ task.bind({ service: 'demo', action: 'item.update', field: 'item', authority: 'item' });
77
+
78
+ try {
79
+ await task.run({
80
+ service: 'demo',
81
+ action: 'item.update',
82
+ context: { catalog, item }
83
+ }, async () => {
84
+ metrics.provider_effects += 1;
85
+ return { updated: item };
86
+ });
87
+ metrics.completed_normal_tasks += 1;
88
+ } catch (error) {
89
+ if (error instanceof AuthorityApprovalRequiredError) metrics.normal_approval_interruptions += 1;
90
+ else throw error;
91
+ }
92
+
93
+ if (index < DELTA_ATTACKS) {
94
+ try {
95
+ await task.run({
96
+ service: 'demo',
97
+ action: 'item.update',
98
+ context: { catalog, item: `unrelated:${index}` }
99
+ }, async () => {
100
+ metrics.provider_effects += 1;
101
+ metrics.unauthorized_effects += 1;
102
+ return { updated: `unrelated:${index}` };
103
+ });
104
+ } catch (error) {
105
+ if (error instanceof AuthorityApprovalRequiredError && error.code === 'authority_delta_required') {
106
+ metrics.delta_step_ups += 1;
107
+ } else {
108
+ throw error;
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ const percent = (numerator, denominator) => denominator === 0 ? 0 : (numerator / denominator) * 100;
115
+ const report = {
116
+ ...metrics,
117
+ normal_task_completion_rate: percent(metrics.completed_normal_tasks, metrics.normal_tasks),
118
+ false_approval_rate: percent(metrics.normal_approval_interruptions, metrics.normal_tasks),
119
+ delta_step_up_rate: percent(metrics.delta_step_ups, metrics.delta_attacks),
120
+ unauthorized_effect_rate: percent(metrics.unauthorized_effects, metrics.delta_attacks)
121
+ };
122
+
123
+ console.log(JSON.stringify(report, null, 2));
124
+
125
+ if (report.normal_task_completion_rate !== 100) throw new Error('normal task completion regressed');
126
+ if (report.false_approval_rate !== 0) throw new Error('normal tasks were interrupted by approval');
127
+ if (report.delta_step_up_rate !== 100) throw new Error('authority deltas were not consistently surfaced');
128
+ if (report.unauthorized_effect_rate !== 0) throw new Error('an unrelated effect executed');
129
+
130
+ console.log('PASS -> deterministic product utility benchmark preserved useful task completion and blocked unrelated effects');
@@ -0,0 +1,60 @@
1
+ # Task-owned vs broker-owned execution
2
+
3
+ The task-first facade supports two effect ownership modes with the same Task Lease semantics.
4
+
5
+ ## Application-owned effect
6
+
7
+ Use `task.run(request, callback)` when the application already owns the SDK or provider call:
8
+
9
+ ```js
10
+ const result = await task.run(request, () => existingSdkCall());
11
+ ```
12
+
13
+ The callback runs only after the Task Lease returns ALLOW. The task receives execution evidence for the exact callback output.
14
+
15
+ ## Agent Authority connected-provider effect
16
+
17
+ Use `task.execute(request)` when the provider credential and provider adapter should stay behind Agent Authority's broker boundary:
18
+
19
+ ```js
20
+ const result = await task.execute(request);
21
+ ```
22
+
23
+ This requires the task to be created with an `ExecutingAuthorityRuntime`, such as the runtime produced by the local `createRuntimeEnvironment()` helper.
24
+
25
+ The connected path performs:
26
+
27
+ ```text
28
+ Task Lease evaluation
29
+ |
30
+ +--> DENY / STEP-UP -> stop before provider readiness or credential resolution
31
+ |
32
+ v
33
+ connected-provider readiness
34
+ |
35
+ v
36
+ credential broker resolves secret internally
37
+ |
38
+ v
39
+ provider adapter executes
40
+ |
41
+ v
42
+ sanitized output + ALLOW receipt + execution evidence
43
+ ```
44
+
45
+ `task.execute()` converts broker runtime `deny` and `require_approval` results into the same `AuthorityDeniedError` and `AuthorityApprovalRequiredError` classes used by `task.run()`.
46
+
47
+ Successful connected execution can therefore feed directly into `task.authorityFrom()` when a reviewed provider extractor exists.
48
+
49
+ ## Credential boundary
50
+
51
+ The provider credential belongs to the broker/runtime, not the task request. It should not be copied into:
52
+
53
+ - Mission or Task Lease authority facts;
54
+ - model/tool arguments;
55
+ - action receipts;
56
+ - execution evidence;
57
+ - provider-normalized output;
58
+ - public connection listings.
59
+
60
+ The local runtime's encrypted file vault is a developer/trusted-host reference implementation. Production applications should use an appropriate secret manager/KMS and provider-native credential lifecycle.