@markus-global/cli 0.6.3 → 0.6.5

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.
Files changed (30) hide show
  1. package/dist/commands/start.d.ts.map +1 -1
  2. package/dist/commands/start.js +39 -11
  3. package/dist/commands/start.js.map +1 -1
  4. package/dist/markus.mjs +1214 -339
  5. package/dist/web-ui/assets/index-DcnwpDqb.css +1 -0
  6. package/dist/web-ui/assets/index-tONQYLWM.js +351 -0
  7. package/dist/web-ui/index.html +2 -2
  8. package/dist/web-ui/logo.png +0 -0
  9. package/package.json +1 -1
  10. package/templates/roles/SHARED.md +3 -6
  11. package/templates/roles/developer/POLICIES.md +1 -1
  12. package/templates/roles/secretary/HEARTBEAT.md +1 -1
  13. package/templates/roles/secretary/ROLE.md +80 -4
  14. package/templates/skills/agent-building/SKILL.md +1 -1
  15. package/templates/skills/chrome-devtools/SKILL.md +56 -0
  16. package/templates/skills/image-generation/SKILL.md +183 -0
  17. package/templates/skills/image-generation/server.mjs +1269 -0
  18. package/templates/skills/image-generation/skill.json +26 -0
  19. package/templates/skills/markus-admin-cli/SKILL.md +1 -1
  20. package/templates/skills/self-evolution/SKILL.md +4 -4
  21. package/templates/skills/skill-building/SKILL.md +1 -1
  22. package/templates/skills/team-building/SKILL.md +1 -1
  23. package/templates/teams/content-team/ANNOUNCEMENT.md +28 -24
  24. package/templates/teams/content-team/NORMS.md +50 -48
  25. package/templates/teams/content-team/team.json +46 -16
  26. package/templates/teams/research-lab/ANNOUNCEMENT.md +24 -19
  27. package/templates/teams/research-lab/NORMS.md +77 -88
  28. package/templates/teams/research-lab/team.json +40 -14
  29. package/dist/web-ui/assets/index-C97PujBE.js +0 -351
  30. package/dist/web-ui/assets/index-Q4_kHftV.css +0 -1
