@integrity-labs/agt-cli 0.14.15 → 0.15.0

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/mcp/index.js CHANGED
@@ -21009,6 +21009,7 @@ var AGT_HOST = process.env.AGT_HOST;
21009
21009
  var AGT_API_KEY = process.env.AGT_API_KEY;
21010
21010
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID;
21011
21011
  var AGT_AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME;
21012
+ var AGT_APP_URL = (process.env.AGT_APP_URL ?? "").replace(/\/+$/, "");
21012
21013
  var AGT_TOKEN = process.env.AGT_TOKEN ?? "";
21013
21014
  if (!AGT_HOST || !AGT_AGENT_ID || !AGT_TOKEN && !AGT_API_KEY) {
21014
21015
  console.error(
@@ -21935,6 +21936,169 @@ server.tool(
21935
21936
  return { content: [{ type: "text", text: lines.join("\n") }] };
21936
21937
  }
21937
21938
  );
21939
+ var WidgetDefSchema = external_exports.object({
21940
+ id: external_exports.string().describe("Stable id within the dashboard, e.g. 'kpi.revenue' or 'chart.cashflow'."),
21941
+ kind: external_exports.enum(["kpi", "area", "line", "bar", "donut"]).describe(
21942
+ "Widget kind. The kind picks both the renderer AND the data shape your refresh must produce:\n kpi \u2192 { value, [delta], [deltaTone: positive|negative|neutral], [values: number[]], [color: '#rrggbb'], [label] }\n area \u2192 { categories: string[], series: [{ name, data: number[] }] }\n line \u2192 { categories: string[], series: [{ name, data: number[] }] }\n bar \u2192 { categories: string[], series: [{ name, data: number[] }] }\n donut \u2192 { labels: string[], series: number[] }"
21943
+ ),
21944
+ title: external_exports.string().optional().describe("Title shown above the widget."),
21945
+ prompt: external_exports.string().describe("What future-you reads on every refresh. Tight, specific \u2014 e.g. 'Total Xero revenue last 30 days. value as $NN.NN, delta vs prior 30d, values = daily revenue.'"),
21946
+ refresh_cron: external_exports.string().optional().describe('Optional per-widget cron override (e.g. "0 6 * * *" for 6am daily).')
21947
+ });
21948
+ server.tool(
21949
+ "dashboards.list",
21950
+ '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.',
21951
+ {
21952
+ status: external_exports.enum(["draft", "active", "archived"]).optional().describe("Filter by status. Defaults to 'active'.")
21953
+ },
21954
+ async (params) => {
21955
+ const data = await apiPost("/host/dashboards/list", {
21956
+ agent_id: AGT_AGENT_ID,
21957
+ // Don't force a default — let the server decide (currently draft +
21958
+ // active so a freshly-authored draft surfaces without a publish step).
21959
+ status: params.status
21960
+ });
21961
+ if (!data.dashboards.length) return { content: [{ type: "text", text: "No dashboards yet." }] };
21962
+ const lines = data.dashboards.map((d) => {
21963
+ const url = AGT_APP_URL ? ` ${AGT_APP_URL}/dashboards/${d.id}` : "";
21964
+ return `- ${d.title} \`${d.slug}\` \u2014 ${d.scope}/${d.status} \xB7 refreshed: ${d.last_refreshed_at ?? "never"}${url}`;
21965
+ });
21966
+ return { content: [{ type: "text", text: `Dashboards (${data.dashboards.length}):
21967
+ ${lines.join("\n")}` }] };
21968
+ }
21969
+ );
21970
+ server.tool(
21971
+ "dashboards.show",
21972
+ "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.",
21973
+ {
21974
+ slug: external_exports.string().describe("Dashboard slug, e.g. 'finance-overview'.")
21975
+ },
21976
+ async (params) => {
21977
+ const data = await apiPost("/host/dashboards/show", {
21978
+ agent_id: AGT_AGENT_ID,
21979
+ slug: params.slug
21980
+ });
21981
+ const d = data.dashboard;
21982
+ const widgetLines = (d.widgets ?? []).map((w) => {
21983
+ const last = d.structured_data?.[w.id]?.refreshed_at ?? "never";
21984
+ return ` - ${w.title ?? w.id} \`${w.id}\` (${w.kind}) \u2014 refreshed: ${last}`;
21985
+ });
21986
+ const url = AGT_APP_URL ? `${AGT_APP_URL}/dashboards/${d.id}` : null;
21987
+ const out = [
21988
+ `# ${d.title} \`${d.slug}\``,
21989
+ d.description ? `
21990
+ ${d.description}` : "",
21991
+ url ? `
21992
+ URL: ${url}` : "",
21993
+ "",
21994
+ `Scope: ${d.scope} \xB7 Status: ${d.status} \xB7 Version: ${d.version}`,
21995
+ `Last refreshed: ${d.last_refreshed_at ?? "never"}`,
21996
+ "",
21997
+ `## Widgets (${d.widgets?.length ?? 0})`,
21998
+ ...widgetLines.length ? widgetLines : [" (none)"]
21999
+ ];
22000
+ return { content: [{ type: "text", text: out.join("\n") }] };
22001
+ }
22002
+ );
22003
+ server.tool(
22004
+ "dashboards.upsert",
22005
+ // The description leads with the user phrasings the agent should match
22006
+ // against AND the explicit forbidden alternatives. Tool selection weights
22007
+ // the first sentence heavily, so we burn it on the right behavior.
22008
+ 'Create or update a dashboard owned by this agent. THIS is how you ship dashboards \u2014 when a user asks you to "build a dashboard", "make a chart", "create a report", "track X over time", or "summarize Y as a dashboard", reach for THIS tool. Do NOT generate static HTML, push to GitHub Pages or S3, take a Chrome-headless screenshot, or sendPhoto via Telegram unless the user explicitly asks for one of those formats. Dashboards built through this tool render in the Augmented console, refresh on demand via your runtime, and follow team scope/policy. New rows pin to scope=agent \u2014 widening to team/org is admin-only. Slug must be unique per agent. Bumps version on every write.',
22009
+ {
22010
+ slug: external_exports.string().describe('Kebab-case identifier, unique per agent (e.g. "eng-overview").'),
22011
+ title: external_exports.string().describe("Title shown at the top of the dashboard and in the list view."),
22012
+ description: external_exports.string().optional().describe("One-line description for the list view."),
22013
+ widgets: external_exports.array(WidgetDefSchema).describe("Widget definitions. Each widget needs id, kind, prompt. Mix kinds \u2014 KPI tiles for headlines, area/line for trends, bar for comparisons, donut for proportions. Schema is derived server-side from kind \u2014 you do not author it.")
22014
+ },
22015
+ async (params) => {
22016
+ const data = await apiPost(
22017
+ "/host/dashboards/upsert",
22018
+ {
22019
+ agent_id: AGT_AGENT_ID,
22020
+ slug: params.slug,
22021
+ title: params.title,
22022
+ description: params.description,
22023
+ // The webapp renders standard chrome from the kind-driven widget
22024
+ // list. Stored html / csp / *_domains are no longer agent-authored;
22025
+ // server fills empty defaults if you don't pass any.
22026
+ widgets: params.widgets
22027
+ }
22028
+ );
22029
+ const base = AGT_APP_URL || "<console-origin>";
22030
+ const url = `${base}/dashboards/${data.dashboard_id}`;
22031
+ return {
22032
+ content: [
22033
+ {
22034
+ type: "text",
22035
+ text: `Dashboard \`${data.slug}\` saved at version ${data.version} (${data.scope}/${data.status}).
22036
+ URL: ${url}
22037
+ ` + (AGT_APP_URL ? "When you tell the user it's live, ALWAYS quote this full URL \u2014 never paste a relative path like /dashboards." : "AGT_APP_URL is not set on this host \u2014 replace <console-origin> with your console URL before quoting to the user, or ask the operator to set AGT_APP_URL.")
22038
+ }
22039
+ ]
22040
+ };
22041
+ }
22042
+ );
22043
+ server.tool(
22044
+ "dashboards.request_refresh",
22045
+ "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.",
22046
+ {
22047
+ slug: external_exports.string(),
22048
+ widget_id: external_exports.string().optional().describe("Optional. Omit to flag all widgets on the dashboard."),
22049
+ reason: external_exports.string().optional()
22050
+ },
22051
+ async (params) => {
22052
+ const data = await apiPost(
22053
+ "/host/dashboards/request-refresh",
22054
+ {
22055
+ agent_id: AGT_AGENT_ID,
22056
+ slug: params.slug,
22057
+ widget_id: params.widget_id,
22058
+ reason: params.reason
22059
+ }
22060
+ );
22061
+ return { content: [{ type: "text", text: `Flagged ${data.widget_ids.length} widget(s) pending refresh on \`${params.slug}\`.` }] };
22062
+ }
22063
+ );
22064
+ server.tool(
22065
+ "dashboards.pending_refreshes",
22066
+ "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.",
22067
+ {
22068
+ 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."),
22069
+ 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.")
22070
+ },
22071
+ async (params) => {
22072
+ const data = await apiPost("/host/dashboards/pending", {
22073
+ agent_id: AGT_AGENT_ID,
22074
+ slug: params.slug,
22075
+ include_all: params.include_all
22076
+ });
22077
+ if (!data.pending.length) {
22078
+ const hint = params.slug ? `No widgets on dashboard \`${params.slug}\` (or the dashboard isn't yours).` : "No widgets are flagged for refresh. To refresh a specific dashboard now, call this with `slug` set \u2014 that returns every widget on it. To refresh all your dashboards, pass `include_all: true`.";
22079
+ return { content: [{ type: "text", text: hint }] };
22080
+ }
22081
+ return { content: [{ type: "text", text: JSON.stringify(data.pending, null, 2) }] };
22082
+ }
22083
+ );
22084
+ server.tool(
22085
+ "dashboards.persist_widget",
22086
+ "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.",
22087
+ {
22088
+ slug: external_exports.string(),
22089
+ widget_id: external_exports.string(),
22090
+ data: external_exports.unknown().describe("JSON value matching the widget kind's schema.")
22091
+ },
22092
+ async (params) => {
22093
+ const result = await apiPost("/host/dashboards/persist", {
22094
+ agent_id: AGT_AGENT_ID,
22095
+ slug: params.slug,
22096
+ widget_id: params.widget_id,
22097
+ data: params.data
22098
+ });
22099
+ return { content: [{ type: "text", text: `Persisted \`${params.widget_id}\` on \`${params.slug}\` (refreshed ${result.refreshed_at}).` }] };
22100
+ }
22101
+ );
21938
22102
  function groupByStatus(items) {
21939
22103
  const groups = {};
21940
22104
  for (const item of items) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.14.15",
3
+ "version": "0.15.0",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {