@integrity-labs/agt-cli 0.28.481 → 0.28.482

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.
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-Q4YE53I5.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-IBMUPQZ2.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-7CI63PCV.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-2XPTEGQR.js.map
@@ -44,7 +44,7 @@ import {
44
44
  resolveEffectivePinRaw,
45
45
  safeWriteJsonAtomic,
46
46
  setConfigHash
47
- } from "../chunk-YYNUZWK7.js";
47
+ } from "../chunk-B6AXXPYQ.js";
48
48
  import {
49
49
  getProjectDir as getProjectDir2,
50
50
  getReadyTasks,
@@ -162,7 +162,7 @@ import {
162
162
  toOpencodeModel,
163
163
  transcriptActivityAgeSeconds,
164
164
  writeEgressAllowlist
165
- } from "../chunk-4XLEJP7G.js";
165
+ } from "../chunk-DNC5JGOV.js";
166
166
  import {
167
167
  reapOrphanChannelMcps
168
168
  } from "../chunk-XWVM4KPK.js";
@@ -10064,7 +10064,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
10064
10064
  var lastVersionCheckAt = 0;
10065
10065
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
10066
10066
  var lastResponsivenessProbeAt = 0;
10067
- var agtCliVersion = true ? "0.28.481" : "dev";
10067
+ var agtCliVersion = true ? "0.28.482" : "dev";
10068
10068
  function resolveBrewPath(execFileSync2) {
10069
10069
  try {
10070
10070
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -11227,7 +11227,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
11227
11227
  if (codeNames.length === 0) return;
11228
11228
  void (async () => {
11229
11229
  try {
11230
- const { collectDiagnostics } = await import("../persistent-session-Q4YE53I5.js");
11230
+ const { collectDiagnostics } = await import("../persistent-session-IBMUPQZ2.js");
11231
11231
  await api.post("/host/heartbeat", {
11232
11232
  host_id: hostId,
11233
11233
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor)
@@ -11334,7 +11334,7 @@ async function pollCycle() {
11334
11334
  }
11335
11335
  try {
11336
11336
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
11337
- const { collectDiagnostics } = await import("../persistent-session-Q4YE53I5.js");
11337
+ const { collectDiagnostics } = await import("../persistent-session-IBMUPQZ2.js");
11338
11338
  const diagCodeNames = [...agentState.persistentSessionAgents];
11339
11339
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor) : void 0;
11340
11340
  let tailscaleHostname;
@@ -11448,7 +11448,7 @@ async function pollCycle() {
11448
11448
  collectResponsivenessProbes,
11449
11449
  collectPanelessActivityProbes,
11450
11450
  getResponsivenessIntervalMs
11451
- } = await import("../responsiveness-probe-MOFFFCOY.js");
11451
+ } = await import("../responsiveness-probe-QBLQIMZR.js");
11452
11452
  const probeIntervalMs = getResponsivenessIntervalMs();
11453
11453
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
11454
11454
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -11512,7 +11512,7 @@ async function pollCycle() {
11512
11512
  collectResponsivenessProbes,
11513
11513
  livePendingInboundOldestAgeSeconds,
11514
11514
  parkPendingInbound
11515
- } = await import("../responsiveness-probe-MOFFFCOY.js");
11515
+ } = await import("../responsiveness-probe-QBLQIMZR.js");
11516
11516
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
11517
11517
  const wedgeNow = /* @__PURE__ */ new Date();
11518
11518
  const liveAgents = agentState.persistentSessionAgents;
@@ -14988,7 +14988,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
14988
14988
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
14989
14989
  void (async () => {
14990
14990
  try {
14991
- const { collectDiagnostics } = await import("../persistent-session-Q4YE53I5.js");
14991
+ const { collectDiagnostics } = await import("../persistent-session-IBMUPQZ2.js");
14992
14992
  await api.post("/host/heartbeat", {
14993
14993
  host_id: hostId,
14994
14994
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
@@ -15038,7 +15038,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
15038
15038
  }
15039
15039
  try {
15040
15040
  const hostId = await getHostId();
15041
- const { collectDiagnostics } = await import("../persistent-session-Q4YE53I5.js");
15041
+ const { collectDiagnostics } = await import("../persistent-session-IBMUPQZ2.js");
15042
15042
  await api.post("/host/heartbeat", {
15043
15043
  host_id: hostId,
15044
15044
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
@@ -15496,7 +15496,7 @@ async function processClaudePairSessions(agents) {
15496
15496
  killPairSession,
15497
15497
  pairTmuxSession,
15498
15498
  finalizeClaudePairOnboarding
15499
- } = await import("../claude-pair-runtime-7CI63PCV.js");
15499
+ } = await import("../claude-pair-runtime-2XPTEGQR.js");
15500
15500
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
15501
15501
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
15502
15502
  const killed = await killPairSession(pairTmuxSession(pairId));
package/dist/mcp/index.js CHANGED
@@ -21415,6 +21415,78 @@ function describeRecruitAgentResult(resp) {
21415
21415
  }
21416
21416
  }
21417
21417
 
21418
+ // src/agent-roles.ts
21419
+ function renderRole(r) {
21420
+ const id = r.id ?? "(unknown)";
21421
+ const title = r.title ?? id;
21422
+ const tier = r.suggested_risk_tier ? ` \xB7 risk ${r.suggested_risk_tier}` : "";
21423
+ const custom3 = r.source === "custom" ? " \xB7 custom to your org" : "";
21424
+ const lines = [`- role_id: ${id} \u2014 ${title}${tier}${custom3}`];
21425
+ if (r.summary && r.summary !== title) lines.push(` ${r.summary}`);
21426
+ const tasks = (r.default_tasks ?? []).filter((t) => t && t.name);
21427
+ if (tasks.length > 0) {
21428
+ const shown = tasks.slice(0, 4).map((t) => `${t.name}${t.cadence ? ` (${t.cadence})` : ""}`);
21429
+ const more = tasks.length > shown.length ? `, +${tasks.length - shown.length} more` : "";
21430
+ lines.push(` comes with ${tasks.length} scheduled task(s): ${shown.join("; ")}${more}`);
21431
+ }
21432
+ if (r.bundled_skill_count && r.bundled_skill_count > 0) {
21433
+ lines.push(` comes with ${r.bundled_skill_count} bundled skill(s)`);
21434
+ }
21435
+ if (r.suggested_integrations && r.suggested_integrations.length > 0) {
21436
+ lines.push(` typically needs: ${r.suggested_integrations.join(", ")}`);
21437
+ }
21438
+ return lines.join("\n");
21439
+ }
21440
+ function describeListAgentRolesResult(resp) {
21441
+ if (resp.status === "feature_disabled") {
21442
+ return {
21443
+ text: `${resp.message ?? "Recruiting teammates is not enabled for this organization."} This is a setting, not a fault \u2014 do not retry.`,
21444
+ isError: true
21445
+ };
21446
+ }
21447
+ if (resp.status !== "ok" || !Array.isArray(resp.roles)) {
21448
+ return {
21449
+ text: `Could not load the role catalog${resp.message ? `: ${resp.message}` : ""}. Do not invent a role in its place \u2014 a role label that is not in the catalog will not provision the role's scheduled tasks or skills. Try again shortly, or tell your operator.`,
21450
+ isError: true
21451
+ };
21452
+ }
21453
+ if (resp.roles.length === 0) {
21454
+ return {
21455
+ text: "The role catalog came back empty, which is unexpected. Do not substitute a made-up role \u2014 tell your operator.",
21456
+ isError: true
21457
+ };
21458
+ }
21459
+ const labels = /* @__PURE__ */ new Map();
21460
+ for (const c of resp.categories ?? []) {
21461
+ if (c?.id) labels.set(c.id, c.label ?? c.id);
21462
+ }
21463
+ const byCategory = /* @__PURE__ */ new Map();
21464
+ for (const r of resp.roles) {
21465
+ const key = r.category ?? "other";
21466
+ const bucket = byCategory.get(key);
21467
+ if (bucket) bucket.push(r);
21468
+ else byCategory.set(key, [r]);
21469
+ }
21470
+ const ordered = [
21471
+ ...[...labels.keys()].filter((k) => byCategory.has(k)),
21472
+ ...[...byCategory.keys()].filter((k) => !labels.has(k))
21473
+ ];
21474
+ const sections = ordered.map((key) => {
21475
+ const heading = labels.get(key) ?? key;
21476
+ const body = (byCategory.get(key) ?? []).map(renderRole).join("\n");
21477
+ return `${heading}:
21478
+ ${body}`;
21479
+ });
21480
+ return {
21481
+ text: `${resp.roles.length} role(s) available to your organization.
21482
+
21483
+ ${sections.join("\n\n")}
21484
+
21485
+ Pass the exact role_id to recruit_agent as \`role_id\`. Choosing a catalog role is strongly preferred over a free-text role: it seeds the new agent's description and risk tier, and its scheduled tasks and bundled skills are provisioned automatically. A made-up role label gets none of that. Only use a custom role when nothing here fits the remit you agreed with the person you are interviewing.`,
21486
+ isError: false
21487
+ };
21488
+ }
21489
+
21418
21490
  // src/request-reconnect.ts
21419
21491
  function describeReconnectRequestResult(resp) {
21420
21492
  switch (resp.status) {
@@ -22599,9 +22671,44 @@ server.tool(
22599
22671
  return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
22600
22672
  }
22601
22673
  );
22674
+ server.tool(
22675
+ "list_agent_roles",
22676
+ "List the predefined agent roles available to your organization. Call this BEFORE recruit_agent, and go through the results with the person who asked, so the new teammate is created from a curated role rather than a label you invented. Each role carries a vetted description, a risk tier, scheduled tasks and bundled skills that are provisioned automatically when the role is used \u2014 a made-up role label gets none of that. Pass the chosen role_id to recruit_agent.",
22677
+ {},
22678
+ async () => {
22679
+ if (!AGT_AGENT_CODE_NAME) {
22680
+ return {
22681
+ content: [{ type: "text", text: "Error: AGT_AGENT_CODE_NAME not configured." }],
22682
+ isError: true
22683
+ };
22684
+ }
22685
+ let resp;
22686
+ try {
22687
+ resp = await apiGet(
22688
+ `/host/agent-roles?agent_code_name=${encodeURIComponent(AGT_AGENT_CODE_NAME)}`
22689
+ );
22690
+ } catch (err) {
22691
+ process.stderr.write(
22692
+ `list_agent_roles: GET /host/agent-roles failed: ${err instanceof Error ? err.message : String(err)}
22693
+ `
22694
+ );
22695
+ return {
22696
+ content: [
22697
+ {
22698
+ type: "text",
22699
+ text: "Could not reach the role catalog. Do not substitute a made-up role \u2014 a label that is not in the catalog provisions no tasks and no skills. Try again shortly."
22700
+ }
22701
+ ],
22702
+ isError: true
22703
+ };
22704
+ }
22705
+ const { text, isError } = describeListAgentRolesResult(resp);
22706
+ return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
22707
+ }
22708
+ );
22602
22709
  server.tool(
22603
22710
  "recruit_agent",
22604
- "Propose a NEW teammate agent for your organization when the work in front of you genuinely needs a different agent \u2014 a distinct role, a separate remit, or capacity you cannot cover yourself. This does NOT create anything: it sends a request to an organization owner, who approves or denies it. Use it sparingly and only when you can say concretely what the new agent is for; do not use it to duplicate yourself, to get around a tool you are missing (use request_feature), or because a single task is large. After calling this, stop and report that it is awaiting approval \u2014 poll with check_approval if you need the outcome.",
22711
+ "Propose a NEW teammate agent for your organization when the work in front of you genuinely needs a different agent \u2014 a distinct role, a separate remit, or capacity you cannot cover yourself. This does NOT create anything: it sends a request to an organization owner, who approves or denies it. INTERVIEW THE PERSON WHO ASKED BEFORE CALLING THIS. You need their answers, not your own assumptions, for: what the agent is responsible for, what it must NOT do, how they will judge whether it is working, and who it reports to (use directory_lookup to get that id). Call list_agent_roles first and agree a role with them \u2014 a catalog role brings a vetted description, scheduled tasks and skills that a made-up role label does not. If you cannot get those answers, ask for them; do not fill them in yourself. Use this sparingly: do not duplicate yourself, do not use it to get around a tool you are missing (use request_feature), and do not use it because a single task is large. After calling this, stop and report that it is awaiting approval \u2014 poll with check_approval if you need the outcome.",
22605
22712
  {
22606
22713
  code_name: external_exports.string().min(1).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "code_name must be kebab-case").describe(
22607
22714
  'kebab-case identifier for the proposed agent (lowercase, hyphen-separated), e.g. "invoice-helper". If the name is already taken in your team a numeric suffix is added automatically.'
@@ -22610,7 +22717,25 @@ server.tool(
22610
22717
  description: external_exports.string().min(20).max(2e3).describe(
22611
22718
  "What this teammate is FOR: its remit, the work it would take on, and why that work needs a separate agent. A human reads this to decide \u2014 be specific and honest. Vague descriptions get denied."
22612
22719
  ),
22613
- role: external_exports.string().max(200).optional().describe('Optional short role label for the proposed agent, e.g. "Accounts receivable".'),
22720
+ role_id: external_exports.string().max(120).optional().describe(
22721
+ "Preferred. The id of a role from list_agent_roles, agreed with the person you interviewed. Using one seeds the new agent's description and risk tier from the catalog and provisions that role's scheduled tasks and bundled skills on approval. A free-text role gets none of that."
22722
+ ),
22723
+ role: external_exports.string().max(200).optional().describe(
22724
+ 'Only when no catalog role fits: a short free-text role label, e.g. "Accounts receivable". Ignored when role_id is supplied.'
22725
+ ),
22726
+ responsibilities: external_exports.array(external_exports.string().min(3).max(300)).min(1).max(8).describe(
22727
+ "What this teammate will be responsible for, in the words of the person who asked for it. Ask them \u2014 do not write this yourself. One short bullet each."
22728
+ ),
22729
+ boundaries: external_exports.array(external_exports.string().min(3).max(300)).min(1).max(8).describe(
22730
+ "What this teammate must NOT do \u2014 the limits of its remit, agreed explicitly with the person you interviewed. This is the field approvers rely on most, so ask the question directly rather than inferring the answer."
22731
+ ),
22732
+ success_criteria: external_exports.array(external_exports.string().min(3).max(300)).min(1).max(8).describe(
22733
+ "How the person will judge whether this teammate is working. Ask them for concrete, checkable statements."
22734
+ ),
22735
+ reports_to: external_exports.string().uuid().describe(
22736
+ "Who this teammate answers to: the uuid of an existing agent or person in your organization. Ask the requester, then use directory_lookup to resolve the id \u2014 do not guess it."
22737
+ ),
22738
+ reports_to_type: external_exports.enum(["agent", "person"]).describe("Whether reports_to is another 'agent' or a 'person' (a human in your organization)."),
22614
22739
  reason: external_exports.string().max(1e3).optional().describe(
22615
22740
  "Optional context for the approver: what prompted this now (a spike in volume, a user request, a gap you keep hitting). Shown as context only \u2014 the decision is made on the description above."
22616
22741
  ),
@@ -22632,6 +22757,16 @@ server.tool(
22632
22757
  code_name: params.code_name,
22633
22758
  display_name: params.display_name,
22634
22759
  description: params.description,
22760
+ // ENG-8306: the interviewed remit. Sent unconditionally — the API
22761
+ // REJECTS a proposal missing any of these, which is what makes the
22762
+ // interview mandatory rather than advisory (the server cannot observe
22763
+ // that the conversation happened, only that its output is present).
22764
+ responsibilities: params.responsibilities,
22765
+ boundaries: params.boundaries,
22766
+ success_criteria: params.success_criteria,
22767
+ reports_to: params.reports_to,
22768
+ reports_to_type: params.reports_to_type,
22769
+ ...params.role_id ? { role_id: params.role_id } : {},
22635
22770
  ...params.role ? { role: params.role } : {},
22636
22771
  ...params.reason ? { reason: params.reason } : {},
22637
22772
  ...params.host_id ? { host_id: params.host_id } : {}
@@ -24034,6 +24169,10 @@ var LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
24034
24169
  // server-side org-scoped agent-recruit flag, see the registration comment).
24035
24170
  // No API tool shares this name; listed here to keep the lockstep guard green.
24036
24171
  "recruit_agent",
24172
+ // ENG-8306: enumerate the org's role catalog so a recruit can pick a curated
24173
+ // role instead of inventing a free-text label. Always registered for the same
24174
+ // reason as recruit_agent — the gate is server-side.
24175
+ "list_agent_roles",
24037
24176
  // ENG-6686: self-service identity edit (always registered). Listed here to
24038
24177
  // keep the lockstep guard green; no API tool shares this name.
24039
24178
  "update_identity",
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-4XLEJP7G.js";
39
+ } from "./chunk-DNC5JGOV.js";
40
40
  import "./chunk-XWVM4KPK.js";
41
41
  export {
42
42
  EGRESS_BASELINE_DOMAINS,
@@ -77,4 +77,4 @@ export {
77
77
  writeEgressAllowlist,
78
78
  writePersistentClaudeWrapper
79
79
  };
80
- //# sourceMappingURL=persistent-session-Q4YE53I5.js.map
80
+ //# sourceMappingURL=persistent-session-IBMUPQZ2.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-4XLEJP7G.js";
3
+ } from "./chunk-DNC5JGOV.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -596,4 +596,4 @@ export {
596
596
  readAndResetSlackReplyBindingClassifications,
597
597
  readAndResetSlackReplyTargetClassifications
598
598
  };
599
- //# sourceMappingURL=responsiveness-probe-MOFFFCOY.js.map
599
+ //# sourceMappingURL=responsiveness-probe-QBLQIMZR.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.481",
3
+ "version": "0.28.482",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {