@myvillage/cli 1.10.1 → 1.17.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,185 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync, existsSync } from 'fs';
3
+ import { join, basename } from 'path';
4
+ import { isAuthenticated } from '../utils/auth.js';
5
+ import { brand, villageSpinner } from '../utils/brand.js';
6
+ import { agentExists } from '../utils/local-agent.js';
7
+ import {
8
+ listVillageBooks,
9
+ exportVillageBook,
10
+ importVillageBook,
11
+ } from '../utils/api.js';
12
+ import {
13
+ parseWisdom,
14
+ serializeWisdom,
15
+ slugify,
16
+ getAgentWisdomDir,
17
+ ensureWisdomDir,
18
+ readAgentWisdom,
19
+ writeWisdomFile,
20
+ } from '../utils/wisdom.js';
21
+
22
+ function requireAuth() {
23
+ if (!isAuthenticated()) {
24
+ console.log(chalk.red(' ✗ Authentication required. Run \'myvillage login\' first.'));
25
+ return false;
26
+ }
27
+ return true;
28
+ }
29
+
30
+ function resolveAgentName(name, action) {
31
+ if (!name) {
32
+ console.log(chalk.red(` ✗ Specify an agent: myvillage wisdom ${action} --agent <name>\n`));
33
+ return null;
34
+ }
35
+ if (!agentExists(name)) {
36
+ console.log(chalk.red(` ✗ Agent "${name}" not found.\n`));
37
+ return null;
38
+ }
39
+ return name;
40
+ }
41
+
42
+ // ── list ────────────────────────────────────────────────
43
+
44
+ export async function wisdomListCommand(options = {}) {
45
+ if (options.remote) {
46
+ if (!requireAuth()) return;
47
+ try {
48
+ const result = await listVillageBooks({ bookTypeId: 'wisdom' });
49
+ // The API can return either an array directly or wrapped in { data }
50
+ const books = Array.isArray(result) ? result : result?.data || [];
51
+ const wisdom = books.filter(b => b.bookType?.name === 'wisdom' || !b.bookType);
52
+ if (wisdom.length === 0) {
53
+ console.log(brand.teal(' No community wisdom files published yet.\n'));
54
+ return;
55
+ }
56
+ console.log(brand.teal(` ${wisdom.length} wisdom file(s) on the network:\n`));
57
+ for (const w of wisdom) {
58
+ console.log(` ${brand.gold(w.id)} ${chalk.bold(w.name)}`);
59
+ if (w.description) console.log(` ${chalk.dim(w.description)}`);
60
+ }
61
+ console.log('');
62
+ } catch (err) {
63
+ const msg = err.response?.data?.error || err.message;
64
+ console.log(chalk.red(` ✗ Failed to list remote wisdom: ${msg}\n`));
65
+ }
66
+ return;
67
+ }
68
+
69
+ // Local mode: list every wisdom file across every agent on disk
70
+ if (!options.agent) {
71
+ console.log(chalk.yellow(' Hint: pass --agent <name> to list one agent\'s wisdom, or --remote to list community-published wisdom.\n'));
72
+ return;
73
+ }
74
+ if (!resolveAgentName(options.agent, 'list')) return;
75
+ const files = readAgentWisdom(options.agent);
76
+ if (files.length === 0) {
77
+ console.log(brand.teal(` Agent "${options.agent}" has no wisdom files yet.\n`));
78
+ return;
79
+ }
80
+ console.log(brand.teal(` ${files.length} local wisdom file(s) for "${options.agent}":\n`));
81
+ for (const w of files) {
82
+ const linked = w.villageBookId ? brand.gold(` (synced ${w.villageBookId})`) : chalk.dim(' (local-only)');
83
+ console.log(` ${chalk.bold(w.name)}${linked}`);
84
+ if (w.description) console.log(` ${chalk.dim(w.description)}`);
85
+ console.log(` ${chalk.dim(w.path)}`);
86
+ }
87
+ console.log('');
88
+ }
89
+
90
+ // ── pull ────────────────────────────────────────────────
91
+
92
+ export async function wisdomPullCommand(id, options = {}) {
93
+ if (!requireAuth()) return;
94
+ if (!id) {
95
+ console.log(chalk.red(' ✗ Specify a village book id: myvillage wisdom pull <id> --into <agent>\n'));
96
+ return;
97
+ }
98
+ if (!options.into) {
99
+ console.log(chalk.red(' ✗ Specify a target agent: myvillage wisdom pull <id> --into <agent>\n'));
100
+ return;
101
+ }
102
+ if (!resolveAgentName(options.into, 'pull')) return;
103
+
104
+ const spinner = villageSpinner('Fetching wisdom from the network...').start();
105
+ try {
106
+ const text = await exportVillageBook(id);
107
+ const { frontmatter } = parseWisdom(text);
108
+ const slug = slugify(frontmatter.name || id);
109
+ const dir = ensureWisdomDir(options.into);
110
+ const filePath = join(dir, `${slug}.wisdom`);
111
+ writeWisdomFile(filePath, parseWisdom(text)); // re-serialize for consistent formatting
112
+ spinner.succeed(`Wisdom "${frontmatter.name || id}" saved to ${filePath}`);
113
+ } catch (err) {
114
+ const msg = err.response?.data?.error || err.message;
115
+ spinner.fail(`Failed to pull wisdom: ${msg}`);
116
+ }
117
+ }
118
+
119
+ // ── push ────────────────────────────────────────────────
120
+
121
+ export async function wisdomPushCommand(filePath) {
122
+ if (!requireAuth()) return;
123
+ if (!filePath) {
124
+ console.log(chalk.red(' ✗ Specify a .wisdom file: myvillage wisdom push <file>\n'));
125
+ return;
126
+ }
127
+ if (!existsSync(filePath)) {
128
+ console.log(chalk.red(` ✗ File not found: ${filePath}\n`));
129
+ return;
130
+ }
131
+
132
+ const content = readFileSync(filePath, 'utf-8');
133
+ const spinner = villageSpinner('Publishing wisdom to the network...').start();
134
+ try {
135
+ const result = await importVillageBook({ content });
136
+ const book = result?.data || result;
137
+ if (book?.id) {
138
+ // Save the assigned id back into the local file so future pushes update
139
+ // the same row instead of creating duplicates.
140
+ const parsed = parseWisdom(content);
141
+ parsed.frontmatter.villageBookId = book.id;
142
+ writeWisdomFile(filePath, parsed);
143
+ spinner.succeed(`Published as ${book.id}`);
144
+ } else {
145
+ spinner.succeed('Published.');
146
+ }
147
+ } catch (err) {
148
+ const msg = err.response?.data?.error || err.message;
149
+ spinner.fail(`Failed to push wisdom: ${msg}`);
150
+ }
151
+ }
152
+
153
+ // ── new ─────────────────────────────────────────────────
154
+
155
+ export async function wisdomNewCommand(name, options = {}) {
156
+ if (!name) {
157
+ console.log(chalk.red(' ✗ Specify a name: myvillage wisdom new <name> --agent <agent>\n'));
158
+ return;
159
+ }
160
+ if (!options.agent) {
161
+ console.log(chalk.red(' ✗ Specify a target agent: myvillage wisdom new <name> --agent <agent>\n'));
162
+ return;
163
+ }
164
+ if (!resolveAgentName(options.agent, 'new')) return;
165
+
166
+ const slug = slugify(name);
167
+ const dir = ensureWisdomDir(options.agent);
168
+ const filePath = join(dir, `${slug}.wisdom`);
169
+ if (existsSync(filePath)) {
170
+ console.log(chalk.yellow(` ${filePath} already exists; edit it instead.\n`));
171
+ return;
172
+ }
173
+
174
+ const template = serializeWisdom({
175
+ frontmatter: {
176
+ name: slug,
177
+ description: options.description || 'Describe what this skill does and when to apply it.',
178
+ trigger: options.trigger || 'Describe the condition that should activate this skill.',
179
+ },
180
+ body: '<!-- The body of this wisdom file. Write it as a short skill: tone, structure, examples. The agent loads it into its system prompt every iteration. -->\n',
181
+ });
182
+ writeWisdomFile(filePath, parseWisdom(template));
183
+ console.log(brand.green(` ✓ Wisdom scaffolded at ${filePath}`));
184
+ console.log(brand.teal(` Edit the body, then publish with: myvillage wisdom push ${filePath}\n`));
185
+ }
package/src/index.js CHANGED
@@ -2,6 +2,12 @@ import { Command } from 'commander';
2
2
  import { createRequire } from 'module';
