@markus-global/cli 0.6.4 → 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.
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
+ }
6388
6395
  }
6389
- parts.push(this.buildIdentitySection(opts));
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.");
6439
+ }
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,91 +6496,78 @@ 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.");
6476
6518
  }
6477
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
+ }
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}`);
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) {
@@ -6551,111 +6580,102 @@ var init_context_engine = __esm({
6551
6580
  const myDone = myTasks.filter((t) => CLOSED_STATUSES.has(t.status));
6552
6581
  const MY_TASK_LIMIT = SYSTEM_MY_TASKS_MAX;
6553
6582
  const TEAM_TASK_LIMIT = SYSTEM_TEAM_TASKS_MAX;
6554
- parts.push("\n## Task Board");
6555
- parts.push("### My Tasks (assigned to you):");
6583
+ dynamic.push("\n## Task Board");
6584
+ dynamic.push("### My Tasks (assigned to you):");
6556
6585
  if (myActive.length > 0) {
6557
6586
  const shown = myActive.slice(0, MY_TASK_LIMIT);
6558
6587
  for (const t of shown) {
6559
- 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})`);
6560
6589
  if (t.description)
6561
- parts.push(` ${t.description.slice(0, SYSTEM_TASK_DESC_CHARS)}`);
6590
+ dynamic.push(` ${t.description.slice(0, SYSTEM_TASK_DESC_CHARS)}`);
6562
6591
  }
6563
6592
  if (myActive.length > MY_TASK_LIMIT) {
6564
- 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)_`);
6565
6594
  }
6566
6595
  } else {
6567
- parts.push("No active tasks assigned to you.");
6596
+ dynamic.push("No active tasks assigned to you.");
6568
6597
  }
6569
6598
  if (myDone.length > 0) {
6570
- parts.push(`_(${myDone.length} completed/closed tasks)_`);
6599
+ dynamic.push(`_(${myDone.length} completed/closed tasks)_`);
6571
6600
  }
6572
6601
  if (otherTasks.length > 0) {
6573
6602
  const otherActive = otherTasks.filter((t) => !CLOSED_STATUSES.has(t.status)).sort(byPriority);
6574
6603
  const otherDone = otherTasks.filter((t) => CLOSED_STATUSES.has(t.status));
6575
6604
  if (otherActive.length > 0) {
6576
- parts.push("### Team Tasks (assigned to others):");
6605
+ dynamic.push("### Team Tasks (assigned to others):");
6577
6606
  const shown = otherActive.slice(0, TEAM_TASK_LIMIT);
6578
6607
  for (const t of shown) {
6579
6608
  const owner = t.assignedAgentName ?? t.assignedAgentId ?? "unassigned";
6580
- 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})`);
6581
6610
  }
6582
6611
  if (otherActive.length > TEAM_TASK_LIMIT) {
6583
- 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)_`);
6584
6613
  }
6585
6614
  }
6586
6615
  if (otherDone.length > 0) {
6587
- parts.push(`_(${otherDone.length} other completed/closed tasks)_`);
6616
+ dynamic.push(`_(${otherDone.length} other completed/closed tasks)_`);
6588
6617
  }
6589
6618
  }
6590
6619
  } else {
6591
- parts.push("\n## Task Board");
6592
- parts.push("No tasks on the board.");
6620
+ dynamic.push("\n## Task Board");
6621
+ dynamic.push("No tasks on the board.");
6593
6622
  }
6594
- parts.push("");
6595
- parts.push("### Task & Requirement Workflow");
6596
- parts.push("");
6597
- parts.push("**Requirements** (governance gate):");
6598
- parts.push("- `requirement_propose` \u2192 pending human approval \u2192 approved \u2192 link tasks via `requirement_id`");
6599
- parts.push("- Every task MUST reference an approved `requirement_id`. Use `requirement_propose` first if no requirement exists.");
6600
- parts.push("");
6601
- parts.push("**Task lifecycle** \u2014 Create \u2192 Execute \u2192 Review \u2192 Complete:");
6602
- parts.push('- **Create**: `task_create` (REQUIRED: `assigned_agent_id`, `reviewer_id`; optional `reviewer_type`: "agent"|"human"). Check `task_list` first to avoid duplicates.');
6603
- 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`.");
6604
- 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.');
6605
- parts.push('- **Blockers**: Use `task_update(status:"blocked", note:"reason")` when unable to proceed.');
6606
- parts.push("");
6607
- parts.push("**Dependencies & DAG decomposition**:");
6608
- 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.");
6609
- 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.");
6610
- parts.push("- If consolidated output is needed, create a final synthesis task assigned to a manager, `blocked_by` ALL prerequisites.");
6611
- parts.push("");
6612
- 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.");
6613
- parts.push("");
6614
- parts.push("**Automatic status notifications** (do NOT duplicate manually):");
6615
- parts.push("- When task status changes, the system **automatically** handles all side effects: execution start/cancel, reviewer notification, dependency unblocking.");
6616
- parts.push("- Task status notifications are placed in assignees' mailboxes as **informational context only**.");
6617
- 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.");
6618
- parts.push("");
6619
- parts.push("**Communicating with humans**:");
6620
- 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.).");
6621
- 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.");
6622
- 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).");
6623
- parts.push("");
6624
- parts.push("**Communicating with other agents**:");
6625
- parts.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).");
6626
- parts.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.");
6627
- parts.push("- For substantial work requests, create a `task_create` assigned to the target agent instead of asking via message.");
6628
- parts.push("- Do NOT use A2A messages for routine task status notifications \u2014 the system handles those automatically.");
6629
6623
  }
6630
- if (opts.environment) {
6631
- 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));
6632
6654
  }
6633
6655
  if (!isDream && opts.senderIdentity) {
6634
- parts.push(`
6656
+ dynamic.push(`
6635
6657
  ## Current Conversation`);
6636
- 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
+ }
6637
6662
  if (opts.senderIdentity.role === "owner") {
6638
- 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.");
6639
6664
  } else if (opts.senderIdentity.role === "admin") {
6640
- 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.");
6641
6666
  } else if (opts.senderIdentity.role === "guest") {
6642
- 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.");
6643
6668
  }
6644
6669
  }
6645
6670
  if (!isDream) {
6646
- parts.push("\n## Tool Usage Rules");
6647
- 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.");
6648
- 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.");
6649
- 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.");
6650
- 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.");
6651
- 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.");
6652
- 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.');
6653
- }
6654
- if (!isDream && opts.mailboxContext) {
6655
- parts.push(this.buildMailboxSection(opts.mailboxContext));
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.');
6656
6678
  }
6657
- const scenario = opts.scenario ?? "chat";
6658
- parts.push(this.buildScenarioSection(scenario, { a2aWaitForReply: opts.a2aWaitForReply }));
6659
6679
  const now2 = /* @__PURE__ */ new Date();
6660
6680
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
6661
6681
  const offset = now2.getTimezoneOffset();
