assistme 0.8.12 → 0.9.1

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/PLAN.md CHANGED
@@ -143,4 +143,4 @@ New page: `/agent` - Agent chat interface
143
143
 
144
144
  ### Phase 4: Edge Function
145
145
 
146
- 1. Create agent-submit-task edge function for mobile clients
146
+ 1. Create main-agent-submit-task edge function for mobile clients
@@ -8,7 +8,7 @@ import {
8
8
  readAuthStore,
9
9
  safeParse,
10
10
  writeAuthStore
11
- } from "./chunk-5ML5YMCM.js";
11
+ } from "./chunk-RRMI6RDG.js";
12
12
  import {
13
13
  log,
14
14
  newCorrelationId,
@@ -318,6 +318,7 @@ var CDP_SETTLE_MS = 300;
318
318
  var PAGE_TRANSITION_MS = 500;
319
319
  var MAX_WAIT_MS = 5e3;
320
320
  var SCREENSHOT_QUALITY = 80;
321
+ var SCREENSHOT_MAX_DIM_PX = 1568;
321
322
  function delay(ms) {
322
323
  return new Promise((r) => {
323
324
  setTimeout(r, ms);
@@ -809,6 +810,56 @@ async function tryDismissOverlay(conn) {
809
810
  }
810
811
  }
811
812
 
813
+ // src/browser/screenshot.ts
814
+ async function getViewportMetrics(conn) {
815
+ try {
816
+ const result = await conn.send("Runtime.evaluate", {
817
+ expression: `JSON.stringify({ width: window.innerWidth, height: window.innerHeight, dpr: window.devicePixelRatio || 1 })`,
818
+ returnByValue: true
819
+ });
820
+ const value = result.result?.value;
821
+ if (value) {
822
+ const parsed = JSON.parse(value);
823
+ const width = Number(parsed.width) || 0;
824
+ const height = Number(parsed.height) || 0;
825
+ const dpr = Number(parsed.dpr) || 1;
826
+ if (width > 0 && height > 0) {
827
+ return { width, height, dpr };
828
+ }
829
+ }
830
+ } catch {
831
+ }
832
+ return { width: 0, height: 0, dpr: 1 };
833
+ }
834
+ async function captureBoundedScreenshot(conn, maxDim = SCREENSHOT_MAX_DIM_PX) {
835
+ const { width, height, dpr } = await getViewportMetrics(conn);
836
+ if (width === 0 || height === 0) {
837
+ const result2 = await conn.send("Page.captureScreenshot", {
838
+ format: "png",
839
+ quality: SCREENSHOT_QUALITY,
840
+ captureBeyondViewport: false
841
+ });
842
+ return result2.data || "";
843
+ }
844
+ const naturalMax = Math.max(width, height) * dpr;
845
+ if (naturalMax <= maxDim) {
846
+ const result2 = await conn.send("Page.captureScreenshot", {
847
+ format: "png",
848
+ quality: SCREENSHOT_QUALITY,
849
+ captureBeyondViewport: false
850
+ });
851
+ return result2.data || "";
852
+ }
853
+ const scale = Math.min(dpr, maxDim / Math.max(width, height));
854
+ const result = await conn.send("Page.captureScreenshot", {
855
+ format: "png",
856
+ quality: SCREENSHOT_QUALITY,
857
+ captureBeyondViewport: false,
858
+ clip: { x: 0, y: 0, width, height, scale }
859
+ });
860
+ return result.data || "";
861
+ }
862
+
812
863
  // src/browser/actions.ts
813
864
  var Actions = class {
814
865
  constructor(conn, snapshotEngine) {
@@ -1004,12 +1055,7 @@ var Actions = class {
1004
1055
  let screenshot;
1005
1056
  if (takeScreenshot) {
1006
1057
  await delay(CDP_SETTLE_MS);
1007
- const screenshotResult = await this.conn.send("Page.captureScreenshot", {
1008
- format: "png",
1009
- quality: SCREENSHOT_QUALITY,
1010
- captureBeyondViewport: false
1011
- });
1012
- screenshot = screenshotResult.data || "";
1058
+ screenshot = await captureBoundedScreenshot(this.conn);
1013
1059
  }
1014
1060
  return { results, screenshot };
1015
1061
  }
@@ -1378,12 +1424,7 @@ URL: ${info.url}`;
1378
1424
  }
1379
1425
  async screenshot() {
1380
1426
  this.conn.ensureConnected();
1381
- const result = await this.conn.send("Page.captureScreenshot", {
1382
- format: "png",
1383
- quality: SCREENSHOT_QUALITY,
1384
- captureBeyondViewport: false
1385
- });
1386
- return result.data || "";
1427
+ return captureBoundedScreenshot(this.conn);
1387
1428
  }
1388
1429
  async detectLoginPage() {
1389
1430
  try {
@@ -1833,12 +1874,7 @@ var SnapshotEngine = class {
1833
1874
  expression: buildAnnotationOverlayJS(refsJson)
1834
1875
  });
1835
1876
  }
1836
- const screenshotResult = await this.conn.send("Page.captureScreenshot", {
1837
- format: "png",
1838
- quality: SCREENSHOT_QUALITY,
1839
- captureBeyondViewport: false
1840
- });
1841
- const image = screenshotResult.data || "";
1877
+ const image = await captureBoundedScreenshot(this.conn);
1842
1878
  if (annotate) {
1843
1879
  await this.conn.send("Runtime.evaluate", {
1844
1880
  expression: `(function() { var el = document.getElementById('__assistme_refs__'); if (el) el.remove(); })()`
@@ -2833,19 +2869,51 @@ var TaskPoller = class {
2833
2869
  // src/agent/memory/manager.ts
2834
2870
  var MemoryManager = class {
2835
2871
  /**
2836
- * Store a new memory. Called by the agent after completing tasks
2837
- * to remember important facts about the user.
2872
+ * When set, all memory operations use this agent's users.id as the
2873
+ * effective user, giving the agent its own memory space.
2874
+ */
2875
+ agentUserId = null;
2876
+ /** The human owner's user ID — used to load owner memories as fallback. */
2877
+ ownerUserId = null;
2878
+ /**
2879
+ * Configure agent-scoped memory.
2880
+ * @param agentUserId The agent's users.id — memories are stored/read here
2881
+ * @param ownerUserId The human owner's users.id — owner memories loaded as fallback
2882
+ */
2883
+ setAgentContext(agentUserId, ownerUserId) {
2884
+ this.agentUserId = agentUserId;
2885
+ this.ownerUserId = ownerUserId;
2886
+ }
2887
+ /** Clear agent context (revert to normal user-scoped mode). */
2888
+ clearAgentContext() {
2889
+ this.agentUserId = null;
2890
+ this.ownerUserId = null;
2891
+ }
2892
+ /** Inject effective_user_id + owner_user_id into params. */
2893
+ agentParams(extra = {}) {
2894
+ const params = { ...extra };
2895
+ if (this.agentUserId) {
2896
+ params.effective_user_id = this.agentUserId;
2897
+ if (this.ownerUserId) {
2898
+ params.owner_user_id = this.ownerUserId;
2899
+ }
2900
+ }
2901
+ return params;
2902
+ }
2903
+ /**
2904
+ * Store a new memory. Called by the agent after completing tasks.
2905
+ * When agent context is set, stores under the agent's user_id.
2838
2906
  */
2839
2907
  async remember(content, category = "general", options) {
2840
2908
  const expiresAt = options?.expiresInDays ? new Date(Date.now() + options.expiresInDays * 864e5).toISOString() : null;
2841
- const data = await callMcpHandler("memory.store", {
2909
+ const data = await callMcpHandler("memory.store", this.agentParams({
2842
2910
  category,
2843
2911
  content,
2844
2912
  importance: options?.importance ?? 5,
2845
2913
  tags: options?.tags ?? [],
2846
2914
  source_message_id: options?.sourceMessageId ?? null,
2847
2915
  expires_at: expiresAt
2848
- });
2916
+ }));
2849
2917
  log.debug(`Memory stored: [${category}] ${content.slice(0, 80)}...`);
2850
2918
  return data;
2851
2919
  }
@@ -2854,10 +2922,10 @@ var MemoryManager = class {
2854
2922
  */
2855
2923
  async search(query9, limit = 10) {
2856
2924
  try {
2857
- return await callMcpHandler("memory.search", {
2925
+ return await callMcpHandler("memory.search", this.agentParams({
2858
2926
  query: query9,
2859
2927
  limit
2860
- });
2928
+ }));
2861
2929
  } catch (err) {
2862
2930
  log.warn(`Memory search failed: ${err instanceof Error ? err.message : err}`);
2863
2931
  return [];
@@ -2865,13 +2933,13 @@ var MemoryManager = class {
2865
2933
  }
2866
2934
  /**
2867
2935
  * Get the most important/recent memories to include in context.
2868
- * Called before each task to build the agent's "working memory".
2869
- * Automatically filters out expired memories.
2936
+ * When agent context is set, loads agent's memories first,
2937
+ * then supplements with the owner's memories as fallback.
2870
2938
  */
2871
2939
  async getContext(maxItems = 20) {
2872
- const all = await callMcpHandler("memory.get_context", {
2940
+ const all = await callMcpHandler("memory.get_context", this.agentParams({
2873
2941
  max_items: maxItems
2874
- });
2942
+ }));
2875
2943
  const seen = /* @__PURE__ */ new Set();
2876
2944
  return (all || []).filter((m) => {
2877
2945
  if (seen.has(m.id)) return false;
@@ -2885,48 +2953,63 @@ var MemoryManager = class {
2885
2953
  async buildMemoryPrompt() {
2886
2954
  const memories = await this.getContext();
2887
2955
  if (memories.length === 0) return "";
2888
- const sections = {};
2889
- for (const m of memories) {
2890
- const key = m.category;
2891
- if (!sections[key]) sections[key] = [];
2892
- sections[key].push(`- ${m.content}`);
2893
- }
2956
+ const hasOwnerFlag = memories.some((m) => m._from_owner);
2957
+ const agentMemories = hasOwnerFlag ? memories.filter((m) => !m._from_owner) : memories;
2958
+ const ownerMemories = hasOwnerFlag ? memories.filter((m) => m._from_owner) : [];
2894
2959
  const categoryLabels = {
2895
2960
  instruction: "Standing Instructions",
2896
- preference: "User Preferences",
2961
+ preference: "Preferences",
2897
2962
  general: "Known Facts",
2898
2963
  context: "Context",
2899
2964
  skill_learned: "Learned Skills",
2900
2965
  fact: "Facts"
2901
2966
  };
2902
- let prompt = "\n\n## What You Know About The User\n";
2903
- for (const [cat, items] of Object.entries(sections)) {
2904
- prompt += `
2967
+ function formatSection(mems) {
2968
+ const sections = {};
2969
+ for (const m of mems) {
2970
+ const key = m.category;
2971
+ if (!sections[key]) sections[key] = [];
2972
+ sections[key].push(`- ${m.content}`);
2973
+ }
2974
+ let out = "";
2975
+ for (const [cat, items] of Object.entries(sections)) {
2976
+ out += `
2905
2977
  ### ${categoryLabels[cat] || cat}
2906
2978
  `;
2907
- prompt += `${items.join("\n")}
2979
+ out += `${items.join("\n")}
2908
2980
  `;
2981
+ }
2982
+ return out;
2983
+ }
2984
+ let prompt = "";
2985
+ if (agentMemories.length > 0) {
2986
+ prompt += this.agentUserId ? "\n\n## Your Memories\n" : "\n\n## What You Know About The User\n";
2987
+ prompt += formatSection(agentMemories);
2988
+ }
2989
+ if (ownerMemories.length > 0) {
2990
+ prompt += "\n\n## Owner's Context\n";
2991
+ prompt += formatSection(ownerMemories);
2909
2992
  }
2910
2993
  return prompt;
2911
2994
  }
2912
2995
  // ── CRUD for CLI ────────────────────────────────────────────
2913
2996
  async list(category, limit = 20) {
2914
- const data = await callMcpHandler("memory.list", {
2997
+ const data = await callMcpHandler("memory.list", this.agentParams({
2915
2998
  category: category || null,
2916
2999
  limit
2917
- });
3000
+ }));
2918
3001
  return data || [];
2919
3002
  }
2920
3003
  async add(content, category = "general", importance = 5, tags = []) {
2921
3004
  return this.remember(content, category, { importance, tags });
2922
3005
  }
2923
3006
  async remove(memoryId) {
2924
- await callMcpHandler("memory.remove", { memory_id: memoryId });
3007
+ await callMcpHandler("memory.remove", this.agentParams({ memory_id: memoryId }));
2925
3008
  }
2926
3009
  async clear(category) {
2927
- const result = await callMcpHandler("memory.clear", {
3010
+ const result = await callMcpHandler("memory.clear", this.agentParams({
2928
3011
  category: category || null
2929
- });
3012
+ }));
2930
3013
  return result.count;
2931
3014
  }
2932
3015
  };
@@ -5528,7 +5611,7 @@ function createJobTools(sessionId, taskId) {
5528
5611
  const job = await runner.loadJob(args.job_name);
5529
5612
  if (!job) {
5530
5613
  const jobs = await runner.listJobs();
5531
- const available = jobs.length > 0 ? `Available jobs: ${jobs.map((j) => `"${j.name}" (${j.skillCount} skills)`).join(", ")}` : "No jobs defined yet. Use skill_generate to create a job from a job description.";
5614
+ const available = jobs.length > 0 ? `Available jobs: ${jobs.map((j) => `"${j.name}" (${j.skillCount} skills, ${j.agentCount} agents)`).join(", ")}` : "No jobs defined yet. Use skill_generate to create a job from a job description.";
5532
5615
  return textResponse(`Job "${args.job_name}" not found. ${available}`);
5533
5616
  }
5534
5617
  if (job.skills.length === 0) {
@@ -5569,11 +5652,38 @@ function createJobTools(sessionId, taskId) {
5569
5652
  }
5570
5653
  }
5571
5654
  }
5572
- const prompt = runner.buildJobPrompt(job, runId || "untracked");
5655
+ const effectiveRunId = runId || "untracked";
5656
+ const prompt = runner.buildJobPrompt(job, effectiveRunId);
5657
+ const primaryAgentForFilter = job.agents.length > 0 ? job.agents.find((p) => p.role === "primary") || job.agents[0] : null;
5658
+ const parallelAgents = job.agents.filter(
5659
+ (a) => a.role === "parallel" && a.agentId !== primaryAgentForFilter?.agentId
5660
+ );
5661
+ let fullResponse = prompt;
5662
+ if (parallelAgents.length > 0) {
5663
+ fullResponse += `
5664
+
5665
+ ---
5666
+
5667
+ ## Parallel Agent Dispatch
5668
+ `;
5669
+ fullResponse += `The following parallel agents are assigned to this job. `;
5670
+ fullResponse += `After you complete your primary tasks, review their responsibilities and execute any that haven't been covered:
5671
+
5672
+ `;
5673
+ for (const pa of parallelAgents) {
5674
+ fullResponse += `### ${pa.agentName} (@${pa.agentUsername})
5675
+ `;
5676
+ if (pa.agentBio) fullResponse += `*${pa.agentBio}*
5677
+ `;
5678
+ fullResponse += `**Instructions:** ${pa.agentPrompt}
5679
+
5680
+ `;
5681
+ }
5682
+ }
5573
5683
  log.info(
5574
- `Job "${args.job_name}" started with ${job.skills.length} skills (run: ${runId?.slice(0, 8) || "untracked"})`
5684
+ `Job "${args.job_name}" started with ${job.skills.length} skills, ${job.agents.length} agents (run: ${effectiveRunId.slice(0, 8)})`
5575
5685
  );
5576
- return textResponse(prompt);
5686
+ return textResponse(fullResponse);
5577
5687
  })
5578
5688
  ),
5579
5689
  tool4(
@@ -5639,6 +5749,80 @@ function createJobTools(sessionId, taskId) {
5639
5749
  }
5640
5750
  )
5641
5751
  ),
5752
+ tool4(
5753
+ "job_assign_agent",
5754
+ "Create or update an agent for a job. The agent is a user with a specific prompt and identity that will execute this job.",
5755
+ {
5756
+ job_name: z4.string().describe("Name of the job to assign the agent to"),
5757
+ agent_name: z4.string().describe("Display name for the agent (e.g. 'Daily Reporter')"),
5758
+ agent_prompt: z4.string().describe("The agent's system prompt that defines its behavior and personality"),
5759
+ agent_bio: z4.string().optional().describe("Short bio describing the agent's role"),
5760
+ role: z4.enum(["primary", "parallel"]).optional().describe("Agent role: 'primary' (default) or 'parallel' for concurrent execution")
5761
+ },
5762
+ typedHandler(
5763
+ async (args) => {
5764
+ try {
5765
+ const result = await callMcpHandler("job.save_agent", {
5766
+ job_name: args.job_name,
5767
+ agent_name: args.agent_name,
5768
+ agent_prompt: args.agent_prompt,
5769
+ agent_bio: args.agent_bio || null,
5770
+ role: args.role || "primary"
5771
+ });
5772
+ if (!result) {
5773
+ return textResponse(`Failed to create agent for job "${args.job_name}".`);
5774
+ }
5775
+ log.info(
5776
+ `Agent "${result.username}" assigned to job "${args.job_name}" (${args.role || "primary"})`
5777
+ );
5778
+ let response = `## Agent Assigned to Job: ${args.job_name}
5779
+
5780
+ `;
5781
+ response += `- **Agent:** ${args.agent_name} (@${result.username})
5782
+ `;
5783
+ response += `- **Role:** ${args.role || "primary"}
5784
+ `;
5785
+ if (args.agent_bio) {
5786
+ response += `- **Bio:** ${args.agent_bio}
5787
+ `;
5788
+ }
5789
+ response += `
5790
+ The agent is now ready to execute this job.`;
5791
+ return textResponse(response);
5792
+ } catch (err) {
5793
+ return textResponse(
5794
+ `Failed to assign agent: ${err instanceof Error ? err.message : err}`
5795
+ );
5796
+ }
5797
+ }
5798
+ )
5799
+ ),
5800
+ tool4(
5801
+ "job_remove_agent",
5802
+ "Remove (deactivate) an agent from a job.",
5803
+ {
5804
+ job_name: z4.string().describe("Name of the job"),
5805
+ agent_name: z4.string().describe("Display name of the agent to remove")
5806
+ },
5807
+ typedHandler(
5808
+ async (args) => {
5809
+ try {
5810
+ await callMcpHandler("job.remove_agent", {
5811
+ job_name: args.job_name,
5812
+ agent_name: args.agent_name
5813
+ });
5814
+ log.info(`Agent "${args.agent_name}" removed from job "${args.job_name}"`);
5815
+ return textResponse(
5816
+ `Agent "${args.agent_name}" has been removed from job "${args.job_name}".`
5817
+ );
5818
+ } catch (err) {
5819
+ return textResponse(
5820
+ `Failed to remove agent: ${err instanceof Error ? err.message : err}`
5821
+ );
5822
+ }
5823
+ }
5824
+ )
5825
+ ),
5642
5826
  tool4(
5643
5827
  "job_status",
5644
5828
  "Check the status and run history of a job.",
@@ -5657,7 +5841,7 @@ function createJobTools(sessionId, taskId) {
5657
5841
  }
5658
5842
  let response2 = "## Your Jobs\n\n";
5659
5843
  for (const job of jobs) {
5660
- response2 += `- **${job.name}** (${job.skillCount} skills): ${job.description.slice(0, 100)}
5844
+ response2 += `- **${job.name}** (${job.skillCount} skills, ${job.agentCount} agents): ${job.description.slice(0, 100)}
5661
5845
  `;
5662
5846
  }
5663
5847
  response2 += "\nUse `job_run` to execute a job, or `job_schedule` to automate it.";
@@ -8420,6 +8604,11 @@ var TaskProcessor = class {
8420
8604
  resetEventSequence();
8421
8605
  newCorrelationId();
8422
8606
  log.info(`Processing task ${task.id.slice(0, 8)}...`);
8607
+ const taskAgentId = task.metadata?.agent_id || (task.sender_id !== this.userId ? task.sender_id : null);
8608
+ if (taskAgentId && this.memoryManager && this.userId) {
8609
+ this.memoryManager.setAgentContext(taskAgentId, this.userId);
8610
+ log.debug(`Memory context set to agent ${taskAgentId.slice(0, 8)}`);
8611
+ }
8423
8612
  const toolCallRecords = [];
8424
8613
  const toolFailures = [];
8425
8614
  try {
@@ -8479,6 +8668,9 @@ var TaskProcessor = class {
8479
8668
  await emitEvent(task.id, "status_change", { status: "failed" });
8480
8669
  } finally {
8481
8670
  setCorrelationId(null);
8671
+ if (taskAgentId && this.memoryManager) {
8672
+ this.memoryManager.clearAgentContext();
8673
+ }
8482
8674
  try {
8483
8675
  const browser = getBrowser();
8484
8676
  if (browser.isConnected()) {
@@ -43,7 +43,7 @@ function getRawToken() {
43
43
  async function callMcpHandler(action, params = {}, overrideToken) {
44
44
  const config = getConfig();
45
45
  const token = overrideToken || getRawToken();
46
- const url = `${config.supabaseUrl}/functions/v1/mcp-handler`;
46
+ const url = `${config.supabaseUrl}/functions/v1/main-mcp-handler`;
47
47
  const response = await fetch(url, {
48
48
  method: "POST",
49
49
  headers: {
@@ -91,6 +91,14 @@ var SkillRowSchema = z.object({
91
91
  source_skill_id: z.string().optional().nullable(),
92
92
  invocation_count: z.number().optional().default(0)
93
93
  });
94
+ var JobAgentSchema = z.object({
95
+ agent_id: z.string(),
96
+ agent_username: z.string(),
97
+ agent_name: z.string().optional().default(""),
98
+ agent_bio: z.string().optional().nullable(),
99
+ agent_prompt: z.string(),
100
+ role: z.string().optional().default("primary")
101
+ });
94
102
  var JobRowSchema = z.object({
95
103
  job_id: z.string(),
96
104
  job_name: z.string(),
@@ -100,14 +108,16 @@ var JobRowSchema = z.object({
100
108
  skill_name: z.string().optional().nullable(),
101
109
  skill_description: z.string().optional().default(""),
102
110
  skill_emoji: z.string().optional().default(""),
103
- skill_content: z.string().optional().default("")
111
+ skill_content: z.string().optional().default(""),
112
+ agents: z.array(JobAgentSchema).optional().default([])
104
113
  });
105
114
  var JobListRowSchema = z.object({
106
115
  id: z.string(),
107
116
  name: z.string(),
108
117
  description: z.string().optional().default(""),
109
118
  prompt: z.string().optional().nullable(),
110
- skill_count: z.number().optional().default(0)
119
+ skill_count: z.number().optional().default(0),
120
+ agent_count: z.number().optional().default(0)
111
121
  });
112
122
  var JobRunRowSchema = z.object({
113
123
  run_id: z.string(),
@@ -193,6 +203,15 @@ var JobRunner = class {
193
203
  const rows = data.map((row) => safeParse(JobRowSchema, row)).filter((r) => r != null);
194
204
  if (rows.length === 0) return null;
195
205
  const first = rows[0];
206
+ const rawAgents = first.agents || [];
207
+ const agents = rawAgents.map((a) => safeParse(JobAgentSchema, a)).filter((a) => a != null).map((a) => ({
208
+ agentId: a.agent_id,
209
+ agentUsername: a.agent_username,
210
+ agentName: a.agent_name || "",
211
+ agentBio: a.agent_bio ?? null,
212
+ agentPrompt: a.agent_prompt,
213
+ role: a.role
214
+ }));
196
215
  return {
197
216
  jobId: first.job_id,
198
217
  jobName: first.job_name,
@@ -204,7 +223,8 @@ var JobRunner = class {
204
223
  skillDescription: row.skill_description,
205
224
  skillEmoji: row.skill_emoji,
206
225
  skillContent: row.skill_content
207
- }))
226
+ })),
227
+ agents
208
228
  };
209
229
  } catch (err) {
210
230
  log.debug(`Failed to load job "${jobName}": ${errorMessage(err)}`);
@@ -221,7 +241,8 @@ var JobRunner = class {
221
241
  id: row.id,
222
242
  name: row.name,
223
243
  description: row.description,
224
- skillCount: row.skill_count
244
+ skillCount: row.skill_count,
245
+ agentCount: row.agent_count
225
246
  }));
226
247
  } catch (err) {
227
248
  log.debug(`Failed to list jobs: ${errorMessage(err)}`);
@@ -313,6 +334,9 @@ var JobRunner = class {
313
334
  `;
314
335
  }
315
336
  prompt += `## Instructions
337
+
338
+ `;
339
+ prompt += `### Step 1: Analyze and set up skills
316
340
  `;
317
341
  prompt += `For each capability the job needs:
318
342
  `;
@@ -326,8 +350,24 @@ var JobRunner = class {
326
350
 
327
351
  `;
328
352
  prompt += `Be practical \u2014 only create skills that would genuinely help automate this job.
353
+
354
+ `;
355
+ prompt += `### Step 2: Create the job's agent
356
+ `;
357
+ prompt += `After setting up skills, create an agent for this job using \`job_assign_agent\`.
358
+ `;
359
+ prompt += `The agent is the identity that will execute this job. Design it with:
329
360
  `;
330
- prompt += `When done, summarize what skills were created, updated, or already existed.
361
+ prompt += `- **agent_name**: A concise, role-based name (e.g. "Daily Reporter", "Code Reviewer")
362
+ `;
363
+ prompt += `- **agent_prompt**: A system prompt that defines the agent's behavior, expertise, and personality. `;
364
+ prompt += `Include the job's goals, preferred approach, and any domain knowledge the agent should have. `;
365
+ prompt += `Reference the linked skills as capabilities the agent can use.
366
+ `;
367
+ prompt += `- **agent_bio**: A one-line description of what this agent does.
368
+
369
+ `;
370
+ prompt += `When done, summarize what skills were created/updated and the agent that was assigned.
331
371
  `;
332
372
  return prompt;
333
373
  }
@@ -340,6 +380,10 @@ var JobRunner = class {
340
380
  * chain them based on what it discovers at runtime.
341
381
  */
342
382
  buildJobPrompt(job, runId) {
383
+ const primaryAgent = job.agents.length > 0 ? job.agents.find((a) => a.role === "primary") || job.agents[0] : null;
384
+ const parallelAgents = job.agents.filter(
385
+ (a) => a.role === "parallel" && a.agentId !== primaryAgent?.agentId
386
+ );
343
387
  const effectiveDescription = job.jobPrompt || job.jobDescription;
344
388
  let prompt = `## Job: ${job.jobName}
345
389
  `;
@@ -349,10 +393,43 @@ var JobRunner = class {
349
393
  prompt += `**Run ID:** ${runId}
350
394
 
351
395
  `;
352
- prompt += `You are now acting as "${job.jobName}". `;
353
- prompt += `Your goal is to accomplish the objectives described above using the skills and tools available to you.
396
+ if (primaryAgent) {
397
+ prompt += `## Agent Identity
398
+ `;
399
+ prompt += `You are **${primaryAgent.agentName}** (@${primaryAgent.agentUsername}).
400
+ `;
401
+ if (primaryAgent.agentBio) {
402
+ prompt += `*${primaryAgent.agentBio}*
403
+ `;
404
+ }
405
+ prompt += `
406
+ ### Agent Instructions
407
+ `;
408
+ prompt += `${primaryAgent.agentPrompt}
409
+
410
+ `;
411
+ } else {
412
+ prompt += `You are now acting as "${job.jobName}". `;
413
+ prompt += `Your goal is to accomplish the objectives described above using the skills and tools available to you.
414
+
415
+ `;
416
+ }
417
+ if (parallelAgents.length > 0) {
418
+ prompt += `### Parallel Agents
419
+ `;
420
+ prompt += `The following agents are running concurrently on this job. `;
421
+ prompt += `They handle their own tasks independently \u2014 coordinate to avoid duplicate work.
354
422
 
355
423
  `;
424
+ for (const pa of parallelAgents) {
425
+ prompt += `- **${pa.agentName}** (@${pa.agentUsername})`;
426
+ if (pa.agentBio) prompt += ` \u2014 ${pa.agentBio}`;
427
+ prompt += `
428
+ `;
429
+ }
430
+ prompt += `
431
+ `;
432
+ }
356
433
  prompt += `### Available Skills
357
434
  `;
358
435
  prompt += `These skills are your capabilities for this job. `;
package/dist/index.js CHANGED
@@ -34,11 +34,11 @@ import {
34
34
  setSessionBusy,
35
35
  toggleScheduledTask,
36
36
  updateHeartbeat
37
- } from "./chunk-MP4E522X.js";
37
+ } from "./chunk-O5K33FBW.js";
38
38
  import {
39
39
  JobRunner,
40
40
  callMcpHandler
41
- } from "./chunk-5ML5YMCM.js";
41
+ } from "./chunk-RRMI6RDG.js";
42
42
  import {
43
43
  log,
44
44
  setLogConversationId,
@@ -382,7 +382,7 @@ function registerJobCommands(program2) {
382
382
  jobCmd.command("list").description("List your defined jobs").action(async () => {
383
383
  try {
384
384
  await getCurrentUserId();
385
- const { JobRunner: JobRunner2 } = await import("./job-runner-NA3KJCKA.js");
385
+ const { JobRunner: JobRunner2 } = await import("./job-runner-4KTCVIQP.js");
386
386
  const runner = new JobRunner2();
387
387
  const jobs = await runner.listJobs();
388
388
  if (jobs.length === 0) {
@@ -406,7 +406,7 @@ function registerJobCommands(program2) {
406
406
  jobCmd.command("status [name]").description("Show run history for a job (or all jobs)").option("-l, --limit <number>", "Max runs to show (default: 5)").action(async (name, opts) => {
407
407
  try {
408
408
  await getCurrentUserId();
409
- const { JobRunner: JobRunner2 } = await import("./job-runner-NA3KJCKA.js");
409
+ const { JobRunner: JobRunner2 } = await import("./job-runner-4KTCVIQP.js");
410
410
  const runner = new JobRunner2();
411
411
  const runs = await runner.getRunHistory(name, parseInt(opts.limit || "5"));
412
412
  if (runs.length === 0) {
@@ -454,7 +454,7 @@ Job Run History${name ? ` \u2014 ${name}` : ""}:`));
454
454
  process.exit(1);
455
455
  }
456
456
  await getCurrentUserId();
457
- const { JobRunner: JobRunner2 } = await import("./job-runner-NA3KJCKA.js");
457
+ const { JobRunner: JobRunner2 } = await import("./job-runner-4KTCVIQP.js");
458
458
  const runner = new JobRunner2();
459
459
  const job = await runner.loadJob(name);
460
460
  if (!job) {
@@ -2002,7 +2002,31 @@ var Orchestrator = class {
2002
2002
  } catch (linkErr) {
2003
2003
  log.debug(`Failed to link session to job run: ${linkErr}`);
2004
2004
  }
2005
- const prompt = runner.buildJobPrompt(job, jobRun.id);
2005
+ let prompt = runner.buildJobPrompt(job, jobRun.id);
2006
+ const primaryAgent = job.agents.length > 0 ? job.agents.find((a) => a.role === "primary") || job.agents[0] : null;
2007
+ const parallelAgents = job.agents.filter(
2008
+ (a) => a.role === "parallel" && a.agentId !== primaryAgent?.agentId
2009
+ );
2010
+ if (parallelAgents.length > 0) {
2011
+ prompt += `
2012
+
2013
+ ---
2014
+
2015
+ ## Parallel Agent Responsibilities
2016
+ `;
2017
+ prompt += `The following agents are also assigned. Execute their responsibilities as part of this run:
2018
+
2019
+ `;
2020
+ for (const pa of parallelAgents) {
2021
+ prompt += `### ${pa.agentName}
2022
+ `;
2023
+ if (pa.agentBio) prompt += `*${pa.agentBio}*
2024
+ `;
2025
+ prompt += `**Instructions:** ${pa.agentPrompt}
2026
+
2027
+ `;
2028
+ }
2029
+ }
2006
2030
  try {
2007
2031
  await this.dispatchAndWait(`[JobRun: ${jobRun.job_name}] ${prompt}`);
2008
2032
  await runner.completeRun(jobRun.id, "completed", "Job executed via web trigger");