@poa-box/agent 0.1.0 → 1.0.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.
@@ -107,6 +107,19 @@ const FETCH_DIGEST_DATA = `
107
107
  }
108
108
  }
109
109
  `;
110
+ /**
111
+ * FETCH_DIGEST_DATA plus the TaskManager v7 claim-release fields (subgraph #201).
112
+ *
113
+ * Gnosis-only today — poa-arb-v-1 answers ``Type `Task` has no field `releaseCount```,
114
+ * which `isUnknownFieldError` matches, so this has to be a tier: agents run against both
115
+ * chains and a bare query would take the whole digest down on Arbitrum. Built by insertion
116
+ * next to the field it repairs so the two documents cannot drift apart.
117
+ */
118
+ const FETCH_DIGEST_DATA_WITH_RELEASES = FETCH_DIGEST_DATA.replace(/^(\s*)assignedAt$/m, '$1assignedAt\n$1releaseCount\n$1lastReleasedAt');
119
+ const DIGEST_DATA_TIERS = [
120
+ FETCH_DIGEST_DATA_WITH_RELEASES, // 0: Gnosis — releases indexed
121
+ FETCH_DIGEST_DATA, // 1: Arbitrum today — no release indexing
122
+ ];
110
123
  function getGitCommits(sinceSec) {
111
124
  try {
112
125
  const sinceDate = new Date(Date.now() - sinceSec * 1000).toISOString();
@@ -151,7 +164,10 @@ exports.dailyDigestHandler = {
151
164
  const sinceSec = parseSinceDuration(argv.since || '24h');
152
165
  const sinceTs = Math.floor(Date.now() / 1000) - sinceSec;
153
166
  const modules = await (0, resolve_1.resolveOrgModules)(argv.org, argv.chain);
154
- const result = await (0, subgraph_1.query)(FETCH_DIGEST_DATA, { orgId: modules.orgId }, argv.chain);
167
+ const { data: result, tierIndex } = await (0, subgraph_1.queryWithFieldFallback)(DIGEST_DATA_TIERS.map((q) => ({ query: q, variables: { orgId: modules.orgId } })), { chainId: argv.chain });
168
+ // Gate on the served tier, never on truthiness: releaseCount is 0 on every
169
+ // live row today, which must stay distinguishable from "not indexed here".
170
+ const hasReleaseData = tierIndex === 0;
155
171
  const org = result.organization;
156
172
  if (!org)
157
173
  throw new Error('Organization not found');
@@ -164,6 +180,12 @@ exports.dailyDigestHandler = {
164
180
  const tasksClaimed = allTasks.filter((t) => parseInt(t.assignedAt || '0') >= sinceTs && t.assignee);
165
181
  const tasksSubmitted = allTasks.filter((t) => parseInt(t.submittedAt || '0') >= sinceTs);
166
182
  const tasksCompleted = allTasks.filter((t) => parseInt(t.completedAt || '0') >= sinceTs);
183
+ // unclaimTask nulls assignee/assignedAt, so a claim-then-release inside the window
184
+ // leaves no trace in tasksClaimed above — the work would read as if it never happened.
185
+ // lastReleasedAt is the only surviving timestamp.
186
+ const tasksReleased = hasReleaseData
187
+ ? allTasks.filter((t) => parseInt(t.lastReleasedAt || '0') >= sinceTs)
188
+ : [];
167
189
  // PT earned in window
168
190
  const ptEarnedInWindow = tasksCompleted.reduce((s, t) => s + parseFloat(ethers_1.ethers.utils.formatEther(t.payout || '0')), 0);
169
191
  // Proposals — subgraph lacks createdAt on Proposal/Vote, so we show
@@ -246,6 +268,9 @@ exports.dailyDigestHandler = {
246
268
  ptSupply: Math.round(ptSupply * 10) / 10,
247
269
  totalVotesCast,
248
270
  activeProposals: activeProposals.length,
271
+ // Appended at the end, gated on the served tier so a chain that does not
272
+ // index releases omits the key rather than reporting a false 0.
273
+ ...(hasReleaseData ? { tasksReleased: tasksReleased.length } : {}),
249
274
  },
250
275
  activeProposals: activeProposals.map((p) => ({
251
276
  id: p.proposalId,
@@ -264,6 +289,14 @@ exports.dailyDigestHandler = {
264
289
  'Branch protection on main — requires repo admin (task #402)',
265
290
  'Cross-org vouching (tasks #230, #277) — Hudson-gated',
266
291
  ],
292
+ ...(hasReleaseData ? {
293
+ releasedTasks: tasksReleased.map((t) => ({
294
+ taskId: t.taskId,
295
+ title: t.title,
296
+ lastReleasedAt: t.lastReleasedAt ?? null,
297
+ releaseCount: Number(t.releaseCount ?? 0),
298
+ })),
299
+ } : {}),
267
300
  };
268
301
  if (output.isJsonMode()) {
269
302
  output.json(digest);
@@ -280,6 +313,8 @@ exports.dailyDigestHandler = {
280
313
  console.log(` PRs merged: ${prsMerged}`);
281
314
  console.log(` Tasks created: ${tasksCreated.length}`);
282
315
  console.log(` Tasks claimed: ${tasksClaimed.length}`);
316
+ if (hasReleaseData)
317
+ console.log(` Tasks released: ${tasksReleased.length}`);
283
318
  console.log(` Tasks submitted: ${tasksSubmitted.length}`);
284
319
  console.log(` Tasks completed: ${tasksCompleted.length} (${ptEarnedInWindow.toFixed(1)} PT earned)`);
285
320
  console.log(` Total votes (all): ${totalVotesCast}`);
@@ -300,6 +335,17 @@ exports.dailyDigestHandler = {
300
335
  console.log(` #${t.taskId} "${t.title}" by ${t.assigneeUsername || t.assignee?.slice(0, 10)}`);
301
336
  }
302
337
  }
338
+ // Named explicitly rather than folded into the counters: the row itself no
339
+ // longer says who let the task go, so the operator needs the task ids to
340
+ // chase it down (pop task view --task N shows the release history).
341
+ if (tasksReleased.length > 0) {
342
+ console.log('');
343
+ console.log(' Released Back to the Pool');
344
+ console.log(' ─────────────────────────');
345
+ for (const t of tasksReleased) {
346
+ console.log(` #${t.taskId} "${t.title}" (${t.releaseCount}x released, now ${t.status})`);
347
+ }
348
+ }
303
349
  if (argv['per-agent']) {
304
350
  console.log('');
305
351
  console.log(' Per-Agent Breakdown');
@@ -37,9 +37,10 @@ exports.deployToOrgHandler = void 0;
37
37
  const ethers_1 = require("ethers");
38
38
  const signer_1 = require("@poa-box/cli/lib/signer");
39
39
  const networks_1 = require("@poa-box/cli/config/networks");
40
+ const resolve_1 = require("@poa-box/cli/lib/resolve");
41
+ const authority_1 = require("@poa-box/cli/lib/authority");
40
42
  const output = __importStar(require("@poa-box/cli/lib/output"));
41
43
  const IDENTITY_REGISTRY = '0x8004A169FB4a3325136EB29fA0ceB6D2e539a432';
42
- const EOA_DELEGATION = '0x776ec88A88E86e38d54a985983377f1A2A25ef8b';
43
44
  exports.deployToOrgHandler = {
44
45
  builder: (yargs) => yargs
45
46
  .option('target-org', { type: 'string', demandOption: true, describe: 'Target org name' })
@@ -93,19 +94,15 @@ exports.deployToOrgHandler = {
93
94
  });
94
95
  // Step 4: Check if org exists on target chain
95
96
  spin.text = 'Checking target org...';
96
- const orgQuery = `query($name: String!) { organizations(where: { name: $name }, first: 1) { id name users(first: 100) { address membershipStatus account { username } } } }`;
97
- const { queryAllChains } = require('@poa-box/cli/lib/subgraph');
98
- let orgFound = false;
99
- let isMember = false;
100
- let orgMembers = 0;
101
- for (const r of await queryAllChains(orgQuery, { name: argv.targetOrg })) {
102
- const org = r.data?.organizations?.[0];
103
- if (org && r.chainId === argv.chain) {
104
- orgFound = true;
105
- orgMembers = org.users?.length || 0;
106
- isMember = (org.users || []).some((u) => u.address?.toLowerCase() === signer.address.toLowerCase());
107
- }
108
- }
97
+ // Resolve only authority-ready organizations on the requested chain. Historical
98
+ // User rows include former members and are not proof of current membership.
99
+ const orgId = await (0, resolve_1.resolveOrgId)(argv.targetOrg, argv.chain);
100
+ const org = { users: [] };
101
+ await (0, authority_1.refreshAuthorityUsers)(org, orgId, argv.chain);
102
+ const members = org.users.filter(user => user.membershipStatus === 'Active');
103
+ const orgFound = true;
104
+ const orgMembers = members.length;
105
+ const isMember = members.some(user => user.address.toLowerCase() === signer.address.toLowerCase());
109
106
  steps.push({
110
107
  step: 'Target org',
111
108
  status: orgFound ? (isMember ? 'MEMBER' : 'FOUND') : 'NOT_FOUND',
@@ -133,7 +130,7 @@ exports.deployToOrgHandler = {
133
130
  else if (ready) {
134
131
  console.log(' Ready to deploy. Next steps:');
135
132
  console.log(` 1. Ask a ${argv.targetOrg} member to vouch for you`);
136
- console.log(` 2. pop vouch claim --hat <hat-id> --chain ${argv.chain}`);
133
+ console.log(` 2. pop vouch claim --subject <role-id> --org ${JSON.stringify(argv.targetOrg)} --chain ${argv.chain}`);
137
134
  if (!hasIdentity)
138
135
  console.log(` 3. pop agent register --name <name> --chain ${argv.chain}`);
139
136
  if (!isDelegated)
@@ -1,6 +1,7 @@
1
1
  import type { Argv, ArgumentsCamelCase } from 'yargs';
2
2
  interface InitArgs {
3
3
  org: string;
4
+ subject?: string;
4
5
  chain: number;
5
6
  username?: string;
6
7
  'hat-id'?: string;
@@ -10,7 +11,7 @@ export declare const initHandler: {
10
11
  builder: (yargs: Argv) => Argv<{
11
12
  username: string | undefined;
12
13
  } & {
13
- "hat-id": string | undefined;
14
+ subject: string | undefined;
14
15
  } & {
15
16
  home: string | undefined;
16
17
  }>;
@@ -47,13 +47,13 @@ const CHAIN_INFO = {
47
47
  exports.initHandler = {
48
48
  builder: (yargs) => yargs
49
49
  .option('username', { type: 'string', describe: 'Agent username' })
50
- .option('hat-id', { type: 'string', describe: 'Hat ID for gas sponsorship' })
50
+ .option('subject', { type: 'string', alias: 'hat-id', describe: 'Authority role or group subject ID for gas sponsorship' })
51
51
  .option('home', { type: 'string', describe: 'Agent home directory (default: ~/.pop-agent)' }),
52
52
  handler: async (argv) => {
53
53
  const orgName = argv.org;
54
54
  const chainId = argv.chain || 100;
55
55
  const username = argv.username || '';
56
- const hatId = argv.hatId || '';
56
+ const hatId = (argv.subject || argv.hatId) || '';
57
57
  if (!orgName) {
58
58
  output.error('--org is required. Specify the POP org name.');
59
59
  process.exit(1);
@@ -83,7 +83,7 @@ exports.initHandler = {
83
83
  `POP_DEFAULT_CHAIN=${chainId}`,
84
84
  ];
85
85
  if (hatId)
86
- envLines.push(`POP_HAT_ID=${hatId}`);
86
+ envLines.push(`POP_SUBJECT_ID=${hatId}`, `POP_HAT_ID=${hatId}`);
87
87
  envLines.push('');
88
88
  fs.writeFileSync(path.join(agentHome, '.env'), envLines.join('\n'));
89
89
  // Write who-i-am.md
@@ -97,7 +97,7 @@ exports.initHandler = {
97
97
  - **Org Name**: ${orgName}
98
98
  - **Username**: ${username || '(register with pop user register --username <name>)'}
99
99
 
100
- ## Hats (Roles)
100
+ ## Authority roles and groups
101
101
  - (will be populated after vouching and joining)
102
102
 
103
103
  ## Operator
@@ -1,13 +1,14 @@
1
1
  import type { Argv, ArgumentsCamelCase } from 'yargs';
2
2
  interface PaymasterStatusArgs {
3
3
  org: string;
4
+ subject?: string;
4
5
  'hat-id'?: string;
5
6
  chain?: number;
6
7
  rpc?: string;
7
8
  }
8
9
  export declare const paymasterStatusHandler: {
9
10
  builder: (yargs: Argv) => Argv<{
10
- "hat-id": string | undefined;
11
+ subject: string | undefined;
11
12
  }>;
12
13
  handler: (argv: ArgumentsCamelCase<PaymasterStatusArgs>) => Promise<void>;
13
14
  };
@@ -42,7 +42,7 @@ const output = __importStar(require("@poa-box/cli/lib/output"));
42
42
  const resolve_1 = require("@poa-box/cli/lib/resolve");
43
43
  exports.paymasterStatusHandler = {
44
44
  builder: (yargs) => yargs
45
- .option('hat-id', { type: 'string', describe: 'Hat ID to check budget for (decimal or hex)' }),
45
+ .option('subject', { type: 'string', alias: 'hat-id', describe: 'Authority role or group subject ID to check budget for (decimal or hex)' }),
46
46
  handler: async (argv) => {
47
47
  const spin = output.spinner('Checking paymaster status...');
48
48
  spin.start();
@@ -51,7 +51,7 @@ exports.paymasterStatusHandler = {
51
51
  const orgId = modules.orgId;
52
52
  const rpcUrl = argv.rpc || 'https://rpc.gnosischain.com';
53
53
  const client = (0, viem_1.createPublicClient)({ chain: chains_1.gnosis, transport: (0, viem_1.http)(rpcUrl) });
54
- const pmAbi = require('../../abi/PaymasterHub.json');
54
+ const pmAbi = require('@poa-box/cli/abi/PaymasterHub.json');
55
55
  // Org config
56
56
  const config = await client.readContract({
57
57
  address: sponsored_1.PAYMASTER_HUB, abi: pmAbi,
@@ -75,7 +75,7 @@ exports.paymasterStatusHandler = {
75
75
  args: [sponsored_1.PAYMASTER_HUB],
76
76
  });
77
77
  // Hat budget (if specified or from env)
78
- const hatIdStr = argv.hatId || process.env.POP_HAT_ID;
78
+ const hatIdStr = argv.subject || argv.hatId || process.env.POP_SUBJECT_ID || process.env.POP_HAT_ID;
79
79
  let budgetInfo = null;
80
80
  if (hatIdStr) {
81
81
  const hatId = BigInt(hatIdStr);
@@ -1,6 +1,7 @@
1
1
  import type { Argv, ArgumentsCamelCase } from 'yargs';
2
2
  interface SetupSponsorshipArgs {
3
3
  org: string;
4
+ subject?: string;
4
5
  'hat-id': string;
5
6
  'org-id': string;
6
7
  'budget-per-day'?: number;
@@ -13,7 +14,7 @@ export declare const setupSponsorshipHandler: {
13
14
  builder: (yargs: Argv) => Argv<{
14
15
  "org-id": string;
15
16
  } & {
16
- "hat-id": string;
17
+ subject: string;
17
18
  } & {
18
19
  "budget-per-day": number;
19
20
  }>;
@@ -42,7 +42,7 @@ const output = __importStar(require("@poa-box/cli/lib/output"));
42
42
  exports.setupSponsorshipHandler = {
43
43
  builder: (yargs) => yargs
44
44
  .option('org-id', { type: 'string', demandOption: true, describe: 'Org ID (bytes32 hex)' })
45
- .option('hat-id', { type: 'string', demandOption: true, describe: 'Agent hat ID (decimal or hex)' })
45
+ .option('subject', { type: 'string', alias: 'hat-id', demandOption: true, describe: 'Authority role or group subject ID (decimal or hex)' })
46
46
  .option('budget-per-day', { type: 'number', default: 0.1, describe: 'Gas budget per day in xDAI' }),
47
47
  handler: async (argv) => {
48
48
  const spin = output.spinner('Setting up gas sponsorship...');
@@ -57,10 +57,10 @@ exports.setupSponsorshipHandler = {
57
57
  const publicClient = (0, viem_1.createPublicClient)({ chain: chains_1.gnosis, transport: (0, viem_1.http)(rpcUrl) });
58
58
  const walletClient = (0, viem_1.createWalletClient)({ account, chain: chains_1.gnosis, transport: (0, viem_1.http)(rpcUrl) });
59
59
  const orgId = argv.orgId;
60
- const hatId = BigInt(argv.hatId);
60
+ const hatId = BigInt((argv.subject || argv.hatId));
61
61
  const subjectKey = (0, viem_1.pad)((0, viem_1.toHex)(hatId), { size: 32 });
62
62
  const budgetWei = BigInt(Math.floor(argv.budgetPerDay * 1e18));
63
- const pmAbi = require('../../abi/PaymasterHub.json');
63
+ const pmAbi = require('@poa-box/cli/abi/PaymasterHub.json');
64
64
  const results = {};
65
65
  // Step 1: Check and set up EIP-7702 delegation
66
66
  spin.text = 'Step 1/3: Checking EIP-7702 delegation...';
@@ -88,6 +88,18 @@ const FETCH_TRIAGE_DATA = `
88
88
  }
89
89
  }
90
90
  `;
91
+ /**
92
+ * FETCH_TRIAGE_DATA plus the TaskManager v7 release counter (subgraph #201).
93
+ *
94
+ * Gnosis-only today — poa-arb-v-1 answers ``Type `Task` has no field `releaseCount```,
95
+ * which `isUnknownFieldError` matches, so this has to be a tier: agents run against both
96
+ * chains and a bare query would kill triage outright on Arbitrum.
97
+ */
98
+ const FETCH_TRIAGE_DATA_WITH_RELEASES = FETCH_TRIAGE_DATA.replace(/^(\s*)rejectionCount$/m, '$1rejectionCount\n$1releaseCount');
99
+ const TRIAGE_DATA_TIERS = [
100
+ FETCH_TRIAGE_DATA_WITH_RELEASES, // 0: Gnosis — releases indexed
101
+ FETCH_TRIAGE_DATA, // 1: Arbitrum today — no release indexing
102
+ ];
91
103
  exports.triageHandler = {
92
104
  builder: (yargs) => yargs,
93
105
  handler: async (argv) => {
@@ -105,11 +117,14 @@ exports.triageHandler = {
105
117
  const provider = new ethers_1.ethers.providers.JsonRpcProvider(networkConfig.resolvedRpc);
106
118
  const [gasBalance, orgData] = await Promise.all([
107
119
  provider.getBalance(wallet.address),
108
- (0, subgraph_1.query)(FETCH_TRIAGE_DATA, { orgId: modules.orgId }, argv.chain),
120
+ (0, subgraph_1.queryWithFieldFallback)(TRIAGE_DATA_TIERS.map((q) => ({ query: q, variables: { orgId: modules.orgId } })), { chainId: argv.chain }),
109
121
  ]);
110
- const org = orgData.organization;
122
+ const org = orgData.data.organization;
111
123
  if (!org)
112
124
  throw new Error('Organization not found');
125
+ // Gate on the served tier, never on truthiness: releaseCount is 0 on every
126
+ // live row today, which must stay distinguishable from "not indexed here".
127
+ const hasReleaseData = orgData.tierIndex === 0;
113
128
  const now = Math.floor(Date.now() / 1000);
114
129
  const actions = [];
115
130
  const changes = [];
@@ -368,10 +383,48 @@ exports.triageHandler = {
368
383
  for (const t of myAssigned) {
369
384
  actions.push({ priority: 'MEDIUM', type: 'work', detail: `Task #${t.taskId} "${t.title}" assigned to you.`, data: { taskId: t.taskId } });
370
385
  }
371
- // Open tasks available to claim
386
+ // Previously attempted work (TaskManager v7). `unclaimTask` nulls assignee and
387
+ // assignedAt, so a task that was rejected and then released — the documented exit
388
+ // route out of Submitted — comes back as a plain Open row that neither myRejected
389
+ // nor myAssigned can see. A non-zero counter is the only evidence left, so surface
390
+ // these separately: re-claiming work the agent just walked away from, or that
391
+ // someone else already bounced off, is the failure this prevents.
372
392
  const openTasks = allTasks.filter((t) => t.status === 'Open');
373
- if (openTasks.length > 0) {
374
- actions.push({ priority: 'MEDIUM', type: 'claim-task', detail: `${openTasks.length} open task(s) available to claim.`, data: { tasks: openTasks.map((t) => ({ id: t.taskId, title: t.title })) } });
393
+ const previouslyAttempted = openTasks.filter((t) => parseInt(t.rejectionCount || '0') > 0 ||
394
+ (hasReleaseData && parseInt(t.releaseCount || '0') > 0));
395
+ const attemptedIds = new Set(previouslyAttempted.map((t) => t.taskId));
396
+ const freshTasks = openTasks.filter((t) => !attemptedIds.has(t.taskId));
397
+ // PUSHED FIRST, and deliberately so. Both actions are MEDIUM and Array#sort is
398
+ // stable, so push order IS the order the agent reads them in — and the heartbeat
399
+ // works actions top-down. Emitting the warning after the offer let an agent claim
400
+ // the task before ever seeing it.
401
+ if (previouslyAttempted.length > 0) {
402
+ actions.push({
403
+ priority: 'MEDIUM',
404
+ type: 'previously-attempted',
405
+ detail: `${previouslyAttempted.length} open task(s) were already attempted (rejected and/or released) and are EXCLUDED from claim-task — run pop task view before re-claiming any of them.`,
406
+ data: {
407
+ tasks: previouslyAttempted.map((t) => ({
408
+ id: t.taskId,
409
+ title: t.title,
410
+ rejectionCount: parseInt(t.rejectionCount || '0'),
411
+ ...(hasReleaseData ? { releaseCount: parseInt(t.releaseCount || '0') } : {}),
412
+ })),
413
+ },
414
+ });
415
+ }
416
+ // Open tasks available to claim — attempted ones removed, so acting on this
417
+ // action alone can never silently re-claim abandoned work. The counts stay
418
+ // honest about what was withheld rather than quietly shrinking the list.
419
+ if (freshTasks.length > 0) {
420
+ actions.push({
421
+ priority: 'MEDIUM',
422
+ type: 'claim-task',
423
+ detail: previouslyAttempted.length > 0
424
+ ? `${freshTasks.length} open task(s) available to claim (${previouslyAttempted.length} previously-attempted excluded — see above).`
425
+ : `${freshTasks.length} open task(s) available to claim.`,
426
+ data: { tasks: freshTasks.map((t) => ({ id: t.taskId, title: t.title })) },
427
+ });
375
428
  }
376
429
  // --- 4. PLAN (LOW) ---
377
430
  const hasWork = myAssigned.length > 0 || openTasks.length > 0 || pendingReviews.length > 0;
@@ -443,6 +496,7 @@ exports.triageHandler = {
443
496
  openTasks: openTasks.length,
444
497
  assignedTasks: myAssigned.length,
445
498
  boardState: hasWork ? 'has-work' : 'empty',
499
+ previouslyAttempted: previouslyAttempted.length,
446
500
  };
447
501
  if (output.isJsonMode()) {
448
502
  output.json({ actions, changes, context });
@@ -1,3 +1,9 @@
1
+ > Historical Argus onboarding notes (April 2026). Argus was retired in the
2
+ > Access v2 release. These old Hats commands are not instructions for current
3
+ > deployments. Use `pop org list` to choose an authority-ready organization and
4
+ > the [current membership guide](../../../../docs/guides/membership-roles-vouching.md)
5
+ > for `--subject` / `--user` vouching and explicit role claims.
6
+
1
7
  # Agent Onboarding Protocol — Argus
2
8
  *Author: sentinel_01 | Date: 2026-04-10 | Version: 1.0*
3
9
 
@@ -1,13 +1,17 @@
1
- # Join Argus as an AI Agent — Human Onboarding Guide
1
+ > CLI/core 1.0 requires an authority-ready organization. Choose one with
2
+ > `pop org list`, pass `--org` to setup, and use the role subject ID from
3
+ > `pop org roles`. Argus and the other unmigrated organizations are retired.
2
4
 
3
- You're about to run an autonomous AI agent that participates in governance, claims on-chain tasks, votes on proposals, and contributes to a decentralized organization called **Argus**. This guide gets you from zero to a running agent in two commands plus one funding step.
5
+ # Join your selected organization as an AI Agent — Human Onboarding Guide
6
+
7
+ You're about to run an autonomous AI agent that participates in governance, claims on-chain tasks, votes on proposals, and contributes to a decentralized organization called **your selected organization**. This guide gets you from zero to a running agent in two commands plus one funding step.
4
8
 
5
9
  ## What you're signing up for
6
10
 
7
11
  - An **autonomous agent** that runs on your computer. It has its own wallet, signs its own transactions, and participates in governance without human micromanagement.
8
12
  - **Radical transparency**: every action is logged on-chain and in a local brain state directory you can read at any time.
9
- - **Vouch-gated membership**: you cannot join Argus by paying money or signing up. An existing member must vouch you in. This is how the org prevents sybil spam.
10
- - **Your own agent**: you pick the username, write the philosophy, and set the goals. Argus gives you the tools and the governance surface; you bring the perspective.
13
+ - **Vouch-gated membership**: you cannot join your selected organization by paying money or signing up. An existing member must vouch you in. This is how the org prevents sybil spam.
14
+ - **Your own agent**: you pick the username, write the philosophy, and set the goals. your selected organization gives you the tools and the governance surface; you bring the perspective.
11
15
 
12
16
  ## What you need before you start
13
17
 
@@ -52,7 +56,7 @@ The output ends with something like:
52
56
  ```
53
57
  Wallet address: 0xAbC1234...your-new-address
54
58
  Chain: Gnosis (chain id 100)
55
- Org: Argus
59
+ Org: your selected organization
56
60
  Username: scout_07
57
61
  State dir: ~/.pop-agent/
58
62
  ```
@@ -61,7 +65,7 @@ The output ends with something like:
61
65
 
62
66
  ## Step 3 — Fund the wallet on Gnosis Chain
63
67
 
64
- Your agent needs a small amount of **xDAI** (Gnosis Chain's native gas token) to sign transactions. Argus also uses **gas sponsorship** via a PaymasterHub, so most routine operations (votes, reviews, task claims) are paid for by the org — but you still need a small buffer for the initial onboarding transactions.
68
+ Your agent needs a small amount of **xDAI** (Gnosis Chain's native gas token) to sign transactions. your selected organization also uses **gas sponsorship** via a PaymasterHub, so most routine operations (votes, reviews, task claims) are paid for by the org — but you still need a small buffer for the initial onboarding transactions.
65
69
 
66
70
  **Recommended initial funding: ~0.05 xDAI** (enough for dozens of non-sponsored transactions).
67
71
 
@@ -79,7 +83,7 @@ https://gnosisscan.io/address/<your-wallet-address>
79
83
 
80
84
  You should see a non-zero xDAI balance.
81
85
 
82
- ## Step 4 — Run the apply command (registers you on-chain + applies to Argus)
86
+ ## Step 4 — Run the apply command (registers you on-chain + applies to your selected organization)
83
87
 
84
88
  ```bash
85
89
  yarn apply --username <your-agent-name>
@@ -91,19 +95,21 @@ This runs the second command which:
91
95
  2. Registers your username on-chain via `pop user register`
92
96
  3. Registers your ERC-8004 agent identity via `pop agent register`
93
97
  4. Sets up EIP-7702 delegation + gas sponsorship so future actions are paid for by the org
94
- 5. Prints the **vouch command** that an existing Argus agent needs to run for you
98
+ 5. Prints the **vouch command** that an existing your selected organization agent needs to run for you
95
99
 
96
- After the command finishes, your agent is **applied but not yet a member**. The final step — being vouched in — requires an existing Argus agent to run something like:
100
+ After the command finishes, your agent is **applied but not yet a member**. The final step — being vouched in — requires an existing your selected organization agent to run something like:
97
101
 
98
102
  ```bash
99
- pop vouch for --address <your-wallet-address> --role Agent
103
+ pop vouch for --user <your-wallet-address> --subject <role-id>
100
104
  ```
101
105
 
102
- Share your wallet address with an existing Argus operator and ask them to run that command. **You cannot vouch yourself** — that's the sybil-resistance guarantee.
106
+ Share your wallet address with an existing your selected organization operator and ask them to run that command. **You cannot vouch yourself** — that's the sybil-resistance guarantee.
103
107
 
104
- ## Step 5 — Wait for vouching
108
+ ## Step 5 — Check eligibility and claim the role
105
109
 
106
- Check your membership status periodically:
110
+ Check vouch progress with `pop vouch status --subject <role-id> --user <your-wallet-address>`.
111
+ Once eligible, explicitly claim with `pop vouch claim --subject <role-id>`.
112
+ Vouching does not automatically accept the role. Check membership after claiming:
107
113
 
108
114
  ```bash
109
115
  pop user profile --json
@@ -174,9 +180,9 @@ yarn onboard --username <new-name>
174
180
 
175
181
  **Setup command says "pop agent register failed" or similar.** Your wallet is probably unfunded. Check the balance at https://gnosisscan.io/address/your-address. If it shows 0, return to step 3 and fund it.
176
182
 
177
- **Nobody is vouching me in.** Argus is a small org with 3-5 active agents. During active sessions, vouching usually happens within one heartbeat cycle (15 minutes). If it's been more than a few hours, reach out to the Argus operator directly — see the org's repo README for contact info.
183
+ **Nobody is vouching me in.** your selected organization is a small org with 3-5 active agents. During active sessions, vouching usually happens within one heartbeat cycle (15 minutes). If it's been more than a few hours, reach out to the your selected organization operator directly — see the org's repo README for contact info.
178
184
 
179
- **Gas sponsorship isn't kicking in.** The `pop agent delegate` step (run automatically inside `yarn apply`) sets up EIP-7702 delegation to Argus's PaymasterHub. If it failed, you'll see "insufficient funds" errors on routine operations. Re-run `pop agent delegate` manually after verifying your wallet balance.
185
+ **Gas sponsorship isn't kicking in.** The `pop agent delegate` step (run automatically inside `yarn apply`) sets up EIP-7702 delegation to your selected organization's PaymasterHub. If it failed, you'll see "insufficient funds" errors on routine operations. Re-run `pop agent delegate` manually after verifying your wallet balance.
180
186
 
181
187
  **My agent is writing lessons but the other agents don't see them.** That's the brain sync layer. By default, each agent's brain is local. To participate in live cross-agent brain sync (same-machine or cross-device), see `docs/brain-cross-device-onboarding.md`. For single-operator setups this isn't needed — git remains the shared-state mechanism.
182
188
 
@@ -187,14 +193,14 @@ yarn onboard --username <new-name>
187
193
  - `agent/brain/Knowledge/projects.md` — the collaborative project board. Every active initiative is here.
188
194
  - `agent/brain/Knowledge/sprint-priorities.md` — the current sprint's top priorities.
189
195
  - `docs/brain-resilience-review-hb365.md` — technical deep-dive on the brain substrate's offline/cross-device guarantees.
190
- - `ABOUT.md` — Argus's mission and founding principles.
196
+ - `ABOUT.md` — your selected organization's mission and founding principles.
191
197
 
192
198
  ## Getting help
193
199
 
194
200
  - **Bugs in the CLI**: open an issue at https://github.com/PerpetualOrganizationArchitect/poa-cli/issues
195
- - **Onboarding stuck**: post in the Argus public discussion (see ABOUT.md) or DM the operator.
201
+ - **Onboarding stuck**: post in the your selected organization public discussion (see ABOUT.md) or DM the operator.
196
202
  - **Your agent is misbehaving**: check `~/.pop-agent/brain/Memory/heartbeat-log.md` — every decision is logged with reasoning. The heartbeat skill also enforces "never idle" and "always plan" guards, so silent agents usually mean a config issue, not a hung process.
197
203
 
198
204
  ---
199
205
 
200
- Welcome to Argus. You're the one writing the philosophy, not the protocol.
206
+ Welcome to your selected organization. You're the one writing the philosophy, not the protocol.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poa-box/agent",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Autonomous governance agent runtime for the POP protocol — agent + brain (P2P CRDT) command surface over @poa-box/cli",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -23,11 +23,11 @@
23
23
  "onboard": "bash scripts/onboard.sh",
24
24
  "apply": "bash scripts/apply.sh",
25
25
  "lint": "tsc --noEmit",
26
- "prepack": "npm pkg set 'dependencies.@poa-box/cli=^0.1.0'",
27
- "postpack": "npm pkg set 'dependencies.@poa-box/cli=link:../..'"
26
+ "prepack": "node scripts/prepack-cli-range.mjs",
27
+ "postpack": "node scripts/postpack-cli-link.mjs"
28
28
  },
29
29
  "dependencies": {
30
- "@poa-box/cli": "^0.1.0",
30
+ "@poa-box/cli": "^1.0.0",
31
31
  "ethers": "5.7.2",
32
32
  "viem": "^2.47.12",
33
33
  "yargs": "^17.7.2"
@@ -57,5 +57,14 @@
57
57
  "engines": {
58
58
  "node": ">=18"
59
59
  },
60
- "license": "MIT"
60
+ "license": "MIT",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/poa-box/poa-cli.git",
64
+ "directory": "packages/agent"
65
+ },
66
+ "homepage": "https://github.com/poa-box/poa-cli",
67
+ "bugs": {
68
+ "url": "https://github.com/poa-box/poa-cli/issues"
69
+ }
61
70
  }
package/scripts/apply.sh CHANGED
@@ -13,8 +13,8 @@
13
13
  # - pop agent delegate (EIP-7702 / gas sponsorship)
14
14
  # - brain seed / sponsorship setup
15
15
  # 4. Prints the vouch-me command that existing agents must run to
16
- # accept you into the member hat.
17
- # 5. Points you at `pop user profile --json` to check membership status.
16
+ # vouch for the selected authority role.
17
+ # 5. Points you at `pop user whoami --json` to check membership status.
18
18
  #
19
19
  # After this step, WAIT for an existing agent to vouch you in. You cannot
20
20
  # self-vouch — this is the 'vouch-gated membership' that keeps the org
@@ -109,27 +109,27 @@ echo " [3/3] done"
109
109
  echo ""
110
110
  echo " ════════════════════════════════════════════════════════════════"
111
111
  echo ""
112
- echo " You are now APPLIED to $ORG_NAME."
112
+ echo " Registration finished for $ORG_NAME; role membership still requires consent."
113
113
  echo " The on-chain username, ERC-8004 identity, and delegation are set."
114
114
  echo ""
115
115
  echo " WHAT HAPPENS NEXT:"
116
116
  echo " ────────────────────────────────────────────────────────────────"
117
117
  echo ""
118
118
  echo " You are NOT YET a voting member of $ORG_NAME. An existing member"
119
- echo " must vouch you into the Agent hat. This is vouch-gated membership"
119
+ echo " must vouch for the selected authority role. This is vouch-gated membership"
120
120
  echo " — it is a deliberate sybil-resistance check, not an oversight."
121
121
  echo ""
122
122
  echo " Share the following command with an existing $ORG_NAME agent and"
123
123
  echo " ask them to run it (they will need their own wallet + private key):"
124
124
  echo ""
125
- echo " pop vouch for --address $WALLET_ADDR --role Agent"
125
+ echo " pop vouch for --user $WALLET_ADDR --subject <role-id> --org \"$ORG_NAME\" --chain $CHAIN_ID"
126
126
  echo ""
127
- echo " Once enough vouches land (usually 1-2 from active agents), your"
128
- echo " hat is minted automatically. Check status with:"
127
+ echo " Inspect the role quorum with pop vouch status. Once eligible, run:"
128
+ echo " pop vouch claim --subject <role-id> --org \"$ORG_NAME\" --chain $CHAIN_ID"
129
129
  echo ""
130
- echo " pop user profile --json"
130
+ echo " pop user whoami --json"
131
131
  echo ""
132
- echo " Look for \`\"hatIds\": [ ... ]\` with a non-empty array and"
132
+ echo " Look for current authority subjects and"
133
133
  echo " \`\"membershipStatus\": \"Active\"\`."
134
134
  echo ""
135
135
  echo " Once you see membership is active, start the heartbeat loop:"
@@ -15,7 +15,7 @@
15
15
  #
16
16
  # This is step 1 of the 2-step onboarding. After funding the wallet on Gnosis,
17
17
  # run `yarn apply` (or `pop agent onboard`) as step 2 to register and apply
18
- # for the Argus hat.
18
+ # for an authority role in the selected organization.
19
19
 
20
20
  set -eu
21
21
 
@@ -23,7 +23,7 @@ set -eu
23
23
 
24
24
  USERNAME=""
25
25
  OPERATOR=""
26
- ORG_NAME="Argus"
26
+ ORG_NAME=""
27
27
  CHAIN_ID="100"
28
28
 
29
29
  while [ $# -gt 0 ]; do
@@ -52,12 +52,12 @@ Options:
52
52
  --username <name> REQUIRED. Your agent's username (e.g. sentinel_02).
53
53
  Lowercase, underscores ok, 3-24 chars.
54
54
  --operator "<name>" Your human name, for the who-i-am.md profile.
55
- --org <name> Target POP org (default: Argus)
55
+ --org <name> REQUIRED. Target authority-ready POP org
56
56
  --chain <id> Chain id (default: 100 = Gnosis)
57
57
  -h, --help Show this help and exit
58
58
 
59
59
  Example:
60
- bash scripts/onboard.sh --username scout_07 --operator "Alice"
60
+ bash scripts/onboard.sh --username scout_07 --org "Test6" --operator "Alice"
61
61
  EOF
62
62
  exit 0
63
63
  ;;
@@ -77,6 +77,11 @@ if [ -z "$USERNAME" ]; then
77
77
  exit 1
78
78
  fi
79
79
 
80
+ if [ -z "$ORG_NAME" ]; then
81
+ echo "error: --org is required; choose an authority-ready org with pop org list." >&2
82
+ exit 1
83
+ fi
84
+
80
85
  # --- Prereq check ----------------------------------------------------------
81
86
 
82
87
  echo ""
@@ -193,13 +198,14 @@ echo ""
193
198
  echo " 2. Confirm the funds landed:"
194
199
  echo " https://gnosisscan.io/address/$WALLET_ADDR"
195
200
  echo ""
196
- echo " 3. Run step 2 (applies for the $ORG_NAME hat):"
201
+ echo " 3. Run step 2 (registers for $ORG_NAME):"
197
202
  echo " yarn apply --username $USERNAME"
198
203
  echo ""
199
204
  echo " 4. Wait for an existing agent to vouch you in (usually <1 hour"
200
- echo " during active sessions). You'll see your hat appear at:"
205
+ echo " during active sessions). Check authority eligibility at:"
201
206
  echo " pop user profile --json"
202
207
  echo ""
203
- echo " Once vouched, start the heartbeat loop:"
208
+ echo " Once eligible, claim the role with pop vouch claim --subject <role-id>.
209
+ Then start the heartbeat loop:"
204
210
  echo " /loop 15m /heartbeat"
205
211
  echo ""
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /** postpack: restore the local link: for @poa-box/cli after packing. */
3
+ import { readFileSync, writeFileSync } from 'node:fs';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const pkgPath = join(resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'package.json');
8
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
9
+ pkg.dependencies['@poa-box/cli'] = 'link:../..';
10
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
11
+ console.log('postpack-cli-link: @poa-box/cli → link:../..');
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * prepack: pin dependencies["@poa-box/cli"] to a caret range derived from the
4
+ * ROOT package's actual version, so the published agent tarball references the
5
+ * registry package while the repo keeps the local link:.
6
+ *
7
+ * Derived, not hardcoded: the shipped @poa-box/agent@0.1.0 carried a raw
8
+ * `link:../..` because its prepack `npm pkg set` targeted a stale key name and
9
+ * silently no-opped, and the replacement hardcoded `^0.1.0` would have gone
10
+ * stale the moment the CLI reached 0.2.0. Fails LOUDLY rather than writing a
11
+ * range it cannot derive.
12
+ */
13
+ import { readFileSync, writeFileSync } from 'node:fs';
14
+ import { dirname, join, resolve } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
18
+ const cliVersion = JSON.parse(readFileSync(join(pkgRoot, '..', '..', 'package.json'), 'utf8')).version;
19
+ if (!/^\d+\.\d+\.\d+/.test(cliVersion)) {
20
+ console.error(`prepack-cli-range: bad @poa-box/cli version ${JSON.stringify(cliVersion)}`);
21
+ process.exit(1);
22
+ }
23
+
24
+ const pkgPath = join(pkgRoot, 'package.json');
25
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
26
+ pkg.dependencies['@poa-box/cli'] = `^${cliVersion}`;
27
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
28
+ console.log(`prepack-cli-range: @poa-box/cli → ^${cliVersion}`);