@@ -6664,10 +6684,23 @@ var init_context_engine = __esm({
6664
6684
  const absM = String(Math.abs(offset) % 60).padStart(2, "0");
6665
6685
  const pad = (n) => String(n).padStart(2, "0");
6666
6686
  const localStr = `${now2.getFullYear()}-${pad(now2.getMonth() + 1)}-${pad(now2.getDate())} ${pad(now2.getHours())}:${pad(now2.getMinutes())}:${pad(now2.getSeconds())}`;
6667
- parts.push(`
6687
+ dynamic.push(`
6668
6688
  ---
6669
6689
  Current date and time: ${localStr} (${tz}, UTC${sign}${absH}:${absM})`);
6670
- 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
+ };
6671
6704
  }
6672
6705
  buildMailboxSection(ctx) {
6673
6706
  const lines = ["\n## Your Attention State"];
@@ -6801,11 +6834,17 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
6801
6834
  lines.push('- Give a generic acknowledgment like "Got it, will look into it" without substantive content');
6802
6835
  lines.push("- Ignore prior comments that provide important context for the current discussion");
6803
6836
  lines.push("");
6804
- lines.push("**Conversation termination \u2014 when NOT to reply:**");
6805
- lines.push('- The comment is just an acknowledgment ("Got it", "Will do", "Thanks", "Agreed") \u2014 do NOT reply');
6806
- lines.push("- Both parties have reached agreement or the discussion is resolved \u2014 do NOT reply");
6807
- lines.push('- Your reply would only be "Sounds good", "Agreed", or similar zero-information response \u2014 do NOT reply');
6808
- 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");
6809
6848
  lines.push("- **Principle**: only comment when your reply adds **new information** or requests a **decision**. Avoid comment ping-pong.");
6810
6849
  break;
6811
6850
  case "review":
@@ -6919,8 +6958,7 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
6919
6958
  lines.push("6. **Hiring & Team Building** \u2014 Two phases: CREATE then INSTALL (only when user requests).");
6920
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.");
6921
6960
  lines.push(" b) *Installing* (deploy into org \u2014 ONLY when user explicitly asks to install/deploy/hire):");
6922
- lines.push(" - Quick hire from template: `team_list_templates` \u2192 `team_hire_agent`");
6923
- 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)");
6924
6962
  lines.push(" - Hub one-step: `hub_install` (download + install)");
6925
6963
  lines.push(" c) After install: onboard via `agent_send_message` (project context) \u2192 `task_create` (initial work)");
6926
6964
  lines.push(" **IMPORTANT**: NEVER auto-install. Creating an artifact does NOT mean deploying it. Wait for explicit user request.");
@@ -7051,7 +7089,8 @@ ${ctx.mergedContent.slice(0, SYSTEM_MAILBOX_MERGED_CHARS)}`);
7051
7089
  totalUsed,
7052
7090
  available,
7053
7091
  usagePercent: Math.round(usagePercent * 10) / 10
7054
- }
7092
+ },
7093
+ systemCacheSegments: opts.systemCacheSegments
7055
7094
  };
7056
7095
  }
