@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.
@@ -0,0 +1,29 @@
1
+ # v0.4.7 connected-execution candidate
2
+
3
+ This candidate closes a concrete adoption gap between the task-first API and the existing credential broker/provider runtime.
4
+
5
+ ## Candidate changes
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
+
15
+ ## Security boundary
16
+
17
+ This 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.
18
+
19
+ The local encrypted vault remains a trusted-local-host developer reference backend.
20
+
21
+ ## Release gate
22
+
23
+ Before version publication:
24
+
25
+ - unit/adversarial tests must prove credential isolation and zero provider execution on task authority delta;
26
+ - packed consumer must import and execute the new public surfaces;
27
+ - live connected GitHub workflow must pass through the encrypted local runtime;
28
+ - Node 20/22, coverage, AI SDK, existing live GitHub proofs and CodeQL must remain green;
29
+ - registry verification must be performed after npm publication before the public release marker is updated.
@@ -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');
@@ -0,0 +1,84 @@
1
+ import { createTask } from '@nullsquare/agent-authority/task';
2
+ import { AuthorityApprovalRequiredError } from '@nullsquare/agent-authority/guard';
3
+
4
+ const allowedRepository = process.argv[2] || 'Null-Square/agent-authority';
5
+ const blockedRepository = process.argv[3] || 'octocat/Hello-World';
6
+
7
+ const task = createTask({
8
+ principal: 'user:quickstart',
9
+ agent: 'agent:quickstart',
10
+ request: `Inspect only ${allowedRepository}`,
11
+ permissions: {
12
+ github: {
13
+ // Model the standing account/app capability as broader than this task.
14
+ // The Task authority root below narrows repo.read to one repository.
15
+ allow: ['repo.read'],
16
+ deny: ['repo.write', 'repo.delete'],
17
+ constraints: {}
18
+ }
19
+ },
20
+ authority: {
21
+ repository: { kind: 'github.repository', value: allowedRepository }
22
+ },
23
+ bindings: [
24
+ { service: 'github', action: 'repo.read', field: 'repository', authority: 'repository' }
25
+ ]
26
+ });
27
+
28
+ let outboundCalls = 0;
29
+
30
+ async function readRepository(repository) {
31
+ return task.run({
32
+ service: 'github',
33
+ action: 'repo.read',
34
+ context: { repository }
35
+ }, async () => {
36
+ outboundCalls += 1;
37
+
38
+ const headers = {
39
+ accept: 'application/vnd.github+json',
40
+ 'user-agent': 'agent-authority-live-quickstart',
41
+ 'x-github-api-version': '2022-11-28'
42
+ };
43
+ if (process.env.GITHUB_TOKEN) {
44
+ headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
45
+ }
46
+
47
+ const response = await fetch(`https://api.github.com/repos/${repository}`, { headers });
48
+ if (!response.ok) {
49
+ throw new Error(`GitHub returned ${response.status} for ${repository}`);
50
+ }
51
+
52
+ const body = await response.json();
53
+ return {
54
+ full_name: body.full_name,
55
+ private: body.private,
56
+ html_url: body.html_url
57
+ };
58
+ });
59
+ }
60
+
61
+ console.log(`Standing GitHub permission -> repo.read`);
62
+ console.log(`Task authority -> ${allowedRepository}`);
63
+ console.log(`GitHub mode -> ${process.env.GITHUB_TOKEN ? 'authenticated token' : 'public API; no credential required'}`);
64
+
65
+ const allowed = await readRepository(allowedRepository);
66
+ console.log(`ALLOW -> real GitHub returned ${allowed.output.full_name}`);
67
+
68
+ try {
69
+ await readRepository(blockedRepository);
70
+ throw new Error('unrelated repository unexpectedly reached GitHub');
71
+ } catch (error) {
72
+ if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') {
73
+ throw error;
74
+ }
75
+
76
+ console.log(`STEP-UP -> ${task.explain(error).summary}`);
77
+ }
78
+
79
+ if (outboundCalls !== 1) {
80
+ throw new Error(`expected exactly one outbound GitHub call, observed ${outboundCalls}`);
81
+ }
82
+
83
+ task.complete('live GitHub quickstart complete');
84
+ console.log('PASS -> broader standing repo.read permission could not reach an unrelated repository for this task');
@@ -0,0 +1,101 @@
1
+ import { createTask } from '@nullsquare/agent-authority/task';
2
+ import { AuthorityApprovalRequiredError } from '@nullsquare/agent-authority/guard';
3
+ import { githubIssueListSelectedNumberAuthorityExtractor } from '@nullsquare/agent-authority/providers/github';
4
+
5
+ const repository = 'acme/app';
6
+ const marker = 'quickstart-selected-issue';
7
+
8
+ const task = createTask({
9
+ principal: 'user:quickstart',
10
+ agent: 'agent:quickstart',
11
+ request: 'Handle the issue selected for this task and comment only on that issue',
12
+ permissions: {
13
+ github: {
14
+ allow: ['issue.list', 'issue.comment'],
15
+ deny: ['repo.delete'],
16
+ constraints: { repository: [repository] }
17
+ }
18
+ },
19
+ authority: {
20
+ repository: { kind: 'github.repository', value: repository },
21
+ marker: { kind: 'github.issue.marker', value: marker }
22
+ },
23
+ bindings: [
24
+ { service: 'github', action: 'issue.list', field: 'repository', authority: 'repository' },
25
+ { service: 'github', action: 'issue.list', field: 'fixture_marker', authority: 'marker' },
26
+ { service: 'github', action: 'issue.comment', field: 'repository', authority: 'repository' }
27
+ ]
28
+ });
29
+
30
+ let effects = 0;
31
+
32
+ const discovery = await task.run({
33
+ service: 'github',
34
+ action: 'issue.list',
35
+ context: { repository, fixture_marker: marker }
36
+ }, async () => {
37
+ effects += 1;
38
+
39
+ // This is provider-shaped fixture output so the quickstart needs no account.
40
+ // In a real app, replace only this callback with the SDK/provider call you already use.
41
+ return {
42
+ provider: 'github',
43
+ status: 200,
44
+ ok: true,
45
+ selected_issue_number: 42,
46
+ selected_issue_title: 'Quickstart issue',
47
+ selected_issue_match_count: 1,
48
+ selected_issue_marker: marker
49
+ };
50
+ });
51
+
52
+ const issue = task.authorityFrom(discovery, {
53
+ name: 'issue',
54
+ kind: 'github.issue.number',
55
+ from: ['repository', 'marker'],
56
+ extractor: githubIssueListSelectedNumberAuthorityExtractor
57
+ });
58
+
59
+ task.bind({
60
+ service: 'github',
61
+ action: 'issue.comment',
62
+ field: 'issue_number',
63
+ authority: 'issue'
64
+ });
65
+
66
+ await task.run({
67
+ service: 'github',
68
+ action: 'issue.comment',
69
+ context: { repository, issue_number: issue.value, body: 'Handled.' }
70
+ }, async () => {
71
+ effects += 1;
72
+ return { comment_id: 1001 };
73
+ });
74
+
75
+ console.log(`ALLOW -> task discovered issue #${issue.value} and the exact comment effect ran`);
76
+
77
+ try {
78
+ await task.run({
79
+ service: 'github',
80
+ action: 'issue.comment',
81
+ context: { repository, issue_number: 7, body: 'This must not run.' }
82
+ }, async () => {
83
+ effects += 1;
84
+ return { comment_id: 1002 };
85
+ });
86
+
87
+ throw new Error('unrelated issue unexpectedly executed');
88
+ } catch (error) {
89
+ if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') {
90
+ throw error;
91
+ }
92
+
93
+ console.log(`STEP-UP -> ${task.explain(error).summary}`);
94
+ }
95
+
96
+ if (effects !== 2) {
97
+ throw new Error(`expected exactly 2 provider-shaped effects, observed ${effects}`);
98
+ }
99
+
100
+ task.complete('quickstart complete');
101
+ console.log('PASS -> useful task work ran; unrelated standing permission did not become task authority');