package/dist/markus.mjs CHANGED
@@ -6381,20 +6381,80 @@ var init_context_engine = __esm({
6381
6381
  this.llmSummarizer = summarizer;
6382
6382
  }
6383
6383
  async buildSystemPrompt(opts) {
6384
- const parts = [];
6385
- parts.push(opts.role.systemPrompt);
6386
- if (opts.dynamicContext) {
6387
- parts.push(opts.dynamicContext);
6384
+ const isDream = opts.scenario === "memory_consolidation";
6385
+ const stable = [];
6386
+ stable.push(opts.role.systemPrompt);
6387
+ if (opts.role.defaultPolicies.length > 0) {
6388
+ stable.push("\n## Policies");
6389
+ for (const policy of opts.role.defaultPolicies) {
6390
+ stable.push(`### ${policy.name}`);
6391
+ for (const rule of policy.rules) {
6392
+ stable.push(`- ${rule}`);
6393
+ }
6394
+ }
6395
+ }
6396
+ if (!isDream) {
6397
+ stable.push("\n## Tool Usage Rules");
6398
+ stable.push("**File editing discipline**: You MUST use `file_write` and `file_edit` for all file creation and modification. NEVER use `shell_execute` with `cat`, `echo`, `printf`, `tee`, pipes (`|`), output redirection (`>`, `>>`), heredocs (`<<`), or `sed`/`awk` to write or modify files \u2014 these bypass file access controls. `shell_execute` is for running commands (build, test, git, etc.), not for writing files.");
6399
+ stable.push("**Large file writing**: NEVER write a document >200 lines in a single `file_write` call. Write section by section: `file_write` the first section, then `file_edit` to append each subsequent section.");
6400
+ stable.push("**Error handling**: If a tool call fails, analyze the error and try a different approach \u2014 do NOT repeat the same failing action.");
6401
+ stable.push("**Subagent delegation**: For heavy subtasks needing many tool calls or lots of file reading, delegate to `spawn_subagent` to keep your context lean. Use `spawn_subagents` to run independent subtasks in parallel.");
6402
+ stable.push("**Built-in tools over CLI**: ALWAYS prefer built-in tools (`task_create`, `task_assign`, `team_hire_agent`, `builder_install`, `agent_send_message`, `memory_save`, etc.) over running `markus` CLI commands via `shell_execute`. The CLI is for human operators \u2014 agents must use their native tool interface. Only fall back to CLI if no built-in tool exists for the operation.");
6403
+ stable.push('**No auto-install/deploy**: NEVER automatically install or deploy agents, teams, or skills via `builder_install`, `team_hire_agent`, or `hub_install` unless explicitly requested by a human team member (e.g., "install", "deploy", "hire", "start"). Creating an artifact (writing files to `builder-artifacts/`) is separate from deploying it into the live organization.');
6404
+ stable.push("");
6405
+ stable.push("\n## Task & Requirement Workflow");
6406
+ stable.push("");
6407
+ stable.push("**Requirements** (governance gate):");
6408
+ stable.push("- `requirement_propose` \u2192 pending human approval \u2192 approved \u2192 link tasks via `requirement_id`");
6409
+ stable.push("- Every task MUST reference an approved `requirement_id`. Use `requirement_propose` first if no requirement exists.");
6410
+ stable.push("");
6411
+ stable.push("**Task lifecycle** \u2014 Create \u2192 Execute \u2192 Review \u2192 Complete:");
6412
+ stable.push('- **Create**: `task_create` (REQUIRED: `assigned_agent_id`, `reviewer_id`; optional `reviewer_type`: "agent"|"human"). Check `task_list` first to avoid duplicates.');
6413
+ stable.push("- **Execute**: Decompose with `subtask_create` \u2192 work through subtasks \u2192 `task_submit_review` with summary + deliverables (MANDATORY). System auto-fills `task_id` and `reviewer`.");
6414
+ stable.push('- **Review**: Reviewer approves with `task_update(status:"completed")` or rejects with `task_update(status:"in_progress", note:"what needs to change")` (auto-restarts execution). Workers MUST NOT set status=completed on their own tasks.');
6415
+ stable.push('- **Blockers**: Use `task_update(status:"blocked", note:"reason")` when unable to proceed.');
6416
+ stable.push("");
6417
+ stable.push("**Dependencies & DAG decomposition**:");
6418
+ stable.push("- **CRITICAL**: Use `blocked_by` to express ALL dependency relationships. If task B needs output from task A, B **MUST** include A's ID in `blocked_by`. Without this, tasks run in parallel and downstream tasks lack upstream deliverables.");
6419
+ stable.push("- For complex goals, create a DAG of tasks. Assign each to the best team member (`team_list`). Independent tasks run in parallel; dependent tasks wait for predecessors.");
6420
+ stable.push("- If consolidated output is needed, create a final synthesis task assigned to a manager, `blocked_by` ALL prerequisites.");
6421
+ stable.push("");
6422
+ stable.push("**Work discovery**: `list_projects` \u2192 `requirement_list` \u2192 `task_list`. Use `memory_save`/`memory_search` for personal notes; `deliverable_create`/`deliverable_search` for shared outputs.");
6423
+ stable.push("");
6424
+ stable.push("**Automatic status notifications** (do NOT duplicate manually):");
6425
+ stable.push("- When task status changes, the system **automatically** handles all side effects: execution start/cancel, reviewer notification, dependency unblocking.");
6426
+ stable.push("- Task status notifications are placed in assignees' mailboxes as **informational context only**.");
6427
+ stable.push("- Do NOT send A2A messages to notify about task status changes \u2014 only send A2A when you have substantive coordination needs beyond the status change itself.");
6428
+ stable.push("");
6429
+ stable.push("**Communicating with humans**:");
6430
+ stable.push("- `notify_user` \u2014 proactive message to a human team member: status updates, progress reports, findings, alerts. Appears in chat timeline AND notification bell. The human can reply. Write comprehensive body with full context. **This is the ONLY way to reach humans from non-chat contexts** (heartbeat, autonomous tasks, etc.).");
6431
+ stable.push("- `request_user_approval` \u2014 when you need a human decision, approval, or input. BLOCKS until the user responds. Supports custom options and freeform text. Do NOT use for routine updates.");
6432
+ stable.push("- `recall_activity` \u2014 query your own past execution logs by task or activity type. Use when you need to review what you did previously (e.g., to answer a follow-up question).");
6433
+ stable.push("");
6434
+ stable.push("**Communicating with other agents**:");
6435
+ stable.push("- `agent_send_message` \u2014 send a direct message to a peer agent. **By default this is asynchronous (fire-and-forget)**: the message enters their mailbox and you continue working without waiting. Set `wait_for_reply: true` only when you need the answer before you can proceed (rare \u2014 prefer async).");
6436
+ stable.push("- A2A messaging is inherently **non-blocking**. You send a message, the recipient processes it on their own schedule, and may reply later via their own `agent_send_message`. Do NOT spin-wait or poll for responses.");
6437
+ stable.push("- For substantial work requests, create a `task_create` assigned to the target agent instead of asking via message.");
6438
+ stable.push("- Do NOT use A2A messages for routine task status notifications \u2014 the system handles those automatically.");
6388
6439
  }
6389
- parts.push(this.buildIdentitySection(opts));
6440
+ const scenario = opts.scenario ?? "chat";
6441
+ stable.push(this.buildScenarioSection(scenario, { a2aWaitForReply: opts.a2aWaitForReply }));
6442
+ const semiStable = [];
6443
+ semiStable.push(this.buildIdentitySection({
6444
+ agentId: opts.agentId,
6445
+ agentName: opts.agentName,
6446
+ role: opts.role,
6447
+ identity: opts.identity,
6448
+ availableSkills: opts.availableSkills
6449
+ }));
6390
6450
  const orgCtx = this.buildOrgContextSection(opts.orgContext, opts.contextMdPath);
6391
6451
  if (orgCtx)
6392
- parts.push(orgCtx);
6452
+ semiStable.push(orgCtx);
6393
6453
  if (opts.teamAnnouncements?.trim()) {
6394
- parts.push("\n## Team Announcements\n" + opts.teamAnnouncements.trim());
6454
+ semiStable.push("\n## Team Announcements\n" + opts.teamAnnouncements.trim());
6395
6455
  }
6396
6456
  if (opts.teamNorms?.trim()) {
6397
- parts.push("\n## Team Working Norms\n" + opts.teamNorms.trim());
6457
+ semiStable.push("\n## Team Working Norms\n" + opts.teamNorms.trim());
6398
6458
  }
6399
6459
  if (opts.teamDataDir) {
6400
6460
  const lines = ["\n## Team Data Directory", `Path: \`${opts.teamDataDir}\``, "Files:", "- `ANNOUNCEMENT.md` \u2014 team announcements", "- `NORMS.md` \u2014 team working norms"];
@@ -6403,50 +6463,32 @@ var init_context_engine = __esm({
6403
6463
  } else {
6404
6464
  lines.push("\nRead and follow the announcements and norms above. If you need changes, ask the team manager.");
6405
6465
  }
6406
- parts.push(lines.join("\n"));
6407
- }
6408
- if (opts.projectContext) {
6409
- const { project, repositories, governanceRules, teamRole } = opts.projectContext;
6410
- parts.push("\n## Current Project");
6411
- parts.push(`- Project: **${project.name}** (${project.status})`);
6412
- if (project.description)
6413
- parts.push(`- ${project.description.slice(0, SYSTEM_PROJECT_DESC_CHARS)}`);
6414
- if (repositories?.length) {
6415
- for (const repo of repositories) {
6416
- parts.push(`- Repository: \`${repo.localPath}\` (${repo.role}, default branch: \`${repo.defaultBranch}\`)`);
6417
- }
6418
- }
6419
- parts.push("");
6420
- parts.push("Some git operations (switching to existing branches, pushing to protected branches, merge, rebase) require human approval \u2014 the system will pause and ask the reviewer. If denied, you will receive a reason; read it and adjust your approach.");
6421
- if (teamRole)
6422
- parts.push(`- Your role: ${teamRole}`);
6423
- if (governanceRules)
6424
- parts.push(`- Governance: ${governanceRules}`);
6466
+ semiStable.push(lines.join("\n"));
6425
6467
  }
6426
6468
  if (opts.agentWorkspace) {
6427
- parts.push("\n## Your Workspace");
6428
- parts.push(`- Working directory: \`${opts.agentWorkspace.primaryWorkspace}\``);
6469
+ semiStable.push("\n## Your Workspace");
6470
+ semiStable.push(`- Working directory: \`${opts.agentWorkspace.primaryWorkspace}\``);
6429
6471
  if (opts.agentWorkspace.sharedWorkspace) {
6430
- parts.push(`- Shared workspace: \`${opts.agentWorkspace.sharedWorkspace}\` (all agents can read/write here)`);
6472
+ semiStable.push(`- Shared workspace: \`${opts.agentWorkspace.sharedWorkspace}\` (all agents can read/write here)`);
6431
6473
  }
6432
6474
  const artifactsDir = opts.agentWorkspace.builderArtifactsDir ?? "~/.markus/builder-artifacts";
6433
6475
  if (opts.agentWorkspace.builderArtifactsDir) {
6434
- parts.push(`- Builder artifacts directory: \`${artifactsDir}/\``);
6435
- parts.push(" When creating agents, teams, or skills, place them in the correct subdirectory:");
6436
- parts.push(` - Agents \u2192 \`${artifactsDir}/agents/{agent-name}/\``);
6437
- parts.push(` - Teams \u2192 \`${artifactsDir}/teams/{team-name}/\``);
6438
- parts.push(` - Skills \u2192 \`${artifactsDir}/skills/{skill-name}/\``);
6439
- parts.push(" The Builder page and install system ONLY recognize these paths.");
6476
+ semiStable.push(`- Builder artifacts directory: \`${artifactsDir}/\``);
6477
+ semiStable.push(" When creating agents, teams, or skills, place them in the correct subdirectory:");
6478
+ semiStable.push(` - Agents \u2192 \`${artifactsDir}/agents/{agent-name}/\``);
6479
+ semiStable.push(` - Teams \u2192 \`${artifactsDir}/teams/{team-name}/\``);
6480
+ semiStable.push(` - Skills \u2192 \`${artifactsDir}/skills/{skill-name}/\``);
6481
+ semiStable.push(" The Builder page and install system ONLY recognize these paths.");
6440
6482
  }
6441
6483
  if (opts.agentDataDir) {
6442
- parts.push(`- Agent data directory: \`${opts.agentDataDir}\` (your ROLE.md, MEMORY.md, and personal files)`);
6484
+ semiStable.push(`- Agent data directory: \`${opts.agentDataDir}\` (your ROLE.md, MEMORY.md, and personal files)`);
6443
6485
  }
6444
- parts.push("- IMPORTANT: Always use **absolute paths** in file operations. Relative paths are error-prone.");
6445
- parts.push("- You can directly read files in the shared workspace using `file_read` \u2014 no need to request them from other agents.");
6486
+ semiStable.push("- IMPORTANT: Always use **absolute paths** in file operations. Relative paths are error-prone.");
6487
+ semiStable.push("- You can directly read files in the shared workspace using `file_read` \u2014 no need to request them from other agents.");
6446
6488
  } else if (opts.agentDataDir) {
6447
- parts.push("\n## Your Workspace");
6448
- parts.push(`- Agent data directory: \`${opts.agentDataDir}\` (your ROLE.md, MEMORY.md, and personal files)`);
6449
- parts.push("- IMPORTANT: Always use **absolute paths** in file operations. Relative paths are error-prone.");
6489
+ semiStable.push("\n## Your Workspace");
6490
+ semiStable.push(`- Agent data directory: \`${opts.agentDataDir}\` (your ROLE.md, MEMORY.md, and personal files)`);
6491
+ semiStable.push("- IMPORTANT: Always use **absolute paths** in file operations. Relative paths are error-prone.");
6450
6492
  }
6451
6493
  if (opts.agentWorkspace?.sharedWorkspace) {
6452
6494
  const userMdPath = `${opts.agentWorkspace.sharedWorkspace}/USER.md`;
@@ -6454,206 +6496,186 @@ var init_context_engine = __esm({
6454
6496
  if (existsSync8(userMdPath)) {
6455
6497
  const userProfile = readFileSync6(userMdPath, "utf-8").trim();
6456
6498
  if (userProfile) {
6457
- parts.push("\n## About the Owner");
6458
- parts.push(userProfile.slice(0, SYSTEM_USER_PROFILE_CHARS));
6459
- parts.push("\n_This profile is maintained by the Secretary. If you notice new preferences or patterns from the owner, mention them to the Secretary via `agent_send_message`._");
6499
+ semiStable.push("\n## About the Owner");
6500
+ semiStable.push(userProfile.slice(0, SYSTEM_USER_PROFILE_CHARS));
6501
+ semiStable.push("\n_This profile is maintained by the Secretary. If you notice new preferences or patterns from the owner, mention them to the Secretary via `agent_send_message`._");
6460
6502
  }
6461
6503
  }
6462
6504
  } catch {
6463
6505
  }
6464
6506
  }
6465
6507
  if (opts.trustLevel) {
6466
- parts.push("\n## Your Trust Level");
6467
- parts.push(`- Level: **${opts.trustLevel.level}** (score: ${opts.trustLevel.score})`);
6508
+ semiStable.push("\n## Your Trust Level");
6509
+ semiStable.push(`- Level: **${opts.trustLevel.level}** (score: ${opts.trustLevel.score})`);
6468
6510
  if (opts.trustLevel.level === "probation") {
6469
- parts.push("- You are on probation. All your task creations require human approval. Focus on quality to build trust.");
6511
+ semiStable.push("- You are on probation. All your task creations require human approval. Focus on quality to build trust.");
6470
6512
  } else if (opts.trustLevel.level === "standard") {
6471
- parts.push("- You are a standard-level agent. Routine tasks may auto-approve; significant tasks need manager approval.");
6513
+ semiStable.push("- You are a standard-level agent. Routine tasks may auto-approve; significant tasks need manager approval.");
6472
6514
  } else if (opts.trustLevel.level === "trusted") {
6473
- parts.push("- You are a trusted agent. You have a proven track record and higher autonomy.");
6515
+ semiStable.push("- You are a trusted agent. You have a proven track record and higher autonomy.");
6474
6516
  } else if (opts.trustLevel.level === "senior") {
6475
- parts.push("- You are a senior agent. You have the highest autonomy. Routine tasks auto-approve.");
6517
+ semiStable.push("- You are a senior agent. You have the highest autonomy. Routine tasks auto-approve.");
6518
+ }
6519
+ }
6520
+ if (opts.environment) {
6521
+ semiStable.push(this.buildEnvironmentSection(opts.environment));
6522
+ }
6523
+ const longTermMem = opts.memory.getLongTermMemory();
6524
+ if (longTermMem) {
6525
+ semiStable.push("\n## Your Knowledge");
6526
+ semiStable.push(longTermMem.slice(0, SYSTEM_KNOWLEDGE_CHARS));
6527
+ }
6528
+ const dynamic = [];
6529
+ if (opts.projectContext) {
6530
+ const { project, repositories, governanceRules, teamRole } = opts.projectContext;
6531
+ dynamic.push("\n## Current Project");
6532
+ dynamic.push(`- Project: **${project.name}** (${project.status})`);
6533
+ if (project.description)
6534
+ dynamic.push(`- ${project.description.slice(0, SYSTEM_PROJECT_DESC_CHARS)}`);
6535
+ if (repositories?.length) {
6536
+ for (const repo of repositories) {
6537
+ dynamic.push(`- Repository: \`${repo.localPath}\` (${repo.role}, default branch: \`${repo.defaultBranch}\`)`);
6538
+ }
6476
6539
  }
6540
+ dynamic.push("");
6541
+ dynamic.push("Some git operations (switching to existing branches, pushing to protected branches, merge, rebase) require human approval \u2014 the system will pause and ask the reviewer. If denied, you will receive a reason; read it and adjust your approach.");
6542
+ if (teamRole)
6543
+ dynamic.push(`- Your role: ${teamRole}`);
6544
+ if (governanceRules)
6545
+ dynamic.push(`- Governance: ${governanceRules}`);
6477
6546
  }
6478
6547
  if (opts.announcements?.length) {
6479
- parts.push("\n## System Announcements");
6548
+ dynamic.push("\n## System Announcements");
6480
6549
  for (const a of opts.announcements) {
6481
6550
  const prefix = a.priority === "urgent" ? "[URGENT] " : a.priority === "high" ? "[HIGH] " : "[INFO] ";
6482
- parts.push(`- ${prefix}${a.title}: ${a.content}`);
6551
+ dynamic.push(`- ${prefix}${a.title}: ${a.content}`);
6483
6552
  }
6484
6553
  }
6485
6554
  if (opts.recentFeedback?.length) {
6486
- parts.push("\n## Human Feedback (recent)");
6555
+ dynamic.push("\n## Human Feedback (recent)");
6487
6556
  for (const fb of opts.recentFeedback) {
6488
6557
  const urgency = fb.priority === "critical" ? "[CRITICAL] " : fb.priority === "important" ? "[IMPORTANT] " : "";
6489
6558
  const anchor = fb.anchor ? ` (re: ${fb.anchor.section}${fb.anchor.itemId ? "/" + fb.anchor.itemId : ""})` : "";
6490
- parts.push(`- ${urgency}**${fb.authorName}**${anchor}: ${fb.content}`);
6559
+ dynamic.push(`- ${urgency}**${fb.authorName}**${anchor}: ${fb.content}`);
6491
6560
  }
6492
6561
  }
6493
6562
  if (opts.projectDeliverables?.length) {
6494
- parts.push("\n## Project Deliverables (key entries)");
6563
+ dynamic.push("\n## Project Deliverables (key entries)");
6495
6564
  for (const k of opts.projectDeliverables) {
6496
- parts.push(`- **[${k.category}]** ${k.title}: ${k.content.slice(0, SYSTEM_DELIVERABLE_PREVIEW_CHARS)}`);
6565
+ dynamic.push(`- **[${k.category}]** ${k.title}: ${k.content.slice(0, SYSTEM_DELIVERABLE_PREVIEW_CHARS)}`);
6497
6566
  }
6498
6567
  }
6499
- if (opts.role.defaultPolicies.length > 0) {
6500
- parts.push("\n## Policies");
6501
- for (const policy of opts.role.defaultPolicies) {
6502
- parts.push(`### ${policy.name}`);
6503
- for (const rule of policy.rules) {
6504
- parts.push(`- ${rule}`);
6505
- }
6506
- }
6507
- }
6508
- const longTermMem = opts.memory.getLongTermMemory();
6509
- if (longTermMem) {
6510
- parts.push("\n## Your Knowledge");
6511
- parts.push(longTermMem.slice(0, SYSTEM_KNOWLEDGE_CHARS));
6512
- }
6513
- const alreadyShownIds = /* @__PURE__ */ new Set();
6514
- const isDream = opts.scenario === "memory_consolidation";
6515
6568
  if (!isDream && (opts.deliverableContext || opts.knowledgeContext)) {
6516
- parts.push("\n## Shared Deliverables");
6517
- parts.push((opts.deliverableContext ?? opts.knowledgeContext ?? "").slice(0, SYSTEM_DELIVERABLES_CHARS));
6518
- }
6519
- const cpp = opts.cognitiveContext;
6520
- if (cpp && !cpp.isEmpty) {
6521
- if (cpp.cognitiveContext) {
6522
- parts.push("\n## Cognitive Context");
6523
- parts.push(cpp.cognitiveContext);
6524
- }
6525
- if (cpp.retrievedContext) {
6526
- parts.push("\n## Retrieved Context");
6527
- parts.push(cpp.retrievedContext);
6528
- }
6529
- if (cpp.reflection) {
6530
- parts.push("\n## Reflection");
6531
- parts.push(cpp.reflection);
6532
- }
6533
- } else if (!isDream) {
6534
- const relevantMemories = await this.retrieveRelevantMemories(opts.memory, opts.currentQuery, opts.agentId, alreadyShownIds);
6535
- if (relevantMemories.length > 0) {
6536
- parts.push("\n## Relevant Memories");
6537
- for (const mem of relevantMemories) {
6538
- const ts = mem.timestamp ? new Date(mem.timestamp).toLocaleDateString() : "";
6539
- parts.push(`- [${ts}] ${mem.content}`);
6540
- }
6541
- }
6569
+ dynamic.push("\n## Shared Deliverables");
6570
+ dynamic.push((opts.deliverableContext ?? opts.knowledgeContext ?? "").slice(0, SYSTEM_DELIVERABLES_CHARS));
6542
6571
  }
6543
6572
  if (!isDream) {
6544
6573
  if (opts.assignedTasks && opts.assignedTasks.length > 0) {
6545
6574
  const priorityOrder = ["critical", "high", "medium", "low"];
6546
6575
  const byPriority = (a, b) => priorityOrder.indexOf(a.priority ?? "medium") - priorityOrder.indexOf(b.priority ?? "medium");
6576
+ const CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "failed", "archived", "rejected"]);
6547
6577
  const myTasks = opts.assignedTasks.filter((t) => t.assignedAgentId === opts.agentId);
6548
6578
  const otherTasks = opts.assignedTasks.filter((t) => t.assignedAgentId !== opts.agentId);
6549
- const myActive = myTasks.filter((t) => !["completed", "cancelled", "failed"].includes(t.status)).sort(byPriority);
6550
- const myDone = myTasks.filter((t) => ["completed", "cancelled", "failed"].includes(t.status));
6579
+ const myActive = myTasks.filter((t) => !CLOSED_STATUSES.has(t.status)).sort(byPriority);
6580
+ const myDone = myTasks.filter((t) => CLOSED_STATUSES.has(t.status));
6551
6581
  const MY_TASK_LIMIT = SYSTEM_MY_TASKS_MAX;
6552
6582
  const TEAM_TASK_LIMIT = SYSTEM_TEAM_TASKS_MAX;
6553
- parts.push("\n## Task Board");
6554
- parts.push("### My Tasks (assigned to you):");
6583
+ dynamic.push("\n## Task Board");
6584
+ dynamic.push("### My Tasks (assigned to you):");
6555
6585
  if (myActive.length > 0) {
6556
6586
  const shown = myActive.slice(0, MY_TASK_LIMIT);
6557
6587
  for (const t of shown) {
6558
- parts.push(`- [${t.status.toUpperCase()}] **${t.title}** (ID: \`${t.id}\`, priority: ${t.priority})`);
6588
+ dynamic.push(`- [${t.status.toUpperCase()}] **${t.title}** (ID: \`${t.id}\`, priority: ${t.priority})`);
6559
6589
  if (t.description)
6560
- parts.push(` ${t.description.slice(0, SYSTEM_TASK_DESC_CHARS)}`);
6590
+ dynamic.push(` ${t.description.slice(0, SYSTEM_TASK_DESC_CHARS)}`);
6561
6591
  }
6562
6592
  if (myActive.length > MY_TASK_LIMIT) {
6563
- parts.push(`_(${myActive.length - MY_TASK_LIMIT} more active tasks not shown \u2014 use \`task_list\` for full list)_`);
6593
+ dynamic.push(`_(${myActive.length - MY_TASK_LIMIT} more active tasks not shown \u2014 use \`task_list\` for full list)_`);
6564
6594
  }
6565
6595
  } else {
6566
- parts.push("No active tasks assigned to you.");
6596
+ dynamic.push("No active tasks assigned to you.");
6567
6597
  }
6568
6598
  if (myDone.length > 0) {
6569
- parts.push(`_(${myDone.length} completed/closed tasks)_`);
6599
+ dynamic.push(`_(${myDone.length} completed/closed tasks)_`);
6570
6600
  }
6571
6601
  if (otherTasks.length > 0) {
6572
- const otherActive = otherTasks.filter((t) => !["completed", "cancelled", "failed"].includes(t.status)).sort(byPriority);
6573
- const otherDone = otherTasks.filter((t) => ["completed", "cancelled", "failed"].includes(t.status));
6602
+ const otherActive = otherTasks.filter((t) => !CLOSED_STATUSES.has(t.status)).sort(byPriority);
6603
+ const otherDone = otherTasks.filter((t) => CLOSED_STATUSES.has(t.status));
6574
6604
  if (otherActive.length > 0) {
6575
- parts.push("### Team Tasks (assigned to others):");
6605
+ dynamic.push("### Team Tasks (assigned to others):");
6576
6606
  const shown = otherActive.slice(0, TEAM_TASK_LIMIT);
6577
6607
  for (const t of shown) {
6578
6608
  const owner = t.assignedAgentName ?? t.assignedAgentId ?? "unassigned";
6579
- parts.push(`- [${t.status.toUpperCase()}] **${t.title}** (ID: \`${t.id}\`, assignee: ${owner}, priority: ${t.priority})`);
6609
+ dynamic.push(`- [${t.status.toUpperCase()}] **${t.title}** (ID: \`${t.id}\`, assignee: ${owner}, priority: ${t.priority})`);
6580
6610
  }
6581
6611
  if (otherActive.length > TEAM_TASK_LIMIT) {
6582
- parts.push(`_(${otherActive.length - TEAM_TASK_LIMIT} more team tasks not shown)_`);
6612
+ dynamic.push(`_(${otherActive.length - TEAM_TASK_LIMIT} more team tasks not shown)_`);
6583
6613
  }
6584
6614
  }
6585
6615
  if (otherDone.length > 0) {
6586
- parts.push(`_(${otherDone.length} other completed/closed tasks)_`);
6616
+ dynamic.push(`_(${otherDone.length} other completed/closed tasks)_`);
6587
6617
  }
6588
6618
  }
6589
6619
  } else {
6590
- parts.push("\n## Task Board");
6591
- parts.push("No tasks on the board.");
6620
+ dynamic.push("\n## Task Board");
6621
+ dynamic.push("No tasks on the board.");
6592
6622
  }
6593
- parts.push("");
6594
- parts.push("### Task & Requirement Workflow");
6595
- parts.push("");
6596
- parts.push("**Requirements** (governance gate):");
6597
- parts.push("- `requirement_propose` \u2192 pending human approval \u2192 approved \u2192 link tasks via `requirement_id`");
6598
- parts.push("- When governance requires it, every task MUST reference an approved `requirement_id`.");
6599
- parts.push("");
6600
- parts.push("**Task lifecycle** \u2014 Create \u2192 Execute \u2192 Review \u2192 Complete:");
6601
- parts.push('- **Create**: `task_create` (REQUIRED: `assigned_agent_id`, `reviewer_id`; optional `reviewer_type`: "agent"|"human"). Check `task_list` first to avoid duplicates.');
6602
- parts.push("- **Execute**: Decompose with `subtask_create` \u2192 work through subtasks \u2192 `task_submit_review` with summary + deliverables (MANDATORY). System auto-fills `task_id` and `reviewer`.");
6603
- parts.push('- **Review**: Reviewer approves with `task_update(status:"completed")` or rejects with `task_update(status:"in_progress", note:"what needs to change")` (auto-restarts execution). Workers MUST NOT set status=completed on their own tasks.');
6604
- parts.push('- **Blockers**: Use `task_update(status:"blocked", note:"reason")` when unable to proceed.');
6605
- parts.push("");
6606
- parts.push("**Dependencies & DAG decomposition**:");
6607
- parts.push("- **CRITICAL**: Use `blocked_by` to express ALL dependency relationships. If task B needs output from task A, B **MUST** include A's ID in `blocked_by`. Without this, tasks run in parallel and downstream tasks lack upstream deliverables.");
6608
- parts.push("- For complex goals, create a DAG of tasks. Assign each to the best team member (`team_list`). Independent tasks run in parallel; dependent tasks wait for predecessors.");
6609
- parts.push("- If consolidated output is needed, create a final synthesis task assigned to a manager, `blocked_by` ALL prerequisites.");
6610
- parts.push("");
6611
- parts.push("**Work discovery**: `list_projects` \u2192 `requirement_list` \u2192 `task_list`. Use `memory_save`/`memory_search` for personal notes; `deliverable_create`/`deliverable_search` for shared outputs.");
6612
- parts.push("");
6613
- parts.push("**Automatic status notifications** (do NOT duplicate manually):");
6614
- parts.push("- When task status changes, the system **automatically** handles all side effects: execution start/cancel, reviewer notification, dependency unblocking.");
6615
- parts.push("- Task status notifications are placed in assignees' mailboxes as **informational context only**.");
6616
- parts.push("- Do NOT send A2A messages to notify about task status changes \u2014 only send A2A when you have substantive coordination needs beyond the status change itself.");
6617
- parts.push("");
6618
- parts.push("**Communicating with humans**:");
6619
- parts.push("- `notify_user` \u2014 proactive message to a human team member: status updates, progress reports, findings, alerts. Appears in chat timeline AND notification bell. The human can reply. Write comprehensive body with full context. **This is the ONLY way to reach humans from non-chat contexts** (heartbeat, autonomous tasks, etc.).");
6620
- parts.push("- `request_user_approval` \u2014 when you need a human decision, approval, or input. BLOCKS until the user responds. Supports custom options and freeform text. Do NOT use for routine updates.");
6621
- parts.push("- `recall_activity` \u2014 query your own past execution logs by task or activity type. Use when you need to review what you did previously (e.g., to answer a follow-up question).");
6622
- parts.push("");
6623
- parts.push("**Communicating with other agents**:");
6624
- parts.push("- `agent_send_message` \u2014 send a direct message to a peer agent. Use for coordination, questions, sharing context, or instructions. The message enters their mailbox and they will process it.");
6625
- parts.push("- For substantial work requests, create a `task_create` assigned to the target agent instead of asking via message.");
6626
- parts.push("- Do NOT use A2A messages for routine task status notifications \u2014 the system handles those automatically.");
6627
6623
  }
6628
- if (opts.environment) {
6629
- parts.push(this.buildEnvironmentSection(opts.environment));
6624
+ if (opts.dynamicContext) {
6625
+ dynamic.push(opts.dynamicContext);
6626
+ }
6627
+ const alreadyShownIds = /* @__PURE__ */ new Set();
6628
+ const cpp = opts.cognitiveContext;
6629
+ if (cpp && !cpp.isEmpty) {
6630
+ if (cpp.cognitiveContext) {
6631
+ dynamic.push("\n## Cognitive Context");
6632
+ dynamic.push(cpp.cognitiveContext);
6633
+ }
6634
+ if (cpp.retrievedContext) {
6635
+ dynamic.push("\n## Retrieved Context");
6636
+ dynamic.push(cpp.retrievedContext);
6637
+ }
6638
+ if (cpp.reflection) {
6639
+ dynamic.push("\n## Reflection");
6640
+ dynamic.push(cpp.reflection);
6641
+ }
6642
+ } else if (!isDream) {
6643
+ const relevantMemories = await this.retrieveRelevantMemories(opts.memory, opts.currentQuery, opts.agentId, alreadyShownIds);
6644
+ if (relevantMemories.length > 0) {
6645
+ dynamic.push("\n## Relevant Memories");
6646
+ for (const mem of relevantMemories) {
6647
+ const ts = mem.timestamp ? new Date(mem.timestamp).toLocaleDateString() : "";
6648
+ dynamic.push(`- [${ts}] ${mem.content}`);
6649
+ }
6650
+ }
6651
+ }
6652
+ if (!isDream && opts.mailboxContext) {
6653
+ dynamic.push(this.buildMailboxSection(opts.mailboxContext));
6630
6654
  }
6631
6655
  if (!isDream && opts.senderIdentity) {
6632
- parts.push(`
6656
+ dynamic.push(`
6633
6657
  ## Current Conversation`);
6634
- parts.push(`You are now talking to **${opts.senderIdentity.name}** (${opts.senderIdentity.role}).`);
6658
+ dynamic.push(`You are now talking to **${opts.senderIdentity.name}** (${opts.senderIdentity.role}).`);
6659
+ if (opts.senderIdentity.isFirstConversation) {
6660
+ dynamic.push("**This is their first conversation** \u2014 they have never used Markus before. Follow your onboarding protocol if you have one.");
6661
+ }
6635
6662
  if (opts.senderIdentity.role === "owner") {
6636
- parts.push("This person is the organization owner. Their instructions have the highest priority. Be proactive in reporting and responsive to their needs.");
6663
+ dynamic.push("This person is the organization owner. Their instructions have the highest priority. Be proactive in reporting and responsive to their needs.");
6637
6664
  } else if (opts.senderIdentity.role === "admin") {
6638
- parts.push("This person is an administrator. Cooperate actively and share progress proactively.");
6665
+ dynamic.push("This person is an administrator. Cooperate actively and share progress proactively.");
6639
6666
  } else if (opts.senderIdentity.role === "guest") {
6640
- parts.push("This person is an external guest. Be polite but cautious \u2014 do not expose internal sensitive information.");
6667
+ dynamic.push("This person is an external guest. Be polite but cautious \u2014 do not expose internal sensitive information.");
6641
6668
  }
6642
6669
  }
6643
6670
  if (!isDream) {
6644
- parts.push("\n## Tool Usage Rules");
6645
- parts.push("**File editing discipline**: You MUST use `file_write` and `file_edit` for all file creation and modification. NEVER use `shell_execute` with `cat`, `echo`, `printf`, `tee`, pipes (`|`), output redirection (`>`, `>>`), heredocs (`<<`), or `sed`/`awk` to write or modify files \u2014 these bypass file access controls. `shell_execute` is for running commands (build, test, git, etc.), not for writing files.");
6646
- parts.push("**Large file writing**: NEVER write a document >200 lines in a single `file_write` call. Write section by section: `file_write` the first section, then `file_edit` to append each subsequent section.");
6647
- parts.push("**Error handling**: If a tool call fails, analyze the error and try a different approach \u2014 do NOT repeat the same failing action.");
6648
- parts.push("**Subagent delegation**: For heavy subtasks needing many tool calls or lots of file reading, delegate to `spawn_subagent` to keep your context lean. Use `spawn_subagents` to run independent subtasks in parallel.");
6649
- parts.push("**Built-in tools over CLI**: ALWAYS prefer built-in tools (`task_create`, `task_assign`, `team_hire_agent`, `builder_install`, `agent_send_message`, `memory_save`, etc.) over running `markus` CLI commands via `shell_execute`. The CLI is for human operators \u2014 agents must use their native tool interface. Only fall back to CLI if no built-in tool exists for the operation.");
6650
- parts.push('**No auto-install/deploy**: NEVER automatically install or deploy agents, teams, or skills via `builder_install`, `team_hire_agent`, or `hub_install` unless explicitly requested by a human team member (e.g., "install", "deploy", "hire", "start"). Creating an artifact (writing files to `builder-artifacts/`) is separate from deploying it into the live organization.');
6671
+ dynamic.push("\n## Tool Usage Rules");
6672
+ dynamic.push("**File editing discipline**: You MUST use `file_write` and `file_edit` for all file creation and modification. NEVER use `shell_execute` with `cat`, `echo`, `printf`, `tee`, pipes (`|`), output redirection (`>`, `>>`), heredocs (`<<`), or `sed`/`awk` to write or modify files \u2014 these bypass file access controls. `shell_execute` is for running commands (build, test, git, etc.), not for writing files.");
6673
+ dynamic.push("**Large file writing**: NEVER write a document >200 lines in a single `file_write` call. Write section by section: `file_write` the first section, then `file_edit` to append each subsequent section.");
6674
+ dynamic.push("**Error handling**: If a tool call fails, analyze the error and try a different approach \u2014 do NOT repeat the same failing action.");
6675
+ dynamic.push("**Subagent delegation**: For heavy subtasks needing many tool calls or lots of file reading, delegate to `spawn_subagent` to keep your context lean. Use `spawn_subagents` to run independent subtasks in parallel.");
6676
+ dynamic.push("**Built-in tools over CLI**: ALWAYS prefer built-in tools (`task_create`, `task_assign`, `package_install`, `agent_send_message`, `memory_save`, etc.) over running `markus` CLI commands via `shell_execute`. The CLI is for human operators \u2014 agents must use their native tool interface. Only fall back to CLI if no built-in tool exists for the operation.");
6677
+ dynamic.push('**No auto-install/deploy**: NEVER automatically install or deploy agents, teams, or skills via `package_install` or `hub_install` unless explicitly requested by a human team member (e.g., "install", "deploy", "hire", "start"). Creating an artifact (writing files to `builder-artifacts/`) is separate from deploying it into the live organization.');
6651
6678
  }
6652
- if (!isDream && opts.mailboxContext) {
6653
- parts.push(this.buildMailboxSection(opts.mailboxContext));
6654
- }
6655
- const scenario = opts.scenario ?? "chat";
6656
- parts.push(this.buildScenarioSection(scenario, { a2aWaitForReply: opts.a2aWaitForReply }));
6657
6679
  const now2 = /* @__PURE__ */ new Date();
6658
6680
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
6659
6681
  const offset = now2.getTimezoneOffset();
@@ -6662,10 +6684,23 @@ var init_context_engine = __esm({
6662
6684
  const absM = String(Math.abs(offset) % 60).padStart(2, "0");
6663
6685
  const pad = (n) => String(n).padStart(2, "0");
6664
6686
  const localStr = `${now2.getFullYear()}-${pad(now2.getMonth() + 1)}-${pad(now2.getDate())} ${pad(now2.getHours())}:${pad(now2.getMinutes())}:${pad(now2.getSeconds())}`;
6665
- parts.push(`
6687
+ dynamic.push(`
6666
6688
  ---
6667
6689
  Current date and time: ${localStr} (${tz}, UTC${sign}${absH}:${absM})`);
6668
- return parts.join("\n");
6690
+ const stableText = stable.join("\n");
6691
+ const semiStableText = semiStable.join("\n");
6692
+ const dynamicText = dynamic.join("\n");
6693
+ const segments = [];
6694
+ if (stableText)
6695
+ segments.push({ content: stableText, cacheBreakpoint: true });
6696
+ if (semiStableText)
6697
+ segments.push({ content: semiStableText, cacheBreakpoint: true });
6698
+ if (dynamicText)
6699
+ segments.push({ content: dynamicText });
6700
+ return {
6701
+ text: segments.map((s) => s.content).join("\n"),
6702
+ segments
6703
+ };
6669
6704
  }
6670
6705
  buildMailboxSection(ctx) {
6671
6706
  const lines = ["\n## Your Attention State"];
@@ -6799,11 +6834,17 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
6799
6834
  lines.push('- Give a generic acknowledgment like "Got it, will look into it" without substantive content');
6800
6835
  lines.push("- Ignore prior comments that provide important context for the current discussion");
6801
6836
  lines.push("");
6802
- lines.push("**Conversation termination \u2014 when NOT to reply:**");
6803
- lines.push('- The comment is just an acknowledgment ("Got it", "Will do", "Thanks", "Agreed") \u2014 do NOT reply');
6804
- lines.push("- Both parties have reached agreement or the discussion is resolved \u2014 do NOT reply");
6805
- lines.push('- Your reply would only be "Sounds good", "Agreed", or similar zero-information response \u2014 do NOT reply');
6806
- lines.push("- The comment does not ask a question, request action, or contain information you need to correct \u2014 do NOT reply");
6837
+ lines.push("**MANDATORY outcome \u2014 you MUST end with exactly one of these:**");
6838
+ lines.push("1. Call `task_comment` or `requirement_comment` tool to post your reply, OR");
6839
+ lines.push("2. Output `[NO_REPLY_NEEDED]` in your text to explicitly signal that no response is warranted.");
6840
+ lines.push("");
6841
+ lines.push("If you finish without doing either of the above, the system will automatically retry your turn \u2014 your text output alone is NEVER sufficient.");
6842
+ lines.push("");
6843
+ lines.push("**When to use `[NO_REPLY_NEEDED]` \u2014 do NOT reply when:**");
6844
+ lines.push('- The comment is just an acknowledgment ("Got it", "Will do", "Thanks", "Agreed")');
6845
+ lines.push("- Both parties have reached agreement or the discussion is resolved");
6846
+ lines.push('- Your reply would only be "Sounds good", "Agreed", or similar zero-information response');
6847
+ lines.push("- The comment does not ask a question, request action, or contain information you need to correct");
6807
6848
  lines.push("- **Principle**: only comment when your reply adds **new information** or requests a **decision**. Avoid comment ping-pong.");
6808
6849
  break;
6809
6850
  case "review":
@@ -6917,8 +6958,7 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
6917
6958
  lines.push("6. **Hiring & Team Building** \u2014 Two phases: CREATE then INSTALL (only when user requests).");
6918
6959
  lines.push(" a) *Creating* (design the artifact): activate `agent-building` or `team-building` skill \u2192 write artifact files. Or `hub_search` to browse community packages.");
6919
6960
  lines.push(" b) *Installing* (deploy into org \u2014 ONLY when user explicitly asks to install/deploy/hire):");
6920
- lines.push(" - Quick hire from template: `team_list_templates` \u2192 `team_hire_agent`");
6921
- lines.push(" - Install artifact: `builder_install` (for custom-built or Hub-downloaded packages)");
6961
+ lines.push(" - `package_list` \u2192 `package_install` (type: agent/team/skill)");
6922
6962
  lines.push(" - Hub one-step: `hub_install` (download + install)");
6923
6963
  lines.push(" c) After install: onboard via `agent_send_message` (project context) \u2192 `task_create` (initial work)");
6924
6964
  lines.push(" **IMPORTANT**: NEVER auto-install. Creating an artifact does NOT mean deploying it. Wait for explicit user request.");
@@ -7049,7 +7089,8 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
7049
7089
  totalUsed,
7050
7090
  available,
7051
7091
  usagePercent: Math.round(usagePercent * 10) / 10
7052
- }
7092
+ },
7093
+ systemCacheSegments: opts.systemCacheSegments
7053
7094
  };
7054
7095
  }
7055
7096
  /**
@@ -8213,7 +8254,7 @@ var init_tool_selector = __esm({
8213
8254
  "\u5206\u914D",
8214
8255
  "\u8DEF\u7531"
8215
8256
  ],
8216
- toolNames: ["team_list", "team_status", "delegate_message", "team_hire_agent", "team_list_templates"]
8257
+ toolNames: ["team_list", "team_status", "delegate_message", "package_list", "package_install"]
8217
8258
  },
8218
8259
  {
8219
8260
  name: "deliverables",
@@ -8243,6 +8284,21 @@ var init_tool_selector = __esm({
8243
8284
  "\u7ECF\u9A8C"
8244
8285
  ],
8245
8286
  toolNames: ["deliverable_create", "deliverable_search", "deliverable_list", "deliverable_update"]
8287
+ },
8288
+ {
8289
+ name: "builder",
8290
+ keywords: [
8291
+ "builder",
8292
+ "artifact",
8293
+ "deploy",
8294
+ "skill",
8295
+ "package",
8296
+ "hub",
8297
+ "\u90E8\u7F72",
8298
+ "\u5DE5\u4EF6",
8299
+ "\u6280\u80FD\u5305"
8300
+ ],
8301
+ toolNames: ["builder_install", "builder_list"]
8246
8302
  }
8247
8303
  ];
8248
8304
  BASE_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -41280,6 +41336,45 @@ async function searchBrave(query2, maxResults) {
41280
41336
  date: r.page_age
41281
41337
  }));
41282
41338
  }
41339
+ async function searchBing(query2, maxResults) {
41340
+ const encoded = encodeURIComponent(query2);
41341
+ let res;
41342
+ try {
41343
+ res = await proxyFetch(`https://www.bing.com/search?q=${encoded}`, {
41344
+ headers: { "User-Agent": BING_UA }
41345
+ });
41346
+ } catch (err) {
41347
+ throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
41348
+ }
41349
+ if (!res.ok)
41350
+ throw new Error(`HTTP ${res.status} ${res.statusText}`);
41351
+ const html = await res.text();
41352
+ const results = parseBingHtml(html, maxResults);
41353
+ if (results.length === 0)
41354
+ throw new Error("Parsed 0 results from Bing");
41355
+ return results;
41356
+ }
41357
+ function parseBingHtml(html, max) {
41358
+ const results = [];
41359
+ const algoRegex = /<li[^>]*class="b_algo"[^>]*>([\s\S]*?)<\/li>/g;
41360
+ let match;
41361
+ while ((match = algoRegex.exec(html)) !== null) {
41362
+ if (results.length >= max)
41363
+ break;
41364
+ const item = match[1];
41365
+ const linkMatch = item.match(/<a[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/i);
41366
+ if (!linkMatch)
41367
+ continue;
41368
+ const url = linkMatch[1];
41369
+ const title = stripHtml(linkMatch[2]);
41370
+ if (!url || !title)
41371
+ continue;
41372
+ const snippetMatch = item.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
41373
+ const snippet = snippetMatch ? stripHtml(snippetMatch[1]) : "";
41374
+ results.push({ title, url, snippet });
41375
+ }
41376
+ return results;
41377
+ }
41283
41378
  async function searchDuckDuckGo(query2, maxResults) {
41284
41379
  const encoded = encodeURIComponent(query2);
41285
41380
  let lastError;
@@ -41368,7 +41463,7 @@ function parseDDGHtml(html, maxResults) {
41368
41463
  function stripHtml(html) {
41369
41464
  return html.replace(/<[^>]*>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
41370
41465
  }
41371
- var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, DDG_UA, DDG_ENDPOINTS;
41466
+ var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, BING_UA, DDG_UA, DDG_ENDPOINTS;
41372
41467
  var init_web_search = __esm({
41373
41468
  "../core/dist/tools/web-search.js"() {
41374
41469
  "use strict";
@@ -41397,6 +41492,7 @@ var init_web_search = __esm({
41397
41492
  const backends = [
41398
41493
  { name: "Serper", fn: searchSerper },
41399
41494
  { name: "Brave", fn: searchBrave },
41495
+ { name: "Bing", fn: searchBing },
41400
41496
  { name: "DuckDuckGo", fn: searchDuckDuckGo }
41401
41497
  ];
41402
41498
  const errors = [];
@@ -41428,6 +41524,7 @@ var init_web_search = __esm({
41428
41524
  });
41429
41525
  }
41430
41526
  };
41527
+ BING_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
41431
41528
  DDG_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
41432
41529
  DDG_ENDPOINTS = [
41433
41530
  "https://lite.duckduckgo.com/lite/",
@@ -45500,6 +45597,7 @@ ${notification.stdoutTail}`);
45500
45597
  senderId,
45501
45598
  senderName: senderInfo?.name,
45502
45599
  senderRole: senderInfo?.role,
45600
+ isFirstConversation: senderInfo?.isFirstConversation,
45503
45601
  responsePromise: { resolve: resolve20, reject }
45504
45602
  }
45505
45603
  });
@@ -45529,6 +45627,7 @@ ${notification.stdoutTail}`);
45529
45627
  senderId,
45530
45628
  senderName: senderInfo?.name,
45531
45629
  senderRole: senderInfo?.role,
45630
+ isFirstConversation: senderInfo?.isFirstConversation,
45532
45631
  responsePromise: { resolve: resolve20, reject }
45533
45632
  }
45534
45633
  });
@@ -45675,7 +45774,7 @@ ${notification.stdoutTail}`);
45675
45774
  this.currentInteractingUserId = item.metadata.senderId;
45676
45775
  }
45677
45776
  const extra = item.payload.extra ?? {};
45678
- const senderInfo = item.metadata?.senderName ? { name: item.metadata.senderName, role: item.metadata.senderRole ?? "user" } : void 0;
45777
+ const senderInfo = item.metadata?.senderName ? { name: item.metadata.senderName, role: item.metadata.senderRole ?? "user", isFirstConversation: item.metadata.isFirstConversation } : void 0;
45679
45778
  const resolveResponse = (reply) => {
45680
45779
  if (typeof item.metadata?.responsePromise?.resolve === "function") {
45681
45780
  item.metadata.responsePromise.resolve(stripCompletionMarker(reply));
@@ -46410,6 +46509,9 @@ ${conversationText}`
46410
46509
  setIdentityContext(ctx) {
46411
46510
  this.identityContext = ctx;
46412
46511
  }
46512
+ getTeamName() {
46513
+ return this.identityContext?.team?.name;
46514
+ }
46413
46515
  addDynamicContextProvider(provider, key2) {
46414
46516
  const providerKey = key2 ?? `provider_${this.dynamicContextProviders.size}`;
46415
46517
  this.dynamicContextProviders.set(providerKey, provider);
@@ -46942,7 +47044,7 @@ ${block}
46942
47044
  await counter.ensureReady();
46943
47045
  }
46944
47046
  const cognitiveContext = await this.prepareCognitiveContext(scenario, effectiveMessage, senderId);
46945
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47047
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
46946
47048
  agentId: this.id,
46947
47049
  agentName: this.config.name,
46948
47050
  role: this.role,
@@ -46985,7 +47087,8 @@ ${block}
46985
47087
  agentId: this.id,
46986
47088
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
46987
47089
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
46988
- toolDefinitions: llmTools
47090
+ toolDefinitions: llmTools,
47091
+ systemCacheSegments
46989
47092
  });
46990
47093
  const messages = prepared.messages;
46991
47094
  log17.debug("Context usage for chat", { usagePercent: prepared.usage.usagePercent, totalUsed: prepared.usage.totalUsed });
@@ -46998,7 +47101,8 @@ ${block}
46998
47101
  messages,
46999
47102
  tools: llmTools.length > 0 ? llmTools : void 0,
47000
47103
  metadata: this.getLLMMetadata(sessionId),
47001
- compaction: useCompaction
47104
+ compaction: useCompaction,
47105
+ systemCacheSegments
47002
47106
  }, this.getEffectiveProvider()), "Chat LLM call");
47003
47107
  const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
47004
47108
  this.updateTokensUsed(tokensThisCall);
@@ -47012,6 +47116,7 @@ ${block}
47012
47116
  });
47013
47117
  let toolIterations = 0;
47014
47118
  const effectiveMaxIter = options?.maxToolIterations ?? this._maxToolIterations;
47119
+ const commentToolUsed = /* @__PURE__ */ new Set();
47015
47120
  while (response.finishReason === "tool_use" && response.toolCalls?.length || response.finishReason === "max_tokens") {
47016
47121
  if (++toolIterations > effectiveMaxIter) {
47017
47122
  log17.warn("Tool loop hit max iterations", {
@@ -47109,6 +47214,9 @@ ${block}
47109
47214
  for (let i = 0; i < response.toolCalls.length; i++) {
47110
47215
  const tc = response.toolCalls[i];
47111
47216
  this.loopDetector.record(tc.name, tc.arguments ?? {}, toolResults[i]?.content ?? "");
47217
+ if (tc.name === "task_comment" || tc.name === "requirement_comment") {
47218
+ commentToolUsed.add(tc.name);
47219
+ }
47112
47220
  }
47113
47221
  const loopCheck = this.loopDetector.check();
47114
47222
  if (loopCheck.detected) {
@@ -47158,7 +47266,8 @@ ${chatYield.item.payload.content}`;
47158
47266
  agentId: this.id,
47159
47267
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47160
47268
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47161
- toolDefinitions: llmTools
47269
+ toolDefinitions: llmTools,
47270
+ systemCacheSegments
47162
47271
  });
47163
47272
  const updatedMessages = prepared2.messages;
47164
47273
  const llmStart2 = Date.now();
@@ -47166,7 +47275,8 @@ ${chatYield.item.payload.content}`;
47166
47275
  messages: updatedMessages,
47167
47276
  tools: llmTools.length > 0 ? llmTools : void 0,
47168
47277
  metadata: this.getLLMMetadata(sessionId),
47169
- compaction: useCompaction
47278
+ compaction: useCompaction,
47279
+ systemCacheSegments
47170
47280
  }, this.getEffectiveProvider()), "Chat LLM continuation");
47171
47281
  const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
47172
47282
  this.updateTokensUsed(tokens2);
@@ -47179,6 +47289,93 @@ ${chatYield.item.payload.content}`;
47179
47289
  success: true
47180
47290
  });
47181
47291
  }
47292
+ const hasNoReplyMarker = /\[NO_REPLY_NEEDED\]/i.test(response.content ?? "");
47293
+ if (scenario === "comment_response" && commentToolUsed.size === 0 && !hasNoReplyMarker && toolIterations < effectiveMaxIter) {
47294
+ this.memory.appendMessage(sessionId, {
47295
+ role: "assistant",
47296
+ content: response.content,
47297
+ toolCalls: response.toolCalls,
47298
+ reasoningContent: response.reasoningContent
47299
+ });
47300
+ this.memory.appendMessage(sessionId, {
47301
+ role: "user",
47302
+ content: "[SYSTEM] You are about to end your turn WITHOUT posting a reply and WITHOUT marking [NO_REPLY_NEEDED]. In this scenario your text output is NOT visible to anyone. You MUST either: (1) call `task_comment` or `requirement_comment` tool to post your reply in the comment thread, OR (2) output exactly [NO_REPLY_NEEDED] if you have determined that no response is warranted. Do it now."
47303
+ });
47304
+ const reminderMessages = this.memory.getRecentMessages(sessionId, maxHistory);
47305
+ const preparedReminder = await this.contextEngine.prepareMessages({
47306
+ systemPrompt,
47307
+ sessionMessages: reminderMessages,
47308
+ memory: this.memory,
47309
+ sessionId,
47310
+ agentId: this.id,
47311
+ modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47312
+ modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47313
+ toolDefinitions: llmTools,
47314
+ systemCacheSegments
47315
+ });
47316
+ response = await this.withNetworkRetry(() => this.llmRouter.chat({
47317
+ messages: preparedReminder.messages,
47318
+ tools: llmTools.length > 0 ? llmTools : void 0,
47319
+ metadata: this.getLLMMetadata(sessionId),
47320
+ compaction: useCompaction,
47321
+ systemCacheSegments
47322
+ }, this.getEffectiveProvider()), "Chat LLM comment-reminder");
47323
+ if (response.finishReason === "tool_use" && response.toolCalls?.length) {
47324
+ const currentActId = this.state.currentActivity?.id;
47325
+ this.memory.appendMessage(sessionId, {
47326
+ role: "assistant",
47327
+ content: response.content,
47328
+ toolCalls: response.toolCalls,
47329
+ reasoningContent: response.reasoningContent
47330
+ });
47331
+ const toolResults = await Promise.all(response.toolCalls.map(async (tc) => {
47332
+ const toolStart = Date.now();
47333
+ if (currentActId) {
47334
+ this.emitActivityLog(currentActId, "tool_start", tc.name, { arguments: tc.arguments });
47335
+ }
47336
+ try {
47337
+ let result = await this.executeTool(tc, void 0, sessionId);
47338
+ result = this.offloadLargeResult(tc.name, result);
47339
+ if (currentActId) {
47340
+ this.emitActivityLog(currentActId, "tool_end", tc.name, {
47341
+ durationMs: Date.now() - toolStart,
47342
+ success: !isErrorResult2(result),
47343
+ arguments: tc.arguments,
47344
+ result
47345
+ });
47346
+ }
47347
+ return { toolCallId: tc.id, content: result, error: false };
47348
+ } catch (toolErr) {
47349
+ if (currentActId) {
47350
+ this.emitActivityLog(currentActId, "error", `Tool ${tc.name} failed: ${String(toolErr)}`);
47351
+ }
47352
+ return { toolCallId: tc.id, content: `Error: ${String(toolErr)}`, error: true };
47353
+ }
47354
+ }));
47355
+ for (const tr of toolResults) {
47356
+ this.memory.appendMessage(sessionId, { role: "tool", content: tr.content, toolCallId: tr.toolCallId });
47357
+ }
47358
+ const finalMessages = this.memory.getRecentMessages(sessionId, maxHistory);
47359
+ const preparedFinal = await this.contextEngine.prepareMessages({
47360
+ systemPrompt,
47361
+ sessionMessages: finalMessages,
47362
+ memory: this.memory,
47363
+ sessionId,
47364
+ agentId: this.id,
47365
+ modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47366
+ modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47367
+ toolDefinitions: llmTools,
47368
+ systemCacheSegments
47369
+ });
47370
+ response = await this.withNetworkRetry(() => this.llmRouter.chat({
47371
+ messages: preparedFinal.messages,
47372
+ tools: llmTools.length > 0 ? llmTools : void 0,
47373
+ metadata: this.getLLMMetadata(sessionId),
47374
+ compaction: useCompaction,
47375
+ systemCacheSegments
47376
+ }, this.getEffectiveProvider()), "Chat LLM comment-reminder-final");
47377
+ }
47378
+ }
47182
47379
  const rawReply = sanitizeLLMReply(response.content);
47183
47380
  const displayReply = stripCompletionMarker(rawReply);
47184
47381
  const outputCheck = await this.guardrails.checkOutput(displayReply, { agentId: this.id });
@@ -47264,7 +47461,7 @@ ${chatYield.item.payload.content}`;
47264
47461
  const userContent = await this.buildUserContent(userMessage, images, fileNames);
47265
47462
  this.memory.appendMessage(this.currentSessionId, { role: "user", content: userContent });
47266
47463
  const cognitiveContext = await this.prepareCognitiveContext("chat", effectiveMessage, senderId);
47267
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47464
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
47268
47465
  agentId: this.id,
47269
47466
  agentName: this.config.name,
47270
47467
  role: this.role,
@@ -47300,7 +47497,8 @@ ${chatYield.item.payload.content}`;
47300
47497
  agentId: this.id,
47301
47498
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47302
47499
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47303
- toolDefinitions: llmTools
47500
+ toolDefinitions: llmTools,
47501
+ systemCacheSegments
47304
47502
  });
47305
47503
  const messages = preparedStream.messages;
47306
47504
  log17.debug("Context usage for stream", { usagePercent: preparedStream.usage.usagePercent });
@@ -47336,7 +47534,7 @@ ${chatYield.item.payload.content}`;
47336
47534
  try {
47337
47535
  this.checkDailyTokenBudget();
47338
47536
  const llmStart = Date.now();
47339
- let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM call", abortController.signal);
47537
+ let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction, systemCacheSegments }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM call", abortController.signal);
47340
47538
  streamMarkerDelta.flush();
47341
47539
  const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
47342
47540
  this.updateTokensUsed(tokensThisCall);
@@ -47494,7 +47692,8 @@ ${streamYield.item.payload.content}`;
47494
47692
  agentId: this.id,
47495
47693
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47496
47694
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47497
- toolDefinitions: llmTools
47695
+ toolDefinitions: llmTools,
47696
+ systemCacheSegments
47498
47697
  });
47499
47698
  const updatedMessages = preparedCont.messages;
47500
47699
  if (cancelToken?.cancelled) {
@@ -47513,7 +47712,7 @@ ${streamYield.item.payload.content}`;
47513
47712
  return lastResponseContent || "";
47514
47713
  }
47515
47714
  const llmStart2 = Date.now();
47516
- response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages: updatedMessages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM continuation", abortController.signal);
47715
+ response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages: updatedMessages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction, systemCacheSegments }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM continuation", abortController.signal);
47517
47716
  streamMarkerDelta.flush();
47518
47717
  const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
47519
47718
  this.updateTokensUsed(tokens2);
@@ -47751,7 +47950,7 @@ ${streamYield.item.payload.content}`;
47751
47950
  this.memory.appendMessage(sessionId, { role: "user", content: taskPrompt });
47752
47951
  }
47753
47952
  const cognitiveContext = await this.prepareCognitiveContext("task_execution", taskPrompt);
47754
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47953
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
47755
47954
  agentId: this.id,
47756
47955
  agentName: this.config.name,
47757
47956
  role: this.role,
@@ -47818,12 +48017,13 @@ ${streamYield.item.payload.content}`;
47818
48017
  agentId: this.id,
47819
48018
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47820
48019
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47821
- toolDefinitions: llmTools
48020
+ toolDefinitions: llmTools,
48021
+ systemCacheSegments
47822
48022
  });