7057
7096
  /**
@@ -8215,7 +8254,7 @@ var init_tool_selector = __esm({
8215
8254
  "\u5206\u914D",
8216
8255
  "\u8DEF\u7531"
8217
8256
  ],
8218
- 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"]
8219
8258
  },
8220
8259
  {
8221
8260
  name: "deliverables",
@@ -8245,6 +8284,21 @@ var init_tool_selector = __esm({
8245
8284
  "\u7ECF\u9A8C"
8246
8285
  ],
8247
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"]
8248
8302
  }
8249
8303
  ];
8250
8304
  BASE_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -41282,6 +41336,45 @@ async function searchBrave(query2, maxResults) {
41282
41336
  date: r.page_age
41283
41337
  }));
41284
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
+ }
41285
41378
  async function searchDuckDuckGo(query2, maxResults) {
41286
41379
  const encoded = encodeURIComponent(query2);
41287
41380
  let lastError;
@@ -41370,7 +41463,7 @@ function parseDDGHtml(html, maxResults) {
41370
41463
  function stripHtml(html) {
41371
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();
41372
41465
  }
41373
- var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, DDG_UA, DDG_ENDPOINTS;
41466
+ var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, BING_UA, DDG_UA, DDG_ENDPOINTS;
41374
41467
  var init_web_search = __esm({
41375
41468
  "../core/dist/tools/web-search.js"() {
41376
41469
  "use strict";
@@ -41399,6 +41492,7 @@ var init_web_search = __esm({
41399
41492
  const backends = [
41400
41493
  { name: "Serper", fn: searchSerper },
41401
41494
  { name: "Brave", fn: searchBrave },
41495
+ { name: "Bing", fn: searchBing },
41402
41496
  { name: "DuckDuckGo", fn: searchDuckDuckGo }
41403
41497
  ];
41404
41498
  const errors = [];
@@ -41430,6 +41524,7 @@ var init_web_search = __esm({
41430
41524
  });
41431
41525
  }
41432
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";
41433
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";
41434
41529
  DDG_ENDPOINTS = [
41435
41530
  "https://lite.duckduckgo.com/lite/",
@@ -45502,6 +45597,7 @@ ${notification.stdoutTail}`);
45502
45597
  senderId,
45503
45598
  senderName: senderInfo?.name,
45504
45599
  senderRole: senderInfo?.role,
45600
+ isFirstConversation: senderInfo?.isFirstConversation,
45505
45601
  responsePromise: { resolve: resolve20, reject }
45506
45602
  }
45507
45603
  });
@@ -45531,6 +45627,7 @@ ${notification.stdoutTail}`);
45531
45627
  senderId,
45532
45628
  senderName: senderInfo?.name,
45533
45629
  senderRole: senderInfo?.role,
45630
+ isFirstConversation: senderInfo?.isFirstConversation,
45534
45631
  responsePromise: { resolve: resolve20, reject }
45535
45632
  }
45536
45633
  });
@@ -45677,7 +45774,7 @@ ${notification.stdoutTail}`);
45677
45774
  this.currentInteractingUserId = item.metadata.senderId;
45678
45775
  }
45679
45776
  const extra = item.payload.extra ?? {};
45680
- 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;
45681
45778
  const resolveResponse = (reply) => {
45682
45779
  if (typeof item.metadata?.responsePromise?.resolve === "function") {
45683
45780
  item.metadata.responsePromise.resolve(stripCompletionMarker(reply));
@@ -46412,6 +46509,9 @@ ${conversationText}`
46412
46509
  setIdentityContext(ctx) {
46413
46510
  this.identityContext = ctx;
46414
46511
  }
46512
+ getTeamName() {
46513
+ return this.identityContext?.team?.name;
46514
+ }
46415
46515
  addDynamicContextProvider(provider, key2) {
46416
46516
  const providerKey = key2 ?? `provider_${this.dynamicContextProviders.size}`;
46417
46517
  this.dynamicContextProviders.set(providerKey, provider);
@@ -46944,7 +47044,7 @@ ${block}
46944
47044
  await counter.ensureReady();
46945
47045
  }
46946
47046
  const cognitiveContext = await this.prepareCognitiveContext(scenario, effectiveMessage, senderId);
46947
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47047
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
46948
47048
  agentId: this.id,
46949
47049
  agentName: this.config.name,
46950
47050
  role: this.role,
@@ -46987,7 +47087,8 @@ ${block}
46987
47087
  agentId: this.id,
46988
47088
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
46989
47089
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
46990
- toolDefinitions: llmTools
47090
+ toolDefinitions: llmTools,
47091
+ systemCacheSegments
46991
47092
  });
46992
47093
  const messages = prepared.messages;
46993
47094
  log17.debug("Context usage for chat", { usagePercent: prepared.usage.usagePercent, totalUsed: prepared.usage.totalUsed });
@@ -47000,7 +47101,8 @@ ${block}
47000
47101
  messages,
47001
47102
  tools: llmTools.length > 0 ? llmTools : void 0,
47002
47103
  metadata: this.getLLMMetadata(sessionId),
47003
- compaction: useCompaction
47104
+ compaction: useCompaction,
47105
+ systemCacheSegments
47004
47106
  }, this.getEffectiveProvider()), "Chat LLM call");
47005
47107
  const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
47006
47108
  this.updateTokensUsed(tokensThisCall);
@@ -47014,6 +47116,7 @@ ${block}
47014
47116
  });
47015
47117
  let toolIterations = 0;
47016
47118
  const effectiveMaxIter = options?.maxToolIterations ?? this._maxToolIterations;
47119
+ const commentToolUsed = /* @__PURE__ */ new Set();
47017
47120
  while (response.finishReason === "tool_use" && response.toolCalls?.length || response.finishReason === "max_tokens") {
47018
47121
  if (++toolIterations > effectiveMaxIter) {
47019
47122
  log17.warn("Tool loop hit max iterations", {
@@ -47111,6 +47214,9 @@ ${block}
47111
47214
  for (let i = 0; i < response.toolCalls.length; i++) {
47112
47215
  const tc = response.toolCalls[i];
47113
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
+ }
47114
47220
  }
47115
47221
  const loopCheck = this.loopDetector.check();
47116
47222
  if (loopCheck.detected) {
@@ -47160,7 +47266,8 @@ ${chatYield.item.payload.content}`;
47160
47266
  agentId: this.id,
47161
47267
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47162
47268
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47163
- toolDefinitions: llmTools
47269
+ toolDefinitions: llmTools,
47270
+ systemCacheSegments
47164
47271
  });
47165
47272
  const updatedMessages = prepared2.messages;
47166
47273
  const llmStart2 = Date.now();
@@ -47168,7 +47275,8 @@ ${chatYield.item.payload.content}`;
47168
47275
  messages: updatedMessages,
47169
47276
  tools: llmTools.length > 0 ? llmTools : void 0,
47170
47277
  metadata: this.getLLMMetadata(sessionId),
47171
- compaction: useCompaction
47278
+ compaction: useCompaction,
47279
+ systemCacheSegments
47172
47280
  }, this.getEffectiveProvider()), "Chat LLM continuation");
47173
47281
  const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
47174
47282
  this.updateTokensUsed(tokens2);
@@ -47181,6 +47289,93 @@ ${chatYield.item.payload.content}`;
47181
47289
  success: true
47182
47290
  });
47183
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
+ }
47184
47379
  const rawReply = sanitizeLLMReply(response.content);
47185
47380
  const displayReply = stripCompletionMarker(rawReply);
47186
47381
  const outputCheck = await this.guardrails.checkOutput(displayReply, { agentId: this.id });
@@ -47266,7 +47461,7 @@ ${chatYield.item.payload.content}`;
47266
47461
  const userContent = await this.buildUserContent(userMessage, images, fileNames);
47267
47462
  this.memory.appendMessage(this.currentSessionId, { role: "user", content: userContent });
47268
47463
  const cognitiveContext = await this.prepareCognitiveContext("chat", effectiveMessage, senderId);
47269
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47464
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
47270
47465
  agentId: this.id,
47271
47466
  agentName: this.config.name,
47272
47467
  role: this.role,
@@ -47302,7 +47497,8 @@ ${chatYield.item.payload.content}`;
47302
47497
  agentId: this.id,
47303
47498
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47304
47499
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47305
- toolDefinitions: llmTools
47500
+ toolDefinitions: llmTools,
47501
+ systemCacheSegments
47306
47502
  });
47307
47503
  const messages = preparedStream.messages;
47308
47504
  log17.debug("Context usage for stream", { usagePercent: preparedStream.usage.usagePercent });
@@ -47338,7 +47534,7 @@ ${chatYield.item.payload.content}`;
47338
47534
  try {
47339
47535
  this.checkDailyTokenBudget();
47340
47536
  const llmStart = Date.now();
47341
- 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);
47342
47538
  streamMarkerDelta.flush();
47343
47539
  const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
47344
47540
  this.updateTokensUsed(tokensThisCall);
@@ -47496,7 +47692,8 @@ ${streamYield.item.payload.content}`;
47496
47692
  agentId: this.id,
47497
47693
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47498
47694
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47499
- toolDefinitions: llmTools
47695
+ toolDefinitions: llmTools,
47696
+ systemCacheSegments
47500
47697
  });
47501
47698
  const updatedMessages = preparedCont.messages;
47502
47699
  if (cancelToken?.cancelled) {
@@ -47515,7 +47712,7 @@ ${streamYield.item.payload.content}`;
47515
47712
  return lastResponseContent || "";
47516
47713
  }
47517
47714
  const llmStart2 = Date.now();
47518
- 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);
47519
47716
  streamMarkerDelta.flush();
47520
47717
  const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
47521
47718
  this.updateTokensUsed(tokens2);
@@ -47753,7 +47950,7 @@ ${streamYield.item.payload.content}`;
47753
47950
  this.memory.appendMessage(sessionId, { role: "user", content: taskPrompt });
47754
47951
  }
47755
47952
  const cognitiveContext = await this.prepareCognitiveContext("task_execution", taskPrompt);
47756
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
47953
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
47757
47954
  agentId: this.id,
47758
47955
  agentName: this.config.name,
47759
47956
  role: this.role,
@@ -47820,12 +48017,13 @@ ${streamYield.item.payload.content}`;
47820
48017
  agentId: this.id,
47821
48018
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
47822
48019
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
47823
- toolDefinitions: llmTools
48020
+ toolDefinitions: llmTools,
48021
+ systemCacheSegments
47824
48022
  });
47825
48023
  const messages = preparedTask.messages;
47826
48024
  log17.debug("Context usage for task execution", { taskId: taskId2, usagePercent: preparedTask.usage.usagePercent, totalUsed: preparedTask.usage.totalUsed });
47827
48025
  let taskLlmStart = Date.now();
47828
- 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);
47829
48027
  let taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
47830
48028
  this.updateTokensUsed(taskLlmTokens);
47831
48029
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48007,14 +48205,16 @@ ${yieldResult.item.payload.content}`
48007
48205
  agentId: this.id,
48008
48206
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48009
48207
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48010
- toolDefinitions: llmTools
48208
+ toolDefinitions: llmTools,
48209
+ systemCacheSegments
48011
48210
  });
48012
48211
  taskLlmStart = Date.now();
48013
48212
  response = await this.withNetworkRetry(() => this.llmRouter.chatStream({
48014
48213
  messages: preparedTaskCont.messages,
48015
48214
  tools: llmTools.length > 0 ? llmTools : void 0,
48016
48215
  metadata: this.getLLMMetadata(sessionId),
48017
- compaction: useCompaction
48216
+ compaction: useCompaction,
48217
+ systemCacheSegments
48018
48218
  }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution LLM continuation", abortController.signal);
48019
48219
  taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
48020
48220
  this.updateTokensUsed(taskLlmTokens);
@@ -48056,14 +48256,16 @@ ${yieldResult.item.payload.content}`
48056
48256
  agentId: this.id,
