ajp-protocol 0.1.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.
@@ -0,0 +1,20 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: '20'
17
+ registry-url: 'https://registry.npmjs.org'
18
+ - run: npm publish --access public --provenance
19
+ env:
20
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ilucky21c
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # ajp-protocol
2
+
3
+ The Agent Job Protocol — standard interaction layer for the agent internet.
4
+ Part of the [Provenance Protocol](https://provenance.dev) family.
5
+
6
+ ```bash
7
+ npm install ajp-protocol
8
+ ```
9
+
10
+ ---
11
+
12
+ ## What it does
13
+
14
+ AJP defines how any party — a human, an agent, or an orchestrator — hands a
15
+ job to another agent, tracks its progress, and receives the result.
16
+
17
+ Three endpoints. Three message types. Runs over standard HTTP.
18
+ Trust verification via `provenance-protocol` built in.
19
+
20
+ ---
21
+
22
+ ## Quick start — receiving agent
23
+
24
+ Add three routes to your agent. AJP handles verification, trust checks,
25
+ and job lifecycle automatically.
26
+
27
+ ```js
28
+ import { AJPServer } from 'ajp-protocol';
29
+ import express from 'express';
30
+
31
+ const app = express();
32
+ app.use(express.json());
33
+
34
+ const server = new AJPServer({
35
+ provenanceId: 'provenance:github:alice/research-assistant',
36
+ secret: process.env.AJP_SECRET,
37
+
38
+ // Trust requirements for incoming agent senders
39
+ trustRequirements: {
40
+ requireDeclared: true, // sender must have PROVENANCE.yml
41
+ requireClean: true, // no open incidents
42
+ requireMinAge: 7, // not a brand-new agent
43
+ },
44
+
45
+ // Your agent logic — receives the job, returns the result
46
+ onJob: async (job) => {
47
+ const papers = await searchPapers(job.task.instruction);
48
+ return { papers };
49
+ },
50
+ });
51
+
52
+ app.post('/jobs', server.receive());
53
+ app.get('/jobs/:id', server.status());
54
+ app.post('/jobs/:id/ack', server.ack());
55
+
56
+ app.listen(3000);
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Quick start — sending agent or platform
62
+
63
+ ```js
64
+ import { AJPClient } from 'ajp-protocol';
65
+
66
+ const client = new AJPClient({
67
+ from: {
68
+ type: 'agent', // 'human' | 'agent' | 'orchestrator'
69
+ provenance_id: 'provenance:github:alice/orchestrator',
70
+ },
71
+ secret: process.env.AJP_SECRET,
72
+ });
73
+
74
+ const result = await client.send(
75
+ 'provenance:github:bob/research-assistant', // who to hire
76
+ {
77
+ type: 'research',
78
+ instruction: 'Find the top 3 papers on transformer attention in 2024.',
79
+ output_format: 'json',
80
+ },
81
+ {
82
+ max_usd: 0.50, // budget cap
83
+ max_seconds: 120, // timeout
84
+ }
85
+ );
86
+
87
+ console.log(result.output);
88
+ // { papers: [...] }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Three use cases, one protocol
94
+
95
+ ### Human hiring an agent
96
+ ```js
97
+ const client = new AJPClient({
98
+ from: { type: 'human', id: 'user_alice_123' },
99
+ secret: process.env.AJP_SECRET,
100
+ });
101
+ const result = await client.send(agentId, task, budget);
102
+ ```
103
+
104
+ ### Agent hiring an agent
105
+ ```js
106
+ const client = new AJPClient({
107
+ from: { type: 'agent', provenance_id: 'provenance:github:alice/pipeline' },
108
+ secret: process.env.AJP_SECRET,
109
+ });
110
+ // Receiving agent automatically verifies sender via Provenance
111
+ const result = await client.send(agentId, task, budget);
112
+ ```
113
+
114
+ ### Orchestrator delegating to sub-agents (with audit chain)
115
+ ```js
116
+ const [resultA, resultB] = await Promise.all([
117
+ client.send(agentA, taskA, budget, { parentJobId: parentJobId }),
118
+ client.send(agentB, taskB, budget, { parentJobId: parentJobId }),
119
+ ]);
120
+ // All sub-jobs linked to parent — full execution tree is auditable
121
+ ```
122
+
123
+ ---
124
+
125
+ ## How trust works
126
+
127
+ When an agent or orchestrator sends a job, the receiving `AJPServer`
128
+ automatically calls `provenance-protocol` to verify the sender:
129
+
130
+ ```
131
+ AJPServer.receive()
132
+ → verify signature
133
+ → provenance.gate(offer.from.provenance_id, trustRequirements)
134
+ → is sender in Provenance index?
135
+ → has PROVENANCE.yml?
136
+ → any open incidents?
137
+ → old enough?
138
+ → run onJob() only if all checks pass
139
+ → return 403 with reason if any check fails
140
+ ```
141
+
142
+ Human senders (`from.type: 'human'`) skip Provenance verification.
143
+ Platform-level auth is assumed for humans.
144
+
145
+ ---
146
+
147
+ ## Declare AJP in your PROVENANCE.yml
148
+
149
+ ```yaml
150
+ provenance: "0.1"
151
+ name: "Research Assistant"
152
+
153
+ capabilities:
154
+ - read:web
155
+ - ajp:receiver # accepts incoming AJP jobs
156
+ - ajp:sender # sends AJP jobs to other agents
157
+
158
+ ajp:
159
+ endpoint: "https://alice.dev/api/agent"
160
+ version: "0.1"
161
+ ```
162
+
163
+ The Provenance crawler reads `ajp.endpoint` and indexes it.
164
+ Senders can discover your endpoint without out-of-band configuration.
165
+
166
+ ---
167
+
168
+ ## Next.js API route example
169
+
170
+ ```js
171
+ // app/api/agent/jobs/route.js
172
+ import { AJPServer } from 'ajp-protocol';
173
+ import { NextResponse } from 'next/server';
174
+
175
+ const server = new AJPServer({
176
+ provenanceId: process.env.PROVENANCE_ID,
177
+ secret: process.env.AJP_SECRET,
178
+ onJob: async (job) => {
179
+ // your agent logic
180
+ return { result: '...' };
181
+ },
182
+ });
183
+
184
+ export async function POST(req) {
185
+ return server.receive()(req, NextResponse);
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## The protocol family
192
+
193
+ | Package | Purpose |
194
+ |---|---|
195
+ | `provenance-protocol` | Query the agent identity index |
196
+ | `ajp-protocol` | Send and receive agent jobs (this package) |
197
+ | `PROVENANCE.yml` | Declare your agent's identity and capabilities |
198
+
199
+ ---
200
+
201
+ ## MIT License — provenance.dev/ajp
@@ -0,0 +1,67 @@
1
+ // Example 2: Agent hiring an agent
2
+ // A data pipeline agent needs PDF extraction.
3
+ // It hires a specialist PDF agent to handle that step.
4
+ //
5
+ // Key difference from human→agent: the receiving agent
6
+ // MUST verify the sender's Provenance ID before accepting.
7
+
8
+ import { AJPClient } from 'ajp-protocol';
9
+ import { provenance } from 'provenance-protocol';
10
+
11
+ // ── Sending side (data pipeline agent) ───────────────────────────────────
12
+
13
+ const client = new AJPClient({
14
+ from: {
15
+ type: 'agent',
16
+ provenance_id: 'provenance:github:alice/data-pipeline',
17
+ },
18
+ secret: process.env.AJP_SECRET,
19
+ });
20
+
21
+ // Send job to a specialist PDF extractor
22
+ const result = await client.send(
23
+ 'provenance:pypi:bob-pdf-extractor',
24
+ {
25
+ type: 'extract',
26
+ instruction: 'Extract all tables from this PDF and return them as structured JSON.',
27
+ input: { url: 'https://example.com/annual-report.pdf' },
28
+ output_format: 'json',
29
+ },
30
+ {
31
+ max_usd: 0.25,
32
+ max_seconds: 60,
33
+ }
34
+ );
35
+
36
+ console.log(result.output.tables);
37
+
38
+ // ── Receiving side (PDF extractor agent) ─────────────────────────────────
39
+ // This runs inside the PDF extractor's server
40
+
41
+ import { AJPServer } from 'ajp-protocol';
42
+
43
+ const server = new AJPServer({
44
+ provenanceId: 'provenance:pypi:bob-pdf-extractor',
45
+ secret: process.env.AJP_SECRET,
46
+
47
+ // Trust requirements for incoming agent jobs
48
+ trustRequirements: {
49
+ requireDeclared: true, // sender must have PROVENANCE.yml
50
+ requireClean: true, // no open incidents
51
+ requireMinAge: 7, // not a brand-new agent
52
+ },
53
+
54
+ onJob: async (job) => {
55
+ // job.from.provenance_id already verified by AJPServer
56
+ // before this function is called
57
+
58
+ const tables = await extractTablesFromPdf(job.task.input.url);
59
+ return { tables };
60
+ },
61
+ });
62
+
63
+ // Wire up to your HTTP framework
64
+ // Express:
65
+ app.post('/jobs', server.receive());
66
+ app.get('/jobs/:id', server.status());
67
+ app.post('/jobs/:id/ack', server.ack());
@@ -0,0 +1,83 @@
1
+ // Example 1: Human hiring an agent
2
+ // A user on ClawMarket hires a research agent.
3
+ // The platform wraps their request in a JobOffer and sends it.
4
+
5
+ // ── What the platform sends ───────────────────────────────────────────────
6
+
7
+ const jobOffer = {
8
+ ajp: "0.1",
9
+ job_id: "job_01J8X2K9M3N4P5Q6R7S8T9U0V1",
10
+ parent_job_id: null,
11
+
12
+ from: {
13
+ type: "human",
14
+ id: "user_alice_123", // ClawMarket user ID
15
+ provenance_id: null, // humans don't have Provenance IDs
16
+ },
17
+
18
+ to: {
19
+ provenance_id: "provenance:github:bob/research-assistant",
20
+ },
21
+
22
+ task: {
23
+ type: "research",
24
+ instruction: "Find the three most cited papers on transformer attention mechanisms published in 2024. Return titles, authors, citation counts, and a 2-sentence summary of each.",
25
+ input: {},
26
+ output_format: "json",
27
+ },
28
+
29
+ context: {
30
+ credentials: {},
31
+ memory: [],
32
+ constraints: [],
33
+ },
34
+
35
+ budget: {
36
+ max_usd: 0.50,
37
+ max_seconds: 120,
38
+ max_llm_tokens: 10000,
39
+ },
40
+
41
+ callback: {
42
+ url: "https://clawmarket.com/api/jobs/job_01J8X2K9M3N4P5Q6R7S8T9U0V1/result",
43
+ headers: { "Authorization": "Bearer clawmarket_token_xyz" },
44
+ },
45
+
46
+ issued_at: "2026-03-05T10:00:00Z",
47
+ expires_at: "2026-03-05T10:02:00Z",
48
+ signature: "sha256:a1b2c3d4e5f6...",
49
+ };
50
+
51
+ // ── What the agent returns ────────────────────────────────────────────────
52
+
53
+ const jobResult = {
54
+ ajp: "0.1",
55
+ job_id: "job_01J8X2K9M3N4P5Q6R7S8T9U0V1",
56
+ status: "completed",
57
+
58
+ output: {
59
+ papers: [
60
+ {
61
+ title: "FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision",
62
+ authors: ["Jay Shah", "Ganesh Bikshandi", "Ying Zhang", "Vijay Thakkar", "Pradeep Ramani", "Tri Dao"],
63
+ citations: 412,
64
+ summary: "Introduces hardware-aware optimizations for H100 GPUs achieving 1.5-2x speedup over FlashAttention-2. Combines asynchronous execution with low-precision arithmetic while maintaining numerical accuracy.",
65
+ },
66
+ ],
67
+ },
68
+
69
+ usage: {
70
+ llm_tokens: 4821,
71
+ duration_seconds: 18,
72
+ cost_usd: 0.12,
73
+ },
74
+
75
+ agent: {
76
+ provenance_id: "provenance:github:bob/research-assistant",
77
+ version: "1.2.0",
78
+ model: { provider: "anthropic", model_id: "claude-sonnet-4-5" },
79
+ },
80
+
81
+ completed_at: "2026-03-05T10:00:19Z",
82
+ signature: "sha256:e5f6g7h8...",
83
+ };
@@ -0,0 +1,55 @@
1
+ // Example 3: Orchestrator delegating to sub-agents
2
+ // A research orchestrator breaks a complex task into parallel sub-tasks.
3
+ // Each sub-job references parent_job_id — creating a full audit chain.
4
+
5
+ import { AJPClient } from 'ajp-protocol';
6
+
7
+ const client = new AJPClient({
8
+ from: {
9
+ type: 'orchestrator',
10
+ provenance_id: 'provenance:github:alice/research-orchestrator',
11
+ },
12
+ secret: process.env.AJP_SECRET,
13
+ });
14
+
15
+ const PARENT_JOB_ID = 'job_parent_01J8X2K9M3N4P5Q6';
16
+
17
+ // ── Dispatch sub-tasks in parallel ───────────────────────────────────────
18
+
19
+ const [webResults, pdfResults] = await Promise.all([
20
+
21
+ // Sub-task 1: web research agent
22
+ client.send(
23
+ 'provenance:github:alice/research-assistant',
24
+ {
25
+ type: 'research',
26
+ instruction: 'Find recent papers on transformer attention mechanisms (2024).',
27
+ output_format: 'json',
28
+ },
29
+ { max_usd: 0.25, max_seconds: 60 },
30
+ { parentJobId: PARENT_JOB_ID } // links to parent for audit trail
31
+ ),
32
+
33
+ // Sub-task 2: PDF extraction agent
34
+ client.send(
35
+ 'provenance:pypi:bob-pdf-extractor',
36
+ {
37
+ type: 'extract',
38
+ instruction: 'Extract tables from the provided PDF.',
39
+ input: { url: 'https://example.com/survey.pdf' },
40
+ output_format: 'json',
41
+ },
42
+ { max_usd: 0.25, max_seconds: 60 },
43
+ { parentJobId: PARENT_JOB_ID }
44
+ ),
45
+
46
+ ]);
47
+
48
+ // ── Audit chain: all three jobs are linked ────────────────────────────────
49
+ //
50
+ // job_parent_01J8X2K9M3N4P5Q6 ← orchestrator job (from human)
51
+ // ├── job_sub_A_... ← web research (parent_job_id set)
52
+ // └── job_sub_B_... ← PDF extraction (parent_job_id set)
53
+ //
54
+ // Anyone querying the Provenance log can see the full chain.
55
+ // What was delegated, to whom, when, and what it cost.
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "ajp-protocol",
3
+ "version": "0.1.0",
4
+ "description": "Agent Job Protocol — standard interaction layer for the agent internet",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js"
9
+ },
10
+ "keywords": ["ai-agent", "agent-protocol", "ajp", "provenance", "llm", "agent-to-agent"],
11
+ "license": "MIT",
12
+ "dependencies": {
13
+ "provenance-protocol": "^0.1.0"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/ilucky21c/ajp-protocol"
18
+ },
19
+ "homepage": "https://provenance.dev/ajp"
20
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://provenance.dev/ajp/schema/0.1/job-offer.json",
4
+ "title": "AJP JobOffer",
5
+ "description": "Agent Job Protocol v0.1 — JobOffer message",
6
+ "type": "object",
7
+ "required": ["ajp", "job_id", "from", "to", "task", "budget", "issued_at", "expires_at", "signature"],
8
+
9
+ "properties": {
10
+
11
+ "ajp": { "type": "string", "enum": ["0.1"] },
12
+
13
+ "job_id": {
14
+ "type": "string",
15
+ "description": "Unique job identifier. Use ULID or UUID.",
16
+ "minLength": 8
17
+ },
18
+
19
+ "parent_job_id": {
20
+ "type": ["string", "null"],
21
+ "description": "Set when this job was created by an orchestrator as a sub-task."
22
+ },
23
+
24
+ "from": {
25
+ "type": "object",
26
+ "required": ["type"],
27
+ "properties": {
28
+ "type": { "type": "string", "enum": ["human", "agent", "orchestrator"] },
29
+ "id": { "type": ["string", "null"], "description": "Platform user ID for human senders" },
30
+ "provenance_id": { "type": ["string", "null"], "description": "Required when type is agent or orchestrator" }
31
+ },
32
+ "if": { "properties": { "type": { "enum": ["agent", "orchestrator"] } } },
33
+ "then": { "required": ["provenance_id"] }
34
+ },
35
+
36
+ "to": {
37
+ "type": "object",
38
+ "required": ["provenance_id"],
39
+ "properties": {
40
+ "provenance_id": { "type": "string" }
41
+ }
42
+ },
43
+
44
+ "task": {
45
+ "type": "object",
46
+ "required": ["type", "instruction"],
47
+ "properties": {
48
+ "type": { "type": "string", "description": "Domain-specific task type e.g. research, extract, summarize" },
49
+ "instruction": { "type": "string", "description": "Natural language task description", "minLength": 1 },
50
+ "input": { "type": "object", "description": "Structured input data" },
51
+ "output_format": { "type": "string", "enum": ["json", "text", "markdown", "html", "any"] }
52
+ }
53
+ },
54
+
55
+ "context": {
56
+ "type": "object",
57
+ "properties": {
58
+ "credentials": { "type": "object", "description": "Any credentials the agent needs to complete the task" },
59
+ "memory": { "type": "array", "description": "Prior conversation or context to include" },
60
+ "constraints": { "type": "array", "items": { "type": "string" }, "description": "Additional constraints for this specific job" }
61
+ }
62
+ },
63
+
64
+ "budget": {
65
+ "type": "object",
66
+ "required": ["max_usd"],
67
+ "properties": {
68
+ "max_usd": { "type": "number", "minimum": 0, "description": "Maximum cost in USD" },
69
+ "max_seconds": { "type": "number", "minimum": 1, "description": "Maximum execution time" },
70
+ "max_llm_tokens": { "type": "number", "minimum": 1, "description": "Maximum LLM tokens across all calls" }
71
+ }
72
+ },
73
+
74
+ "callback": {
75
+ "type": ["object", "null"],
76
+ "description": "Where to POST the JobResult when complete. Null = polling only.",
77
+ "properties": {
78
+ "url": { "type": "string", "format": "uri" },
79
+ "headers": { "type": "object" }
80
+ },
81
+ "required": ["url"]
82
+ },
83
+
84
+ "issued_at": { "type": "string", "format": "date-time" },
85
+ "expires_at": { "type": "string", "format": "date-time" },
86
+
87
+ "signature": {
88
+ "type": "string",
89
+ "description": "HMAC-SHA256 of the message body excluding this field. Format: sha256:hex",
90
+ "pattern": "^sha256:[a-f0-9]{64}$"
91
+ }
92
+ },
93
+
94
+ "additionalProperties": false
95
+ }