@nullsquare/agent-authority 0.4.0

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.
Files changed (58) hide show
  1. package/CONTRIBUTING.md +93 -0
  2. package/LICENSE +201 -0
  3. package/README.md +390 -0
  4. package/ROADMAP.md +149 -0
  5. package/SECURITY.md +116 -0
  6. package/docs/account-connections.md +173 -0
  7. package/docs/announcement-draft.md +13 -0
  8. package/docs/architecture.md +106 -0
  9. package/docs/assets/agent-authority-cover.svg +41 -0
  10. package/docs/clear-path.md +53 -0
  11. package/docs/cli.md +130 -0
  12. package/docs/evidence.md +143 -0
  13. package/docs/harness-bridge-mode.md +136 -0
  14. package/docs/harness-integration.md +223 -0
  15. package/docs/integration-contract.md +132 -0
  16. package/docs/integrations/vercel-ai-sdk.md +161 -0
  17. package/docs/launch-checklist.md +29 -0
  18. package/docs/npm-release.md +19 -0
  19. package/docs/openclaw-integration.md +97 -0
  20. package/docs/package-consumer-validation.md +18 -0
  21. package/docs/release-candidate-status.md +3 -0
  22. package/docs/release-guardrails.md +8 -0
  23. package/docs/release-notes-v0.4.md +26 -0
  24. package/docs/release-scope.md +3 -0
  25. package/docs/ship-criteria.md +3 -0
  26. package/docs/task-leases.md +253 -0
  27. package/docs/validation.md +124 -0
  28. package/examples/demo.js +19 -0
  29. package/examples/direct-guard.js +50 -0
  30. package/examples/harness-managed-connectors.js +72 -0
  31. package/examples/live-github-derived-mutation.js +208 -0
  32. package/examples/live-github-task-lease.js +80 -0
  33. package/examples/mission.json +20 -0
  34. package/examples/missions/chatgpt-web-validation.json +33 -0
  35. package/examples/openclaw-tool-wrapper.js +49 -0
  36. package/examples/task-lease-demo.js +98 -0
  37. package/examples/validation-mcp-upstream.js +112 -0
  38. package/package.json +80 -0
  39. package/src/agent-auth.js +135 -0
  40. package/src/approvals.js +157 -0
  41. package/src/cli.js +335 -0
  42. package/src/connections.js +203 -0
  43. package/src/execution.js +174 -0
  44. package/src/guard.js +79 -0
  45. package/src/harness-bridge.js +131 -0
  46. package/src/idempotency.js +118 -0
  47. package/src/index.js +291 -0
  48. package/src/integrations/ai-sdk.js +59 -0
  49. package/src/keys.js +15 -0
  50. package/src/mcp-gateway.js +142 -0
  51. package/src/mcp-remote.js +102 -0
  52. package/src/mcp-server.js +102 -0
  53. package/src/providers/github.js +149 -0
  54. package/src/runtime-env.js +53 -0
  55. package/src/sdk.js +75 -0
  56. package/src/server.js +146 -0
  57. package/src/storage.js +213 -0
  58. package/src/task-lease.js +266 -0
