@integrity-labs/agt-cli 0.27.7-test.5 → 0.27.7-test.7
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/assets/impersonate-statusline.sh +88 -0
- package/dist/bin/agt.js +246 -85
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-WCY7QM3R.js → chunk-2G35ES7G.js} +1 -1
- package/dist/lib/manager-worker.js +9 -9
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +47 -0
- package/dist/mcp/index.js +100 -81
- package/dist/mcp/slack-channel.js +50 -0
- package/dist/mcp/telegram-channel.js +50 -1
- package/package.json +3 -2
- /package/dist/{chunk-WCY7QM3R.js.map → chunk-2G35ES7G.js.map} +0 -0
|
@@ -14368,6 +14368,50 @@ function isMode(value) {
|
|
|
14368
14368
|
return value === "thinking" || value === "working" || value === "waiting";
|
|
14369
14369
|
}
|
|
14370
14370
|
|
|
14371
|
+
// src/impersonation.ts
|
|
14372
|
+
var ENV_VAR = "AGT_ACT_AS_AGENT_ID";
|
|
14373
|
+
var OVERRIDE_ENV_VAR = "ENABLE_IMPERSONATION_CHANNELS";
|
|
14374
|
+
function isImpersonating() {
|
|
14375
|
+
return impersonatedAgentId() !== null;
|
|
14376
|
+
}
|
|
14377
|
+
function impersonatedAgentId() {
|
|
14378
|
+
const raw = process.env[ENV_VAR];
|
|
14379
|
+
if (typeof raw !== "string") return null;
|
|
14380
|
+
const trimmed = raw.trim();
|
|
14381
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
14382
|
+
}
|
|
14383
|
+
function channelsEnabledOverride() {
|
|
14384
|
+
const raw = process.env[OVERRIDE_ENV_VAR];
|
|
14385
|
+
if (typeof raw !== "string") return false;
|
|
14386
|
+
const trimmed = raw.trim().toLowerCase();
|
|
14387
|
+
return trimmed === "1" || trimmed === "true" || trimmed === "yes" || trimmed === "on";
|
|
14388
|
+
}
|
|
14389
|
+
var IMPERSONATION_REFUSAL_CODE = "CHANNEL.IMPERSONATION_DISABLED";
|
|
14390
|
+
var IMPERSONATION_REFUSAL_MESSAGE = "Posting as an agent under impersonation is disabled in v0 \u2014 quit impersonation with `agt impersonate exit` to send as yourself.";
|
|
14391
|
+
function buildImpersonationRefusal(toolName) {
|
|
14392
|
+
return {
|
|
14393
|
+
content: [
|
|
14394
|
+
{
|
|
14395
|
+
type: "text",
|
|
14396
|
+
text: JSON.stringify({
|
|
14397
|
+
error: {
|
|
14398
|
+
code: IMPERSONATION_REFUSAL_CODE,
|
|
14399
|
+
message: IMPERSONATION_REFUSAL_MESSAGE,
|
|
14400
|
+
tool: toolName
|
|
14401
|
+
}
|
|
14402
|
+
})
|
|
14403
|
+
}
|
|
14404
|
+
],
|
|
14405
|
+
isError: true
|
|
14406
|
+
};
|
|
14407
|
+
}
|
|
14408
|
+
|
|
14409
|
+
// src/channel-egress-tools.ts
|
|
14410
|
+
var DIRECT_CHAT_EGRESS_TOOLS = /* @__PURE__ */ new Set([
|
|
14411
|
+
"direct_chat.reply",
|
|
14412
|
+
"channel_request_input"
|
|
14413
|
+
]);
|
|
14414
|
+
|
|
14371
14415
|
// src/mcp-spawn-lock.ts
|
|
14372
14416
|
import {
|
|
14373
14417
|
existsSync,
|
|
@@ -14597,6 +14641,9 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
14597
14641
|
}));
|
|
14598
14642
|
mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
14599
14643
|
const { name, arguments: args } = req.params;
|
|
14644
|
+
if (isImpersonating() && !channelsEnabledOverride() && DIRECT_CHAT_EGRESS_TOOLS.has(name)) {
|
|
14645
|
+
return buildImpersonationRefusal(name);
|
|
14646
|
+
}
|
|
14600
14647
|
const progressResult = await progressRegistry.handle(name, args ?? {});
|
|
14601
14648
|
if (progressResult !== void 0) return progressResult;
|
|
14602
14649
|
if (name === "channel_request_input") {
|
package/dist/mcp/index.js
CHANGED
|
@@ -21165,7 +21165,7 @@ var server = new McpServer({
|
|
|
21165
21165
|
version: "0.1.0"
|
|
21166
21166
|
});
|
|
21167
21167
|
server.tool(
|
|
21168
|
-
"
|
|
21168
|
+
"kanban_list",
|
|
21169
21169
|
"List kanban board items for this agent. Returns active items and recently completed items (last 7 days).",
|
|
21170
21170
|
{},
|
|
21171
21171
|
async () => {
|
|
@@ -21194,7 +21194,7 @@ server.tool(
|
|
|
21194
21194
|
}
|
|
21195
21195
|
);
|
|
21196
21196
|
server.tool(
|
|
21197
|
-
"
|
|
21197
|
+
"kanban_add",
|
|
21198
21198
|
"Add a new item to the kanban board.",
|
|
21199
21199
|
{
|
|
21200
21200
|
title: external_exports.string().describe("Item title (max 200 chars)"),
|
|
@@ -21235,7 +21235,7 @@ server.tool(
|
|
|
21235
21235
|
}
|
|
21236
21236
|
);
|
|
21237
21237
|
server.tool(
|
|
21238
|
-
"
|
|
21238
|
+
"kanban_move",
|
|
21239
21239
|
"Move a kanban item to a different status column.",
|
|
21240
21240
|
{
|
|
21241
21241
|
id: external_exports.string().optional().describe("Item UUID (preferred)"),
|
|
@@ -21273,7 +21273,7 @@ server.tool(
|
|
|
21273
21273
|
}
|
|
21274
21274
|
);
|
|
21275
21275
|
server.tool(
|
|
21276
|
-
"
|
|
21276
|
+
"kanban_update",
|
|
21277
21277
|
"Update notes or result on a kanban item without changing its status.",
|
|
21278
21278
|
{
|
|
21279
21279
|
id: external_exports.string().optional().describe("Item UUID (preferred)"),
|
|
@@ -21324,7 +21324,7 @@ server.tool(
|
|
|
21324
21324
|
}
|
|
21325
21325
|
);
|
|
21326
21326
|
server.tool(
|
|
21327
|
-
"
|
|
21327
|
+
"kanban_progress",
|
|
21328
21328
|
"Report what you're doing RIGHT NOW on a kanban item. Overwrites a single live status line (the Step row on the Slack progress card the requester is watching) \u2014 not a log. Call it at the start of each phase of a long task (ideally ~every minute) so the requester can watch the work move. Each call also renews the task heartbeat. Pass an empty step to clear it.",
|
|
21329
21329
|
{
|
|
21330
21330
|
id: external_exports.string().optional().describe("Item UUID (preferred)"),
|
|
@@ -21362,7 +21362,7 @@ server.tool(
|
|
|
21362
21362
|
}
|
|
21363
21363
|
);
|
|
21364
21364
|
server.tool(
|
|
21365
|
-
"
|
|
21365
|
+
"kanban_done",
|
|
21366
21366
|
"Mark a kanban item as done with an optional result.",
|
|
21367
21367
|
{
|
|
21368
21368
|
id: external_exports.string().optional().describe("Item UUID (preferred)"),
|
|
@@ -21401,7 +21401,7 @@ server.tool(
|
|
|
21401
21401
|
}
|
|
21402
21402
|
);
|
|
21403
21403
|
server.tool(
|
|
21404
|
-
"
|
|
21404
|
+
"status_standup",
|
|
21405
21405
|
"Submit a daily standup update with yesterday, today, and blockers.",
|
|
21406
21406
|
{
|
|
21407
21407
|
yesterday: external_exports.string().describe("What was accomplished yesterday/last session"),
|
|
@@ -21434,7 +21434,7 @@ server.tool(
|
|
|
21434
21434
|
}
|
|
21435
21435
|
);
|
|
21436
21436
|
server.tool(
|
|
21437
|
-
"
|
|
21437
|
+
"status_update",
|
|
21438
21438
|
"Report current task progress or status update.",
|
|
21439
21439
|
{
|
|
21440
21440
|
current_tasks: external_exports.string().describe("Summary of what you are currently working on")
|
|
@@ -21461,7 +21461,7 @@ server.tool(
|
|
|
21461
21461
|
}
|
|
21462
21462
|
);
|
|
21463
21463
|
server.tool(
|
|
21464
|
-
"
|
|
21464
|
+
"drift_report",
|
|
21465
21465
|
"Report configuration drift detected in local agent files (CHARTER.md, TOOLS.md, etc.). Compares local file hashes against API-side versions and reports any differences.",
|
|
21466
21466
|
{
|
|
21467
21467
|
drifted_files: external_exports.array(external_exports.string()).describe('List of filenames that have drifted from provisioned state (e.g. ["CHARTER.md", "TOOLS.md"])'),
|
|
@@ -21484,7 +21484,7 @@ server.tool(
|
|
|
21484
21484
|
}
|
|
21485
21485
|
);
|
|
21486
21486
|
server.tool(
|
|
21487
|
-
"
|
|
21487
|
+
"token_refresh",
|
|
21488
21488
|
"Get a working OAuth access token for an integration. Returns the current cached token when it is still valid (the server-side cron rotates tokens every ~15 minutes) and forces a refresh only when the cached token is expiring or missing. Pass `integration_id` to force-refresh a specific integration after a 401/TokenExpired from the provider.",
|
|
21489
21489
|
{
|
|
21490
21490
|
integration_id: external_exports.string().optional().describe("Specific integration UUID to force-refresh (use after a 401 from the provider). If omitted, returns current tokens for all OAuth integrations, refreshing any that are within ~10 minutes of expiry.")
|
|
@@ -21578,7 +21578,7 @@ async function runAcpxCommand(args, cwd) {
|
|
|
21578
21578
|
});
|
|
21579
21579
|
}
|
|
21580
21580
|
server.tool(
|
|
21581
|
-
"
|
|
21581
|
+
"acpx_prompt",
|
|
21582
21582
|
"Send a prompt to a coding agent (claude, codex, openclaw) via ACP. Uses persistent sessions scoped to the working directory.",
|
|
21583
21583
|
{
|
|
21584
21584
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent to use"),
|
|
@@ -21603,7 +21603,7 @@ server.tool(
|
|
|
21603
21603
|
}
|
|
21604
21604
|
);
|
|
21605
21605
|
server.tool(
|
|
21606
|
-
"
|
|
21606
|
+
"acpx_exec",
|
|
21607
21607
|
"Run a one-shot task on a coding agent. Does not reuse sessions \u2014 fire and forget.",
|
|
21608
21608
|
{
|
|
21609
21609
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent to use"),
|
|
@@ -21622,7 +21622,7 @@ server.tool(
|
|
|
21622
21622
|
}
|
|
21623
21623
|
);
|
|
21624
21624
|
server.tool(
|
|
21625
|
-
"
|
|
21625
|
+
"acpx_spawn",
|
|
21626
21626
|
"Ensure an ACP session exists for a coding agent. Creates one if needed, reuses existing.",
|
|
21627
21627
|
{
|
|
21628
21628
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent to use"),
|
|
@@ -21642,7 +21642,7 @@ server.tool(
|
|
|
21642
21642
|
}
|
|
21643
21643
|
);
|
|
21644
21644
|
server.tool(
|
|
21645
|
-
"
|
|
21645
|
+
"acpx_cancel",
|
|
21646
21646
|
"Send a cooperative cancel to the running ACP session. Does not destroy state.",
|
|
21647
21647
|
{
|
|
21648
21648
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent to cancel"),
|
|
@@ -21662,7 +21662,7 @@ server.tool(
|
|
|
21662
21662
|
}
|
|
21663
21663
|
);
|
|
21664
21664
|
server.tool(
|
|
21665
|
-
"
|
|
21665
|
+
"acpx_status",
|
|
21666
21666
|
"Check the status of the current ACP session (running, idle, dead).",
|
|
21667
21667
|
{
|
|
21668
21668
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent to check"),
|
|
@@ -21676,7 +21676,7 @@ server.tool(
|
|
|
21676
21676
|
}
|
|
21677
21677
|
);
|
|
21678
21678
|
server.tool(
|
|
21679
|
-
"
|
|
21679
|
+
"acpx_close",
|
|
21680
21680
|
"Soft-close an ACP session. Terminates the process but preserves history.",
|
|
21681
21681
|
{
|
|
21682
21682
|
agent: external_exports.enum(["claude", "codex", "openclaw"]).describe("Coding agent session to close"),
|
|
@@ -21696,7 +21696,7 @@ server.tool(
|
|
|
21696
21696
|
}
|
|
21697
21697
|
);
|
|
21698
21698
|
server.tool(
|
|
21699
|
-
"
|
|
21699
|
+
"schedule_list",
|
|
21700
21700
|
"List your scheduled tasks (cron jobs, reminders, recurring work).",
|
|
21701
21701
|
{},
|
|
21702
21702
|
async () => {
|
|
@@ -21716,7 +21716,7 @@ server.tool(
|
|
|
21716
21716
|
}
|
|
21717
21717
|
);
|
|
21718
21718
|
server.tool(
|
|
21719
|
-
"
|
|
21719
|
+
"schedule_create",
|
|
21720
21720
|
"Create a new scheduled task. Supports cron expressions, intervals (every), and one-time reminders (at).",
|
|
21721
21721
|
{
|
|
21722
21722
|
name: external_exports.string().describe('Display name for the task (e.g. "Daily standup", "Check emails")'),
|
|
@@ -21752,10 +21752,10 @@ server.tool(
|
|
|
21752
21752
|
}
|
|
21753
21753
|
);
|
|
21754
21754
|
server.tool(
|
|
21755
|
-
"
|
|
21755
|
+
"schedule_delete",
|
|
21756
21756
|
"Delete a scheduled task by ID.",
|
|
21757
21757
|
{
|
|
21758
|
-
task_id: external_exports.string().describe("The task ID to delete (from
|
|
21758
|
+
task_id: external_exports.string().describe("The task ID to delete (from schedule_list)")
|
|
21759
21759
|
},
|
|
21760
21760
|
async (params) => {
|
|
21761
21761
|
await apiPost("/host/schedules/delete", {
|
|
@@ -21768,12 +21768,17 @@ server.tool(
|
|
|
21768
21768
|
}
|
|
21769
21769
|
);
|
|
21770
21770
|
server.tool(
|
|
21771
|
-
"
|
|
21771
|
+
"schedule_update",
|
|
21772
21772
|
// Lead with user phrasings the agent should match against, then explain
|
|
21773
21773
|
// the two modes. Order matters for tool selection.
|
|
21774
|
-
|
|
21774
|
+
// CR PR #1601: field names + delivery_to shape brought into line with the
|
|
21775
|
+
// actual zod schema below. The prior copy referenced `cron_expression` /
|
|
21776
|
+
// `interval` (which the schema doesn't accept) and described
|
|
21777
|
+
// `delivery_to` as a raw channel ID (it's a structured DeliveryTargetSchema
|
|
21778
|
+
// object). Agents reading the old wording were producing invalid calls.
|
|
21779
|
+
'Update an existing scheduled task. USE THIS TOOL when the user says: "change the schedule for X", "run the daily report at 10am instead of 9am", "pause the standup schedule", "move the weekly digest to Mondays", "make this report more concise", "add a section about X to the daily summary", "tell the Y schedule to also cover Z", "post these to the Customer Success channel from now on". Two modes: (1) DIRECT FIELD UPDATES \u2014 set schedule_expr/schedule_every/schedule_at/timezone/delivery_channel/delivery_to/enabled/name/prompt to change those values directly (pair the expression field with the matching schedule_kind: cron/every/at). (2) LLM DESCRIPTION REWRITE \u2014 pass description_feedback to have the platform rewrite the task prompt based on natural-language feedback (e.g. "make this more concise" or "add a section about the team backlog"). Default for LLM rewrites is preview-then-apply: call once to get the proposed rewrite, show the diff to the user, call again with auto_apply=true on confirmation. Direct field updates apply immediately. The two modes are mutually exclusive \u2014 one request is EITHER a field update OR a description rewrite.\n\nIMPORTANT \u2014 `delivery_to` is a structured target object, not a human channel name. If the user references a Slack channel by name ("the Customer Success channel"), call `mcp__slack__slack.list_channels` first with `query: "customer success"` to get the matching `id`, then pass `delivery_to: { kind: "channel", provider: "slack", channel_id: "<id>" }`. Do not guess channel ids.',
|
|
21775
21780
|
{
|
|
21776
|
-
task_id: external_exports.string().describe("The task ID to update (from
|
|
21781
|
+
task_id: external_exports.string().describe("The task ID to update (from schedule_list)"),
|
|
21777
21782
|
// Direct-field mode
|
|
21778
21783
|
name: external_exports.string().optional().describe("New task display name"),
|
|
21779
21784
|
schedule_kind: external_exports.enum(["cron", "every", "at"]).optional().describe(
|
|
@@ -21796,7 +21801,7 @@ server.tool(
|
|
|
21796
21801
|
'Where results are delivered (e.g. "slack", "telegram", "email")'
|
|
21797
21802
|
),
|
|
21798
21803
|
delivery_to: DeliveryTargetSchema.optional().describe(
|
|
21799
|
-
"Structured delivery target (see
|
|
21804
|
+
"Structured delivery target (see schedule_create description for shape). Pass null-equivalent by omitting the field; pass a new structured target to change routing."
|
|
21800
21805
|
),
|
|
21801
21806
|
enabled: external_exports.boolean().optional().describe(
|
|
21802
21807
|
"Set to false to pause the schedule without deleting it"
|
|
@@ -21833,7 +21838,7 @@ server.tool(
|
|
|
21833
21838
|
if (!data.applied) {
|
|
21834
21839
|
lines.push("");
|
|
21835
21840
|
lines.push("---");
|
|
21836
|
-
lines.push("Show this diff to the user. If they confirm, call `
|
|
21841
|
+
lines.push("Show this diff to the user. If they confirm, call `schedule_update` again with the same `task_id` and `description_feedback` plus `auto_apply: true` to commit the change.");
|
|
21837
21842
|
}
|
|
21838
21843
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21839
21844
|
}
|
|
@@ -21846,8 +21851,8 @@ server.tool(
|
|
|
21846
21851
|
}
|
|
21847
21852
|
);
|
|
21848
21853
|
server.tool(
|
|
21849
|
-
"
|
|
21850
|
-
"List team knowledge entries (ids, titles, source types). Use before
|
|
21854
|
+
"knowledge_list",
|
|
21855
|
+
"List team knowledge entries (ids, titles, source types). Use before knowledge_add to check if a fact is already saved, or before knowledge_update/delete to find the target id. Returns metadata only \u2014 fetch the full content via the webapp if you need to read an entry back.",
|
|
21851
21856
|
{},
|
|
21852
21857
|
async () => {
|
|
21853
21858
|
const data = await apiPost("/host/my-knowledge", {
|
|
@@ -21864,8 +21869,8 @@ server.tool(
|
|
|
21864
21869
|
}
|
|
21865
21870
|
);
|
|
21866
21871
|
server.tool(
|
|
21867
|
-
"
|
|
21868
|
-
"Save a new durable fact or learning to the team knowledge base. Use when the user explicitly asks you to save/remember something, or when you discover information that will be useful on future runs (e.g. a tenant id, an API key name, a company policy). Do NOT use for tasks (use
|
|
21872
|
+
"knowledge_add",
|
|
21873
|
+
"Save a new durable fact or learning to the team knowledge base. Use when the user explicitly asks you to save/remember something, or when you discover information that will be useful on future runs (e.g. a tenant id, an API key name, a company policy). Do NOT use for tasks (use kanban_add) or trivial one-off answers. Returns the new entry id so you can target it with knowledge_update / knowledge_delete.",
|
|
21869
21874
|
{
|
|
21870
21875
|
title: external_exports.string().describe("Short title summarising the entry (max 200 chars)"),
|
|
21871
21876
|
content: external_exports.string().describe("The knowledge content \u2014 the actual fact, learning, or reference material"),
|
|
@@ -21903,10 +21908,10 @@ server.tool(
|
|
|
21903
21908
|
}
|
|
21904
21909
|
);
|
|
21905
21910
|
server.tool(
|
|
21906
|
-
"
|
|
21907
|
-
"Edit an existing knowledge entry by id. Use to correct a saved fact or refine its wording. Only the fields you pass are changed; others are left alone. Call
|
|
21911
|
+
"knowledge_update",
|
|
21912
|
+
"Edit an existing knowledge entry by id. Use to correct a saved fact or refine its wording. Only the fields you pass are changed; others are left alone. Call knowledge_list first if you need to find the target id.",
|
|
21908
21913
|
{
|
|
21909
|
-
id: external_exports.string().describe("Knowledge entry id (uuid) \u2014 from
|
|
21914
|
+
id: external_exports.string().describe("Knowledge entry id (uuid) \u2014 from knowledge_list or knowledge_add"),
|
|
21910
21915
|
title: external_exports.string().optional().describe("New title (max 200 chars). Regenerates the slug."),
|
|
21911
21916
|
content: external_exports.string().optional().describe("Replacement content for the entry"),
|
|
21912
21917
|
is_active: external_exports.boolean().optional().describe("Set false to soft-disable without deleting")
|
|
@@ -21914,7 +21919,7 @@ server.tool(
|
|
|
21914
21919
|
async (params) => {
|
|
21915
21920
|
if (params.title === void 0 && params.content === void 0 && params.is_active === void 0) {
|
|
21916
21921
|
return {
|
|
21917
|
-
content: [{ type: "text", text: "Error:
|
|
21922
|
+
content: [{ type: "text", text: "Error: knowledge_update needs at least one of title, content, or is_active." }],
|
|
21918
21923
|
isError: true
|
|
21919
21924
|
};
|
|
21920
21925
|
}
|
|
@@ -21940,8 +21945,8 @@ server.tool(
|
|
|
21940
21945
|
}
|
|
21941
21946
|
);
|
|
21942
21947
|
server.tool(
|
|
21943
|
-
"
|
|
21944
|
-
"Remove a knowledge entry permanently. Prefer
|
|
21948
|
+
"knowledge_delete",
|
|
21949
|
+
"Remove a knowledge entry permanently. Prefer knowledge_update with is_active=false for temporary hides; use delete only when the fact was wrong or no longer applies.",
|
|
21945
21950
|
{
|
|
21946
21951
|
id: external_exports.string().describe("Knowledge entry id (uuid) to delete")
|
|
21947
21952
|
},
|
|
@@ -21961,8 +21966,8 @@ server.tool(
|
|
|
21961
21966
|
}
|
|
21962
21967
|
);
|
|
21963
21968
|
server.tool(
|
|
21964
|
-
"
|
|
21965
|
-
`List the plugins currently installed on this agent, with their current typed context values and a preview of any freeform overrides. Use this when the user asks "what plugins do I have?", "show me the plugins", "what is the Coding plugin configured with?", or before calling
|
|
21969
|
+
"plugin_list",
|
|
21970
|
+
`List the plugins currently installed on this agent, with their current typed context values and a preview of any freeform overrides. Use this when the user asks "what plugins do I have?", "show me the plugins", "what is the Coding plugin configured with?", or before calling plugin_improve to see what fields exist and what they're set to.`,
|
|
21966
21971
|
{},
|
|
21967
21972
|
async () => {
|
|
21968
21973
|
const data = await apiPost("/host/list-plugins", {
|
|
@@ -21994,12 +21999,12 @@ server.tool(
|
|
|
21994
21999
|
}
|
|
21995
22000
|
lines.push("");
|
|
21996
22001
|
}
|
|
21997
|
-
lines.push("To update any plugin's behavior, use the `
|
|
22002
|
+
lines.push("To update any plugin's behavior, use the `plugin_improve` tool with the user's feedback.");
|
|
21998
22003
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
21999
22004
|
}
|
|
22000
22005
|
);
|
|
22001
22006
|
server.tool(
|
|
22002
|
-
"
|
|
22007
|
+
"plugin_improve",
|
|
22003
22008
|
// Description leads with the user phrasings the agent should match against,
|
|
22004
22009
|
// then explains the safety rule. Order matters — Claude\'s tool selection
|
|
22005
22010
|
// weights the first line of a description heavily.
|
|
@@ -22055,7 +22060,7 @@ server.tool(
|
|
|
22055
22060
|
if (!data.applied) {
|
|
22056
22061
|
lines.push("");
|
|
22057
22062
|
lines.push("---");
|
|
22058
|
-
lines.push("Show this diff to the user. If they confirm, call `
|
|
22063
|
+
lines.push("Show this diff to the user. If they confirm, call `plugin_improve` again with `auto_apply: true` and the same arguments to apply.");
|
|
22059
22064
|
} else {
|
|
22060
22065
|
lines.push("");
|
|
22061
22066
|
lines.push("Update applied. The manager will re-render the plugin's SKILL.md files on the next refresh cycle.");
|
|
@@ -22073,7 +22078,7 @@ var WidgetDefSchema = external_exports.object({
|
|
|
22073
22078
|
refresh_cron: external_exports.string().optional().describe('Optional per-widget cron override (e.g. "0 6 * * *" for 6am daily).')
|
|
22074
22079
|
});
|
|
22075
22080
|
server.tool(
|
|
22076
|
-
"
|
|
22081
|
+
"dashboards_list",
|
|
22077
22082
|
'List dashboards visible to your team. Use to answer "what dashboards exist", "show me the dashboards I built", or before authoring a new one to avoid duplicating an existing slug.',
|
|
22078
22083
|
{
|
|
22079
22084
|
status: external_exports.enum(["draft", "active", "archived"]).optional().describe("Filter by status. Defaults to 'active'.")
|
|
@@ -22095,8 +22100,8 @@ ${lines.join("\n")}` }] };
|
|
|
22095
22100
|
}
|
|
22096
22101
|
);
|
|
22097
22102
|
server.tool(
|
|
22098
|
-
"
|
|
22099
|
-
"Get full metadata for one dashboard by slug \u2014 title, description, scope, status, widget list, last refresh per widget. Use when investigating a specific dashboard or before editing it via
|
|
22103
|
+
"dashboards_show",
|
|
22104
|
+
"Get full metadata for one dashboard by slug \u2014 title, description, scope, status, widget list, last refresh per widget. Use when investigating a specific dashboard or before editing it via dashboards_upsert.",
|
|
22100
22105
|
{
|
|
22101
22106
|
slug: external_exports.string().describe("Dashboard slug, e.g. 'finance-overview'.")
|
|
22102
22107
|
},
|
|
@@ -22128,7 +22133,7 @@ URL: ${url}` : "",
|
|
|
22128
22133
|
}
|
|
22129
22134
|
);
|
|
22130
22135
|
server.tool(
|
|
22131
|
-
"
|
|
22136
|
+
"dashboards_upsert",
|
|
22132
22137
|
// The description leads with the user phrasings the agent should match
|
|
22133
22138
|
// against AND the explicit forbidden alternatives. Tool selection weights
|
|
22134
22139
|
// the first sentence heavily, so we burn it on the right behavior.
|
|
@@ -22168,8 +22173,8 @@ URL: ${url}
|
|
|
22168
22173
|
}
|
|
22169
22174
|
);
|
|
22170
22175
|
server.tool(
|
|
22171
|
-
"
|
|
22172
|
-
"Mark widgets on a dashboard as needing refresh. Use this when authoring a new dashboard (to populate the first snapshot) or when you want widgets to update on the next
|
|
22176
|
+
"dashboards_request_refresh",
|
|
22177
|
+
"Mark widgets on a dashboard as needing refresh. Use this when authoring a new dashboard (to populate the first snapshot) or when you want widgets to update on the next dashboards_pending_refreshes call. Pass widget_id to flag just one widget; omit to flag every widget.",
|
|
22173
22178
|
{
|
|
22174
22179
|
slug: external_exports.string(),
|
|
22175
22180
|
widget_id: external_exports.string().optional().describe("Optional. Omit to flag all widgets on the dashboard."),
|
|
@@ -22189,8 +22194,8 @@ server.tool(
|
|
|
22189
22194
|
}
|
|
22190
22195
|
);
|
|
22191
22196
|
server.tool(
|
|
22192
|
-
"
|
|
22193
|
-
"Return widgets that need refresh \u2014 by default ONLY those flagged pending (user clicked \u21BB in the iframe, or someone called request_refresh). Pass slug=<dashboard slug> to instead get every widget on that dashboard so you can proactively refresh after authoring or when the user asks 'update the X dashboard'. Pass include_all=true with no slug to get every flagged-or-not widget across all your dashboards. Each entry: {slug, widget_id, kind, prompt, schema, requested_at}. Loop over them, read the prompt, gather data with your other tools, match the schema, and call
|
|
22197
|
+
"dashboards_pending_refreshes",
|
|
22198
|
+
"Return widgets that need refresh \u2014 by default ONLY those flagged pending (user clicked \u21BB in the iframe, or someone called request_refresh). Pass slug=<dashboard slug> to instead get every widget on that dashboard so you can proactively refresh after authoring or when the user asks 'update the X dashboard'. Pass include_all=true with no slug to get every flagged-or-not widget across all your dashboards. Each entry: {slug, widget_id, kind, prompt, schema, requested_at}. Loop over them, read the prompt, gather data with your other tools, match the schema, and call dashboards_persist_widget for each.",
|
|
22194
22199
|
{
|
|
22195
22200
|
slug: external_exports.string().optional().describe("Restrict to one dashboard by slug. When set, returns every widget on that dashboard \u2014 flagged or not \u2014 so you can refresh the whole thing without calling request_refresh first."),
|
|
22196
22201
|
include_all: external_exports.boolean().optional().describe("When true (and no slug), returns every widget on every dashboard you own \u2014 flagged or not. Use sparingly; one refresh = one LLM call.")
|
|
@@ -22209,7 +22214,7 @@ server.tool(
|
|
|
22209
22214
|
}
|
|
22210
22215
|
);
|
|
22211
22216
|
server.tool(
|
|
22212
|
-
"
|
|
22217
|
+
"dashboards_persist_widget",
|
|
22213
22218
|
"Persist refreshed data for one widget. The platform validates `data` against the widget's stored JSON Schema server-side \u2014 invalid output is rejected. Call this once per widget after gathering data with your tools.",
|
|
22214
22219
|
{
|
|
22215
22220
|
slug: external_exports.string(),
|
|
@@ -22279,32 +22284,46 @@ async function forwardToolCall(toolName, args) {
|
|
|
22279
22284
|
FORWARD_TOOL_CALL_TIMEOUT_MS
|
|
22280
22285
|
);
|
|
22281
22286
|
}
|
|
22287
|
+
var LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
22288
|
+
"kanban_list",
|
|
22289
|
+
"kanban_add",
|
|
22290
|
+
"kanban_move",
|
|
22291
|
+
"kanban_update",
|
|
22292
|
+
"kanban_progress",
|
|
22293
|
+
"kanban_done",
|
|
22294
|
+
"status_standup",
|
|
22295
|
+
"status_update",
|
|
22296
|
+
"drift_report",
|
|
22297
|
+
"token_refresh",
|
|
22298
|
+
"acpx_prompt",
|
|
22299
|
+
"acpx_exec",
|
|
22300
|
+
"acpx_spawn",
|
|
22301
|
+
"acpx_cancel",
|
|
22302
|
+
"acpx_status",
|
|
22303
|
+
"acpx_close",
|
|
22304
|
+
"schedule_list",
|
|
22305
|
+
"schedule_create",
|
|
22306
|
+
"schedule_delete",
|
|
22307
|
+
"schedule_update",
|
|
22308
|
+
"knowledge_list",
|
|
22309
|
+
"knowledge_add",
|
|
22310
|
+
"knowledge_update",
|
|
22311
|
+
"knowledge_delete",
|
|
22312
|
+
"plugin_list",
|
|
22313
|
+
"plugin_improve",
|
|
22314
|
+
"dashboards_list",
|
|
22315
|
+
"dashboards_show",
|
|
22316
|
+
"dashboards_upsert",
|
|
22317
|
+
"dashboards_request_refresh",
|
|
22318
|
+
"dashboards_pending_refreshes",
|
|
22319
|
+
"dashboards_persist_widget",
|
|
22320
|
+
"projects_list",
|
|
22321
|
+
"projects_get",
|
|
22322
|
+
"projects_get_source_chunk"
|
|
22323
|
+
]);
|
|
22282
22324
|
async function registerForwardedApiTools() {
|
|
22283
22325
|
const apiTools = await discoverApiTools();
|
|
22284
|
-
const localToolNames =
|
|
22285
|
-
"kanban.list",
|
|
22286
|
-
"kanban.add",
|
|
22287
|
-
"kanban.move",
|
|
22288
|
-
"kanban.update",
|
|
22289
|
-
"kanban.progress",
|
|
22290
|
-
"kanban.done",
|
|
22291
|
-
"status.standup",
|
|
22292
|
-
"status.update",
|
|
22293
|
-
"drift.report",
|
|
22294
|
-
"token.refresh",
|
|
22295
|
-
"acpx.prompt",
|
|
22296
|
-
"acpx.exec",
|
|
22297
|
-
"acpx.spawn",
|
|
22298
|
-
"acpx.cancel",
|
|
22299
|
-
"knowledge.list",
|
|
22300
|
-
"knowledge.search",
|
|
22301
|
-
"knowledge.read",
|
|
22302
|
-
"knowledge.add",
|
|
22303
|
-
"knowledge.update",
|
|
22304
|
-
"knowledge.delete",
|
|
22305
|
-
"plugin.list",
|
|
22306
|
-
"plugin.improve"
|
|
22307
|
-
]);
|
|
22326
|
+
const localToolNames = LOCAL_TOOL_NAMES;
|
|
22308
22327
|
let registered = 0;
|
|
22309
22328
|
let skipped = 0;
|
|
22310
22329
|
for (const tool of apiTools) {
|
|
@@ -22335,7 +22354,7 @@ async function registerForwardedApiTools() {
|
|
|
22335
22354
|
return { registered, skipped };
|
|
22336
22355
|
}
|
|
22337
22356
|
server.tool(
|
|
22338
|
-
"
|
|
22357
|
+
"projects_list",
|
|
22339
22358
|
"List artefacts (websites, slide decks, mockups) this agent has previously published. Call this BEFORE re-publishing to find and modify existing work instead of creating duplicates.",
|
|
22340
22359
|
{
|
|
22341
22360
|
publisher: external_exports.string().optional().describe('Filter by publisher (e.g. "here-now")'),
|
|
@@ -22361,10 +22380,10 @@ server.tool(
|
|
|
22361
22380
|
}
|
|
22362
22381
|
);
|
|
22363
22382
|
server.tool(
|
|
22364
|
-
"
|
|
22365
|
-
"Fetch metadata for a single published project. Returns the project record without the source HTML \u2014 call
|
|
22383
|
+
"projects_get",
|
|
22384
|
+
"Fetch metadata for a single published project. Returns the project record without the source HTML \u2014 call projects_get_source_chunk to retrieve the HTML in chunks when you need to modify it.",
|
|
22366
22385
|
{
|
|
22367
|
-
project_id: external_exports.string().uuid().describe("The project id returned by
|
|
22386
|
+
project_id: external_exports.string().uuid().describe("The project id returned by projects_list")
|
|
22368
22387
|
},
|
|
22369
22388
|
async (params) => {
|
|
22370
22389
|
const data = await apiPost(`/host/published-project/${encodeURIComponent(params.project_id)}`, {
|
|
@@ -22379,16 +22398,16 @@ server.tool(
|
|
|
22379
22398
|
`Published: ${p.published_at}`,
|
|
22380
22399
|
p.expires_at ? `Expires: ${p.expires_at}` : null,
|
|
22381
22400
|
`Last modified: ${p.last_modified_at}`,
|
|
22382
|
-
p.has_source_html ? `Source HTML: ${p.source_html_length} bytes (call
|
|
22401
|
+
p.has_source_html ? `Source HTML: ${p.source_html_length} bytes (call projects_get_source_chunk to fetch)` : "(source HTML not preserved for this project)"
|
|
22383
22402
|
].filter((s) => s !== null).join("\n");
|
|
22384
22403
|
return { content: [{ type: "text", text: header }] };
|
|
22385
22404
|
}
|
|
22386
22405
|
);
|
|
22387
22406
|
server.tool(
|
|
22388
|
-
"
|
|
22389
|
-
"Fetch a chunk of source HTML for a published project. Use this to assemble the HTML for modification when
|
|
22407
|
+
"projects_get_source_chunk",
|
|
22408
|
+
"Fetch a chunk of source HTML for a published project. Use this to assemble the HTML for modification when projects_get reports a non-zero source_html_length.",
|
|
22390
22409
|
{
|
|
22391
|
-
project_id: external_exports.string().uuid().describe("The project id returned by
|
|
22410
|
+
project_id: external_exports.string().uuid().describe("The project id returned by projects_list"),
|
|
22392
22411
|
offset: external_exports.number().int().min(0).describe("Byte offset into the source HTML to start from"),
|
|
22393
22412
|
limit: external_exports.number().int().min(1).max(65536).optional().describe("Max bytes to return (default 32KB, max 64KB)")
|
|
22394
22413
|
},
|
|
@@ -14813,6 +14813,53 @@ function isMode(value) {
|
|
|
14813
14813
|
return value === "thinking" || value === "working" || value === "waiting";
|
|
14814
14814
|
}
|
|
14815
14815
|
|
|
14816
|
+
// src/impersonation.ts
|
|
14817
|
+
var ENV_VAR = "AGT_ACT_AS_AGENT_ID";
|
|
14818
|
+
var OVERRIDE_ENV_VAR = "ENABLE_IMPERSONATION_CHANNELS";
|
|
14819
|
+
function isImpersonating() {
|
|
14820
|
+
return impersonatedAgentId() !== null;
|
|
14821
|
+
}
|
|
14822
|
+
function impersonatedAgentId() {
|
|
14823
|
+
const raw = process.env[ENV_VAR];
|
|
14824
|
+
if (typeof raw !== "string") return null;
|
|
14825
|
+
const trimmed = raw.trim();
|
|
14826
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
14827
|
+
}
|
|
14828
|
+
function channelsEnabledOverride() {
|
|
14829
|
+
const raw = process.env[OVERRIDE_ENV_VAR];
|
|
14830
|
+
if (typeof raw !== "string") return false;
|
|
14831
|
+
const trimmed = raw.trim().toLowerCase();
|
|
14832
|
+
return trimmed === "1" || trimmed === "true" || trimmed === "yes" || trimmed === "on";
|
|
14833
|
+
}
|
|
14834
|
+
var IMPERSONATION_REFUSAL_CODE = "CHANNEL.IMPERSONATION_DISABLED";
|
|
14835
|
+
var IMPERSONATION_REFUSAL_MESSAGE = "Posting as an agent under impersonation is disabled in v0 \u2014 quit impersonation with `agt impersonate exit` to send as yourself.";
|
|
14836
|
+
function buildImpersonationRefusal(toolName) {
|
|
14837
|
+
return {
|
|
14838
|
+
content: [
|
|
14839
|
+
{
|
|
14840
|
+
type: "text",
|
|
14841
|
+
text: JSON.stringify({
|
|
14842
|
+
error: {
|
|
14843
|
+
code: IMPERSONATION_REFUSAL_CODE,
|
|
14844
|
+
message: IMPERSONATION_REFUSAL_MESSAGE,
|
|
14845
|
+
tool: toolName
|
|
14846
|
+
}
|
|
14847
|
+
})
|
|
14848
|
+
}
|
|
14849
|
+
],
|
|
14850
|
+
isError: true
|
|
14851
|
+
};
|
|
14852
|
+
}
|
|
14853
|
+
|
|
14854
|
+
// src/channel-egress-tools.ts
|
|
14855
|
+
var SLACK_EGRESS_TOOLS = /* @__PURE__ */ new Set([
|
|
14856
|
+
"slack.reply",
|
|
14857
|
+
"slack.react",
|
|
14858
|
+
"slack.send_structured",
|
|
14859
|
+
"slack.ask_user",
|
|
14860
|
+
"slack.upload_file"
|
|
14861
|
+
]);
|
|
14862
|
+
|
|
14816
14863
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
14817
14864
|
import process2 from "process";
|
|
14818
14865
|
|
|
@@ -16909,6 +16956,9 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
16909
16956
|
}));
|
|
16910
16957
|
mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
16911
16958
|
const { name, arguments: args } = req.params;
|
|
16959
|
+
if (isImpersonating() && !channelsEnabledOverride() && SLACK_EGRESS_TOOLS.has(name)) {
|
|
16960
|
+
return buildImpersonationRefusal(name);
|
|
16961
|
+
}
|
|
16912
16962
|
const progressResult = await progressRegistry.handle(name, args ?? {});
|
|
16913
16963
|
if (progressResult !== void 0) return progressResult;
|
|
16914
16964
|
if (name === "slack.reply") {
|