48057
48257
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48058
48258
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48059
- toolDefinitions: llmTools
48259
+ toolDefinitions: llmTools,
48260
+ systemCacheSegments
48060
48261
  });
48061
48262
  taskLlmStart = Date.now();
48062
48263
  response = await this.withNetworkRetry(() => this.llmRouter.chatStream({
48063
48264
  messages: preparedFinal.messages,
48064
48265
  tools: llmTools.length > 0 ? llmTools : void 0,
48065
48266
  metadata: this.getLLMMetadata(sessionId),
48066
- compaction: useCompaction
48267
+ compaction: useCompaction,
48268
+ systemCacheSegments
48067
48269
  }, handleStreamEvent, this.getEffectiveProvider(), abortController.signal), "Task execution final submit reminder", abortController.signal);
48068
48270
  taskLlmTokens = response.usage.inputTokens + response.usage.outputTokens;
48069
48271
  this.updateTokensUsed(taskLlmTokens);
@@ -48187,7 +48389,7 @@ ${yieldResult.item.payload.content}`
48187
48389
  this.memory.getOrCreateSession(this.id, sessionId);
48188
48390
  this.memory.appendMessage(sessionId, { role: "user", content: userMessage });
48189
48391
  const cognitiveContext = await this.prepareCognitiveContext("chat", userMessage);
48190
- const systemPrompt = await this.contextEngine.buildSystemPrompt({
48392
+ const { text: systemPrompt, segments: systemCacheSegments } = await this.contextEngine.buildSystemPrompt({
48191
48393
  agentId: this.id,
48192
48394
  agentName: this.config.name,
48193
48395
  role: this.role,
@@ -48248,11 +48450,12 @@ ${yieldResult.item.payload.content}`
48248
48450
  agentId: this.id,
48249
48451
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48250
48452
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48251
- toolDefinitions: llmTools
48453
+ toolDefinitions: llmTools,
48454
+ systemCacheSegments
48252
48455
  });
48253
48456
  const messages = prepared.messages;
48254
48457
  let risLlmStart = Date.now();
48255
- 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");
48256
48459
  let risTokens = response.usage.inputTokens + response.usage.outputTokens;
48257
48460
  this.updateTokensUsed(risTokens);
48258
48461
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48310,10 +48513,11 @@ ${yieldResult.item.payload.content}`
48310
48513
  agentId: this.id,
48311
48514
  modelContextWindow: this.llmRouter.getModelContextWindow(this.getEffectiveProvider()),
48312
48515
  modelMaxOutput: this.llmRouter.getModelMaxOutput(this.getEffectiveProvider()),
48313
- toolDefinitions: llmTools
48516
+ toolDefinitions: llmTools,
48517
+ systemCacheSegments
48314
48518
  });
48315
48519
  risLlmStart = Date.now();
48316
- 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");
48317
48521
  risTokens = response.usage.inputTokens + response.usage.outputTokens;
48318
48522
  this.updateTokensUsed(risTokens);
48319
48523
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -48973,8 +49177,8 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
48973
49177
  "- Common sections: `procedures`, `conventions`, `preferences`, `domain-knowledge`.",
48974
49178
  "",
48975
49179
  "**Shareable skills** (for team-wide practices):",
48976
- '- Check existing skills first: `discover_tools({ mode: "list_skills" })` and `builder_list`.',
48977
- "- 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`.",
48978
49182
  "",
48979
49183
  "**Direct self-evolution** (simplest and most impactful):",
48980
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.",
@@ -48986,7 +49190,7 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
48986
49190
  '| Single insight / gotcha | `memory_save` with tags: `["insight"]` |',
48987
49191
  '| Tool tip or preference | `memory_save` with tags: `["insight", "tool:<name>"]` |',
48988
49192
  '| Multi-step repeatable workflow | `memory_update_longterm({ section: "procedures", mode: "patch" })` |',
48989
- "| 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` |",
48990
49194
  "| Behavioral rule or guiding principle | Update ROLE.md (`file_read` \u2192 `file_edit` to append) |",
48991
49195
  "| New recurring check for your patrol | Update HEARTBEAT.md (`file_read` \u2192 `file_edit`) |",
48992
49196
  "",
@@ -49092,10 +49296,12 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
49092
49296
  "discover_tools",
49093
49297
  "notify_user",
49094
49298
  "request_user_approval",
49095
- "recall_activity"
49299
+ "recall_activity",
49300
+ "builder_install",
49301
+ "builder_list"
49096
49302
  ];
49097
49303
  if (isManager) {
49098
- 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");
49099
49305
  }
49100
49306
  const HEARTBEAT_ALLOWED_TOOLS = new Set(baseTools);
49101
49307
  const HEARTBEAT_MAX_RETRIES = 3;
@@ -50245,6 +50451,73 @@ var init_browser_session = __esm({
50245
50451
  });
50246
50452
 