@@ -0,0 +1,208 @@
1
+ import { AuthorityRuntime } from '../src/index.js';
2
+ import {
3
+ AuthorityApprovalRequiredError,
4
+ AuthorityDeniedError,
5
+ createTaskLeaseGuard
6
+ } from '../src/guard.js';
7
+ import { createTaskLease } from '../src/task-lease.js';
8
+
9
+ const repository = process.env.AA_VALIDATION_REPOSITORY || 'Null-Square/agent-authority';
10
+ const marker = process.env.AA_VALIDATION_MARKER || 'agent-authority-live-fixture-v1';
11
+ const token = process.env.GITHUB_TOKEN;
12
+
13
+ if (!token) {
14
+ throw new Error('GITHUB_TOKEN is required for the live derived-mutation validation');
15
+ }
16
+
17
+ const [owner, repo] = repository.split('/');
18
+ if (!owner || !repo) throw new Error(`invalid repository: ${repository}`);
19
+
20
+ const mission = {
21
+ version: '0.1',
22
+ mission_id: 'mission:live-derived-github-mutation',
23
+ principal: { id: 'user:validation' },
24
+ agent: { id: 'agent:github-actions-validation' },
25
+ objective: 'Discover the validation issue and comment only on that issue',
26
+ resources: [
27
+ {
28
+ service: 'github',
29
+ allow: ['issue.list', 'issue.comment'],
30
+ deny: ['issue.close', 'issue.delete', 'repo.write', 'repo.delete'],
31
+ constraints: { repository: [repository] }
32
+ }
33
+ ],
34
+ constraints: { expires_at: '2099-01-01T00:00:00Z' }
35
+ };
36
+
37
+ const lease = createTaskLease({
38
+ mission,
39
+ request: 'Find the Agent Authority live validation fixture and leave one validation comment',
40
+ roots: [
41
+ {
42
+ fact_id: 'fact:repository',
43
+ kind: 'github.repository',
44
+ value: repository,
45
+ source: 'validation-task'
46
+ }
47
+ ],
48
+ bindings: [
49
+ {
50
+ service: 'github',
51
+ action: 'issue.list',
52
+ context_field: 'repository',
53
+ fact_id: 'fact:repository'
54
+ },
55
+ {
56
+ service: 'github',
57
+ action: 'issue.comment',
58
+ context_field: 'repository',
59
+ fact_id: 'fact:repository'
60
+ },
61
+ {
62
+ service: 'github',
63
+ action: 'issue.comment',
64
+ context_field: 'issue_number',
65
+ fact_id: 'fact:discovered-issue-number'
66
+ }
67
+ ]
68
+ });
69
+
70
+ const guard = createTaskLeaseGuard({ lease, runtime: new AuthorityRuntime() });
71
+ const headers = {
72
+ accept: 'application/vnd.github+json',
73
+ authorization: `Bearer ${token}`,
74
+ 'user-agent': 'agent-authority-derived-mutation-validation',
75
+ 'x-github-api-version': '2022-11-28'
76
+ };
77
+
78
+ let providerReadCalls = 0;
79
+ let providerMutationCalls = 0;
80
+ let cleanupCalls = 0;
81
+ let createdCommentId = null;
82
+
83
+ async function githubJson(url, options = {}) {
84
+ const response = await fetch(url, {
85
+ ...options,
86
+ headers: { ...headers, ...(options.headers || {}) }
87
+ });
88
+ if (!response.ok) {
89
+ const body = await response.text();
90
+ throw new Error(`GitHub ${response.status}: ${body.slice(0, 300)}`);
91
+ }
92
+ if (response.status === 204) return null;
93
+ return response.json();
94
+ }
95
+
96
+ async function discoverFixtureIssue() {
97
+ return guard.run(
98
+ {
99
+ service: 'github',
100
+ action: 'issue.list',
101
+ context: { repository }
102
+ },
103
+ async () => {
104
+ providerReadCalls += 1;
105
+ const issues = await githubJson(
106
+ `https://api.github.com/repos/${owner}/${repo}/issues?state=open&per_page=100`
107
+ );
108
+ const fixture = issues.find((issue) => !issue.pull_request && issue.body?.includes(marker));
109
+ if (!fixture) throw new Error(`validation fixture with marker ${marker} was not found`);
110
+ return { number: fixture.number, title: fixture.title };
111
+ }
112
+ );
113
+ }
114
+
115
+ async function commentOnIssue(issueNumber, body) {
116
+ return guard.run(
117
+ {
118
+ service: 'github',
119
+ action: 'issue.comment',
120
+ context: { repository, issue_number: issueNumber }
121
+ },
122
+ async () => {
123
+ providerMutationCalls += 1;
124
+ const comment = await githubJson(
125
+ `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
126
+ {
127
+ method: 'POST',
128
+ headers: { 'content-type': 'application/json' },
129
+ body: JSON.stringify({ body })
130
+ }
131
+ );
132
+ return { id: comment.id, html_url: comment.html_url };
133
+ }
134
+ );
135
+ }
136
+
137
+ async function cleanupComment(commentId) {
138
+ cleanupCalls += 1;
139
+ await githubJson(
140
+ `https://api.github.com/repos/${owner}/${repo}/issues/comments/${commentId}`,
141
+ { method: 'DELETE' }
142
+ );
143
+ }
144
+
145
+ try {
146
+ console.log(`Task root repository: ${repository}`);
147
+ console.log('1. Discover fixture through an authorized live GitHub issue-list call');
148
+ const discovered = await discoverFixtureIssue();
149
+ console.log(` ALLOW -> discovered issue #${discovered.output.number}: ${discovered.output.title}`);
150
+
151
+ lease.derive({
152
+ fact_id: 'fact:discovered-issue-number',
153
+ kind: 'github.issue.number',
154
+ value: discovered.output.number,
155
+ from: ['fact:repository'],
156
+ receipt: discovered.receipt,
157
+ selector: 'output.number'
158
+ });
159
+ console.log(`2. Derived authority -> issue #${discovered.output.number}`);
160
+
161
+ const validationBody = `Agent Authority live derived-authority validation (${new Date().toISOString()}). Temporary comment; CI removes it after the proof.`;
162
+ const allowedMutation = await commentOnIssue(discovered.output.number, validationBody);
163
+ createdCommentId = allowedMutation.output.id;
164
+ console.log(`3. ALLOW -> real GitHub comment mutation executed (comment ${createdCommentId})`);
165
+
166
+ const unrelatedIssue = discovered.output.number === 1 ? 2 : 1;
167
+ try {
168
+ await commentOnIssue(unrelatedIssue, 'THIS MUST NEVER REACH GITHUB');
169
+ throw new Error('unrelated issue mutation unexpectedly executed');
170
+ } catch (error) {
171
+ if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') {
172
+ throw error;
173
+ }
174
+ console.log(`4. STEP-UP -> unrelated issue #${unrelatedIssue} blocked before provider mutation`);
175
+ }
176
+
177
+ if (providerMutationCalls !== 1) {
178
+ throw new Error(`expected exactly one task-side provider mutation before completion, got ${providerMutationCalls}`);
179
+ }
180
+
181
+ lease.complete('live derived-mutation validation complete');
182
+ try {
183
+ await commentOnIssue(discovered.output.number, 'THIS MUST NOT RUN AFTER TASK COMPLETION');
184
+ throw new Error('post-completion mutation unexpectedly executed');
185
+ } catch (error) {
186
+ if (!(error instanceof AuthorityDeniedError) || error.code !== 'task_lease_completed') {
187
+ throw error;
188
+ }
189
+ console.log(`5. DENY -> post-completion mutation blocked for issue #${discovered.output.number}`);
190
+ }
191
+
192
+ if (providerReadCalls !== 1) {
193
+ throw new Error(`expected exactly one provider discovery call, got ${providerReadCalls}`);
194
+ }
195
+ if (providerMutationCalls !== 1) {
196
+ throw new Error(`expected exactly one provider mutation after blocked attempts, got ${providerMutationCalls}`);
197
+ }
198
+
199
+ console.log('PASS -> dynamic resource was discovered from GitHub, derived into the Task Lease, and mutated exactly once');
200
+ console.log('PASS -> unrelated and post-completion mutations produced zero additional provider mutation calls');
201
+ } finally {
202
+ if (createdCommentId) {
203
+ await cleanupComment(createdCommentId);
204
+ console.log(`Cleanup -> deleted temporary validation comment ${createdCommentId} (outside the agent authority proof)`);
205
+ }
206
+ console.log(`Provider calls observed before cleanup: reads=${providerReadCalls}, task_mutations=${providerMutationCalls}`);
207
+ console.log(`Harness cleanup calls: ${cleanupCalls}`);
208
+ }
@@ -0,0 +1,80 @@
1
+ import { AuthorityRuntime } from '../src/index.js';
2
+ import { AuthorityApprovalRequiredError, createTaskLeaseGuard } from '../src/guard.js';
3
+ import { createTaskLease } from '../src/task-lease.js';
4
+
5
+ const allowedRepository = process.argv[2] || 'Null-Square/agent-authority';
6
+ const blockedRepository = process.argv[3] || 'octocat/Hello-World';
7
+
8
+ const mission = {
9
+ version: '0.1',
10
+ mission_id: 'mission:live-github-validation',
11
+ principal: { id: 'user:validation' },
12
+ agent: { id: 'agent:ordinary-node-app' },
13
+ objective: 'Read only the repository authorized for this task',
14
+ resources: [{
15
+ service: 'github',
16
+ allow: ['repo.read'],
17
+ deny: ['repo.delete', 'repo.write'],
18
+ constraints: {}
19
+ }],
20
+ constraints: { expires_at: '2099-01-01T00:00:00Z' }
21
+ };
22
+
23
+ const lease = createTaskLease({
24
+ mission,
25
+ request: `Inspect ${allowedRepository}`,
26
+ roots: [{
27
+ fact_id: 'fact:repository',
28
+ kind: 'github.repository',
29
+ value: allowedRepository
30
+ }],
31
+ bindings: [{
32
+ service: 'github',
33
+ action: 'repo.read',
34
+ context_field: 'repository',
35
+ fact_id: 'fact:repository'
36
+ }]
37
+ });
38
+
39
+ const guard = createTaskLeaseGuard({ lease, runtime: new AuthorityRuntime() });
40
+ let outboundCalls = 0;
41
+
42
+ async function githubRead(repository) {
43
+ return guard.run({
44
+ service: 'github',
45
+ action: 'repo.read',
46
+ context: { repository }
47
+ }, async () => {
48
+ outboundCalls += 1;
49
+ const headers = {
50
+ accept: 'application/vnd.github+json',
51
+ 'user-agent': 'agent-authority-live-validation'
52
+ };
53
+ if (process.env.GITHUB_TOKEN) headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
54
+
55
+ const response = await fetch(`https://api.github.com/repos/${repository}`, { headers });
56
+ if (!response.ok) throw new Error(`GitHub returned ${response.status}`);
57
+ const body = await response.json();
58
+ return { full_name: body.full_name, private: body.private, html_url: body.html_url };
59
+ });
60
+ }
61
+
62
+ console.log(`Task allows exactly: ${allowedRepository}`);
63
+ console.log(`Credential mode: ${process.env.GITHUB_TOKEN ? 'authenticated GitHub token' : 'public GitHub API'}`);
64
+
65
+ const allowed = await githubRead(allowedRepository);
66
+ console.log(`ALLOW -> live GitHub returned ${allowed.output.full_name}`);
67
+ console.log(`Outbound GitHub calls: ${outboundCalls}`);
68
+
69
+ try {
70
+ await githubRead(blockedRepository);
71
+ throw new Error('blocked repository unexpectedly executed');
72
+ } catch (error) {
73
+ if (!(error instanceof AuthorityApprovalRequiredError) || error.code !== 'authority_delta_required') throw error;
74
+ console.log(`STEP-UP -> ${blockedRepository} is outside task authority`);
75
+ }
76
+
77
+ if (outboundCalls !== 1) throw new Error(`expected exactly one outbound GitHub call, got ${outboundCalls}`);
78
+ console.log('PASS -> unrelated repository was blocked before fetch()');
79
+
80
+ lease.complete('live validation complete');
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": "0.1",
3
+ "mission_id": "mission:demo",
4
+ "principal": { "id": "user:demo" },
5
+ "agent": { "id": "agent:demo", "harness": "local" },
6
+ "objective": "Maintain an approved project without destructive or billing actions",
7
+ "resources": [
8
+ { "service": "github", "allow": ["repo.read", "repo.write", "pull_request.*"], "deny": ["repo.delete", "billing.*"] },
9
+ { "service": "google", "allow": ["gmail.read", "gmail.draft", "gmail.send"], "deny": ["gmail.delete"] },
10
+ { "service": "cloudflare", "allow": ["workers.read", "workers.deploy"], "deny": ["account.delete", "billing.*"] }
11
+ ],
12
+ "constraints": {
13
+ "max_delegation_depth": 2,
14
+ "budget": { "currency": "USD", "amount": 100 },
15
+ "expires_at": "2027-01-01T00:00:00Z"
16
+ },
17
+ "approvals": [
18
+ { "match": { "service": "google", "action": "gmail.send" }, "required": true, "reason": "outbound communication requires human approval" }
19
+ ]
20
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": "0.1",
3
+ "mission_id": "mission:chatgpt-web-validation",
4
+ "principal": {
5
+ "id": "user:local"
6
+ },
7
+ "agent": {
8
+ "id": "agent:chatgpt-web"
9
+ },
10
+ "objective": "Validate that a web agent can read only the approved public Agent Authority repository through an MCP gateway.",
11
+ "resources": [
12
+ {
13
+ "service": "mcp:validation-upstream",
14
+ "allow": [
15
+ "tool.github_repo_metadata"
16
+ ],
17
+ "deny": [
18
+ "tool.dangerous_demo_write",
19
+ "*write*",
20
+ "*delete*"
21
+ ],
22
+ "constraints": {
23
+ "repository": [
24
+ "Null-Square/agent-authority"
25
+ ]
26
+ }
27
+ }
28
+ ],
29
+ "constraints": {
30
+ "max_delegation_depth": 0
31
+ },
32
+ "approvals": []
33
+ }
@@ -0,0 +1,49 @@
1
+ import { AgentAuthorityClient } from '../src/sdk.js';
2
+
3
+ // Thin integration pattern for an OpenClaw/plugin-style tool wrapper.
4
+ // The wrapper owns no provider credentials. It forwards normalized authority
5
+ // requests to the Agent Authority sidecar and returns sanitized output.
6
+
7
+ const authority = new AgentAuthorityClient({
8
+ baseUrl: process.env.AGENT_AUTHORITY_URL || 'http://127.0.0.1:8787'
9
+ });
10
+
11
+ export function createAuthorityTool({ mission }) {
12
+ return async function executeAuthorityTool({ service, action, context = {}, params = {} }) {
13
+ const result = await authority.execute(mission, {
14
+ service,
15
+ action,
16
+ context,
17
+ params
18
+ });
19
+
20
+ if (result.result?.decision === 'deny') {
21
+ const error = new Error(result.result.reason || 'Agent Authority denied the action');
22
+ error.code = result.result.code || 'authority_denied';
23
+ throw error;
24
+ }
25
+
26
+ if (result.result?.decision === 'require_approval') {
27
+ return {
28
+ status: 'require_approval',
29
+ receipt: result.receipt,
30
+ reason: result.result.reason
31
+ };
32
+ }
33
+
34
+ return {
35
+ status: 'ok',
36
+ receipt: result.receipt,
37
+ output: result.output
38
+ };
39
+ };
40
+ }
41
+
42
+ // Example OpenClaw tool mapping:
43
+ //
44
+ // await tool({
45
+ // service: 'github',
46
+ // action: 'repo.contents.read',
47
+ // context: { repository: 'Null-Square/agent-authority' },
48
+ // params: { path: 'src/index.js' }
49
+ // });
@@ -0,0 +1,98 @@
1
+ import { AuthorityRuntime } from '../src/index.js';
2
+ import { createTaskLeaseGuard } from '../src/guard.js';
3
+ import { createTaskLease } from '../src/task-lease.js';
4
+
5
+ const mission = {
6
+ version: '0.1',
7
+ mission_id: 'mission:handle-demo-request',
8
+ principal: { id: 'user:demo' },
9
+ agent: { id: 'agent:ops-demo' },
10
+ objective: 'Handle one demo inquiry',
11
+ resources: [
12
+ {
13
+ service: 'gmail',
14
+ allow: ['thread.read'],
15
+ deny: ['email.delete'],
16
+ constraints: { thread: ['thread:demo-91'] }
17
+ },
18
+ {
19
+ service: 'calendar',
20
+ allow: ['event.create'],
21
+ deny: ['event.delete'],
22
+ constraints: {}
23
+ }
24
+ ],
25
+ constraints: { expires_at: '2099-01-01T00:00:00Z' }
26
+ };
27
+
28
+ const lease = createTaskLease({
29
+ mission,
30
+ request: 'Handle the demo request in thread:demo-91',
31
+ roots: [
32
+ { fact_id: 'fact:origin-thread', kind: 'gmail.thread', value: 'thread:demo-91' }
33
+ ],
34
+ bindings: [
35
+ {
36
+ service: 'calendar',
37
+ action: 'event.create',
38
+ context_field: 'attendee',
39
+ fact_id: 'fact:requester-email'
40
+ }
41
+ ]
42
+ });
43
+
44
+ const guard = createTaskLeaseGuard({ lease, runtime: new AuthorityRuntime() });
45
+
46
+ console.log('1. Read only the task-authorized Gmail thread');
47
+ const read = await guard.run({
48
+ service: 'gmail',
49
+ action: 'thread.read',
50
+ context: { thread: 'thread:demo-91' }
51
+ }, async () => ({
52
+ sender: 'customer@example.com',
53
+ subject: 'Can we book a demo next week?'
54
+ }));
55
+ console.log(` ALLOW -> sender discovered: ${read.output.sender}`);
56
+
57
+ console.log('2. Bind discovered sender to future calendar authority');
58
+ lease.derive({
59
+ fact_id: 'fact:requester-email',
60
+ kind: 'email.address',
61
+ value: read.output.sender,
62
+ from: ['fact:origin-thread'],
63
+ receipt: read.receipt,
64
+ selector: 'output.sender'
65
+ });
66
+ console.log(' derived fact -> fact:requester-email');
67
+
68
+ console.log('3. Create a meeting only with that derived attendee');
69
+ const meeting = await guard.run({
70
+ service: 'calendar',
71
+ action: 'event.create',
72
+ context: { attendee: 'customer@example.com' }
73
+ }, async () => ({ event_id: 'event:724' }));
74
+ console.log(` ALLOW -> ${meeting.output.event_id}`);
75
+
76
+ console.log('4. Try the same calendar capability for an unrelated person');
77
+ try {
78
+ await guard.run({
79
+ service: 'calendar',
80
+ action: 'event.create',
81
+ context: { attendee: 'other@example.com' }
82
+ }, async () => ({ event_id: 'should-never-exist' }));
83
+ } catch (error) {
84
+ console.log(` ${error.result.decision.toUpperCase()} -> ${error.code}`);
85
+ console.log(' side effect did not run');
86
+ }
87
+
88
+ console.log('5. Complete the task');
89
+ lease.complete('demo request handled');
90
+ try {
91
+ await guard.run({
92
+ service: 'gmail',
93
+ action: 'thread.read',
94
+ context: { thread: 'thread:demo-91' }
95
+ }, async () => ({ sender: 'should-not-run@example.com' }));
96
+ } catch (error) {
97
+ console.log(` ${error.result.decision.toUpperCase()} -> ${error.code}`);
98
+ }
@@ -0,0 +1,112 @@
1
+ import http from 'node:http';
2
+ import { toNodeHandler } from '@modelcontextprotocol/node';
3
+ import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
4
+ import { z } from 'zod';
5
+
6
+ const host = process.env.VALIDATION_MCP_HOST || '127.0.0.1';
7
+ const port = Number(process.env.VALIDATION_MCP_PORT || 8791);
8
+
9
+ function createValidationServer() {
10
+ const server = new McpServer({
11
+ name: 'agent-authority-validation-upstream',
12
+ version: '0.1.0',
13
+ description: 'Tiny upstream used to validate Agent Authority with real public GitHub data.'
14
+ });
15
+
16
+ server.registerTool(
17
+ 'github_repo_metadata',
18
+ {
19
+ title: 'Read public GitHub repository metadata',
20
+ description: 'Read public metadata for exactly one owner/repository value.',
21
+ inputSchema: z.object({
22
+ repository: z.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/)
23
+ }),
24
+ annotations: {
25
+ readOnlyHint: true,
26
+ destructiveHint: false,
27
+ idempotentHint: true,
28
+ openWorldHint: true
29
+ }
30
+ },
31
+ async ({ repository }) => {
32
+ const response = await fetch(`https://api.github.com/repos/${repository}`, {
33
+ headers: {
34
+ accept: 'application/vnd.github+json',
35
+ 'user-agent': 'nullsquare-agent-authority-validation'
36
+ }
37
+ });
38
+
39
+ if (!response.ok) {
40
+ return {
41
+ isError: true,
42
+ content: [{ type: 'text', text: `GitHub returned ${response.status}` }]
43
+ };
44
+ }
45
+
46
+ const repo = await response.json();
47
+ const output = {
48
+ full_name: repo.full_name,
49
+ description: repo.description,
50
+ visibility: repo.visibility,
51
+ default_branch: repo.default_branch,
52
+ stars: repo.stargazers_count,
53
+ forks: repo.forks_count,
54
+ open_issues: repo.open_issues_count,
55
+ html_url: repo.html_url
56
+ };
57
+
58
+ return {
59
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
60
+ structuredContent: output
61
+ };
62
+ }
63
+ );
64
+
65
+ server.registerTool(
66
+ 'dangerous_demo_write',
67
+ {
68
+ title: 'Validation-only fake write tool',
69
+ description: 'A harmless fake mutation used only to prove that Agent Authority hides and blocks write-capable tools in read-only mode.',
70
+ annotations: {
71
+ readOnlyHint: false,
72
+ destructiveHint: true,
73
+ idempotentHint: false,
74
+ openWorldHint: false
75
+ }
76
+ },
77
+ async () => ({
78
+ content: [{ type: 'text', text: 'This validation-only fake write tool was called.' }]
79
+ })
80
+ );
81
+
82
+ return server;
83
+ }
84
+
85
+ const handler = createMcpHandler(createValidationServer);
86
+ const nodeHandler = toNodeHandler(handler);
87
+
88
+ const httpServer = http.createServer((req, res) => {
89
+ const url = new URL(req.url || '/', `http://${req.headers.host || `${host}:${port}`}`);
90
+ if (req.method === 'GET' && url.pathname === '/health') {
91
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
92
+ return res.end(JSON.stringify({ ok: true, service: 'agent-authority-validation-upstream' }));
93
+ }
94
+ if (url.pathname !== '/mcp') {
95
+ res.writeHead(404, { 'content-type': 'application/json' });
96
+ return res.end(JSON.stringify({ error: 'not_found' }));
97
+ }
98
+ return nodeHandler(req, res);
99
+ });
100
+
101
+ httpServer.listen(port, host, () => {
102
+ console.log(`Validation MCP upstream: http://${host}:${port}/mcp`);
103
+ console.log('Tools: github_repo_metadata (read-only), dangerous_demo_write (fake write)');
104
+ });
105
+
106
+ async function shutdown() {
107
+ await new Promise((resolve) => httpServer.close(resolve));
108
+ await handler.close();
109
+ }
110
+
111
+ process.once('SIGINT', () => shutdown().finally(() => process.exit(0)));
112
+ process.once('SIGTERM', () => shutdown().finally(() => process.exit(0)));
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@nullsquare/agent-authority",
3
+ "version": "0.4.0",
4
+ "description": "Task-bounded authority runtime for AI agents: give agents tasks, not standing account permissions.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "private": false,
8
+ "keywords": [
9
+ "ai-agent-authorization",
10
+ "agent-auth",
11
+ "agent-security",
12
+ "task-scoped-authorization",
13
+ "least-privilege",
14
+ "delegated-authorization",
15
+ "derived-authority",
16
+ "oauth",
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "credential-broker",
20
+ "coding-agents",
21
+ "llm-agents",
22
+ "human-in-the-loop",
23
+ "agent-permissions",
24
+ "agentic-ai-security"
25
+ ],
26
+ "bin": {
27
+ "agent-authority": "src/cli.js",
28
+ "aauth": "src/cli.js"
29
+ },
30
+ "scripts": {
31
+ "start": "node src/cli.js serve",
32
+ "setup": "node src/cli.js setup",
33
+ "doctor": "node src/cli.js doctor",
34
+ "demo": "node examples/demo.js",
35
+ "demo:task-lease": "node examples/task-lease-demo.js",
36
+ "demo:live-github": "node examples/live-github-task-lease.js",
37
+ "demo:live-derived-github": "node examples/live-github-derived-mutation.js",
38
+ "demo:mcp-upstream": "node examples/validation-mcp-upstream.js",
39
+ "demo:guard": "node examples/direct-guard.js",
40
+ "test": "node --test test/*.test.js",
41
+ "test:ai-sdk": "node --test test/integrations/ai-sdk.integration.mjs",
42
+ "test:coverage": "node --experimental-test-coverage --test test/*.test.js",
43
+ "check:syntax": "node --check src/index.js && node --check src/connections.js && node --check src/execution.js && node --check src/providers/github.js && node --check src/storage.js && node --check src/runtime-env.js && node --check src/sdk.js && node --check src/server.js && node --check src/cli.js && node --check src/agent-auth.js && node --check src/approvals.js && node --check src/idempotency.js && node --check src/keys.js && node --check src/harness-bridge.js && node --check src/guard.js && node --check src/task-lease.js && node --check src/mcp-gateway.js && node --check src/mcp-remote.js && node --check src/mcp-server.js && node --check src/integrations/ai-sdk.js && node --check examples/validation-mcp-upstream.js && node --check examples/direct-guard.js && node --check examples/task-lease-demo.js && node --check examples/live-github-task-lease.js && node --check examples/live-github-derived-mutation.js",
44
+ "check:package": "npm pack --dry-run",
45
+ "check": "npm run check:syntax && npm test && npm run demo:task-lease && npm run check:package"
46
+ },
47
+ "dependencies": {
48
+ "@modelcontextprotocol/client": "^2.0.0",
49
+ "@modelcontextprotocol/node": "^2.0.0",
50
+ "@modelcontextprotocol/server": "^2.0.0",
51
+ "zod": "^4.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "ai": "^7.0.73"
55
+ },
56
+ "engines": { "node": ">=20" },
57
+ "main": "src/index.js",
58
+ "exports": {
59
+ ".": "./src/index.js",
60
+ "./agent-auth": "./src/agent-auth.js",
61
+ "./approvals": "./src/approvals.js",
62
+ "./connections": "./src/connections.js",
63
+ "./execution": "./src/execution.js",
64
+ "./guard": "./src/guard.js",
65
+ "./harness-bridge": "./src/harness-bridge.js",
66
+ "./idempotency": "./src/idempotency.js",
67
+ "./integrations/ai-sdk": "./src/integrations/ai-sdk.js",
68
+ "./mcp-gateway": "./src/mcp-gateway.js",
69
+ "./mcp-remote": "./src/mcp-remote.js",
70
+ "./mcp-server": "./src/mcp-server.js",
71
+ "./sdk": "./src/sdk.js",
72
+ "./storage": "./src/storage.js",
73
+ "./task-lease": "./src/task-lease.js",
74
+ "./providers/github": "./src/providers/github.js"
75
+ },
76
+ "files": ["src", "docs", "examples", "README.md", "LICENSE", "SECURITY.md", "ROADMAP.md", "CONTRIBUTING.md"],
77
+ "repository": { "type": "git", "url": "git+https://github.com/Null-Square/agent-authority.git" },
78
+ "bugs": { "url": "https://github.com/Null-Square/agent-authority/issues" },
79
+ "homepage": "https://github.com/Null-Square/agent-authority#readme"
80
+ }