47823
48023
  const messages = preparedTask.messages;
47824
48024
  log17.debug("Context usage for task execution", { taskId: taskId2, usagePercent: preparedTask.usage.usagePercent, totalUsed: preparedTask.usage.totalUsed });
47825
48025
  let taskLlmStart = Date.now();
47826
- let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution LLM call", abortController.signal);
48026
+ let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction, systemCacheSegments }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution LLM call", abortController.signal);
47827
48027
  let taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
47828
48028
  this.updateTokensUsed(taskLlmTokens);
47829
48029
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48005,14 +48205,16 @@ ${yieldResult.item.payload.content}`
48005
48205
  agentId: this.id,
48006
48206
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48007
48207
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48008
- toolDefinitions: llmTools
48208
+ toolDefinitions: llmTools,
48209
+ systemCacheSegments
48009
48210
  });
48010
48211
  taskLlmStart = Date.now();
48011
48212
  response = await this.withNetworkRetry(() => this.llmRouter.chatStream({
48012
48213
  messages: preparedTaskCont.messages,
48013
48214
  tools: llmTools.length > 0 ? llmTools : void 0,
48014
48215
  metadata: this.getLLMMetadata(sessionId),
48015
- compaction: useCompaction
48216
+ compaction: useCompaction,
48217
+ systemCacheSegments
48016
48218
  }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution LLM continuation", abortController.signal);
48017
48219
  taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
48018
48220
  this.updateTokensUsed(taskLlmTokens);
@@ -48054,14 +48256,16 @@ ${yieldResult.item.payload.content}`
48054
48256
  agentId: this.id,
48055
48257
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48056
48258
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48057
- toolDefinitions: llmTools
48259
+ toolDefinitions: llmTools,
48260
+ systemCacheSegments
48058
48261
  });
48059
48262
  taskLlmStart = Date.now();
48060
48263
  response = await this.withNetworkRetry(() => this.llmRouter.chatStream({
48061
48264
  messages: preparedFinal.messages,
48062
48265
  tools: llmTools.length > 0 ? llmTools : void 0,
48063
48266
  metadata: this.getLLMMetadata(sessionId),
48064
- compaction: useCompaction
48267
+ compaction: useCompaction,
48268
+ systemCacheSegments
48065
48269
  }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution final submit reminder", abortController.signal);
48066
48270
  taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
48067
48271
  this.updateTokensUsed(taskLlmTokens);
@@ -48185,7 +48389,7 @@ ${yieldResult.item.payload.content}`
48185
48389
  this.memory.getOrCreateSession(this.id, sessionId);
48186
48390
  this.memory.appendMessage(sessionId, { role: "user", content: userMessage });
48187
48391
  const cognitiveContext = await this.prepareCognitiveContext("chat", userMessage);
48188
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
48392
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
48189
48393
  agentId: this.id,
48190
48394
  agentName: this.config.name,
48191
48395
  role: this.role,
@@ -48246,11 +48450,12 @@ ${yieldResult.item.payload.content}`
48246
48450
  agentId: this.id,
48247
48451
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48248
48452
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48249
- toolDefinitions: llmTools
48453
+ toolDefinitions: llmTools,
48454
+ systemCacheSegments
48250
48455
  });
48251
48456
  const messages = prepared.messages;
48252
48457
  let risLlmStart = Date.now();
48253
- let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction }, handleStreamEvent, this.getEffectiveProvider()), "RespondInSession LLM call");
48458
+ let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction, systemCacheSegments }, handleStreamEvent, this.getEffectiveProvider()), "RespondInSession LLM call");
48254
48459
  let risTokens = response.usage.inputTokens + response.usage.outputTokens;
48255
48460
  this.updateTokensUsed(risTokens);
48256
48461
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48308,10 +48513,11 @@ ${yieldResult.item.payload.content}`
48308
48513
  agentId: this.id,
48309
48514
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48310
48515
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48311
- toolDefinitions: llmTools
48516
+ toolDefinitions: llmTools,
48517
+ systemCacheSegments
48312
48518
  });
48313
48519
  risLlmStart = Date.now();
48314
- response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages: preparedCont.messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction }, handleStreamEvent, this.getEffectiveProvider()), "RespondInSession LLM continuation");
48520
+ response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages: preparedCont.messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(sessionId), compaction: useCompaction, systemCacheSegments }, handleStreamEvent, this.getEffectiveProvider()), "RespondInSession LLM continuation");
48315
48521
  risTokens = response.usage.inputTokens + response.usage.outputTokens;
48316
48522
  this.updateTokensUsed(risTokens);
48317
48523
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48860,6 +49066,19 @@ ${escalationReason}`;
48860
49066
  }