50247
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
+ }
50248
50521
  function createManagerTools(ctx) {
50249
50522
  return [
50250
50523
  {
@@ -50363,79 +50636,34 @@ function createManagerTools(ctx) {
50363
50636
  }
50364
50637
  }
50365
50638
  ] : [],
50366
- ...ctx.listTemplates ? [
50367
- {
50368
- name: "team_list_templates",
50369
- description: "List available agent templates that can be hired. Each template has a role, description, and category.",
50370
- inputSchema: {
50371
- type: "object",
50372
- properties: {}
50373
- },
50374
- async execute() {
50375
- try {
50376
- const templates = ctx.listTemplates();
50377
- return JSON.stringify({ templates, count: templates.length });
50378
- } catch (error) {
50379
- return JSON.stringify({ status: "error", error: String(error) });
50380
- }
50381
- }
50382
- }
50383
- ] : [],
50384
- ...ctx.hireFromTemplate ? [
50385
- {
50386
- name: "team_hire_agent",
50387
- 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.",
50388
- inputSchema: {
50389
- type: "object",
50390
- properties: {
50391
- template_id: { type: "string", description: "Template ID (from team_list_templates)" },
50392
- name: { type: "string", description: "Display name for the new agent" },
50393
- skills: {
50394
- type: "array",
50395
- items: { type: "string" },
50396
- description: "Optional skill IDs to assign"
50397
- }
50398
- },
50399
- required: ["template_id", "name"]
50400
- },
50401
- async execute(args) {
50402
- try {
50403
- const templateId = args["template_id"]?.trim();
50404
- const name = args["name"]?.trim();
50405
- if (!templateId)
50406
- return JSON.stringify({ status: "error", error: "template_id is required" });
50407
- if (!name)
50408
- return JSON.stringify({ status: "error", error: "name is required \u2014 please provide a display name for the new agent" });
50409
- const result = await ctx.hireFromTemplate(templateId, name, args["skills"]);
50410
- return JSON.stringify({
50411
- status: "success",
50412
- agent: result,
50413
- next_steps: "Agent created and started. Next: onboard them with project context via agent_send_message, then assign initial tasks via task_create."
50414
- });
50415
- } catch (error) {
50416
- return JSON.stringify({ status: "error", error: String(error) });
50417
- }
50418
- }
50419
- }
50420
- ] : [],
50421
50639
  ...ctx.listArtifacts ? [
50422
50640
  {
50423
- name: "builder_list",
50424
- 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.',
50425
50643
  inputSchema: {
50426
50644
  type: "object",
50427
50645
  properties: {
50428
50646
  type: {
50429
50647
  type: "string",
50430
50648
  enum: ["agent", "team", "skill"],
50431
- description: "Filter by artifact type (optional)"
50649
+ description: "Filter by type (optional). Omit to list all."
50432
50650
  }
50433
50651
  }
50434
50652
  },
50435
50653
  async execute(args) {
50436
50654
  try {
50437
- const artifacts = ctx.listArtifacts(args["type"]);
50438
- 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 });
50439
50667
  } catch (error) {
50440
50668
  return JSON.stringify({ status: "error", error: String(error) });
50441
50669
  }
@@ -50507,17 +50735,23 @@ function createManagerTools(ctx) {
50507
50735
  ] : [],
50508
50736
  ...ctx.installArtifact ? [
50509
50737
  {
50510
- name: "builder_install",
50511
- 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.',
50512
50740
  inputSchema: {
50513
50741
  type: "object",
50514
50742
  properties: {
50515
50743
  type: {
50516
50744
  type: "string",
50517
50745
  enum: ["agent", "team", "skill"],
50518
- description: "Artifact type"
50746
+ description: "Package type"
50519
50747
  },
50520
- 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
+ }
50521
50755
  },
50522
50756
  required: ["type", "name"]
50523
50757
  },
@@ -50528,13 +50762,36 @@ function createManagerTools(ctx) {
50528
50762
  if (!type || !["agent", "team", "skill"].includes(type))
50529
50763
  return JSON.stringify({ status: "error", error: "type is required and must be one of: agent, team, skill" });
50530
50764
  if (!name)
50531
- 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
+ }
50532
50790
  const result = await ctx.installArtifact(type, name);
50533
- const isAgentOrTeam = result.type === "agent" || result.type === "team";
50534
50791
  return JSON.stringify({
50535
50792
  status: "success",
50536
50793
  ...result,
50537
- ...isAgentOrTeam ? {
50794
+ ...type === "team" ? {
50538
50795
  next_steps: "Installed successfully. Next: onboard new agent(s) with project context via agent_send_message, then assign initial tasks via task_create."
50539
50796
  } : {}
50540
50797
  });
@@ -50679,14 +50936,40 @@ function createA2ATools(ctx) {
50679
50936
  },
50680
50937
  {
50681
50938
  name: "agent_list_colleagues",
50682
- 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.",
50683
50940
  inputSchema: {
50684
50941
  type: "object",
50685
50942
  properties: {}
50686
50943
  },
50687
50944
  async execute() {
50688
50945
  const colleagues = ctx.listColleagues().filter((a) => a.id !== ctx.selfId);
50689
- 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");
50690
50973
  }
50691
50974
  },
50692
50975
  ...ctx.sendGroupMessage ? [{
@@ -54479,6 +54762,14 @@ Known issues: ${knownIssues}` : ""}`
54479
54762
  agent.registerTool(tool);
54480
54763
  }
54481
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
+ }
54482
54773
  const mcpConfigs = request.mcpServers ?? this.globalMcpServers;
54483
54774
  if (mcpConfigs) {
54484
54775
  for (const [serverName, serverConfig] of Object.entries(mcpConfigs)) {
@@ -55040,6 +55331,14 @@ Known issues: ${knownIssues}` : ""}`
55040
55331
  agent.registerTool(tool);
55041
55332
  }
55042
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
+ }
55043
55342
  if (this.agentAuditCallback) {
55044
55343
  const cb = this.agentAuditCallback;
55045
55344
  agent.setAuditCallback((event) => cb(id, event));
@@ -55266,6 +55565,7 @@ Known issues: ${knownIssues}` : ""}`
55266
55565
  skills: a.config.skills,
55267
55566
  activeTaskCount: state.activeTaskCount ?? 0,
55268
55567
  teamId: a.config.teamId,
55568
+ teamName: a.getTeamName(),
55269
55569
  lastError: state.lastError,
55270
55570
  lastErrorAt: state.lastErrorAt,
55271
55571
  currentTaskId: state.currentTaskId,
@@ -55687,8 +55987,9 @@ var init_anthropic = __esm({
55687
55987
  max_tokens: request.maxTokens ?? this.maxTokens,
55688
55988
  messages
55689
55989
  };
55690
- if (systemMsg)
55691
- body["system"] = getTextContent(systemMsg.content);
55990
+ if (systemMsg) {
55991
+ body["system"] = this.buildSystemContent(getTextContent(systemMsg.content), request.systemCacheSegments);
55992
+ }
55692
55993
  if (request.temperature !== void 0)
55693
55994
  body["temperature"] = request.temperature;
55694
55995
  if (request.stopSequences?.length)
@@ -55728,8 +56029,9 @@ var init_anthropic = __esm({
55728
56029
  messages,
55729
56030
  stream: true
55730
56031
  };
55731
- if (systemMsg)
55732
- body["system"] = getTextContent(systemMsg.content);
56032
+ if (systemMsg) {
56033
+ body["system"] = this.buildSystemContent(getTextContent(systemMsg.content), request.systemCacheSegments);
56034
+ }
55733
56035
  if (request.temperature !== void 0)
55734
56036
  body["temperature"] = request.temperature;
55735
56037
  if (request.stopSequences?.length)
@@ -55914,6 +56216,21 @@ var init_anthropic = __esm({
55914
56216
  isCompactionSupported() {
55915
56217
  return this.model.startsWith("claude-opus-4") || this.model.startsWith("claude-sonnet-4");
55916
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
+ }
55917
56234
  convertResponse(data) {
55918
56235
  let content = "";
55919
56236
  let compactionContent;
@@ -57239,6 +57556,13 @@ var init_oauth_manager = __esm({
57239
57556
  });
57240
57557
 
57241
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
+ }
57242
57566
  function buildTiers(providerNames, defaultProvider) {
57243
57567
  const tiers = [];
57244
57568
  if (providerNames.includes(defaultProvider)) {
@@ -57917,6 +58241,8 @@ var init_router = __esm({
57917
58241
  const builtinModels = BUILTIN_MODEL_CATALOG.filter((m) => m.provider === name);
57918
58242
  const customCatalogModels = this.customModelCatalog.get(name) ?? [];
57919
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;
57920
58246
  providers[name] = {
57921
58247
  name,
57922
58248
  displayName: PROVIDER_DISPLAY_NAMES[name] ?? name,
@@ -57924,6 +58250,8 @@ var init_router = __esm({
57924
58250
  baseUrl: p.baseUrl,
57925
58251
  configured: true,
57926
58252
  enabled: this.isProviderEnabled(name),
58253
+ apiKeyPreview: maskApiKey(rawKey),
58254
+ apiKeySource: keySource,
57927
58255
  contextWindow: customModels?.contextWindow ?? modelDef?.contextWindow,
57928
58256
  maxOutputTokens: customModels?.maxOutputTokens ?? modelDef?.maxOutputTokens,
57929
58257
  cost: customModels?.cost ?? modelDef?.cost,
@@ -59999,11 +60327,13 @@ function loadTeamTemplateFromDir(dirPath) {
59999
60327
  name: m.name,
60000
60328
  count: m.count,
60001
60329
  role: m.role,
60330
+ description: m.description,
60002
60331
  skills: m.skills ?? []
60003
60332
  })),
60004
60333
  tags: manifest.tags ?? [],
60005
60334
  category: manifest.category,
60006
60335
  icon: manifest.icon,
60336
+ starterTasks: manifest.starterTasks,
60007
60337
  announcements: existsSync21(annPath) ? readFileSync16(annPath, "utf-8") : void 0,
60008
60338
  norms: existsSync21(normsPath) ? readFileSync16(normsPath, "utf-8") : void 0,
60009
60339
  i18n: manifest.i18n
@@ -60226,6 +60556,34 @@ var init_org_service = __esm({
60226
60556
  listHumanUsers(orgId2) {
60227
60557
  return [...this.humans.values()].filter((h) => h.orgId === orgId2);
60228
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
+ }
60229
60587
  updateHumanUser(userId2, updates) {
60230
60588
  const user = this.humans.get(userId2);
60231
60589
  if (!user)
@@ -61303,10 +61661,12 @@ var init_task_service = __esm({
61303
61661
  }
61304
61662
  }
61305
61663
  if (!isReviewerDuringReview) {
61306
- 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) {
61307
61667
  enqueueFor(task.assignedAgentId, "task_comment", `New comment from ${authorName} on your assigned task`);
61308
61668
  }
61309
- if (task?.createdBy && task.status !== "in_progress") {
61669
+ if (task?.createdBy && !handledByInjection) {
61310
61670
  enqueueFor(task.createdBy, "task_comment", `New comment from ${authorName} on a task you created`);
61311
61671
  }
61312
61672
  }
@@ -62540,7 +62900,7 @@ ${c.content}`;
62540
62900
  type: "custom",
62541
62901
  title: task.title,
62542
62902
  description: `Agent "${creatorName}" wants to create task "${task.title}" (priority: ${task.priority}).`,
62543
- details: { taskId: task.id, priority: task.priority, subType: "task" }
62903
+ details: { taskId: task.id, taskTitle: task.title, priority: task.priority, subType: "task" }
62544
62904
  }).then((result) => {
62545
62905
  const current = this.tasks.get(task.id);
62546
62906
  if (!current || current.status !== "pending")
@@ -62779,6 +63139,12 @@ ${c.content}`;
62779
63139
  if (from === "review" && to !== "review") {
62780
63140
  this.activeReviews.delete(taskId2);
62781
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
+ }
62782
63148
  }
62783
63149
  // ─── Stage 2: DB persistence ─────────────────────────────────────────────────
62784
63150
  persistStatusChange(id, status, updatedBy) {
@@ -62834,6 +63200,7 @@ ${deliverablesSummary}` : ""
62834
63200
  type: "custom",
62835
63201
  title: `Review: ${task.title}`,
62836
63202
  description,
63203
+ details: { taskId: task.id, taskTitle: task.title, subType: "task_review" },
62837
63204
  targetUserId: task.reviewerId,
62838
63205
  options: [
62839
63206
  { id: "approve", label: "Approve" },
@@ -63862,7 +64229,7 @@ Action: ${guidance}` : ""
63862
64229
  "",
63863
64230
  'Save each lesson using `memory_save` with tags `["lesson", ...]`.',
63864
64231
  'If it is a repeatable multi-step procedure, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
63865
- "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`.",
63866
64233
  "",
63867
64234
  "**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
63868
64235
  "- If this lesson reveals a behavioral rule that should always guide your work, append it to your ROLE.md via `file_edit`.",
@@ -63882,7 +64249,7 @@ Action: ${guidance}` : ""
63882
64249
  "",
63883
64250
  'If you identify a meaningful insight, save it using `memory_save` with tags `["lesson", "best-practice", ...]`.',
63884
64251
  'If it is a multi-step workflow, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
63885
- "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`.",
63886
64253
  "",
63887
64254
  "**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
63888
64255
  "- If this success reveals a guiding principle or working style worth keeping, append it to your ROLE.md via `file_edit`.",
@@ -64135,7 +64502,7 @@ ${reason}`
64135
64502
  updated.runAt = void 0;
64136
64503
  }
64137
64504
  if (fields.maxRuns !== void 0)
64138
- updated.maxRuns = fields.maxRuns;
64505
+ updated.maxRuns = fields.maxRuns > 0 ? fields.maxRuns : void 0;
64139
64506
  if (fields.timezone !== void 0)
64140
64507
  updated.timezone = fields.timezone;
64141
64508
  updated.nextRunAt = computeNextRunFromConfig(updated);
@@ -64505,8 +64872,10 @@ ${task.description}`;
64505
64872
  content,
64506
64873
  "",
64507
64874
  `(This task "${task.title}" is already ${task.status}. You have full context from your execution above.`,
64508
- "Respond to the comment. If the feedback contains something worth remembering, save it to memory.",
64509
- "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.)"
64510
64879
  ].join("\n");
64511
64880
  try {
64512
64881
  log50.info("Triggering post-task agent reply", { taskId: taskId2, taskSessionId, agentId: agentId2, authorName });
@@ -64618,11 +64987,19 @@ var init_builder_service = __esm({
64618
64987
  orgService;
64619
64988
  skillRegistry;
64620
64989
  wsBroadcast;
64990
+ taskService;
64991
+ builtinTeamTemplatesDir;
64621
64992
  constructor(orgService, skillRegistry, wsBroadcast) {
64622
64993
  this.orgService = orgService;
64623
64994
  this.skillRegistry = skillRegistry;
64624
64995
  this.wsBroadcast = wsBroadcast;
64625
64996
  }
64997
+ setTaskService(taskService) {
64998
+ this.taskService = taskService;
64999
+ }
65000
+ setBuiltinTeamTemplatesDir(dir) {
65001
+ this.builtinTeamTemplatesDir = dir;
65002
+ }
64626
65003
  get baseDir() {
64627
65004
  return join20(homedir12(), ".markus", "builder-artifacts");
64628
65005
  }
@@ -64655,14 +65032,42 @@ var init_builder_service = __esm({
64655
65032
  });
64656
65033
  }
64657
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
+ }
64658
65054
  artifacts.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
64659
65055
  return artifacts;
64660
65056
  }
64661
65057
  async installArtifact(type, name) {
64662
65058
  const typeDir = type === "agent" ? "agents" : type === "team" ? "teams" : "skills";
64663
- const artDir = join20(this.baseDir, typeDir, name);
65059
+ let artDir = join20(this.baseDir, typeDir, name);
64664
65060
  if (!existsSync24(artDir)) {
64665
- 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
+ }
64666
65071
  }
64667
65072
  const installType = type;
64668
65073
  const manifest = readManifest(artDir, installType, FS_HELPER);
@@ -64778,9 +65183,35 @@ var init_builder_service = __esm({
64778
65183
  createdAgents.push({ id: agent.id, name: agent.config.name, role: agent.role.name });
64779
65184
  }
64780
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
+ }
64781
65212
  return {
64782
65213
  type: "team",
64783
- installed: { team: { id: team.id, name: teamName }, agents: createdAgents }
65214
+ installed: { team: { id: team.id, name: teamName }, agents: createdAgents, starterTaskIds: createdTaskIds }
64784
65215
  };
64785
65216
  }
64786
65217
  /**
@@ -65379,7 +65810,12 @@ var init_sse_handler = __esm({
65379
65810
  replyLength: persistReply.length
65380
65811
  });
65381
65812
  if (this.options.wsBroadcaster) {
65382
- 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
+ }
65383
65819
  }
65384
65820
  } else {
65385
65821
  this.sseBuffer.send({
@@ -66194,6 +66630,7 @@ ${cleanText}`,
66194
66630
  setSkillRegistry(registry) {
66195
66631
  this.skillRegistry = registry;
66196
66632
  this.builderService = new BuilderService(this.orgService, registry, (msg) => this.ws?.broadcast(msg));
66633
+ this.builderService.setTaskService(this.taskService);
66197
66634
  }
66198
66635
  getBuilderService() {
66199
66636
  return this.builderService;
@@ -66866,6 +67303,24 @@ ${cleanText}`,
66866
67303
  log56.warn("Failed to persist assistant message", { error: String(err) });
66867
67304
  }
66868
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
+ }
66869
67324
  start() {
66870
67325
  this.server = createServer2((req, res) => this.handleRequest(req, res));
66871
67326
  this.ws.attach(this.server);
@@ -66928,6 +67383,68 @@ ${cleanText}`,
66928
67383
  await this.readBody(req);
66929
67384
  }
66930
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
+ }
66931
67448
  if (path === "/api/auth/login" && req.method === "POST") {
66932
67449
  const body = await this.readBody(req);
66933
67450
  const email = (body["email"] ?? "").trim().toLowerCase();
@@ -66936,7 +67453,18 @@ ${cleanText}`,
66936
67453
  this.json(res, 200, { user: { id: "anonymous", name: "Admin", role: "owner" } });
66937
67454
  return;
66938
67455
  }
66939
- 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
+ }
66940
67468
  if (!userRow || !userRow.passwordHash) {
66941
67469
  this.json(res, 401, { error: "Invalid email or password" });
66942
67470
  return;
@@ -66946,6 +67474,7 @@ ${cleanText}`,
66946
67474
  this.json(res, 401, { error: "Invalid email or password" });
66947
67475
  return;
66948
67476
  }
67477
+ const isFirstLogin = !userRow.lastLoginAt;
66949
67478
  await this.storage.userRepo.updateLastLogin(userRow.id);
66950
67479
  const exp = Math.floor(Date.now() / 1e3) + 7 * 24 * 3600;
66951
67480
  const token = await signToken({ userId: userRow.id, orgId: userRow.orgId, role: userRow.role, exp }, this.jwtSecret);
@@ -66958,7 +67487,8 @@ ${cleanText}`,
66958
67487
  role: userRow.role,
66959
67488
  orgId: userRow.orgId,
66960
67489
  avatarUrl: userRow.avatarUrl ?? void 0
66961
- }
67490
+ },
67491
+ needsOnboarding: isFirstLogin
66962
67492
  });
66963
67493
  return;
66964
67494
  }
@@ -67250,6 +67780,18 @@ ${cleanText}`,
67250
67780
  }
67251
67781
  return;
67252
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
+ }
67253
67795
  if (path.match(/^\/api\/agents\/[^/]+\/sessions$/) && req.method === "GET") {
67254
67796
  const authUser = await this.getAuthUser(req);
67255
67797
  const agentId2 = path.split("/")[3];
@@ -67664,7 +68206,9 @@ ${cleanText}`,
67664
68206
  const fileNames = body["fileNames"]?.filter(Boolean);
67665
68207
  const isRetry = body["isRetry"];
67666
68208
  const isResume = body["isResume"];
67667
- 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;
67668
68212
  const agent = this.orgService.getAgentManager().getAgent(agentId2);
67669
68213
  this.ws.broadcastAgentUpdate(agentId2, "working");
67670
68214
  if (!sessionId) {
@@ -67777,9 +68321,15 @@ ${cleanText}`,
67777
68321
  return;
67778
68322
  }
67779
68323
  if (path === "/api/group-chats" && req.method === "GET") {
68324
+ const authUser = await this.requireAuth(req, res);
68325
+ if (!authUser)
68326
+ return;
67780
68327
  const orgId2 = url.searchParams.get("orgId") ?? "default";
68328
+ const userId2 = authUser.userId;
68329
+ const isAdmin = authUser.role === "owner" || authUser.role === "admin";
67781
68330
  const teams = this.orgService.listTeamsWithMembers(orgId2);
67782
- 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) => ({
67783
68333
  id: `group:${t.id}`,
67784
68334
  name: t.name,
67785
68335
  type: "team",
@@ -67787,7 +68337,7 @@ ${cleanText}`,
67787
68337
  memberCount: t.members.length,
67788
68338
  channelKey: `group:${t.id}`
67789
68339
  }));
67790
- 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) => ({
67791
68341
  id: c.id,
67792
68342
  name: c.name,
67793
68343
  type: "custom",
@@ -74836,6 +75386,14 @@ var init_hitl_service = __esm({
74836
75386
  setTimeout(() => {
74837
75387
  if (this.pendingResolvers.has(approval.id)) {
74838
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
+ }
74839
75397
  resolve20({ approved: false, comment: "Approval timed out" });
74840
75398
  }
74841
75399
  }, opts.expiresInMs);
@@ -74863,6 +75421,35 @@ var init_hitl_service = __esm({
74863
75421
  }
74864
75422
  return approval;
74865
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
+ }
74866
75453
  persistApproval(approval) {
74867
75454
  if (!this.approvalRepo)
74868
75455
  return;
@@ -76017,6 +76604,9 @@ var init_requirement_service = __esm({
76017
76604
  const req = this.requirements.get(id);
76018
76605
  if (!req)
76019
76606
  throw new Error(`Requirement ${id} not found`);
76607
+ if (this.hitlService) {
76608
+ this.hitlService.cancelApprovalsByDetail("requirementId", id, cancelledBy ?? "system", "Requirement cancelled");
76609
+ }
76020
76610
  const oldStatus = req.status;
76021
76611
  req.status = "cancelled";
76022
76612
  req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -76110,6 +76700,9 @@ var init_requirement_service = __esm({
76110
76700
  }
76111
76701
  }
76112
76702
  deleteRequirement(id) {
76703
+ if (this.hitlService) {
76704
+ this.hitlService.cancelApprovalsByDetail("requirementId", id, "system", "Requirement deleted");
76705
+ }
76113
76706
  this.requirements.delete(id);
76114
76707
  if (this.requirementRepo) {
76115
76708
  this.requirementRepo.delete(id).catch((e) => log61.error("Failed to delete requirement from storage", { id, error: String(e) }));
@@ -77451,7 +78044,7 @@ var init_scheduled_task_runner = __esm({
77451
78044
  if (!task.scheduleConfig)
77452
78045
  continue;
77453
78046
  const config = task.scheduleConfig;
77454
- if (config.maxRuns !== void 0 && (config.currentRuns ?? 0) >= config.maxRuns) {
78047
+ if (config.maxRuns && config.maxRuns > 0 && (config.currentRuns ?? 0) >= config.maxRuns) {
77455
78048
  continue;
77456
78049
  }
77457
78050
  const nextRun = config.nextRunAt ? new Date(config.nextRunAt).getTime() : 0;
@@ -78960,6 +79553,10 @@ CREATE INDEX IF NOT EXISTS idx_st_entity ON status_transitions(entity_type, enti
78960
79553
  }
78961
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));
78962
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
+ }
78963
79560
  getSession(sessionId) {
78964
79561
  const r = this.db.prepare("SELECT * FROM chat_sessions WHERE id = ?").get(sessionId);
78965
79562
  return r ? this._mapSession(r) : null;
@@ -80430,6 +81027,17 @@ CREATE INDEX IF NOT EXISTS idx_st_entity ON status_transitions(entity_type, enti
80430
81027
  memberCount: r["member_count"]
80431
81028
  }));
80432
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
+ }
80433
81041
  getById(id) {
80434
81042
  const r = this.db.prepare("SELECT * FROM group_chats WHERE id = ?").get(id);
80435
81043
  if (!r)
@@ -82324,10 +82932,10 @@ function registerStartCommand(program2) {
82324
82932
  const configPath = globalOpts.config ?? getDefaultConfigPath();
82325
82933
  if (opts.setup || !existsSync33(configPath)) {
82326
82934
  if (!existsSync33(configPath)) {
82327
- console.log(" No configuration found \u2014 running first-time setup...\n");
82935
+ console.log(" No configuration found \u2014 auto-configuring from environment...\n");
82328
82936
  }
82329
82937
  const { quickInit: quickInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
82330
- await quickInit2();
82938
+ await quickInit2({ nonInteractive: true });
82331
82939
  }
82332
82940
  const config = loadConfig(globalOpts.config);
82333
82941
  await startServer(config, { port: globalOpts.port, config: globalOpts.config });
@@ -82570,7 +83178,8 @@ async function startServer(config, values) {
82570
83178
  startupLog("INFO", " 2. \u4EA4\u4E92\u5F0F\u914D\u7F6E \u2192 \u6B63\u5728\u542F\u52A8\u5411\u5BFC...");
82571
83179
  startupBlank();
82572
83180
  const { quickInit: quickInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
82573
- 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 });
82574
83183
  const { loadConfig: reloadConfig } = await Promise.resolve().then(() => (init_dist(), dist_exports));
82575
83184
  const updatedConfig = reloadConfig(values["config"]);
82576
83185
  for (const [name, cfg] of Object.entries(updatedConfig.llm?.providers ?? {})) {
@@ -82733,6 +83342,10 @@ async function startServer(config, values) {
82733
83342
  orgService.registerBuilderContextProviders(skillRegistry);
82734
83343
  const builderService = apiServer.getBuilderService();
82735
83344
  if (builderService) {
83345
+ const builtinTeamsDir = resolveTemplatesDir("teams");
83346
+ if (builtinTeamsDir && existsSync33(builtinTeamsDir)) {
83347
+ builderService.setBuiltinTeamTemplatesDir(builtinTeamsDir);
83348
+ }
82736
83349
  agentManager.setBuilderService(builderService);
82737
83350
  }
82738
83351
  const hubClient = apiServer.getHubClient();
@@ -82764,6 +83377,7 @@ async function startServer(config, values) {
82764
83377
  return { installed: result.installed, name: result.name, method: result.method };
82765
83378
  });
82766
83379
  agentManager.setUserApprovalRequester(async (opts) => {
83380
+ const taskTitle = opts.relatedTaskId ? taskService.getTask(opts.relatedTaskId)?.title : void 0;
82767
83381
  return hitlService.requestApprovalAndWait({
82768
83382
  agentId: opts.agentId,
82769
83383
  agentName: opts.agentName,
@@ -82773,7 +83387,7 @@ async function startServer(config, values) {
82773
83387
  targetUserId: ownerUserId,
82774
83388
  options: opts.options,
82775
83389
  allowFreeform: opts.allowFreeform,
82776
- details: { priority: opts.priority, taskId: opts.relatedTaskId }
83390
+ details: { priority: opts.priority, taskId: opts.relatedTaskId, taskTitle }
82777
83391
  });
82778
83392
  });
82779
83393
  agentManager.setUserNotifier((opts) => {
@@ -82849,7 +83463,8 @@ async function startServer(config, values) {
82849
83463
  agentManager.getEventBus().on("agent:notify-user", async (evt) => {
82850
83464
  const { agentId: agentId2, title, body, priority, taskId: taskId2, requirementId: requirementId2, targetUserId } = evt;
82851
83465
  try {
82852
- const mainSession = storage.chatSessionRepo.getOrCreateMainSession(agentId2, defaultSessionUserId);
83466
+ const sessionUserId = targetUserId || defaultSessionUserId;
83467
+ const mainSession = storage.chatSessionRepo.getOrCreateMainSession(agentId2, sessionUserId);
82853
83468
  const agent = agentManager.getAgent(agentId2);
82854
83469
  const formattedMsg = `**${title}**
82855
83470
 
@@ -82865,7 +83480,7 @@ ${body}`;
82865
83480
  storage.chatSessionRepo.updateLastMessage(mainSession.id);
82866
83481
  ws.broadcastProactiveMessage(agentId2, agent.config.name, mainSession.id, msg.id, formattedMsg, {
82867
83482
  isMainSession: true
82868
- }, defaultSessionUserId);
83483
+ }, sessionUserId);
82869
83484
  const hasTask = !!taskId2;
82870
83485
  hitlService.notify({
82871
83486
  targetUserId: targetUserId ?? "all",
@@ -83029,13 +83644,14 @@ ${reason}`;
83029
83644
  const reasonMatch = request.reason.match(/requires approval:\s*(.+?)\.?\s*Command:/);
83030
83645
  title = reasonMatch ? `Git: ${reasonMatch[1]}` : "Shell: command approval";
83031
83646
  }
83647
+ const taskTitle = request.taskId ? taskService.getTask(request.taskId)?.title : void 0;
83032
83648
  const result = await hitlService.requestApprovalAndWait({
83033
83649
  agentId: agentId2,
83034
83650
  agentName,
83035
83651
  type: "action",
83036
83652
  title,
83037
83653
  description: request.reason,
83038
- details: { ...request.toolArgs, toolName: request.toolName, agentId: agentId2, taskId: request.taskId },
83654
+ details: { ...request.toolArgs, toolName: request.toolName, agentId: agentId2, taskId: request.taskId, taskTitle },
83039
83655
  targetUserId: ownerUserId
83040
83656
  });
83041
83657
  auditService.record({