thepopebot 1.2.76-beta.23 → 1.2.76-beta.25

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.
package/api/CLAUDE.md CHANGED
@@ -29,7 +29,7 @@ Browser-facing data fetching uses **fetch route handlers** colocated with pages
29
29
  | Method | Path | Auth | Handler |
30
30
  |--------|------|------|---------|
31
31
  | GET | `/api/ping` | None | Health check |
32
- | POST | `/api/create-agent-job` | `x-api-key` | Create agent job |
32
+ | POST | `/api/create-agent-job` | `x-api-key` | Create agent job. Body: `{ agent_job, llm_model?, agent_backend?, scope? }` |
33
33
  | GET | `/api/get-agent-job-secret` | `agent_job_api_key` only | Get an agent job secret. `oauth2` credentials return only the access_token (auto-refreshed under a lock; rotated refresh tokens are persisted back). Other secret types return the raw value. |
34
34
  | POST | `/api/set-agent-job-secret` | `agent_job_api_key` only | Create or update an agent-job secret from inside the container (used by the `set-secret` skill). |
35
35
  | GET | `/api/agent-job-list-secrets` | `x-api-key` | List agent job secret keys (no values); returns `{secrets: [{key, isSet, updatedAt, secretType}]}` |
package/api/index.js CHANGED
@@ -94,13 +94,14 @@ function extractAgentJobId(branchName) {
94
94
 
95
95
  async function handleCreateAgentJob(request) {
96
96
  const body = await request.json();
97
- const { job } = body;
98
- if (!job) return Response.json({ error: 'Missing job field' }, { status: 400 });
97
+ const { agent_job } = body;
98
+ if (!agent_job) return Response.json({ error: 'Missing agent_job field' }, { status: 400 });
99
99
 
100
100
  try {
101
- const result = await createAgentJob(job, {
101
+ const result = await createAgentJob(agent_job, {
102
102
  llmModel: body.llm_model,
103
103
  agentBackend: body.agent_backend,
104
+ scope: body.scope,
104
105
  });
105
106
  return Response.json(result);
106
107
  } catch (err) {
package/lib/actions.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import { exec } from 'child_process';
2
2
  import { promisify } from 'util';
3
3
  import { createAgentJob } from './tools/create-agent-job.js';
4
- import { buildCodingAgentSystemPrompt } from './ai/system-prompt.js';
5
- import { resolveAgentScope } from './ai/scope.js';
6
- import { PROJECT_ROOT } from './paths.js';
7
4
 
8
5
  const execAsync = promisify(exec);
9
6
 
@@ -40,11 +37,7 @@ async function executeAction(action, opts = {}) {
40
37
  const options = {};
41
38
  if (action.llm_model) options.llmModel = action.llm_model;
42
39
  if (action.agent_backend) options.agentBackend = action.agent_backend;
43
- if (action.scope) {
44
- options.scope = action.scope;
45
- const { skillsDir } = resolveAgentScope(PROJECT_ROOT, action.scope);
46
- options.systemPrompt = buildCodingAgentSystemPrompt('agent', skillsDir, action.scope);
47
- }
40
+ if (action.scope) options.scope = action.scope;
48
41
  const result = await createAgentJob(action.job, options);
49
42
  return `agent-job ${result.agent_job_id} — ${result.title}`;
50
43
  }
@@ -3,6 +3,9 @@ import { z } from 'zod';
3
3
  import { githubApi } from './github.js';
4
4
  import { callHelperLlmStructured } from '../ai/helper-llm.js';
5
5
  import { getConfig } from '../config.js';
6
+ import { resolveAgentScope } from '../ai/scope.js';
7
+ import { buildCodingAgentSystemPrompt } from '../ai/system-prompt.js';
8
+ import { PROJECT_ROOT } from '../paths.js';
6
9
  /**
7
10
  * Generate a short descriptive title for an agent job using the helper LLM.
8
11
  * Uses structured output to avoid thinking-token leaks with extended-thinking models.
@@ -31,6 +34,9 @@ async function generateAgentJobTitle(agentJobDescription) {
31
34
  * @param {Object} [options] - Optional overrides
32
35
  * @param {string} [options.llmModel] - LLM model override
33
36
  * @param {string} [options.agentBackend] - Agent backend override ('claude-code', 'pi', etc.)
37
+ * @param {string} [options.scope] - Scope subdirectory (e.g. 'agents/gary-vee'). When set,
38
+ * the scope's SYSTEM.md is pre-rendered with full template resolution and stored in the
39
+ * job config so the container picks it up directly (named volumes can't render host-side).
34
40
  * @returns {Promise<{agent_job_id: string, branch: string, title: string}>}
35
41
  */
36
42
  async function createAgentJob(agentJobDescription, options = {}) {
@@ -52,8 +58,11 @@ async function createAgentJob(agentJobDescription, options = {}) {
52
58
  const config = { title, job: agentJobDescription };
53
59
  if (options.llmModel) config.llm_model = options.llmModel;
54
60
  if (options.agentBackend) config.agent_backend = options.agentBackend;
55
- if (options.scope) config.scope = options.scope;
56
- if (options.systemPrompt) config.system_prompt = options.systemPrompt;
61
+ if (options.scope) {
62
+ config.scope = options.scope;
63
+ const { skillsDir } = resolveAgentScope(PROJECT_ROOT, options.scope);
64
+ config.system_prompt = buildCodingAgentSystemPrompt('agent', skillsDir, options.scope);
65
+ }
57
66
 
58
67
  const treeEntries = [
59
68
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.76-beta.23",
3
+ "version": "1.2.76-beta.25",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: agent-job-tools
3
+ description: Use when you need to access agent secrets, API keys, or create and manage background jobs. Supports listing keys and retrieving values, with OAuth credentials auto-refreshed. Also handles requests to "create a background job," "spawn a job," or "kick off an agent job."
4
+ ---
5
+
6
+ ## Usage
7
+
8
+ ```bash
9
+ # List available secret keys (fetches current list from server)
10
+ node skills/agent-job-tools/agent-job-tools.js secrets list
11
+
12
+ # Get a secret value (OAuth credentials are auto-refreshed)
13
+ node skills/agent-job-tools/agent-job-tools.js secrets get MY_CREDENTIALS
14
+
15
+ # Run an agent job in the background
16
+ node skills/agent-job-tools/agent-job-tools.js jobs create "Update the README with installation instructions"
17
+
18
+ # With overrides
19
+ node skills/agent-job-tools/agent-job-tools.js jobs create "Refactor the auth module" \
20
+ --llm-model claude-opus-4-7 \
21
+ --agent-backend claude-code \
22
+ --scope agents/refactor
23
+
24
+ # Status of running jobs (all, or one by id)
25
+ node skills/agent-job-tools/agent-job-tools.js jobs status
26
+ node skills/agent-job-tools/agent-job-tools.js jobs status <agent_job_id>
27
+ ```
28
+
29
+ ## Important: pass-through behavior for `jobs create`
30
+
31
+ The `<description>` arg becomes the new job's prompt verbatim. **Pass it through unchanged — do not summarize, condense, or rewrite the user's request before calling.** The new job's agent reads this description directly as its task. If the user gave you a multi-paragraph spec, pass the multi-paragraph spec.
32
+
33
+ ## Scope inheritance
34
+
35
+ If the calling agent is running with a `SCOPE` env var set, `jobs create` defaults the new job to that same scope. Pass `--scope <value>` to override, or `--scope ""` to clear scope on the new job.
36
+
37
+ ## Notes
38
+
39
+ - `AGENT_JOB_TOKEN` and `APP_URL` are injected automatically — no setup required.
40
+ - Plain (non-OAuth) secrets are also available directly as env vars (e.g. `echo $MY_KEY`).
41
+ - OAuth credentials must be fetched via `secrets get` — they are not available as env vars.
42
+ - `secrets get` on an OAuth credential refreshes it server-side and returns a fresh access token.
43
+ - If a fetched credential stops working (expired token, 401 error), call `secrets get` again to obtain a fresh one.
44
+ - `secrets list` always fetches from the server, so it reflects secrets added after the container started.
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ const apiKey = process.env.AGENT_JOB_TOKEN;
4
+ const appUrl = process.env.APP_URL;
5
+
6
+ const args = process.argv.slice(2);
7
+ const [category, subcommand, ...rest] = args;
8
+
9
+ function usage() {
10
+ console.error('Usage:');
11
+ console.error(' agent-job-tools secrets list');
12
+ console.error(' agent-job-tools secrets get <key>');
13
+ console.error(' agent-job-tools jobs create <description> [--llm-model M] [--agent-backend B] [--scope S]');
14
+ console.error(' agent-job-tools jobs status [agent_job_id]');
15
+ process.exit(1);
16
+ }
17
+
18
+ function requireAuth() {
19
+ if (!apiKey) { console.error('AGENT_JOB_TOKEN not available'); process.exit(1); }
20
+ if (!appUrl) { console.error('APP_URL not available'); process.exit(1); }
21
+ }
22
+
23
+ async function httpJson(method, path, body) {
24
+ const url = `${appUrl}${path}`;
25
+ const opts = { method, headers: { 'x-api-key': apiKey } };
26
+ if (body !== undefined) {
27
+ opts.headers['Content-Type'] = 'application/json';
28
+ opts.body = JSON.stringify(body);
29
+ }
30
+ const res = await fetch(url, opts);
31
+ if (!res.ok) {
32
+ const text = await res.text();
33
+ console.error(`${method} ${url} → ${res.status} ${text}`);
34
+ process.exit(1);
35
+ }
36
+ return res.json();
37
+ }
38
+
39
+ if (!category) usage();
40
+
41
+ if (category === 'secrets') {
42
+ requireAuth();
43
+
44
+ if (subcommand === 'list') {
45
+ const json = await httpJson('GET', '/api/agent-job-list-secrets');
46
+ const secrets = json.secrets;
47
+ if (!secrets || secrets.length === 0) {
48
+ console.log('No agent secrets configured.');
49
+ } else {
50
+ console.log('Available secrets:');
51
+ secrets.forEach(s => {
52
+ const hint = s.secretType === 'oauth2' ? ' (OAuth — use get to fetch access token)'
53
+ : s.secretType === 'oauth_token' ? ' (OAuth token — use get to fetch)'
54
+ : '';
55
+ console.log(` - ${s.key}${hint}`);
56
+ });
57
+ console.log('\nUse: agent-job-tools secrets get KEY_NAME');
58
+ console.log('If a fetched value stops working, call get again for a fresh one.');
59
+ }
60
+ process.exit(0);
61
+ }
62
+
63
+ if (subcommand === 'get') {
64
+ const key = rest[0];
65
+ if (!key) { console.error('Usage: agent-job-tools secrets get KEY_NAME'); process.exit(1); }
66
+ const json = await httpJson('GET', `/api/get-agent-job-secret?key=${encodeURIComponent(key)}`);
67
+ console.log(json.value);
68
+ process.exit(0);
69
+ }
70
+
71
+ console.error(`Unknown secrets subcommand: ${subcommand || '(none)'}`);
72
+ usage();
73
+ }
74
+
75
+ if (category === 'jobs') {
76
+ requireAuth();
77
+
78
+ if (subcommand === 'create') {
79
+ let description = null;
80
+ const opts = {};
81
+ let scopeExplicit = false;
82
+ for (let i = 0; i < rest.length; i++) {
83
+ const arg = rest[i];
84
+ if (arg === '--llm-model') opts.llm_model = rest[++i];
85
+ else if (arg === '--agent-backend') opts.agent_backend = rest[++i];
86
+ else if (arg === '--scope') { opts.scope = rest[++i]; scopeExplicit = true; }
87
+ else if (description === null) description = arg;
88
+ else { console.error(`Unexpected arg: ${arg}`); usage(); }
89
+ }
90
+ if (!description) { console.error('Missing job description'); usage(); }
91
+
92
+ if (!scopeExplicit && process.env.SCOPE) {
93
+ opts.scope = process.env.SCOPE;
94
+ }
95
+
96
+ const body = { agent_job: description };
97
+ if (opts.llm_model) body.llm_model = opts.llm_model;
98
+ if (opts.agent_backend) body.agent_backend = opts.agent_backend;
99
+ if (opts.scope) body.scope = opts.scope;
100
+
101
+ const json = await httpJson('POST', '/api/create-agent-job', body);
102
+ console.log(JSON.stringify(json, null, 2));
103
+ process.exit(0);
104
+ }
105
+
106
+ if (subcommand === 'status') {
107
+ const agentJobId = rest[0];
108
+ const qs = agentJobId ? `?agent_job_id=${encodeURIComponent(agentJobId)}` : '';
109
+ const json = await httpJson('GET', `/api/agent-jobs/status${qs}`);
110
+ console.log(JSON.stringify(json, null, 2));
111
+ process.exit(0);
112
+ }
113
+
114
+ console.error(`Unknown jobs subcommand: ${subcommand || '(none)'}`);
115
+ usage();
116
+ }
117
+
118
+ console.error(`Unknown category: ${category}`);
119
+ usage();
@@ -1,23 +0,0 @@
1
- ---
2
- name: agent-job-secrets
3
- description: List and retrieve agent secrets. Plain secrets are also available as env vars. OAuth credentials are auto-refreshed on every get call.
4
- ---
5
-
6
- ## Usage
7
-
8
- ```bash
9
- # List available secret keys (fetches current list from server)
10
- node skills/agent-job-secrets/agent-job-secrets.js
11
-
12
- # Get a secret value (OAuth credentials are auto-refreshed)
13
- node skills/agent-job-secrets/agent-job-secrets.js get MY_CREDENTIALS
14
- ```
15
-
16
- ## Notes
17
-
18
- - `AGENT_JOB_TOKEN` and `APP_URL` are injected automatically — no setup required
19
- - Plain (non-OAuth) secrets are also available directly as env vars (e.g. `echo $MY_KEY`)
20
- - OAuth credentials must be fetched via `get` — they are not available as env vars
21
- - `get` on an OAuth credential refreshes it server-side and returns a fresh access token
22
- - If a fetched credential stops working (expired token, 401 error), call `get` again to obtain a fresh one
23
- - `list` always fetches from the server, so it reflects secrets added after the container started
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const [cmd, key] = process.argv.slice(2);
4
-
5
- const apiKey = process.env.AGENT_JOB_TOKEN;
6
- const appUrl = process.env.APP_URL;
7
-
8
- // Default to list
9
- if (!cmd || cmd === 'list') {
10
- if (!apiKey || !appUrl) {
11
- console.log('No agent secrets available (missing AGENT_JOB_TOKEN or APP_URL).');
12
- process.exit(0);
13
- }
14
- const url = `${appUrl}/api/agent-job-list-secrets`;
15
- const res = await fetch(url, {
16
- headers: { 'x-api-key': apiKey },
17
- });
18
- if (!res.ok) {
19
- const body = await res.text();
20
- console.error(`GET ${url} → ${res.status} ${body}`);
21
- process.exit(1);
22
- }
23
- const json = await res.json();
24
- const secrets = json.secrets;
25
- if (!secrets || secrets.length === 0) {
26
- console.log('No agent secrets configured.');
27
- } else {
28
- console.log('Available secrets:');
29
- secrets.forEach(s => {
30
- const hint = s.secretType === 'oauth2' ? ' (OAuth — use get to fetch access token)'
31
- : s.secretType === 'oauth_token' ? ' (OAuth token — use get to fetch)'
32
- : '';
33
- console.log(` - ${s.key}${hint}`);
34
- });
35
- console.log('\nUse: agent-job-secrets get KEY_NAME');
36
- console.log('If a fetched value stops working, call get again for a fresh one.');
37
- }
38
- process.exit(0);
39
- }
40
-
41
- if (!apiKey) { console.error('AGENT_JOB_TOKEN not available'); process.exit(1); }
42
- if (!appUrl) { console.error('APP_URL not available'); process.exit(1); }
43
-
44
- if (cmd === 'get') {
45
- if (!key) { console.error('Usage: agent-job-secrets get KEY_NAME'); process.exit(1); }
46
- const url = `${appUrl}/api/get-agent-job-secret?key=${encodeURIComponent(key)}`;
47
- const res = await fetch(url, {
48
- headers: { 'x-api-key': apiKey },
49
- });
50
- if (!res.ok) {
51
- const body = await res.text();
52
- console.error(`GET ${url} → ${res.status} ${body}`);
53
- process.exit(1);
54
- }
55
- const json = await res.json();
56
- console.log(json.value);
57
- process.exit(0);
58
- }
59
-
60
- console.error(`Unknown command: ${cmd}`);
61
- console.error('Available commands: list, get');
62
- process.exit(1);