3
3
  import updateNotifier from 'update-notifier';
4
4
  import { loginCommand } from './commands/login.js';
5
+ import {
6
+ mediaDraftCreateCommand,
7
+ mediaDraftEditCommand,
8
+ mediaDraftListCommand,
9
+ mediaDraftStatusCommand,
10
+ } from './commands/media.js';
5
11
  import { logoutCommand } from './commands/logout.js';
6
12
  import { createGameCommand } from './commands/create-game.js';
7
13
  import { createCommand } from './commands/create-app.js';
@@ -45,7 +51,25 @@ import {
45
51
  agentLogsCommand,
46
52
  agentAddToolCommand,
47
53
  agentRemoveToolCommand,
54
+ agentTaskListCommand,
55
+ agentTaskAssignCommand,
56
+ agentMemoryCommand,
57
+ agentRecallCommand,
58
+ agentRememberCommand,
48
59
  } from './commands/agent-local.js';
60
+ import {
61
+ agentGrantCommand,
62
+ agentRevokeCommand,
63
+ agentGrantsCommand,
64
+ } from './commands/agent-grant.js';
65
+ import {
66
+ agentRegisterClientCommand,
67
+ agentListClientsCommand,
68
+ agentViewClientCommand,
69
+ agentEditClientCommand,
70
+ agentDeactivateClientCommand,
71
+ agentRotateClientKeyCommand,
72
+ } from './commands/agent-client.js';
49
73
  import {
50
74
  bizreqsNewCommand,
51
75
  bizreqsSpecCommand,
@@ -58,6 +82,7 @@ import {
58
82
  gameUploadThumbnailCommand,
59
83
  gameUploadBannerCommand,
60
84
  gameSubmitCommand,
85
+ gameDraftCommand,
61
86
  gameMissionsInitCommand,
62
87
  gameMissionsSyncCommand,
63
88
  gameMissionsListCommand,
@@ -68,6 +93,12 @@ import { checkinCommand } from './commands/checkin.js';
68
93
  import { discoverCommand } from './commands/discover.js';
69
94
  import { logCommand } from './commands/log.js';
70
95
  import { storyCommand } from './commands/story.js';
96
+ import {
97
+ wisdomListCommand,
98
+ wisdomPullCommand,
99
+ wisdomPushCommand,
100
+ wisdomNewCommand,
101
+ } from './commands/wisdom.js';
71
102
  import {
72
103
  soulprintInitCommand,
73
104
  soulprintIngestCommand,
@@ -155,6 +186,11 @@ export function run() {
155
186
  .description('Submit a DRAFT game for admin review')
156
187
  .action(gameSubmitCommand);
157
188
 
189
+ gameCmd
190
+ .command('draft')
191
+ .description('Return a game to DRAFT status (withdraw from review)')
192
+ .action(gameDraftCommand);
193
+
158
194
  // Game missions subcommands
159
195
  const gameMissionsCmd = gameCmd
160
196
  .command('missions')
@@ -402,6 +438,98 @@ export function run() {
402
438
  .description('Remove an MCP server tool from a local agent')
403
439
  .action(agentRemoveToolCommand);
404
440
 
441
+ // Task queue commands — assign and inspect work for a developer's agent
442
+ agentCmd
443
+ .command('task-list <name>')
444
+ .description('List tasks queued for a local agent')
445
+ .option('--status <status>', 'Filter by status (PENDING|IN_PROGRESS|COMPLETED|FAILED|CANCELLED)')
446
+ .option('--limit <n>', 'Max number of tasks to show', '20')
447
+ .action(agentTaskListCommand);
448
+
449
+ agentCmd
450
+ .command('task-assign <name>')
451
+ .description('Assign a task to a local agent (it will pick it up on next poll)')
452
+ .option('--type <type>', 'Task type (e.g., CLIENT_TASK, GENERATE_POST, SHARE_KNOWLEDGE)')
453
+ .option('--instruction <text>', 'Free-text instruction (required for CLIENT_TASK)')
454
+ .option('--input <json>', 'JSON-encoded structured input payload')
455
+ .option('--priority <n>', 'Priority 1-10 (lower runs first)', '5')
456
+ .action(agentTaskAssignCommand);
457
+
458
+ // Agent memory (short-term KV state) and recall (long-term searchable memory)
459
+ agentCmd
460
+ .command('memory <name> <action> [args...]')
461
+ .description('Read/write an agent\'s short-term memory: list | get <key> | set <key> <value> | delete <key>')
462
+ .action((name, action, args = []) => agentMemoryCommand(name, action, ...args));
463
+
464
+ agentCmd
465
+ .command('recall <name> <query>')
466
+ .description('Search this agent\'s long-term memory (Knowledge submissions it authored)')
467
+ .option('--limit <n>', 'Max results to show', '10')
468
+ .action(agentRecallCommand);
469
+
470
+ agentCmd
471
+ .command('remember <name> <text>')
472
+ .description('Save a memory to this agent\'s long-term store (Knowledge submission with source=AI_AGENT)')
473
+ .option('--summary <text>', 'Short summary of the memory')
474
+ .option('--themes <list>', 'Comma-separated tags')
475
+ .option('--sharing <option>', 'PRIVATE | VILLAGE_ONLY | PUBLIC', 'PRIVATE')
476
+ .action(agentRememberCommand);
477
+
478
+ // Per-agent OAuth credential grants
479
+ agentCmd
480
+ .command('grants <name>')
481
+ .description('List active OAuth credential grants for a local agent')
482
+ .action(agentGrantsCommand);
483
+
484
+ agentCmd
485
+ .command('grant <name> <provider>')
486
+ .description('Grant the agent access to a connected OAuth provider (google|microsoft|zoom)')
487
+ .action(agentGrantCommand);
488
+
489
+ agentCmd
490
+ .command('revoke <name> <provider>')
491
+ .description('Revoke an OAuth provider grant from the agent')
492
+ .action(agentRevokeCommand);
493
+
494
+ // Client agent registration commands
495
+ agentCmd
496
+ .command('register-client')
497
+ .description('Register a client application for agent automation')
498
+ .option('--agent <handle>', 'Agent handle')
499
+ .option('--client-id <id>', 'Client identifier (lowercase)')
500
+ .option('--name <name>', 'Client display name')
501
+ .option('--url <url>', 'Client base URL')
502
+ .option('--workflow <type>', 'Workflow type (e.g., submission_processor)')
503
+ .option('--schedule <cron>', 'Cron schedule expression')
504
+ .option('--timezone <tz>', 'Timezone (default: America/Chicago)')
505
+ .option('--env-file <path>', 'Write API key to this .env file')
506
+ .action(agentRegisterClientCommand);
507
+
508
+ agentCmd
509
+ .command('list-clients')
510
+ .description('List all registered client agent configs')
511
+ .action(agentListClientsCommand);
512
+
513
+ agentCmd
514
+ .command('view-client <configId>')
515
+ .description('View a client agent config')
516
+ .action(agentViewClientCommand);
517
+
518
+ agentCmd
519
+ .command('edit-client <configId>')
520
+ .description('Edit a client agent config')
521
+ .action(agentEditClientCommand);
522
+
523
+ agentCmd
524
+ .command('deactivate-client <configId>')
525
+ .description('Deactivate a client agent config')
526
+ .action(agentDeactivateClientCommand);
527
+
528
+ agentCmd
529
+ .command('rotate-client-key <configId>')
530
+ .description('Rotate the API key for a client agent config')
531
+ .action(agentRotateClientKeyCommand);
532
+
405
533
  // ── Village Content Pipeline ───────────────────────────
406
534
 
407
535
  program
@@ -426,6 +554,49 @@ export function run() {
426
554
  .description('Share a story or wisdom with the network')
427
555
  .action(storyCommand);
428
556
 
557
+ // ── Media: Soulprint Reels (mobile-app posts) ──────────
558
+
559
+ const mediaCmd = program
560
+ .command('media')
561
+ .description('Manage Soulprint Reels (internal social posts shown in the mobile app)');
562
+
563
+ const mediaDraftCmd = mediaCmd
564
+ .command('draft')
565
+ .description('Reel draft commands');
566
+
567
+ mediaDraftCmd
568
+ .command('create')
569
+ .description('Create a new reel draft (video, photo, carousel, audio, text)')
570
+ .option('-f, --file <path>', 'Attach a media file (repeatable)', (v, prev) => prev ? [...prev, v] : [v])
571
+ .option('-a, --asset-type <type>', 'Asset type when --file is given (IMAGE, VIDEO, THUMBNAIL, DOCUMENT)')
572
+ .option('--submit', 'Auto-submit the draft for review after creation')
573
+ .action(mediaDraftCreateCommand);
574
+
575
+ mediaDraftCmd
576
+ .command('edit <id>')
577
+ .description('Edit a DRAFT or REJECTED reel draft')
578
+ .action(mediaDraftEditCommand);
579
+
580
+ mediaDraftCmd
581
+ .command('list')
582
+ .description('List reel drafts')
583
+ .option('--status <status>', 'Filter by status (DRAFT, SUBMITTED, IN_REVIEW, APPROVED, REJECTED, PUBLISHED)')
584
+ .option('--content-type <type>', 'Filter by content type (VIDEO, PHOTO, CAROUSEL, AUDIO, TEXT)')
585
+ .option('--platform <platform>', '[Deprecated] Legacy platform alias — maps forward to content type')
586
+ .option('--visibility <visibility>', 'Filter by visibility (PUBLIC, COMMUNITY, PRIVATE)')
587
+ .option('--community-id <id>', 'Filter by community')
588
+ .option('--search <query>', 'Search captions / titles / quotes')
589
+ .option('-n, --limit <number>', 'Number of drafts', '20')
590
+ .option('--offset <number>', 'Pagination offset', '0')
591
+ .option('--sort <sort>', 'Sort: newest, oldest, publish_date', 'newest')
592
+ .option('--json', 'Output raw JSON')
593
+ .action(mediaDraftListCommand);
594
+
595
+ mediaDraftCmd
596
+ .command('status <id>')
597
+ .description('Show status of a reel draft')
598
+ .action(mediaDraftStatusCommand);
599
+
429
600
  // ── BizReqs: Business Requirements Pipeline ───────────
430
601
 
431
602
  const bizreqsCmd = program
@@ -477,6 +648,40 @@ export function run() {
477
648
 
478
649
  // ── SoulPrint Studio: Model Training Pipeline ───────────
479
650
 
651
+ // \u2500\u2500 Wisdom: agent skill packs (Books of Wisdom) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
652
+
653
+ const wisdomCmd = program
654
+ .command('wisdom')
655
+ .description('Manage .wisdom files \u2014 agent skill packs backed by the network\'s Books of Wisdom');
656
+
657
+ wisdomCmd
658
+ .command('list')
659
+ .description('List wisdom files (use --remote for community-published, --agent for local)')
660
+ .option('--remote', 'List community-published wisdom on the network')
661
+ .option('--agent <name>', 'List a local agent\'s wisdom files')
662
+ .action(wisdomListCommand);
663
+
664
+ wisdomCmd
665
+ .command('pull <id>')
666
+ .description('Fetch a community wisdom file and save it into a local agent')
667
+ .requiredOption('--into <agent>', 'Local agent to save the wisdom file into')
668
+ .action(wisdomPullCommand);
669
+
670
+ wisdomCmd
671
+ .command('push <file>')
672
+ .description('Publish a .wisdom file to the network so other agents can use it')
673
+ .action(wisdomPushCommand);
674
+
675
+ wisdomCmd
676
+ .command('new <name>')
677
+ .description('Scaffold a new local .wisdom file for an agent')
678
+ .requiredOption('--agent <name>', 'Local agent to add the wisdom file to')
679
+ .option('--description <text>', 'Short description shown in the agent\'s skill list')
680
+ .option('--trigger <text>', 'Condition that should activate this skill')
681
+ .action(wisdomNewCommand);
682
+
683
+ // \u2500\u2500 SoulPrint Studio (legacy): training pipeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
684
+
480
685
  const soulprintCmd = program
481
686
  .command('soulprint')
482
687
  .description('SoulPrint Studio \u2014 datasets, training, and model publishing');
@@ -40,6 +40,7 @@ export function createAgenticAppProject(targetDir, options) {
40
40
  includeRestApi = true,
41
41
  mcpToolGroups = [],
42
42
  oauthCredentials = null,
43
+ agentConfig = null,
43
44
  } = options;
44
45
 
45
46
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
@@ -88,7 +89,7 @@ export function createAgenticAppProject(targetDir, options) {
88
89
  writeFileSync(join(targetDir, 'package.json'), generatePackageJson(slug, description, includeMcp));
89
90
  writeFileSync(join(targetDir, 'next.config.mjs'), generateNextConfig());
90
91
  writeFileSync(join(targetDir, '.gitignore'), generateGitignore());
91
- writeFileSync(join(targetDir, '.env.local'), generateEnv(oauthCredentials, hasOAuth, includeMcp));
92
+ writeFileSync(join(targetDir, '.env.local'), generateEnv(oauthCredentials, hasOAuth, includeMcp, agentConfig));
92
93
  writeFileSync(join(targetDir, '.env.example'), generateEnvExample(hasOAuth, includeMcp));
93
94
  writeFileSync(join(targetDir, 'README.md'), generateReadme(name, description, hasOAuth, includeMcp, includeRestApi, features));
94
95
  writeFileSync(join(targetDir, 'jsconfig.json'), generateJsConfig());
@@ -214,7 +215,7 @@ out/
214
215
  `;
215
216
  }
216
217
 
217
- function generateEnv(oauthCredentials, hasOAuth, includeMcp) {
218
+ function generateEnv(oauthCredentials, hasOAuth, includeMcp, agentConfig = null) {
218
219
  const lines = [];
219
220
 
220
221
  if (hasOAuth) {
@@ -233,6 +234,11 @@ function generateEnv(oauthCredentials, hasOAuth, includeMcp) {
233
234
  lines.push('MYVILLAGEOS_MCP_URL=https://mcp.myvillageproject.ai');
234
235
  }
235
236
 
237
+ if (agentConfig?.apiKey) {
238
+ lines.push(`MYVILLAGE_AGENT_API_KEY=${agentConfig.apiKey}`);
239
+ lines.push(`MYVILLAGE_AGENT_CLIENT_ID=${agentConfig.clientId}`);
240
+ }
241
+
236
242
  lines.push('ANTHROPIC_API_KEY=');
237
243
 
238
244
  return lines.join('\n') + '\n';
@@ -254,6 +260,8 @@ function generateEnvExample(hasOAuth, includeMcp) {
254
260
  lines.push('MYVILLAGEOS_MCP_URL=https://mcp.myvillageproject.ai');
255
261
  }
256
262
 
263
+ lines.push('MYVILLAGE_AGENT_API_KEY=');
264
+ lines.push('MYVILLAGE_AGENT_CLIENT_ID=');
257
265
  lines.push('ANTHROPIC_API_KEY=');
258
266
 
259
267
  return lines.join('\n') + '\n';
package/src/utils/api.js CHANGED
@@ -418,6 +418,44 @@ export async function listPostsByFilters(params = {}) {
418
418
  return response.data;
419
419
  }
420
420
 
421
+ // ── Client Agent Configs API (/api/client-agents/configs) ──
422
+
423
+ export async function registerClientAgent(data) {
424
+ const client = getPlatformClient();
425
+ const response = await client.post('/client-agents/configs', data);
426
+ return response.data;
427
+ }
428
+
429
+ export async function listClientAgentConfigs() {
430
+ const client = getPlatformClient();
431
+ const response = await client.get('/client-agents/configs');
432
+ return response.data;
433
+ }
434
+
435
+ export async function getClientAgentConfig(configId) {
436
+ const client = getPlatformClient();
437
+ const response = await client.get(`/client-agents/configs/${encodeURIComponent(configId)}`);
438
+ return response.data;
439
+ }
440
+
441
+ export async function updateClientAgentConfig(configId, data) {
442
+ const client = getPlatformClient();
443
+ const response = await client.patch(`/client-agents/configs/${encodeURIComponent(configId)}`, data);
444
+ return response.data;
445
+ }
446
+
447
+ export async function deactivateClientAgent(configId) {
448
+ const client = getPlatformClient();
449
+ const response = await client.delete(`/client-agents/configs/${encodeURIComponent(configId)}`);
450
+ return response.data;
451
+ }
452
+
453
+ export async function rotateClientAgentKey(configId) {
454
+ const client = getPlatformClient();
455
+ const response = await client.post(`/client-agents/configs/${encodeURIComponent(configId)}/rotate-key`);
456
+ return response.data;
457
+ }
458
+
421
459
  // ── BizReqs API Client (/api/bizreqs) ───────────────────
422
460
 
423
461
  export function getBizReqsClient() {
@@ -564,3 +602,122 @@ export async function submitGameForReview(gameId) {
564
602
  const response = await client.put(`/games/${encodeURIComponent(gameId)}`, { status: 'SUBMITTED' });
565
603
  return response.data;
566
604
  }
605
+
606
+ // ── Village Agents (developer agents) ───────────────────
607
+
608
+ // AgentProfiles owned by the caller that aren't yet linked to a VillageAgent.
609
+ // Used by the CLI "attach existing network identity" picker.
610
+ export async function listMyUnlinkedAgentProfiles() {
611
+ const client = getNetworkClient();
612
+ const response = await client.get('/agents/my', { params: { unlinked: 'true' } });
613
+ return response.data;
614
+ }
615
+
616
+ // Create a VillageAgent. Optionally either:
617
+ // - link to an existing AgentProfile via `agentProfileId`
618
+ // - auto-create a fresh AgentProfile via `createNetworkIdentity: true`
619
+ export async function createVillageAgent(data) {
620
+ const client = getPlatformClient();
621
+ const response = await client.post('/village-agents', data);
622
+ return response.data;
623
+ }
624
+
625
+ export async function listVillageAgents() {
626
+ const client = getPlatformClient();
627
+ const response = await client.get('/village-agents');
628
+ return response.data;
629
+ }
630
+
631
+ export async function getVillageAgent(id) {
632
+ const client = getPlatformClient();
633
+ const response = await client.get(`/village-agents/${encodeURIComponent(id)}`);
634
+ return response.data;
635
+ }
636
+
637
+ export async function updateVillageAgent(id, data) {
638
+ const client = getPlatformClient();
639
+ const response = await client.patch(`/village-agents/${encodeURIComponent(id)}`, data);
640
+ return response.data;
641
+ }
642
+
643
+ // ── Agent Task Queue ────────────────────────────────────
644
+
645
+ export async function listAgentTasks(villageAgentId, params = {}) {
646
+ const client = getPlatformClient();
647
+ const response = await client.get(
648
+ `/village-agents/${encodeURIComponent(villageAgentId)}/tasks`,
649
+ { params },
650
+ );
651
+ return response.data;
652
+ }
653
+
654
+ export async function assignAgentTask(villageAgentId, data) {
655
+ const client = getPlatformClient();
656
+ const response = await client.post(
657
+ `/village-agents/${encodeURIComponent(villageAgentId)}/tasks`,
658
+ data,
659
+ );
660
+ return response.data;
661
+ }
662
+
663
+ export async function claimAgentTask(villageAgentId, taskId) {
664
+ const client = getPlatformClient();
665
+ const response = await client.post(
666
+ `/village-agents/${encodeURIComponent(villageAgentId)}/tasks/${encodeURIComponent(taskId)}/claim`,
667
+ );
668
+ return response.data;
669
+ }
670
+
671
+ // completeAgentTask is also used for "fail" — pass `errorMessage` to mark FAILED,
672
+ // otherwise the task is marked COMPLETED with the given `output`.
673
+ export async function completeAgentTask(villageAgentId, taskId, data = {}) {
674
+ const client = getPlatformClient();
675
+ const response = await client.post(
676
+ `/village-agents/${encodeURIComponent(villageAgentId)}/tasks/${encodeURIComponent(taskId)}/complete`,
677
+ data,
678
+ );
679
+ return response.data;
680
+ }
681
+
682
+ // ── Wisdom (VillageBooks repurposed as agent skill packs) ──────────
683
+
684
+ export async function listVillageBooks(params = {}) {
685
+ const client = getPlatformClient();
686
+ const response = await client.get('/village-books', { params });
687
+ return response.data;
688
+ }
689
+
690
+ // Returns the raw .wisdom file text (YAML frontmatter + markdown body).
691
+ export async function exportVillageBook(id) {
692
+ const client = getPlatformClient();
693
+ const response = await client.get(
694
+ `/village-books/${encodeURIComponent(id)}/export`,
695
+ { responseType: 'text', transformResponse: (data) => data },
696
+ );
697
+ return response.data;
698
+ }
699
+
700
+ export async function importVillageBook(payload) {
701
+ const client = getPlatformClient();
702
+ const response = await client.post('/village-books/import', payload);
703
+ return response.data;
704
+ }
705
+
706
+ // ── Knowledge (long-term agent memory) ──────────────────
707
+
708
+ // Filter / search knowledge submissions. Pass `createdByAgentId` to narrow
709
+ // to a specific agent's own memories.
710
+ export async function listKnowledgeFiltered(params = {}) {
711
+ const client = getPlatformClient();
712
+ const response = await client.get('/knowledge/filtered', { params });
713
+ return response.data;
714
+ }
715
+
716
+ // Write a memory by submitting a Knowledge entry attributed to the agent.
717
+ // Backed by /api/agent-tools/share-knowledge — the agent_profile_id override
718
+ // makes the entry appear in this specific agent's recall results.
719
+ export async function shareKnowledgeAsAgent(data) {
720
+ const client = getPlatformClient();
721
+ const response = await client.post('/agent-tools/share-knowledge', data);
722
+ return response.data;
723
+ }