48861
49067
  async handleHeartbeat(ctx) {
48862
49068
  log17.info("Processing heartbeat check-in");
49069
+ const queuedNonHeartbeat = this.mailbox.getQueuedItems().filter((i) => i.sourceType !== "heartbeat" && i.status === "queued").length;
49070
+ const fingerprint = `q:${queuedNonHeartbeat}`;
49071
+ if (fingerprint === this.lastHeartbeatFingerprint && queuedNonHeartbeat === 0) {
49072
+ this.consecutiveIdleHeartbeats++;
49073
+ log17.info("Heartbeat: no changes detected, skipping LLM call", {
49074
+ consecutiveIdle: this.consecutiveIdleHeartbeats
49075
+ });
49076
+ this.state.lastHeartbeat = (/* @__PURE__ */ new Date()).toISOString();
49077
+ this.metricsCollector.recordHeartbeat(true);
49078
+ return;
49079
+ }
49080
+ this.lastHeartbeatFingerprint = fingerprint;
49081
+ this.consecutiveIdleHeartbeats = 0;
48863
49082
  const activityId = this.startActivity("heartbeat", "Heartbeat check-in", {});
48864
49083
  let lastHeartbeatSummary = "";
48865
49084
  try {
@@ -48958,8 +49177,8 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
48958
49177
  "- Common sections: `procedures`, `conventions`, `preferences`, `domain-knowledge`.",
48959
49178
  "",
48960
49179
  "**Shareable skills** (for team-wide practices):",
48961
- '- Check existing skills first: `discover_tools({ mode: "list_skills" })` and `builder_list`.',
48962
- "- To update an existing skill: edit files in `~/.markus/builder-artifacts/skills/{name}/`, bump version, re-install with `builder_install`.",
49180
+ '- Check existing skills first: `discover_tools({ mode: "list_skills" })` and `package_list`.',
49181
+ "- To update an existing skill: edit files in `~/.markus/builder-artifacts/skills/{name}/`, bump version, re-install with `package_install`.",
48963
49182
  "",
48964
49183
  "**Direct self-evolution** (simplest and most impactful):",
48965
49184
  "- **Update ROLE.md** \u2014 When you discover a behavioral rule, working style, or guiding principle that should always apply, append it to your ROLE.md via `file_edit`. ROLE.md is loaded into every conversation, so changes take effect immediately. Read first, then append. No need to accumulate 3 insights \u2014 even a single validated lesson can warrant a role update if it is fundamental.",
@@ -48971,7 +49190,7 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
48971
49190
  '| Single insight / gotcha | `memory_save` with tags: `["insight"]` |',
48972
49191
  '| Tool tip or preference | `memory_save` with tags: `["insight", "tool:<name>"]` |',
48973
49192
  '| Multi-step repeatable workflow | `memory_update_longterm({ section: "procedures", mode: "patch" })` |',
48974
- "| Practice worth sharing with the team | Create skill via **skill-building**, then install with `builder_install` |",
49193
+ "| Practice worth sharing with the team | Create skill via **skill-building**, then install with `package_install` |",
48975
49194
  "| Behavioral rule or guiding principle | Update ROLE.md (`file_read` \u2192 `file_edit` to append) |",
48976
49195
  "| New recurring check for your patrol | Update HEARTBEAT.md (`file_read` \u2192 `file_edit`) |",
48977
49196
  "",
@@ -49077,10 +49296,12 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
49077
49296
  "discover_tools",
49078
49297
  "notify_user",
49079
49298
  "request_user_approval",
49080
- "recall_activity"
49299
+ "recall_activity",
49300
+ "builder_install",
49301
+ "builder_list"
49081
49302
  ];
49082
49303
  if (isManager) {
49083
- baseTools.push("task_board_health", "task_cleanup_duplicates", "task_assign", "team_status", "deliverable_create", "deliverable_search", "team_hire_agent", "team_list_templates", "builder_install", "builder_list");
49304
+ baseTools.push("task_board_health", "task_cleanup_duplicates", "task_assign", "team_status", "deliverable_create", "deliverable_search", "package_install", "package_list");
49084
49305
  }
49085
49306
  const HEARTBEAT_ALLOWED_TOOLS = new Set(baseTools);
49086
49307
  const HEARTBEAT_MAX_RETRIES = 3;
@@ -49177,12 +49398,15 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
49177
49398
  * 2. Memory dream: prune, deduplicate, merge (once per day)
49178
49399
  */
49179
49400
  lastDreamDate = "";
49401
+ lastHeartbeatFingerprint = "";
49402
+ consecutiveIdleHeartbeats = 0;
49180
49403
  async consolidateMemory() {
49181
49404
  try {
49182
49405
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
49183
49406
  const entries2 = this.memory.getEntries();
49184
- if (entries2.length >= 50 && this.lastDreamDate !== today) {
49185
- this.lastDreamDate = today;
49407
+ const dreamKey = entries2.length > 500 ? `${today}_${Math.floor(Date.now() / (6 * 36e5))}` : today;
49408
+ if (entries2.length >= 50 && this.lastDreamDate !== dreamKey) {
49409
+ this.lastDreamDate = dreamKey;
49186
49410
  await this.dreamConsolidateMemory(entries2);
49187
49411
  this.pruneMemoryMd();
49188
49412
  }
@@ -49196,7 +49420,7 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
49196
49420
  * outdated items, and merge opportunities. Apply changes programmatically.
49197
49421
  */
49198
49422
  async dreamConsolidateMemory(entries2) {
49199
- const MAX_ENTRIES_FOR_LLM = 200;
49423
+ const MAX_ENTRIES_FOR_LLM = 500;
49200
49424
  const truncated = entries2.length > MAX_ENTRIES_FOR_LLM;
49201
49425
  const batch = truncated ? entries2.slice(-MAX_ENTRIES_FOR_LLM) : entries2;
49202
49426
  const entryList = batch.map((e, i) => {
@@ -49267,8 +49491,8 @@ ${knowledgePreview}
49267
49491
  return;
49268
49492
  }
49269
49493
  const rawPlan = JSON.parse(jsonMatch[0]);
49270
- const MAX_REMOVE_PER_CYCLE = 10;
49271
- const MAX_MERGE_PER_CYCLE = 5;
49494
+ const MAX_REMOVE_PER_CYCLE = 50;
49495
+ const MAX_MERGE_PER_CYCLE = 20;
49272
49496
  const entryIds = new Set(batch.map((e) => e.id));
49273
49497
  const plan = {
49274
49498
  remove: (rawPlan.remove ?? []).filter((id) => entryIds.has(id)).slice(0, MAX_REMOVE_PER_CYCLE),
@@ -49389,6 +49613,52 @@ ${promo.content}` : promo.content;
49389
49613
  if (!inDailyReport)
49390
49614
  afterSectionPrune.push(line);
49391
49615
  }
49616
+ const sections = [];
49617
+ let currentHeading = "";
49618
+ let currentBody = [];
49619
+ let sectionStart = 0;
49620
+ for (let i = 0; i <= afterSectionPrune.length; i++) {
49621
+ const line = i < afterSectionPrune.length ? afterSectionPrune[i] : void 0;
49622
+ const isHeading = line !== void 0 && /^#{1,3}\s/.test(line);
49623
+ if (isHeading || line === void 0) {
49624
+ if (currentHeading || currentBody.length > 0) {
49625
+ sections.push({
49626
+ heading: currentHeading,
49627
+ body: currentBody.join("\n").trim(),
49628
+ startIdx: sectionStart
49629
+ });
49630
+ }
49631
+ currentHeading = line ?? "";
49632
+ currentBody = [];
49633
+ sectionStart = i;
49634
+ } else {
49635
+ currentBody.push(line);
49636
+ }
49637
+ }
49638
+ const headingLastIdx = /* @__PURE__ */ new Map();
49639
+ const normalizeHeading = (h) => h.replace(/^#+\s*/, "").trim().toLowerCase();
49640
+ for (let i = 0; i < sections.length; i++) {
49641
+ const key2 = normalizeHeading(sections[i].heading);
49642
+ if (key2)
49643
+ headingLastIdx.set(key2, i);
49644
+ }
49645
+ const deduped = [];
49646
+ for (let i = 0; i < sections.length; i++) {
49647
+ const s = sections[i];
49648
+ const key2 = normalizeHeading(s.heading);
49649
+ if (key2 && headingLastIdx.get(key2) !== i && headingLastIdx.has(key2)) {
49650
+ continue;
49651
+ }
49652
+ deduped.push(s);
49653
+ }
49654
+ afterSectionPrune.length = 0;
49655
+ for (const s of deduped) {
49656
+ if (s.heading)
49657
+ afterSectionPrune.push(s.heading);
49658
+ if (s.body)
49659
+ afterSectionPrune.push(s.body);
49660
+ afterSectionPrune.push("");
49661
+ }
49392
49662
  const outputLines = [];
49393
49663
  let inThinkBlock = false;
49394
49664
  for (const line of afterSectionPrune) {
@@ -50181,6 +50451,73 @@ var init_browser_session = __esm({
50181
50451
  });
50182
50452
 
50183
50453
  // ../core/dist/tools/manager.js
50454
+ function createBuilderTools(ctx) {
50455
+ return [
50456
+ ...ctx.listArtifacts ? [
50457
+ {
50458
+ name: "builder_list",
50459
+ description: "List builder artifacts (custom-created or Hub-downloaded agent/team/skill packages). These can be installed with builder_install.",
50460
+ inputSchema: {
50461
+ type: "object",
50462
+ properties: {
50463
+ type: {
50464
+ type: "string",
50465
+ enum: ["agent", "team", "skill"],
50466
+ description: "Filter by artifact type (optional)"
50467
+ }
50468
+ }
50469
+ },
50470
+ async execute(args) {
50471
+ try {
50472
+ const artifacts = ctx.listArtifacts(args["type"]);
50473
+ return JSON.stringify({ artifacts, count: artifacts.length });
50474
+ } catch (error) {
50475
+ return JSON.stringify({ status: "error", error: String(error) });
50476
+ }
50477
+ }
50478
+ }
50479
+ ] : [],
50480
+ ...ctx.installArtifact ? [
50481
+ {
50482
+ name: "builder_install",
50483
+ description: "Install a builder artifact \u2014 deploys an agent, team, or skill package into the live organization. For agents/teams: after installation, onboard with project context and assign initial tasks.",
50484
+ inputSchema: {
50485
+ type: "object",
50486
+ properties: {
50487
+ type: {
50488
+ type: "string",
50489
+ enum: ["agent", "team", "skill"],
50490
+ description: "Artifact type"
50491
+ },
50492
+ name: { type: "string", description: "Artifact name (from builder_list)" }
50493
+ },
50494
+ required: ["type", "name"]
50495
+ },
50496
+ async execute(args) {
50497
+ try {
50498
+ const type = args["type"]?.trim();
50499
+ const name = args["name"]?.trim();
50500
+ if (!type || !["agent", "team", "skill"].includes(type))
50501
+ return JSON.stringify({ status: "error", error: "type is required and must be one of: agent, team, skill" });
50502
+ if (!name)
50503
+ return JSON.stringify({ status: "error", error: "name is required \u2014 provide the artifact name from builder_list" });
50504
+ const result = await ctx.installArtifact(type, name);
50505
+ const isAgentOrTeam = result.type === "agent" || result.type === "team";
50506
+ return JSON.stringify({
50507
+ status: "success",
50508
+ ...result,
50509
+ ...isAgentOrTeam ? {
50510
+ next_steps: "Installed successfully. Next: onboard new agent(s) with project context via agent_send_message, then assign initial tasks via task_create."
50511
+ } : {}
50512
+ });
50513
+ } catch (error) {
50514
+ return JSON.stringify({ status: "error", error: String(error) });
50515
+ }
50516
+ }
50517
+ }
50518
+ ] : []
50519
+ ];
50520
+ }
50184
50521
  function createManagerTools(ctx) {
50185
50522
  return [
50186
50523
  {
@@ -50299,79 +50636,34 @@ function createManagerTools(ctx) {
50299
50636
  }
50300
50637
  }
50301
50638
  ] : [],
50302
- ...ctx.listTemplates ? [
50303
- {
50304
- name: "team_list_templates",
50305
- description: "List available agent templates that can be hired. Each template has a role, description, and category.",
50306
- inputSchema: {
50307
- type: "object",
50308
- properties: {}
50309
- },
50310
- async execute() {
50311
- try {
50312
- const templates = ctx.listTemplates();
50313
- return JSON.stringify({ templates, count: templates.length });
50314
- } catch (error) {
50315
- return JSON.stringify({ status: "error", error: String(error) });
50316
- }
50317
- }
50318
- }
50319
- ] : [],
50320
- ...ctx.hireFromTemplate ? [
50321
- {
50322
- name: "team_hire_agent",
50323
- description: "Hire a new agent from a template and add them to your team. After hiring, onboard the agent: send a welcome message with project context via agent_send_message, then assign initial tasks via task_create.",
50324
- inputSchema: {
50325
- type: "object",
50326
- properties: {
50327
- template_id: { type: "string", description: "Template ID (from team_list_templates)" },
50328
- name: { type: "string", description: "Display name for the new agent" },
50329
- skills: {
50330
- type: "array",
50331
- items: { type: "string" },
50332
- description: "Optional skill IDs to assign"
50333
- }
50334
- },
50335
- required: ["template_id", "name"]
50336
- },
50337
- async execute(args) {
50338
- try {
50339
- const templateId = args["template_id"]?.trim();
50340
- const name = args["name"]?.trim();
50341
- if (!templateId)
50342
- return JSON.stringify({ status: "error", error: "template_id is required" });
50343
- if (!name)
50344
- return JSON.stringify({ status: "error", error: "name is required \u2014 please provide a display name for the new agent" });
50345
- const result = await ctx.hireFromTemplate(templateId, name, args["skills"]);
50346
- return JSON.stringify({
50347
- status: "success",
50348
- agent: result,
50349
- next_steps: "Agent created and started. Next: onboard them with project context via agent_send_message, then assign initial tasks via task_create."
50350
- });
50351
- } catch (error) {
50352
- return JSON.stringify({ status: "error", error: String(error) });
50353
- }
50354
- }
50355
- }
50356
- ] : [],
50357
50639
  ...ctx.listArtifacts ? [
50358
50640
  {
50359
- name: "builder_list",
50360
- description: "List builder artifacts (custom-created or Hub-downloaded agent/team/skill packages). These can be installed with builder_install.",
50641
+ name: "package_list",
50642
+ description: 'List all available packages. type "agent": built-in roles (developer, content-writer, etc.) and custom agent packages. type "team": team templates (content-team, research-lab, etc.). type "skill": skill packages. Omit type to list all. Install with package_install. To find more online, use hub_search.',
50361
50643
  inputSchema: {
50362
50644
  type: "object",
50363
50645
  properties: {
50364
50646
  type: {
50365
50647
  type: "string",
50366
50648
  enum: ["agent", "team", "skill"],
50367
- description: "Filter by artifact type (optional)"
50649
+ description: "Filter by type (optional). Omit to list all."
50368
50650
  }
50369
50651
  }
50370
50652
  },
50371
50653
  async execute(args) {
50372
50654
  try {
50373
- const artifacts = ctx.listArtifacts(args["type"]);
50374
- return JSON.stringify({ artifacts, count: artifacts.length });
50655
+ const type = args["type"];
50656
+ const artifacts = ctx.listArtifacts(type);
50657
+ const roles = (!type || type === "agent") && ctx.listTemplates ? ctx.listTemplates() : [];
50658
+ const roleItems = roles.map((r) => ({
50659
+ type: "agent",
50660
+ source: "role",
50661
+ name: r.id ?? r.name,
50662
+ description: r.description ?? r.name,
50663
+ ...r
50664
+ }));
50665
+ const items = [...roleItems, ...artifacts];
50666
+ return JSON.stringify({ items, count: items.length });
50375
50667
  } catch (error) {
50376
50668
  return JSON.stringify({ status: "error", error: String(error) });
50377
50669
  }
@@ -50443,17 +50735,23 @@ function createManagerTools(ctx) {
50443
50735
  ] : [],
50444
50736
  ...ctx.installArtifact ? [
50445
50737
  {
50446
- name: "builder_install",
50447
- description: "Install a builder artifact \u2014 deploys an agent, team, or skill package into the live organization. For agents/teams: after installation, onboard with project context and assign initial tasks.",
50738
+ name: "package_install",
50739
+ description: 'Install a package into the live organization. type "agent": hire/install an agent (from a built-in role or a custom package). type "team": deploy a full team with all members, norms, and starter tasks. type "skill": install a skill package. Use package_list to see what is available.',
50448
50740
  inputSchema: {
50449
50741
  type: "object",
50450
50742
  properties: {
50451
50743
  type: {
50452
50744
  type: "string",
50453
50745
  enum: ["agent", "team", "skill"],
50454
- description: "Artifact type"
50746
+ description: "Package type"
50455
50747
  },
50456
- name: { type: "string", description: "Artifact name (from builder_list)" }
50748
+ name: { type: "string", description: "Package name (from package_list)" },
50749
+ agent_name: { type: "string", description: 'Display name for the new agent (required when installing from a built-in role, e.g. "developer")' },
50750
+ skills: {
50751
+ type: "array",
50752
+ items: { type: "string" },
50753
+ description: "Optional skill IDs to assign to the new agent"
50754
+ }
50457
50755
  },
50458
50756
  required: ["type", "name"]
50459
50757
  },
@@ -50464,13 +50762,36 @@ function createManagerTools(ctx) {
50464
50762
  if (!type || !["agent", "team", "skill"].includes(type))
50465
50763
  return JSON.stringify({ status: "error", error: "type is required and must be one of: agent, team, skill" });
50466
50764
  if (!name)
50467
- return JSON.stringify({ status: "error", error: "name is required \u2014 provide the artifact name from builder_list" });
50765
+ return JSON.stringify({ status: "error", error: "name is required \u2014 use package_list to see available packages" });
50766
+ if (type === "agent") {
50767
+ try {
50768
+ const result2 = await ctx.installArtifact(type, name);
50769
+ return JSON.stringify({
50770
+ status: "success",
50771
+ ...result2,
50772
+ next_steps: "Installed successfully. Next: onboard new agent with project context via agent_send_message, then assign tasks via task_create."
50773
+ });
50774
+ } catch {
50775
+ if (ctx.hireFromTemplate) {
50776
+ const agentName = args["agent_name"]?.trim();
50777
+ if (!agentName)
50778
+ return JSON.stringify({ status: "error", error: "agent_name is required when installing from a built-in role \u2014 provide a display name for the new agent" });
50779
+ const result2 = await ctx.hireFromTemplate(name, agentName, args["skills"]);
50780
+ return JSON.stringify({
50781
+ status: "success",
50782
+ type: "agent",
50783
+ agent: result2,
50784
+ next_steps: "Agent hired and started. Next: onboard with project context via agent_send_message, then assign tasks via task_create."
50785
+ });
50786
+ }
50787
+ throw new Error(`Agent package not found: ${name}. Use package_list to see available packages.`);
50788
+ }
50789
+ }
50468
50790
  const result = await ctx.installArtifact(type, name);
50469
- const isAgentOrTeam = result.type === "agent" || result.type === "team";
50470
50791
  return JSON.stringify({
50471
50792
  status: "success",
50472
50793
  ...result,
50473
- ...isAgentOrTeam ? {
50794
+ ...type === "team" ? {
50474
50795
  next_steps: "Installed successfully. Next: onboard new agent(s) with project context via agent_send_message, then assign initial tasks via task_create."
50475
50796
  } : {}
50476
50797
  });
@@ -50615,14 +50936,40 @@ function createA2ATools(ctx) {
50615
50936
  },
50616
50937
  {
50617
50938
  name: "agent_list_colleagues",
50618
- description: "List all other agents in your organization that you can collaborate with. Shows their names, roles, skills, and current status.",
50939
+ description: "List all other agents in your organization grouped by team. Shows team structure, roles, skills, and current status.",
50619
50940
  inputSchema: {
50620
50941
  type: "object",
50621
50942
  properties: {}
50622
50943
  },
50623
50944
  async execute() {
50624
50945
  const colleagues = ctx.listColleagues().filter((a) => a.id !== ctx.selfId);
50625
- return JSON.stringify({ colleagues, count: colleagues.length });
50946
+ const byTeam = /* @__PURE__ */ new Map();
50947
+ for (const c of colleagues) {
50948
+ const key2 = c.teamId ?? "__ungrouped__";
50949
+ if (!byTeam.has(key2))
50950
+ byTeam.set(key2, []);
50951
+ byTeam.get(key2).push(c);
50952
+ }
50953
+ const lines = [];
50954
+ lines.push(`Organization colleagues: ${colleagues.length} agents
50955
+ `);
50956
+ for (const [teamId, members] of byTeam) {
50957
+ const manager = members.find((m) => m.agentRole === "manager");
50958
+ const teamLabel = members[0]?.teamName ?? teamId;
50959
+ if (teamId === "__ungrouped__") {
50960
+ lines.push(`\u2500\u2500 Ungrouped \u2500\u2500`);
50961
+ } else {
50962
+ lines.push(`\u2500\u2500 Team: ${teamLabel}${manager ? ` (manager: ${manager.name})` : ""} \u2500\u2500`);
50963
+ }
50964
+ const sorted = [...members].sort((a, b) => a.agentRole === "manager" ? -1 : b.agentRole === "manager" ? 1 : 0);
50965
+ for (const m of sorted) {
50966
+ const badge = m.agentRole === "manager" ? " [Manager]" : "";
50967
+ const skills = m.skills?.length ? ` | skills: ${m.skills.join(", ")}` : "";
50968
+ lines.push(` \u2022 ${m.name} (${m.id})${badge} \u2014 ${m.role} | ${m.status}${skills}`);
50969
+ }
50970
+ lines.push("");
50971
+ }
50972
+ return lines.join("\n");
50626
50973
  }
50627
50974
  },
50628
50975
  ...ctx.sendGroupMessage ? [{
@@ -50710,7 +51057,7 @@ function createA2ATools(ctx) {
50710
51057
  type: "string",
50711
51058
  description: 'Required when scope="channel". The channel key (e.g., "group:<teamId>" for team chats, "dm:<id1>_<id2>" for DMs).'
50712
51059
  },
50713
- limit: { type: "number", description: "Number of items to fetch (default 30, max 50)." },
51060
+ limit: { type: "number", description: "Number of items to fetch (default 80, max 200)." },
50714
51061
  before: { type: "string", description: "ISO timestamp \u2014 fetch items older than this for pagination. Omit for most recent." }
50715
51062
  },
50716
51063
  required: ["scope"]
@@ -50722,11 +51069,11 @@ function createA2ATools(ctx) {
50722
51069
  if (!channelKey) {
50723
51070
  return JSON.stringify({ status: "error", error: 'channel_key is required when scope="channel"' });
50724
51071
  }
50725
- const limit = Math.min(args["limit"] ?? 30, 50);
51072
+ const limit = Math.min(args["limit"] ?? 80, 200);
50726
51073
  const before2 = args["before"];
50727
51074
  try {
50728
51075
  const result = await ctx.getChannelMessages(channelKey, limit, before2);
50729
- const formatted = result.messages.map((m) => `[${m.createdAt}] ${m.senderType === "agent" ? `[agent] ${m.senderName}` : `[human] ${m.senderName}`}: ${m.text.slice(0, 500)}`);
51076
+ const formatted = result.messages.map((m) => `[${m.createdAt}] ${m.senderType === "agent" ? `[agent] ${m.senderName}` : `[human] ${m.senderName}`}: ${m.text.slice(0, 2e3)}`);
50730
51077
  return JSON.stringify({
50731
51078
  messages: formatted,
50732
51079
  count: result.messages.length,
@@ -51182,6 +51529,15 @@ function createAgentTaskTools(ctx) {
51182
51529
  items: { type: "string" },
51183
51530
  description: "Update the list of task IDs that block this task. Only the task creator can modify this field. Pass an empty array to clear all blockers."
51184
51531
  },
51532
+ reviewer_id: {
51533
+ type: "string",
51534
+ description: "New reviewer agent or human ID. Only the task creator or a manager can change the reviewer."
51535
+ },
51536
+ reviewer_type: {
51537
+ type: "string",
51538
+ enum: ["agent", "human"],
51539
+ description: "Whether the new reviewer is an agent or a human user. Required when changing reviewer_id."
51540
+ },
51185
51541
  schedule: {
51186
51542
  type: "object",
51187
51543
  description: 'Update the schedule for a scheduled (recurring) task. Only works on tasks with taskType "scheduled". Set either "every" OR "cron", not both.',
@@ -51202,20 +51558,30 @@ function createAgentTaskTools(ctx) {
51202
51558
  const note = args["note"];
51203
51559
  const description = args["description"];
51204
51560
  const blockedBy = args["blocked_by"];
51205
- if (blockedBy !== void 0) {
51561
+ const reviewerId = args["reviewer_id"];
51562
+ const reviewerType = args["reviewer_type"];
51563
+ if (blockedBy !== void 0 || reviewerId !== void 0) {
51206
51564
  const existing = ctx.getTask ? await ctx.getTask(taskId2) : null;
51207
51565
  const createdBy = existing?.["createdBy"];
51208
- if (createdBy && createdBy !== ctx.agentId) {
51566
+ if (blockedBy !== void 0 && createdBy && createdBy !== ctx.agentId) {
51209
51567
  return JSON.stringify({
51210
51568
  status: "denied",
51211
51569
  error: "Only the task creator can modify blocked_by. You are not the creator of this task."
51212
51570
  });
51213
51571
  }
51572
+ if (reviewerId !== void 0 && createdBy && createdBy !== ctx.agentId) {
51573
+ return JSON.stringify({
51574
+ status: "denied",
51575
+ error: "Only the task creator or a manager can change the reviewer."
51576
+ });
51577
+ }
51214
51578
  }
51215
- if ((description !== void 0 || blockedBy !== void 0) && ctx.updateTaskFields) {
51579
+ if ((description !== void 0 || blockedBy !== void 0 || reviewerId !== void 0) && ctx.updateTaskFields) {
51216
51580
  await ctx.updateTaskFields(taskId2, {
51217
51581
  ...description !== void 0 ? { description } : {},
51218
- ...blockedBy !== void 0 ? { blockedBy } : {}
51582
+ ...blockedBy !== void 0 ? { blockedBy } : {},
51583
+ ...reviewerId !== void 0 ? { reviewerId } : {},
51584
+ ...reviewerType !== void 0 ? { reviewerType } : {}
51219
51585
  });
51220
51586
  }
51221
51587
  const schedule = args["schedule"];
@@ -51404,7 +51770,7 @@ function createAgentTaskTools(ctx) {
51404
51770
  ...ctx.addTaskNote ? [
51405
51771
  {
51406
51772
  name: "task_note",
51407
- description: "Add a progress note or comment to a task without changing its status. Use this to log intermediate findings, decisions, or observations while working on a task.",
51773
+ description: "Add a one-way progress note to a task's timeline log without changing its status. Use this to record intermediate findings, decisions, or milestones. For interactive discussion with other agents or humans, use task_comment instead.",
51408
51774
  inputSchema: {
51409
51775
  type: "object",
51410
51776
  properties: {
@@ -52642,6 +53008,48 @@ ${content}` : content;
52642
53008
  log24.info("Agent updated long-term memory", { agentId: ctx.agentId, section: section4, mode, contentLen: content.length });
52643
53009
  return JSON.stringify({ status: "updated", section: section4, mode });
52644
53010
  }
53011
+ },
53012
+ {
53013
+ name: "memory_delete",
53014
+ description: "Delete specific entries from your memory buffer (memories.json). Use this to clean up outdated, incorrect, or redundant observations. Provide either a list of entry IDs (from memory_list/memory_search) or a tag to remove all entries with that tag. Maximum 20 entries per call.",
53015
+ inputSchema: {
53016
+ type: "object",
53017
+ properties: {
53018
+ ids: {
53019
+ type: "array",
53020
+ items: { type: "string" },
53021
+ description: "Array of memory entry IDs to delete. Use memory_list or memory_search to find IDs."
53022
+ },
53023
+ tag: {
53024
+ type: "string",
53025
+ description: "Delete all entries with this tag. Alternative to specifying individual IDs."
53026
+ }
53027
+ }
53028
+ },
53029
+ async execute(args) {
53030
+ const ids = args["ids"];
53031
+ const tag = args["tag"];
53032
+ if (!ids?.length && !tag) {
53033
+ return JSON.stringify({ status: "error", error: "Provide either ids or tag to delete." });
53034
+ }
53035
+ const MAX_DELETE = 20;
53036
+ let removed = 0;
53037
+ if (ids?.length) {
53038
+ const capped = ids.slice(0, MAX_DELETE);
53039
+ removed = ctx.memory.removeEntries(capped);
53040
+ if (ctx.semanticSearch?.isEnabled()) {
53041
+ for (const id of capped) {
53042
+ ctx.semanticSearch.deleteMemory(id).catch((err) => {
53043
+ log24.warn("Failed to remove memory from semantic index", { error: String(err) });
53044
+ });
53045
+ }
53046
+ }
53047
+ } else if (tag) {
53048
+ removed = ctx.memory.removeEntriesByTag(tag);
53049
+ }
53050
+ log24.info("Agent deleted memories", { agentId: ctx.agentId, removed, byTag: tag ?? null });
53051
+ return JSON.stringify({ status: "deleted", removed });
53052
+ }
52645
53053
  }
52646
53054
  ];
52647
53055
  }
@@ -54354,6 +54762,14 @@ Known issues: ${knownIssues}` : ""}`
54354
54762
  agent.registerTool(tool);
54355
54763
  }
54356
54764
  }
54765
+ if (this.builderService) {
54766
+ const builderTools = createBuilderTools({
54767
+ installArtifact: (type, name) => this.builderService.installArtifact(type, name),
54768
+ listArtifacts: (type) => this.builderService.listArtifacts(type)
54769
+ });
54770
+ for (const tool of builderTools)
54771
+ agent.registerTool(tool);
54772
+ }
54357
54773
  const mcpConfigs = request.mcpServers ?? this.globalMcpServers;
54358
54774
  if (mcpConfigs) {
54359
54775
  for (const [serverName, serverConfig] of Object.entries(mcpConfigs)) {
@@ -54915,6 +55331,14 @@ Known issues: ${knownIssues}` : ""}`
54915
55331
  agent.registerTool(tool);
54916
55332
  }
54917
55333
  }
55334
+ if (this.builderService) {
55335
+ const builderTools = createBuilderTools({
55336
+ installArtifact: (type, name) => this.builderService.installArtifact(type, name),
55337
+ listArtifacts: (type) => this.builderService.listArtifacts(type)
55338
+ });
55339
+ for (const tool of builderTools)
55340
+ agent.registerTool(tool);
55341
+ }
54918
55342
  if (this.agentAuditCallback) {
54919
55343
  const cb = this.agentAuditCallback;
54920
55344
  agent.setAuditCallback((event) => cb(id, event));
@@ -55141,6 +55565,7 @@ Known issues: ${knownIssues}` : ""}`
55141
55565
  skills: a.config.skills,
55142
55566
  activeTaskCount: state.activeTaskCount ?? 0,
55143
55567
  teamId: a.config.teamId,
55568
+ teamName: a.getTeamName(),
55144
55569
  lastError: state.lastError,
55145
55570
  lastErrorAt: state.lastErrorAt,
55146
55571
  currentTaskId: state.currentTaskId,
@@ -55562,8 +55987,9 @@ var init_anthropic = __esm({
55562
55987
  max_tokens: request.maxTokens ?? this.maxTokens,
55563
55988
  messages
55564
55989
  };
55565
- if (systemMsg)
55566
- body["system"] = getTextContent(systemMsg.content);
55990
+ if (systemMsg) {
55991
+ body["system"] = this.buildSystemContent(getTextContent(systemMsg.content), request.systemCacheSegments);
55992
+ }
55567
55993
  if (request.temperature !== void 0)
55568
55994
  body["temperature"] = request.temperature;
55569
55995
  if (request.stopSequences?.length)
@@ -55603,8 +56029,9 @@ var init_anthropic = __esm({
55603
56029
  messages,
55604
56030
  stream: true
55605
56031
  };
55606
- if (systemMsg)
55607
- body["system"] = getTextContent(systemMsg.content);
56032
+ if (systemMsg) {
56033
+ body["system"] = this.buildSystemContent(getTextContent(systemMsg.content), request.systemCacheSegments);
56034
+ }
55608
56035
  if (request.temperature !== void 0)
55609
56036
  body["temperature"] = request.temperature;
55610
56037
  if (request.stopSequences?.length)
@@ -55789,6 +56216,21 @@ var init_anthropic = __esm({
55789
56216
  isCompactionSupported() {
55790
56217
  return this.model.startsWith("claude-opus-4") || this.model.startsWith("claude-sonnet-4");
55791
56218
  }
56219
+ /**
56220
+ * Build the `system` field for the Anthropic API. When structured cache
56221
+ * segments are provided, returns an array of content blocks with
56222
+ * `cache_control` breakpoints so the API can cache stable prefixes.
56223
+ * Falls back to a plain string when no segments are available.
56224
+ */
56225
+ buildSystemContent(plainText, segments) {
56226
+ if (!segments || segments.length <= 1)
56227
+ return plainText;
56228
+ return segments.filter((s) => s.content.length > 0).map((s) => ({
56229
+ type: "text",
56230
+ text: s.content,
56231
+ ...s.cacheBreakpoint ? { cache_control: { type: "ephemeral" } } : {}
56232
+ }));
56233
+ }
55792
56234
  convertResponse(data) {
55793
56235
  let content = "";
55794
56236
  let compactionContent;
@@ -57114,6 +57556,13 @@ var init_oauth_manager = __esm({
57114
57556
  });
57115
57557
 
57116
57558
  // ../core/dist/llm/router.js
57559
+ function maskApiKey(key2) {
57560
+ if (!key2)
57561
+ return void 0;
57562
+ if (key2.length <= 8)
57563
+ return "****";
57564
+ return key2.slice(0, 4) + "..." + key2.slice(-4);
57565
+ }
57117
57566
  function buildTiers(providerNames, defaultProvider) {
57118
57567
  const tiers = [];
57119
57568
  if (providerNames.includes(defaultProvider)) {
@@ -57792,6 +58241,8 @@ var init_router = __esm({
57792
58241
  const builtinModels = BUILTIN_MODEL_CATALOG.filter((m) => m.provider === name);
57793
58242
  const customCatalogModels = this.customModelCatalog.get(name) ?? [];
57794
58243
  const mergedModels = [...builtinModels, ...customCatalogModels.filter((cm) => !builtinModels.some((bm) => bm.id === cm.id))];
58244
+ const rawKey = p.apiKey ?? "";
58245
+ const keySource = oauthProfile?.authType === "oauth" ? "oauth" : rawKey ? "config" : void 0;
57795
58246
  providers[name] = {
57796
58247
  name,
57797
58248
  displayName: PROVIDER_DISPLAY_NAMES[name] ?? name,
@@ -57799,6 +58250,8 @@ var init_router = __esm({
57799
58250
  baseUrl: p.baseUrl,
57800
58251
  configured: true,
57801
58252
  enabled: this.isProviderEnabled(name),
58253
+ apiKeyPreview: maskApiKey(rawKey),
58254
+ apiKeySource: keySource,
57802
58255
  contextWindow: customModels?.contextWindow ?? modelDef?.contextWindow,
57803
58256
  maxOutputTokens: customModels?.maxOutputTokens ?? modelDef?.maxOutputTokens,
57804
58257
  cost: customModels?.cost ?? modelDef?.cost,
@@ -59874,11 +60327,13 @@ function loadTeamTemplateFromDir(dirPath) {
59874
60327
  name: m.name,
59875
60328
  count: m.count,
59876
60329
  role: m.role,
60330
+ description: m.description,
59877
60331
  skills: m.skills ?? []
59878
60332
  })),
59879
60333
  tags: manifest.tags ?? [],
59880
60334
  category: manifest.category,
59881
60335
  icon: manifest.icon,
60336
+ starterTasks: manifest.starterTasks,
59882
60337
  announcements: existsSync21(annPath) ? readFileSync16(annPath, "utf-8") : void 0,
59883
60338
  norms: existsSync21(normsPath) ? readFileSync16(normsPath, "utf-8") : void 0,
59884
60339
  i18n: manifest.i18n
@@ -60101,6 +60556,34 @@ var init_org_service = __esm({
60101
60556
  listHumanUsers(orgId2) {
60102
60557
  return [...this.humans.values()].filter((h) => h.orgId === orgId2);
60103
60558
  }
60559
+ /**
60560
+ * Update (or insert) a user's in-memory identity without touching the DB.
60561
+ * Safe to call from auth endpoints that already persisted DB changes.
60562
+ * Never throws — logs warnings on failure.
60563
+ */
60564
+ syncHumanIdentity(userId2, orgId2, name, role, email) {
60565
+ try {
60566
+ const existing = this.humans.get(userId2);
60567
+ if (existing) {
60568
+ existing.name = name;
60569
+ if (email !== void 0)
60570
+ existing.email = email;
60571
+ existing.role = role;
60572
+ } else {
60573
+ this.humans.set(userId2, {
60574
+ id: userId2,
60575
+ name,
60576
+ email,
60577
+ role,
60578
+ orgId: orgId2,
60579
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
60580
+ });
60581
+ }
60582
+ this.refreshIdentityContextsForOrg(orgId2);
60583
+ } catch (err) {
60584
+ log49.warn("syncHumanIdentity failed (non-fatal)", { userId: userId2, error: String(err) });
60585
+ }
60586
+ }
60104
60587
  updateHumanUser(userId2, updates) {
60105
60588
  const user = this.humans.get(userId2);
60106
60589
  if (!user)
@@ -61178,10 +61661,12 @@ var init_task_service = __esm({
61178
61661
  }
61179
61662
  }
61180
61663
  if (!isReviewerDuringReview) {
61181
- if (task?.assignedAgentId) {
61664
+ const terminalStatuses = /* @__PURE__ */ new Set(["completed", "accepted", "archived"]);
61665
+ const handledByInjection = task?.status === "in_progress" || task?.status && terminalStatuses.has(task.status);
61666
+ if (task?.assignedAgentId && !handledByInjection) {
61182
61667
  enqueueFor(task.assignedAgentId, "task_comment", `New comment from ${authorName} on your assigned task`);
61183
61668
  }
61184
- if (task?.createdBy && task.status !== "in_progress") {
61669
+ if (task?.createdBy && !handledByInjection) {
61185
61670
  enqueueFor(task.createdBy, "task_comment", `New comment from ${authorName} on a task you created`);
61186
61671
  }
61187
61672
  }
@@ -62407,14 +62892,15 @@ ${c.content}`;
62407
62892
  reviewer: task.reviewerId
62408
62893
  });
62409
62894
  if (this.hitlService && request.creatorRole !== "human" && task.status === "pending") {
62410
- const creatorName = request.createdBy ?? "unknown agent";
62895
+ const creatorId = request.createdBy ?? "system";
62896
+ const creatorName = this.resolveActorName(creatorId, "agent") ?? creatorId;
62411
62897
  this.hitlService.requestApprovalAndWait({
62412
- agentId: request.createdBy ?? "system",
62898
+ agentId: creatorId,
62413
62899
  agentName: creatorName,
62414
62900
  type: "custom",
62415
- title: `Task approval: ${task.title}`,
62901
+ title: task.title,
62416
62902
  description: `Agent "${creatorName}" wants to create task "${task.title}" (priority: ${task.priority}).`,
62417
- details: { taskId: task.id, priority: task.priority }
62903
+ details: { taskId: task.id, taskTitle: task.title, priority: task.priority, subType: "task" }
62418
62904
  }).then((result) => {
62419
62905
  const current = this.tasks.get(task.id);
62420
62906
  if (!current || current.status !== "pending")
@@ -62653,6 +63139,12 @@ ${c.content}`;
62653
63139
  if (from === "review" && to !== "review") {
62654
63140
  this.activeReviews.delete(taskId2);
62655
63141
  }
63142
+ if (TERMINAL_STATUSES.has(to) && this.hitlService) {
63143
+ const cancelled = this.hitlService.cancelApprovalsByDetail("taskId", taskId2, "system", `Task ${to}`);
63144
+ if (cancelled > 0) {
63145
+ log50.info(`Cancelled ${cancelled} pending approval(s) for task ${taskId2} (status \u2192 ${to})`);
63146
+ }
63147
+ }
62656
63148
  }
62657
63149
  // ─── Stage 2: DB persistence ─────────────────────────────────────────────────
62658
63150
  persistStatusChange(id, status, updatedBy) {
@@ -62701,12 +63193,14 @@ ${c.content}`;
62701
63193
  Deliverables:
62702
63194
  ${deliverablesSummary}` : ""
62703
63195
  ].filter(Boolean).join("\n");
63196
+ const assigneeName = this.resolveActorName(task.assignedAgentId, "agent") ?? task.assignedAgentId;
62704
63197
  this.hitlService.requestApprovalAndWait({
62705
63198
  agentId: task.assignedAgentId,
62706
- agentName: task.assignedAgentId,
63199
+ agentName: assigneeName,
62707
63200
  type: "custom",
62708
63201
  title: `Review: ${task.title}`,
62709
63202
  description,
63203
+ details: { taskId: task.id, taskTitle: task.title, subType: "task_review" },
62710
63204
  targetUserId: task.reviewerId,
62711
63205
  options: [
62712
63206
  { id: "approve", label: "Approve" },
@@ -63061,6 +63555,12 @@ Action: ${guidance}` : ""
63061
63555
  if (data.reviewerType !== void 0)
63062
63556
  task.reviewerType = data.reviewerType;
63063
63557
  if (data.blockedBy !== void 0) {
63558
+ if (data.blockedBy.length > 0) {
63559
+ const cycle = this.detectBlockedByCycle(id, data.blockedBy);
63560
+ if (cycle) {
63561
+ throw new Error(`Circular dependency detected: ${cycle.join(" \u2192 ")}. Cannot set blocked_by \u2014 this would create a deadlock.`);
63562
+ }
63563
+ }
63064
63564
  task.blockedBy = data.blockedBy;
63065
63565
  if (this.taskRepo && "updateBlockedBy" in this.taskRepo) {
63066
63566
  this.taskRepo.updateBlockedBy(id, data.blockedBy).catch((err) => log50.warn("Failed to persist blockedBy to DB", { error: String(err) }));
@@ -63185,6 +63685,31 @@ Action: ${guidance}` : ""
63185
63685
  this.cascadeCancelDependents(task);
63186
63686
  }
63187
63687
  }
63688
+ /**
63689
+ * Detect cycles in blocked_by dependencies using BFS.
63690
+ * Returns the cycle path if found, or null if no cycle exists.
63691
+ */
63692
+ detectBlockedByCycle(taskId2, proposedBlockers) {
63693
+ for (const blockerId of proposedBlockers) {
63694
+ const visited = /* @__PURE__ */ new Set();
63695
+ const queue = [{ id: blockerId, path: [taskId2, blockerId] }];
63696
+ while (queue.length > 0) {
63697
+ const { id: current, path } = queue.shift();
63698
+ if (current === taskId2)
63699
+ return path;
63700
+ if (visited.has(current))
63701
+ continue;
63702
+ visited.add(current);
63703
+ const blockerTask = this.tasks.get(current);
63704
+ if (blockerTask?.blockedBy) {
63705
+ for (const nextId of blockerTask.blockedBy) {
63706
+ queue.push({ id: nextId, path: [...path, nextId] });
63707
+ }
63708
+ }
63709
+ }
63710
+ }
63711
+ return null;
63712
+ }
63188
63713
  areBlockersSatisfied(task) {
63189
63714
  if (!task.blockedBy?.length)
63190
63715
  return true;
@@ -63704,7 +64229,7 @@ Action: ${guidance}` : ""
63704
64229
  "",
63705
64230
  'Save each lesson using `memory_save` with tags `["lesson", ...]`.',
63706
64231
  'If it is a repeatable multi-step procedure, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
63707
- "If the best practice would benefit other agents on the team, create a shareable skill via **skill-building** and install it with `builder_install`.",
64232
+ "If the best practice would benefit other agents on the team, create a shareable skill via **skill-building** and install it with `package_install`.",
63708
64233
  "",
63709
64234
  "**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
63710
64235
  "- If this lesson reveals a behavioral rule that should always guide your work, append it to your ROLE.md via `file_edit`.",
@@ -63724,7 +64249,7 @@ Action: ${guidance}` : ""
63724
64249
  "",
63725
64250
  'If you identify a meaningful insight, save it using `memory_save` with tags `["lesson", "best-practice", ...]`.',
63726
64251
  'If it is a multi-step workflow, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
63727
- "If worth sharing with the team, create a skill via **skill-building** and install with `builder_install`.",
64252
+ "If worth sharing with the team, create a skill via **skill-building** and install with `package_install`.",
63728
64253
  "",
63729
64254
  "**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
63730
64255
  "- If this success reveals a guiding principle or working style worth keeping, append it to your ROLE.md via `file_edit`.",
@@ -63977,7 +64502,7 @@ ${reason}`
63977
64502
  updated.runAt = void 0;
63978
64503
  }
63979
64504
  if (fields.maxRuns !== void 0)
63980
- updated.maxRuns = fields.maxRuns;
64505
+ updated.maxRuns = fields.maxRuns > 0 ? fields.maxRuns : void 0;
63981
64506
  if (fields.timezone !== void 0)
63982
64507
  updated.timezone = fields.timezone;
63983
64508
  updated.nextRunAt = computeNextRunFromConfig(updated);
@@ -64347,8 +64872,10 @@ ${task.description}`;
64347
64872
  content,
64348
64873
  "",
64349
64874
  `(This task "${task.title}" is already ${task.status}. You have full context from your execution above.`,
64350
- "Respond to the comment. If the feedback contains something worth remembering, save it to memory.",
64351
- "You have all your tools available \u2014 take action if appropriate.)"
64875
+ "Your text response will be posted as a reply in the task comment thread automatically.",
64876
+ "Do NOT use the `task_comment` tool \u2014 your text output IS the reply visible to the commenter.",
64877
+ "If the feedback contains something worth remembering, save it to memory.",
64878
+ "You have all your tools available for taking action if appropriate.)"
64352
64879
  ].join("\n");
64353
64880
  try {
64354
64881
  log50.info("Triggering post-task agent reply", { taskId: taskId2, taskSessionId, agentId: agentId2, authorName });
@@ -64460,11 +64987,19 @@ var init_builder_service = __esm({
64460
64987
  orgService;
64461
64988
  skillRegistry;
64462
64989
  wsBroadcast;
64990
+ taskService;
64991
+ builtinTeamTemplatesDir;
64463
64992
  constructor(orgService, skillRegistry, wsBroadcast) {
64464
64993
  this.orgService = orgService;
64465
64994
  this.skillRegistry = skillRegistry;
64466
64995
  this.wsBroadcast = wsBroadcast;
64467
64996
  }
64997
+ setTaskService(taskService) {
64998
+ this.taskService = taskService;
64999
+ }
65000
+ setBuiltinTeamTemplatesDir(dir) {
65001
+ this.builtinTeamTemplatesDir = dir;
65002
+ }
64468
65003
  get baseDir() {
64469
65004
  return join20(homedir12(), ".markus", "builder-artifacts");
64470
65005
  }
@@ -64497,14 +65032,42 @@ var init_builder_service = __esm({
64497
65032
  });
64498
65033
  }
64499
65034
  }
65035
+ if ((!type || type === "team") && this.builtinTeamTemplatesDir && existsSync24(this.builtinTeamTemplatesDir)) {
65036
+ const artifactNames = new Set(artifacts.filter((a) => a.type === "team").map((a) => a.name));
65037
+ for (const entry of readdirSync8(this.builtinTeamTemplatesDir, { withFileTypes: true })) {
65038
+ if (!entry.isDirectory() || artifactNames.has(entry.name))
65039
+ continue;
65040
+ const tplDir = join20(this.builtinTeamTemplatesDir, entry.name);
65041
+ const manifest = readManifest(tplDir, "team", FS_HELPER);
65042
+ if (!manifest)
65043
+ continue;
65044
+ artifacts.push({
65045
+ type: "team",
65046
+ name: entry.name,
65047
+ description: manifest.description ?? void 0,
65048
+ meta: { ...manifest, source: "builtin" },
65049
+ path: tplDir,
65050
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
65051
+ });
65052
+ }
65053
+ }
64500
65054
  artifacts.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
64501
65055
  return artifacts;
64502
65056
  }
64503
65057
  async installArtifact(type, name) {
64504
65058
  const typeDir = type === "agent" ? "agents" : type === "team" ? "teams" : "skills";
64505
- const artDir = join20(this.baseDir, typeDir, name);
65059
+ let artDir = join20(this.baseDir, typeDir, name);
64506
65060
  if (!existsSync24(artDir)) {
64507
- throw new Error(`Artifact not found: ${type}/${name}`);
65061
+ if (type === "team" && this.builtinTeamTemplatesDir) {
65062
+ const builtinDir = join20(this.builtinTeamTemplatesDir, name);
65063
+ if (existsSync24(builtinDir)) {
65064
+ artDir = builtinDir;
65065
+ } else {
65066
+ throw new Error(`Team template not found: ${name}. Use package_list to see available packages.`);
65067
+ }
65068
+ } else {
65069
+ throw new Error(`Artifact not found: ${type}/${name}`);
65070
+ }
64508
65071
  }
64509
65072
  const installType = type;
64510
65073
  const manifest = readManifest(artDir, installType, FS_HELPER);
@@ -64620,9 +65183,35 @@ var init_builder_service = __esm({
64620
65183
  createdAgents.push({ id: agent.id, name: agent.config.name, role: agent.role.name });
64621
65184
  }
64622
65185
  }
65186
+ const starterTasks = manifest.starterTasks ?? manifest.team?.starterTasks ?? [];
65187
+ const createdTaskIds = [];
65188
+ if (starterTasks.length > 0 && this.taskService) {
65189
+ const managerId = createdAgents.find((a) => a.role === "manager" || members.find((m) => m.name === a.name)?.role === "manager")?.id ?? createdAgents[0]?.id;
65190
+ if (managerId) {
65191
+ for (const st of starterTasks) {
65192
+ try {
65193
+ const task = this.taskService.createTask({
65194
+ orgId: "default",
65195
+ title: st.title,
65196
+ description: st.description,
65197
+ priority: st.priority ?? "medium",
65198
+ assignedAgentId: managerId,
65199
+ reviewerId: managerId,
65200
+ reviewerType: "human",
65201
+ creatorRole: "human",
65202
+ taskType: "standard"
65203
+ });
65204
+ createdTaskIds.push(task.id);
65205
+ log51.info("installTeam: created starterTask", { taskId: task.id, title: st.title });
65206
+ } catch (err) {
65207
+ log51.warn("installTeam: failed to create starterTask", { title: st.title, error: String(err) });
65208
+ }
65209
+ }
65210
+ }
65211
+ }
64623
65212
  return {
64624
65213
  type: "team",
64625
- installed: { team: { id: team.id, name: teamName }, agents: createdAgents }
65214
+ installed: { team: { id: team.id, name: teamName }, agents: createdAgents, starterTaskIds: createdTaskIds }
64626
65215
  };
64627
65216
  }
64628
65217
  /**
@@ -65221,7 +65810,12 @@ var init_sse_handler = __esm({
65221
65810
  replyLength: persistReply.length
65222
65811
  });
65223
65812
  if (this.options.wsBroadcaster) {
65224
- this.options.wsBroadcaster.broadcastChat(this.options.agentId, persistReply, "agent");
65813
+ if (this.options.wsBroadcaster.broadcastProactiveMessage && this.sessionId) {
65814
+ const agentName = this.options.agent.config?.name ?? this.options.agentId;
65815
+ this.options.wsBroadcaster.broadcastProactiveMessage(this.options.agentId, agentName, this.sessionId, `ws_fallback_${Date.now()}`, persistReply, { isMainSession: true }, this.options.senderId);
65816
+ } else {
65817
+ this.options.wsBroadcaster.broadcastChat(this.options.agentId, persistReply, "agent");
65818
+ }
65225
65819
  }
65226
65820
  } else {
65227
65821
  this.sseBuffer.send({
@@ -65943,7 +66537,7 @@ var init_api_server = __esm({
65943
66537
  let channelContext = [];
65944
66538
  if (this.storage) {
65945
66539
  try {
65946
- const recent = await this.storage.channelMessageRepo.getMessages(channelKey, 20);
66540
+ const recent = await this.storage.channelMessageRepo.getMessages(channelKey, 80);
65947
66541
  channelContext = (recent.messages ?? []).map((m) => ({
65948
66542
  role: m.senderType === "agent" ? "assistant" : "user",
65949
66543
  content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
@@ -66036,6 +66630,7 @@ ${cleanText}`,
66036
66630
  setSkillRegistry(registry) {
66037
66631
  this.skillRegistry = registry;
66038
66632
  this.builderService = new BuilderService(this.orgService, registry, (msg) => this.ws?.broadcast(msg));
66633
+ this.builderService.setTaskService(this.taskService);
66039
66634
  }
66040
66635
  getBuilderService() {
66041
66636
  return this.builderService;
@@ -66513,13 +67108,33 @@ ${cleanText}`,
66513
67108
  toolEventCollector: toolEvents,
66514
67109
  waitForReply: isA2A ? true : void 0
66515
67110
  });
67111
+ const emitNoResponse = () => {
67112
+ const evt = {
67113
+ type: "chat:agent_no_response",
67114
+ payload: { channel, agentId: agentId2 },
67115
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
67116
+ };
67117
+ if (channel.startsWith("dm:") || channel.startsWith("notes:")) {
67118
+ const parts = channel.startsWith("notes:") ? [channel.slice(6)] : channel.slice(3).split(":");
67119
+ this.ws.sendToUsers(parts, evt);
67120
+ } else {
67121
+ const humanIds = this.resolveChannelHumanIds(channel);
67122
+ if (humanIds.length > 0)
67123
+ this.ws.sendToUsers(humanIds, evt);
67124
+ else
67125
+ this.ws.broadcast(evt);
67126
+ }
67127
+ };
66516
67128
  if (!reply || !reply.trim() || reply.includes("[NO_RESPONSE]")) {
67129
+ emitNoResponse();
66517
67130
  return;
66518
67131
  }
66519
67132
  const { thinking, clean: rawClean } = extractThinkBlocks(reply);
66520
67133
  const cleanReply = rawClean.replace(/\[NO_RESPONSE\]/gi, "").trim();
66521
- if (!cleanReply)
67134
+ if (!cleanReply) {
67135
+ emitNoResponse();
66522
67136
  return;
67137
+ }
66523
67138
  const metadata = {};
66524
67139
  if (thinking.length > 0)
66525
67140
  metadata["thinking"] = thinking;
@@ -66622,7 +67237,7 @@ ${cleanText}`,
66622
67237
  let channelContext = [];
66623
67238
  if (this.storage) {
66624
67239
  try {
66625
- const recent = await this.storage.channelMessageRepo.getMessages(channel, 20);
67240
+ const recent = await this.storage.channelMessageRepo.getMessages(channel, 80);
66626
67241
  channelContext = (recent.messages ?? []).map((m) => ({
66627
67242
  role: m.senderType === "agent" ? "assistant" : "user",
66628
67243
  content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
@@ -66688,6 +67303,24 @@ ${cleanText}`,
66688
67303
  log56.warn("Failed to persist assistant message", { error: String(err) });
66689
67304
  }
66690
67305
  }
67306
+ triggerSecretaryWelcome(userId2, userName, userRole) {
67307
+ try {
67308
+ const mgr = this.orgService.getAgentManager();
67309
+ const agentList = mgr.listAgents();
67310
+ const secretaryInfo = agentList.find((a) => a.agentRole === "secretary" || a.role?.toLowerCase() === "secretary");
67311
+ if (!secretaryInfo)
67312
+ return;
67313
+ const secretary = mgr.getAgent(secretaryInfo.id);
67314
+ const welcomeMsg = `[SYSTEM] A new team member just joined: "${userName}" (role: ${userRole}, id: ${userId2}). They have completed their account setup. As their Secretary, proactively guide them through the system capabilities. Send them a welcome message using notify_user (target the new user by their id: ${userId2}) explaining what they can do in Markus \u2014 projects, tasks, deliverables, team collaboration, and how to work with AI agents. Help them get started with their first steps.`;
67315
+ secretary.sendMessage(welcomeMsg, userId2, {
67316
+ name: userName,
67317
+ role: userRole,
67318
+ isFirstConversation: true
67319
+ }, { sourceType: "human_chat", scenario: "chat" });
67320
+ } catch (err) {
67321
+ log56.warn("Failed to trigger secretary welcome for new user", { userId: userId2, error: String(err) });
67322
+ }
67323
+ }
66691
67324
  start() {
66692
67325
  this.server = createServer2((req, res) => this.handleRequest(req, res));
66693
67326
  this.ws.attach(this.server);
@@ -66750,6 +67383,68 @@ ${cleanText}`,
66750
67383
  await this.readBody(req);
66751
67384
  }
66752
67385
  }
67386
+ if (path === "/api/auth/status" && req.method === "GET") {
67387
+ if (!this.storage || !this.authEnabled) {
67388
+ this.json(res, 200, { initialized: true });
67389
+ return;
67390
+ }
67391
+ const allUsers = await this.storage.userRepo.listByOrg("default");
67392
+ const hasRealUsers = allUsers.some((u) => u.passwordHash && u.email !== "admin@markus.local");
67393
+ this.json(res, 200, { initialized: hasRealUsers });
67394
+ return;
67395
+ }
67396
+ if (path === "/api/auth/init" && req.method === "POST") {
67397
+ if (!this.storage) {
67398
+ this.json(res, 503, { error: "Storage not available" });
67399
+ return;
67400
+ }
67401
+ const allUsers = await this.storage.userRepo.listByOrg("default");
67402
+ const hasRealUsers = allUsers.some((u) => u.passwordHash && u.email !== "admin@markus.local");
67403
+ if (hasRealUsers) {
67404
+ this.json(res, 403, { error: "System already initialized" });
67405
+ return;
67406
+ }
67407
+ const body = await this.readBody(req);
67408
+ const name = (body["name"] ?? "").trim();
67409
+ const email = (body["email"] ?? "").trim().toLowerCase();
67410
+ const password = body["password"] ?? "";
67411
+ if (!name || !email || !password) {
67412
+ this.json(res, 400, { error: "name, email and password are required" });
67413
+ return;
67414
+ }
67415
+ if (password.length < 6) {
67416
+ this.json(res, 400, { error: "Password must be at least 6 characters" });
67417
+ return;
67418
+ }
67419
+ const hash = await hashPassword(password);
67420
+ const placeholder = allUsers.find((u) => u.role === "owner" && u.email === "admin@markus.local");
67421
+ let userId2;
67422
+ if (placeholder) {
67423
+ userId2 = placeholder.id;
67424
+ this.storage.userRepo.updateProfile(userId2, { name, email });
67425
+ await this.storage.userRepo.updatePassword(userId2, hash);
67426
+ } else {
67427
+ userId2 = userId();
67428
+ await this.storage.userRepo.upsert({
67429
+ id: userId2,
67430
+ orgId: "default",
67431
+ name,
67432
+ email,
67433
+ role: "owner",
67434
+ passwordHash: hash
67435
+ });
67436
+ }
67437
+ this.orgService.syncHumanIdentity(userId2, "default", name, "owner", email);
67438
+ await this.storage.userRepo.updateLastLogin(userId2);
67439
+ const exp = Math.floor(Date.now() / 1e3) + 7 * 24 * 3600;
67440
+ const token = await signToken({ userId: userId2, orgId: "default", role: "owner", exp }, this.jwtSecret);
67441
+ res.setHeader("Set-Cookie", `markus_token=${token}; HttpOnly; SameSite=Strict; Path=/; Max-Age=${7 * 24 * 3600}`);
67442
+ this.json(res, 200, {
67443
+ user: { id: userId2, name, email, role: "owner", orgId: "default" },
67444
+ needsOnboarding: true
67445
+ });
67446
+ return;
67447
+ }
66753
67448
  if (path === "/api/auth/login" && req.method === "POST") {
66754
67449
  const body = await this.readBody(req);
66755
67450
  const email = (body["email"] ?? "").trim().toLowerCase();
@@ -66758,7 +67453,18 @@ ${cleanText}`,
66758
67453
  this.json(res, 200, { user: { id: "anonymous", name: "Admin", role: "owner" } });
66759
67454
  return;
66760
67455
  }
66761
- const userRow = this.storage ? await this.storage.userRepo.findByEmail(email) : null;
67456
+ let userRow = this.storage ? await this.storage.userRepo.findByEmail(email) : null;
67457
+ if (!userRow && this.storage) {
67458
+ const allUsers = await this.storage.userRepo.listByOrg("default");
67459
+ const unclaimedOwner = allUsers.find((u) => u.role === "owner" && !u.lastLoginAt && u.email === "admin@markus.local");
67460
+ if (unclaimedOwner && unclaimedOwner.passwordHash) {
67461
+ const ownerPasswordValid = await verifyPassword(password, unclaimedOwner.passwordHash);
67462
+ if (ownerPasswordValid) {
67463
+ this.storage.userRepo.updateProfile(unclaimedOwner.id, { email });
67464
+ userRow = { ...unclaimedOwner, email };
67465
+ }
67466
+ }
67467
+ }
66762
67468
  if (!userRow || !userRow.passwordHash) {
66763
67469
  this.json(res, 401, { error: "Invalid email or password" });
66764
67470
  return;
@@ -66768,6 +67474,7 @@ ${cleanText}`,
66768
67474
  this.json(res, 401, { error: "Invalid email or password" });
66769
67475
  return;
66770
67476
  }
67477
+ const isFirstLogin = !userRow.lastLoginAt;
66771
67478
  await this.storage.userRepo.updateLastLogin(userRow.id);
66772
67479
  const exp = Math.floor(Date.now() / 1e3) + 7 * 24 * 3600;
66773
67480
  const token = await signToken({ userId: userRow.id, orgId: userRow.orgId, role: userRow.role, exp }, this.jwtSecret);
@@ -66780,7 +67487,8 @@ ${cleanText}`,
66780
67487
  role: userRow.role,
66781
67488
  orgId: userRow.orgId,
66782
67489
  avatarUrl: userRow.avatarUrl ?? void 0
66783
- }
67490
+ },
67491
+ needsOnboarding: isFirstLogin
66784
67492
  });
66785
67493
  return;
66786
67494
  }
@@ -67072,6 +67780,18 @@ ${cleanText}`,
67072
67780
  }
67073
67781
  return;
67074
67782
  }
67783
+ if (path === "/api/sessions/has-any" && req.method === "GET") {
67784
+ const authUser = await this.requireAuth(req, res);
67785
+ if (!authUser)
67786
+ return;
67787
+ if (!this.storage) {
67788
+ this.json(res, 200, { hasAny: false });
67789
+ return;
67790
+ }
67791
+ const hasAny = this.storage.chatSessionRepo.hasAnySessions(authUser.userId);
67792
+ this.json(res, 200, { hasAny });
67793
+ return;
67794
+ }
67075
67795
  if (path.match(/^\/api\/agents\/[^/]+\/sessions$/) && req.method === "GET") {
67076
67796
  const authUser = await this.getAuthUser(req);
67077
67797
  const agentId2 = path.split("/")[3];
@@ -67162,7 +67882,7 @@ ${cleanText}`,
67162
67882
  if (!this.storage)
67163
67883
  return [];
67164
67884
  try {
67165
- const recent = await this.storage.channelMessageRepo.getMessages(channel, 20);
67885
+ const recent = await this.storage.channelMessageRepo.getMessages(channel, 80);
67166
67886
  return (recent.messages ?? []).map((m) => ({
67167
67887
  role: m.senderType === "agent" ? "assistant" : "user",
67168
67888
  content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
@@ -67486,7 +68206,9 @@ ${cleanText}`,
67486
68206
  const fileNames = body["fileNames"]?.filter(Boolean);
67487
68207
  const isRetry = body["isRetry"];
67488
68208
  const isResume = body["isResume"];
67489
- const senderInfo = this.orgService.resolveHumanIdentity(senderId);
68209
+ const baseSenderInfo = this.orgService.resolveHumanIdentity(senderId);
68210
+ const isFirstConversation = this.storage ? !this.storage.chatSessionRepo.hasAnySessions(senderId) : false;
68211
+ const senderInfo = baseSenderInfo ? { ...baseSenderInfo, isFirstConversation } : void 0;
67490
68212
  const agent = this.orgService.getAgentManager().getAgent(agentId2);
67491
68213
  this.ws.broadcastAgentUpdate(agentId2, "working");
67492
68214
  if (!sessionId) {
@@ -67599,9 +68321,15 @@ ${cleanText}`,
67599
68321
  return;
67600
68322
  }
67601
68323
  if (path === "/api/group-chats" && req.method === "GET") {
68324
+ const authUser = await this.requireAuth(req, res);
68325
+ if (!authUser)
68326
+ return;
67602
68327
  const orgId2 = url.searchParams.get("orgId") ?? "default";
68328
+ const userId2 = authUser.userId;
68329
+ const isAdmin = authUser.role === "owner" || authUser.role === "admin";
67603
68330
  const teams = this.orgService.listTeamsWithMembers(orgId2);
67604
- const teamChats = teams.map((t) => ({
68331
+ const filteredTeams = isAdmin ? teams : teams.filter((t) => t.members.some((m) => m.id === userId2));
68332
+ const teamChats = filteredTeams.map((t) => ({
67605
68333
  id: `group:${t.id}`,
67606
68334
  name: t.name,
67607
68335
  type: "team",
@@ -67609,7 +68337,7 @@ ${cleanText}`,
67609
68337
  memberCount: t.members.length,
67610
68338
  channelKey: `group:${t.id}`
67611
68339
  }));
67612
- const customChats = this.storage?.groupChatRepo ? this.storage.groupChatRepo.list(orgId2).map((c) => ({
68340
+ const customChats = this.storage?.groupChatRepo ? (isAdmin ? this.storage.groupChatRepo.list(orgId2) : this.storage.groupChatRepo.listByMember(orgId2, userId2)).map((c) => ({
67613
68341
  id: c.id,
67614
68342
  name: c.name,
67615
68343
  type: "custom",
@@ -71345,6 +72073,50 @@ EXPLANATION_END`;
71345
72073
  this.json(res, 200, { success: read ?? false });
71346
72074
  return;
71347
72075
  }
72076
+ if (path === "/api/activity" && req.method === "GET") {
72077
+ const authUser = await this.requireAuth(req, res);
72078
+ if (!authUser)
72079
+ return;
72080
+ const userId2 = authUser.userId;
72081
+ const limit = Math.min(parseInt(url.searchParams.get("limit") ?? "50", 10), 200);
72082
+ const typeFilter = url.searchParams.get("type") ?? void 0;
72083
+ const items = [];
72084
+ if (!typeFilter || typeFilter === "notification") {
72085
+ const notifications = this.hitlService?.listNotifications(userId2, false, { limit }) ?? [];
72086
+ for (const n of notifications) {
72087
+ items.push({
72088
+ id: n.id,
72089
+ type: n.type,
72090
+ title: n.title,
72091
+ body: n.body,
72092
+ timestamp: n.createdAt,
72093
+ source: "notification",
72094
+ metadata: n.metadata
72095
+ });
72096
+ }
72097
+ }
72098
+ if ((!typeFilter || typeFilter === "task_comment") && this.storage?.taskCommentRepo) {
72099
+ try {
72100
+ const recentComments = this.storage.taskCommentRepo.listRecent?.(limit) ?? [];
72101
+ for (const c of recentComments) {
72102
+ items.push({
72103
+ id: c.id,
72104
+ type: "task_comment",
72105
+ title: `Comment on task ${c.taskId}`,
72106
+ body: typeof c.body === "string" ? c.body.slice(0, 300) : String(c.body ?? ""),
72107
+ timestamp: c.createdAt,
72108
+ source: "task_comment",
72109
+ metadata: { taskId: c.taskId, authorId: c.authorId, authorName: c.authorName }
72110
+ });
72111
+ }
72112
+ } catch {
72113
+ }
72114
+ }
72115
+ items.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
72116
+ const page = items.slice(0, limit);
72117
+ this.json(res, 200, { items: page, totalCount: items.length });
72118
+ return;
72119
+ }
71348
72120
  if (path === "/api/usage" && req.method === "GET") {
71349
72121
  const orgId2 = url.searchParams.get("orgId") ?? "default";
71350
72122
  const plan = this.billingService?.getOrgPlan(orgId2);
@@ -73980,6 +74752,8 @@ EXPLANATION_END`;
73980
74752
  exact("/api/notifications", "GET"),
73981
74753
  exact("/api/notifications/mark-all-read", "POST"),
73982
74754
  startsWith("/api/notifications/", "POST"),
74755
+ // ── Activity feed ─────────────────────────────────────────────────
74756
+ exact("/api/activity", "GET"),
73983
74757
  // ── Users ────────────────────────────────────────────────────────────
73984
74758
  exact("/api/users", "GET", "POST"),
73985
74759
  regex(/^\/api\/users\/[^/]+$/, "PATCH"),
@@ -74595,7 +75369,7 @@ var init_hitl_service = __esm({
74595
75369
  this.notify({
74596
75370
  targetUserId: opts.targetUserId ?? "all",
74597
75371
  type: "approval_request",
74598
- title: `Approval needed: ${opts.title}`,
75372
+ title: opts.title,
74599
75373
  body: opts.description,
74600
75374
  priority: "high",
74601
75375
  actionType: "navigate",
@@ -74612,6 +75386,14 @@ var init_hitl_service = __esm({
74612
75386
  setTimeout(() => {
74613
75387
  if (this.pendingResolvers.has(approval.id)) {
74614
75388
  this.pendingResolvers.delete(approval.id);
75389
+ const a = this.approvals.get(approval.id);
75390
+ if (a && a.status === "pending") {
75391
+ a.status = "expired";
75392
+ a.respondedAt = (/* @__PURE__ */ new Date()).toISOString();
75393
+ a.responseComment = "Approval timed out";
75394
+ this.persistApproval(a);
75395
+ this.markApprovalNotificationsRead(a.id);
75396
+ }
74615
75397
  resolve20({ approved: false, comment: "Approval timed out" });
74616
75398
  }
74617
75399
  }, opts.expiresInMs);
@@ -74639,6 +75421,35 @@ var init_hitl_service = __esm({
74639
75421
  }
74640
75422
  return approval;
74641
75423
  }
75424
+ cancelApproval(id, cancelledBy, comment) {
75425
+ const approval = this.approvals.get(id);
75426
+ if (!approval || approval.status !== "pending")
75427
+ return void 0;
75428
+ approval.status = "cancelled";
75429
+ approval.respondedAt = (/* @__PURE__ */ new Date()).toISOString();
75430
+ approval.respondedBy = cancelledBy;
75431
+ if (comment)
75432
+ approval.responseComment = comment;
75433
+ this.persistApproval(approval);
75434
+ log57.info(`Approval ${id} cancelled by ${cancelledBy}`, { comment });
75435
+ this.markApprovalNotificationsRead(id);
75436
+ const resolve20 = this.pendingResolvers.get(id);
75437
+ if (resolve20) {
75438
+ this.pendingResolvers.delete(id);
75439
+ resolve20({ approved: false, comment: comment ?? "Approval cancelled" });
75440
+ }
75441
+ return approval;
75442
+ }
75443
+ cancelApprovalsByDetail(key2, value, cancelledBy, comment) {
75444
+ let count = 0;
75445
+ for (const approval of this.approvals.values()) {
75446
+ if (approval.status === "pending" && approval.details[key2] === value) {
75447
+ this.cancelApproval(approval.id, cancelledBy, comment);
75448
+ count++;
75449
+ }
75450
+ }
75451
+ return count;
75452
+ }
74642
75453
  persistApproval(approval) {
74643
75454
  if (!this.approvalRepo)
74644
75455
  return;
@@ -75469,13 +76280,14 @@ var init_requirement_service = __esm({
75469
76280
  }
75470
76281
  this.broadcast("requirement:created", req);
75471
76282
  if (this.hitlService && req.source === "agent") {
76283
+ const creatorName = this.resolveAgentName(req.createdBy);
75472
76284
  this.hitlService.requestApprovalAndWait({
75473
76285
  agentId: req.createdBy,
75474
- agentName: req.createdBy,
76286
+ agentName: creatorName,
75475
76287
  type: "custom",
75476
- title: `Requirement approval: ${req.title}`,
75477
- description: `Agent "${req.createdBy}" proposed requirement "${req.title}" (priority: ${req.priority}).`,
75478
- details: { requirementId: req.id, priority: req.priority },
76288
+ title: req.title,
76289
+ description: `Agent "${creatorName}" proposed requirement "${req.title}" (priority: ${req.priority}).`,
76290
+ details: { requirementId: req.id, priority: req.priority, subType: "requirement" },
75479
76291
  targetUserId: "all"
75480
76292
  }).then((result) => {
75481
76293
  const current = this.requirements.get(req.id);
@@ -75611,13 +76423,14 @@ var init_requirement_service = __esm({
75611
76423
  this.broadcast("requirement:resubmitted", req);
75612
76424
  log61.info("Requirement resubmitted for review", { id, hasUpdates: !!updates });
75613
76425
  if (this.hitlService && req.source === "agent") {
76426
+ const creatorName = this.resolveAgentName(req.createdBy);
75614
76427
  this.hitlService.requestApprovalAndWait({
75615
76428
  agentId: req.createdBy,
75616
- agentName: req.createdBy,
76429
+ agentName: creatorName,
75617
76430
  type: "custom",
75618
- title: `Requirement approval (resubmitted): ${req.title}`,
75619
- description: `Agent "${req.createdBy}" resubmitted requirement "${req.title}" (priority: ${req.priority}).`,
75620
- details: { requirementId: req.id, priority: req.priority },
76431
+ title: req.title,
76432
+ description: `Agent "${creatorName}" resubmitted requirement "${req.title}" (priority: ${req.priority}).`,
76433
+ details: { requirementId: req.id, priority: req.priority, subType: "requirement_resubmit" },
75621
76434
  targetUserId: "all"
75622
76435
  }).then((result) => {
75623
76436
  const current = this.requirements.get(req.id);
@@ -75791,6 +76604,9 @@ var init_requirement_service = __esm({
75791
76604
  const req = this.requirements.get(id);
75792
76605
  if (!req)
75793
76606
  throw new Error(`Requirement ${id} not found`);
76607
+ if (this.hitlService) {
76608
+ this.hitlService.cancelApprovalsByDetail("requirementId", id, cancelledBy ?? "system", "Requirement cancelled");
76609
+ }
75794
76610
  const oldStatus = req.status;
75795
76611
  req.status = "cancelled";
75796
76612
  req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -75884,6 +76700,9 @@ var init_requirement_service = __esm({
75884
76700
  }
75885
76701
  }
75886
76702
  deleteRequirement(id) {
76703
+ if (this.hitlService) {
76704
+ this.hitlService.cancelApprovalsByDetail("requirementId", id, "system", "Requirement deleted");
76705
+ }
75887
76706
  this.requirements.delete(id);
75888
76707
  if (this.requirementRepo) {
75889
76708
  this.requirementRepo.delete(id).catch((e) => log61.error("Failed to delete requirement from storage", { id, error: String(e) }));
@@ -75955,6 +76774,17 @@ var init_requirement_service = __esm({
75955
76774
  });
75956
76775
  }
75957
76776
  }
76777
+ resolveAgentName(agentId2) {
76778
+ if (this.agentManager) {
76779
+ try {
76780
+ const agent = this.agentManager.getAgent(agentId2);
76781
+ if (agent)
76782
+ return agent.config?.name ?? agent.name ?? agentId2;
76783
+ } catch {
76784
+ }
76785
+ }
76786
+ return agentId2;
76787
+ }
75958
76788
  broadcast(type, data) {
75959
76789
  if (this.ws) {
75960
76790
  this.ws.broadcast({
@@ -77106,13 +77936,19 @@ var init_stale_detector = __esm({
77106
77936
  taskService;
77107
77937
  config;
77108
77938
  scanInterval;
77109
- constructor(taskService, config) {
77939
+ onStaleItems;
77940
+ constructor(taskService, config, onStaleItems) {
77110
77941
  this.taskService = taskService;
77111
77942
  this.config = { ...DEFAULT_CONFIG4, ...config };
77943
+ this.onStaleItems = onStaleItems;
77112
77944
  }
77113
77945
  start(intervalMs = 36e5) {
77114
77946
  this.scanInterval = setInterval(() => {
77115
- this.scan().catch((err) => log68.warn("Stale scan failed", { error: String(err) }));
77947
+ this.scan().then((items) => {
77948
+ if (items.length > 0 && this.onStaleItems) {
77949
+ this.onStaleItems(items);
77950
+ }
77951
+ }).catch((err) => log68.warn("Stale scan failed", { error: String(err) }));
77116
77952
  }, intervalMs);
77117
77953
  log68.info("Stale detector started", { intervalMs });
77118
77954
  }
@@ -77208,7 +78044,7 @@ var init_scheduled_task_runner = __esm({
77208
78044
  if (!task.scheduleConfig)
77209
78045
  continue;
77210
78046
  const config = task.scheduleConfig;
77211
- if (config.maxRuns !== void 0 && (config.currentRuns ?? 0) >= config.maxRuns) {
78047
+ if (config.maxRuns && config.maxRuns > 0 && (config.currentRuns ?? 0) >= config.maxRuns) {
77212
78048
  continue;
77213
78049
  }
77214
78050
  const nextRun = config.nextRunAt ? new Date(config.nextRunAt).getTime() : 0;
@@ -78717,6 +79553,10 @@ CREATE INDEX IF NOT EXISTS idx_st_entity ON status_transitions(entity_type, enti
78717
79553
  }
78718
79554
  return this.db.prepare("SELECT * FROM chat_sessions WHERE agent_id = ? ORDER BY is_main DESC, last_message_at DESC LIMIT ?").all(agentId2, limit).map((r) => this._mapSession(r));
78719
79555
  }
79556
+ hasAnySessions(userId2) {
79557
+ const r = this.db.prepare("SELECT 1 FROM chat_sessions WHERE user_id = ? LIMIT 1").get(userId2);
79558
+ return r !== null && r !== void 0;
79559
+ }
78720
79560
  getSession(sessionId) {
78721
79561
  const r = this.db.prepare("SELECT * FROM chat_sessions WHERE id = ?").get(sessionId);
78722
79562
  return r ? this._mapSession(r) : null;
@@ -80187,6 +81027,17 @@ CREATE INDEX IF NOT EXISTS idx_st_entity ON status_transitions(entity_type, enti
80187
81027
  memberCount: r["member_count"]
80188
81028
  }));
80189
81029
  }
81030
+ listByMember(orgId2, memberId) {
81031
+ const rows = this.db.prepare(`SELECT g.*, (SELECT COUNT(*) FROM group_chat_members WHERE group_chat_id = g.id) AS member_count
81032
+ FROM group_chats g
81033
+ INNER JOIN group_chat_members m ON m.group_chat_id = g.id
81034
+ WHERE g.org_id = ? AND m.member_id = ?
81035
+ ORDER BY g.created_at DESC`).all(orgId2, memberId);
81036
+ return rows.map((r) => ({
81037
+ ...this.mapRow(r),
81038
+ memberCount: r["member_count"]
81039
+ }));
81040
+ }
80190
81041
  getById(id) {
80191
81042
  const r = this.db.prepare("SELECT * FROM group_chats WHERE id = ?").get(id);
80192
81043
  if (!r)
@@ -82081,10 +82932,10 @@ function registerStartCommand(program2) {
82081
82932
  const configPath = globalOpts.config ?? getDefaultConfigPath();
82082
82933
  if (opts.setup || !existsSync33(configPath)) {
82083
82934
  if (!existsSync33(configPath)) {
82084
- console.log(" No configuration found \u2014 running first-time setup...\n");
82935
+ console.log(" No configuration found \u2014 auto-configuring from environment...\n");
82085
82936
  }
82086
82937
  const { quickInit: quickInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
82087
- await quickInit2();
82938
+ await quickInit2({ nonInteractive: true });
82088
82939
  }
82089
82940
  const config = loadConfig(globalOpts.config);
82090
82941
  await startServer(config, { port: globalOpts.port, config: globalOpts.config });
@@ -82327,7 +83178,8 @@ async function startServer(config, values) {
82327
83178
  startupLog("INFO", " 2. \u4EA4\u4E92\u5F0F\u914D\u7F6E \u2192 \u6B63\u5728\u542F\u52A8\u5411\u5BFC...");
82328
83179
  startupBlank();
82329
83180
  const { quickInit: quickInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
82330
- await quickInit2();
83181
+ startupLog("INFO", "\u81EA\u52A8\u4ECE\u73AF\u5883\u53D8\u91CF/\u5DF2\u77E5\u914D\u7F6E\u5BFC\u5165 LLM \u8BBE\u7F6E...");
83182
+ await quickInit2({ nonInteractive: true });
82331
83183
  const { loadConfig: reloadConfig } = await Promise.resolve().then(() => (init_dist(), dist_exports));
82332
83184
  const updatedConfig = reloadConfig(values["config"]);
82333
83185
  for (const [name, cfg] of Object.entries(updatedConfig.llm?.providers ?? {})) {
@@ -82442,6 +83294,21 @@ async function startServer(config, values) {
82442
83294
  const archiveService = new ArchiveService(taskService, projectService);
82443
83295
  archiveService.setRequirementService(requirementService);
82444
83296
  archiveService.start();
83297
+ const staleDetector = new StaleDetector(taskService, void 0, (items) => {
83298
+ for (const item of items) {
83299
+ hitlService.notify({
83300
+ targetUserId: "all",
83301
+ type: "system",
83302
+ title: item.type === "review_stale" ? "Stale review" : item.type === "stuck_task" ? "Stuck task" : "Unstarted task",
83303
+ body: item.message,
83304
+ priority: item.type === "review_stale" ? "high" : "normal",
83305
+ actionType: item.taskId ? "navigate" : "none",
83306
+ actionTarget: item.taskId ? JSON.stringify({ path: `/work?openTask=${item.taskId}` }) : void 0,
83307
+ metadata: { taskId: item.taskId, agentId: item.agentId, staleType: item.type }
83308
+ });
83309
+ }
83310
+ });
83311
+ staleDetector.start();
82445
83312
  apiServer.setLLMRouter(llmRouter);
82446
83313
  apiServer.setConfigPath(values["config"] ?? getDefaultConfigPath());
82447
83314
  if (config.hub?.url) apiServer.setHubUrl(config.hub.url);
@@ -82475,6 +83342,10 @@ async function startServer(config, values) {
82475
83342
  orgService.registerBuilderContextProviders(skillRegistry);
82476
83343
  const builderService = apiServer.getBuilderService();
82477
83344
  if (builderService) {
83345
+ const builtinTeamsDir = resolveTemplatesDir("teams");
83346
+ if (builtinTeamsDir && existsSync33(builtinTeamsDir)) {
83347
+ builderService.setBuiltinTeamTemplatesDir(builtinTeamsDir);
83348
+ }
82478
83349
  agentManager.setBuilderService(builderService);
82479
83350
  }
82480
83351
  const hubClient = apiServer.getHubClient();
@@ -82506,6 +83377,7 @@ async function startServer(config, values) {
82506
83377
  return { installed: result.installed, name: result.name, method: result.method };
82507
83378
  });
82508
83379
  agentManager.setUserApprovalRequester(async (opts) => {
83380
+ const taskTitle = opts.relatedTaskId ? taskService.getTask(opts.relatedTaskId)?.title : void 0;
82509
83381
  return hitlService.requestApprovalAndWait({
82510
83382
  agentId: opts.agentId,
82511
83383
  agentName: opts.agentName,
@@ -82515,7 +83387,7 @@ async function startServer(config, values) {
82515
83387
  targetUserId: ownerUserId,
82516
83388
  options: opts.options,
82517
83389
  allowFreeform: opts.allowFreeform,
82518
- details: { priority: opts.priority, taskId: opts.relatedTaskId }
83390
+ details: { priority: opts.priority, taskId: opts.relatedTaskId, taskTitle }
82519
83391
  });
82520
83392
  });
82521
83393
  agentManager.setUserNotifier((opts) => {
@@ -82591,7 +83463,8 @@ async function startServer(config, values) {
82591
83463
  agentManager.getEventBus().on("agent:notify-user", async (evt) => {
82592
83464
  const { agentId: agentId2, title, body, priority, taskId: taskId2, requirementId: requirementId2, targetUserId } = evt;
82593
83465
  try {
82594
- const mainSession = storage.chatSessionRepo.getOrCreateMainSession(agentId2, defaultSessionUserId);
83466
+ const sessionUserId = targetUserId || defaultSessionUserId;
83467
+ const mainSession = storage.chatSessionRepo.getOrCreateMainSession(agentId2, sessionUserId);
82595
83468
  const agent = agentManager.getAgent(agentId2);
82596
83469
  const formattedMsg = `**${title}**
82597
83470
 
@@ -82607,7 +83480,7 @@ ${body}`;
82607
83480
  storage.chatSessionRepo.updateLastMessage(mainSession.id);
82608
83481
  ws.broadcastProactiveMessage(agentId2, agent.config.name, mainSession.id, msg.id, formattedMsg, {
82609
83482
  isMainSession: true
82610
- }, defaultSessionUserId);
83483
+ }, sessionUserId);
82611
83484
  const hasTask = !!taskId2;
82612
83485
  hitlService.notify({
82613
83486
  targetUserId: targetUserId ?? "all",
@@ -82771,13 +83644,14 @@ ${reason}`;
82771
83644
  const reasonMatch = request.reason.match(/requires approval:\s*(.+?)\.?\s*Command:/);
82772
83645
  title = reasonMatch ? `Git: ${reasonMatch[1]}` : "Shell: command approval";
82773
83646
  }
83647
+ const taskTitle = request.taskId ? taskService.getTask(request.taskId)?.title : void 0;
82774
83648
  const result = await hitlService.requestApprovalAndWait({
82775
83649
  agentId: agentId2,
82776
83650
  agentName,
82777
83651
  type: "action",
82778
83652
  title,
82779
83653
  description: request.reason,
82780
- details: { ...request.toolArgs, toolName: request.toolName, agentId: agentId2, taskId: request.taskId },
83654
+ details: { ...request.toolArgs, toolName: request.toolName, agentId: agentId2, taskId: request.taskId, taskTitle },
82781
83655
  targetUserId: ownerUserId
82782
83656
  });
82783
83657
  auditService.record({
@@ -83278,6 +84152,7 @@ ${reason}`;
83278
84152
  closeStartupLogger();
83279
84153
  closeRuntimeLogger();
83280
84154
  archiveService.stop();
84155
+ staleDetector.stop();
83281
84156
  scheduledTaskRunner.stop();
83282
84157
  apiServer.stop();
83283
84158
  agentManager.shutdown().then(() => messageRouter.disconnectAll()).then(() => process.exit(0)).catch(() => process.exit(1));