@aiaiai-pt/martha-cli 0.9.1 → 0.10.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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to `@aiaiai-pt/martha-cli`. Format: [Keep a Changelog](https
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.10.0] — 2026-06-10
8
+
9
+ ### Added — #540 Phase 2.0/2.1 chat copilot + task operator
10
+ - `martha init --agent` provisions a personal **chat copilot** alongside the run-as bootstrap: a `cloud` agent (no extra credentials) linked to your client, with the intent's tools granted to the **agent** surface (where agent grants fire in chat). `--agent-name` names it; `--model` overrides the model (defaults to the platform default). Persists `default_agent` to the profile.
11
+ - `martha chat` now defaults to your bootstrapped client + copilot so its tools work out of the box; `--agent <name>` selects a specific copilot.
12
+ - **Task operator**: with the `tasks` intent, the copilot can `create_task` / `list_tasks` / `get_task` — tasks run on Martha's task pipeline and are scoped to the copilot's own tasks. (Backend platform functions; surfaced through chat.)
13
+
7
14
  ## [0.9.1] — 2026-06-10
8
15
 
9
16
  ### Fixed
package/dist/index.js CHANGED
@@ -11051,7 +11051,7 @@ import { createInterface as createInterface2 } from "node:readline";
11051
11051
  init_errors();
11052
11052
 
11053
11053
  // src/version.ts
11054
- var CLI_VERSION = "0.9.1";
11054
+ var CLI_VERSION = "0.10.0";
11055
11055
 
11056
11056
  // src/commands/sessions.ts
11057
11057
  function relativeTime(iso) {
@@ -11204,8 +11204,13 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
11204
11204
  signal: opts.signal,
11205
11205
  timeout: opts.timeoutMs ?? DEFAULT_CHAT_TIMEOUT_MS
11206
11206
  };
11207
- if (opts.clientId) {
11208
- reqOpts.params = { selected_client: opts.clientId };
11207
+ if (opts.clientId || opts.agentName) {
11208
+ const params = {};
11209
+ if (opts.clientId)
11210
+ params.selected_client = opts.clientId;
11211
+ if (opts.agentName)
11212
+ params.selected_agent = opts.agentName;
11213
+ reqOpts.params = params;
11209
11214
  }
11210
11215
  const res = await ctx.api.postRaw(`/api/chat/${encodeURIComponent(sessionId)}`, { content: message }, reqOpts);
11211
11216
  const contentType = res.headers.get("content-type") ?? "";
@@ -11375,7 +11380,7 @@ async function showHistoryPicker(ctx, rl) {
11375
11380
  }
11376
11381
  return data.items[idx].session_id;
11377
11382
  }
11378
- async function runRepl(ctx, initialSessionId, clientId, showTools, timeoutMs) {
11383
+ async function runRepl(ctx, initialSessionId, clientId, agentName, showTools, timeoutMs) {
11379
11384
  let sessionId = initialSessionId;
11380
11385
  process.stderr.write(source_default.dim(`martha v${CLI_VERSION} | session: ${sessionId.slice(0, 8)}...
11381
11386
  `));
@@ -11483,7 +11488,8 @@ async function runRepl(ctx, initialSessionId, clientId, showTools, timeoutMs) {
11483
11488
  onClear: showTools ? () => process.stderr.write(source_default.dim(` --- tool iteration ---
11484
11489
  `)) : undefined,
11485
11490
  signal: activeAbort.signal,
11486
- clientId
11491
+ clientId,
11492
+ agentName
11487
11493
  });
11488
11494
  if (!result.aborted) {
11489
11495
  process.stdout.write(`
@@ -11511,7 +11517,7 @@ Error: ${err instanceof Error ? err.message : String(err)}
11511
11517
  process.stderr.write(`
11512
11518
  `);
11513
11519
  }
11514
- async function runOneShot(ctx, sessionId, message, isJson, clientId, showTools, timeoutMs) {
11520
+ async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName, showTools, timeoutMs) {
11515
11521
  const toolCalls = [];
11516
11522
  function recordToolStatus(data) {
11517
11523
  try {
@@ -11533,6 +11539,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, showTools,
11533
11539
  }
11534
11540
  const { response } = await sendMessage(ctx, sessionId, message, {
11535
11541
  clientId,
11542
+ agentName,
11536
11543
  timeoutMs,
11537
11544
  onToolStatus: isJson ? recordToolStatus : showTools ? (data) => {
11538
11545
  recordToolStatus(data);
@@ -11557,7 +11564,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, showTools,
11557
11564
  }
11558
11565
  }
11559
11566
  function registerChatCommand(program2) {
11560
- program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
11567
+ program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
11561
11568
  const ctx = createContext({
11562
11569
  profileOverride: program2.opts().profile,
11563
11570
  verbose: program2.opts().verbose
@@ -11580,6 +11587,10 @@ function registerChatCommand(program2) {
11580
11587
  clientId = String(match.id);
11581
11588
  }
11582
11589
  }
11590
+ if (!clientId && ctx.profile.default_client) {
11591
+ clientId = ctx.profile.default_client;
11592
+ }
11593
+ const agentName = opts.agent ?? ctx.profile.default_agent;
11583
11594
  let sessionId;
11584
11595
  if (opts.session) {
11585
11596
  sessionId = opts.session;
@@ -11589,12 +11600,12 @@ function registerChatCommand(program2) {
11589
11600
  const showTools = !!opts.showTools;
11590
11601
  const timeoutMs = opts.timeout ? parseTimeoutSeconds(opts.timeout) * 1000 : undefined;
11591
11602
  if (opts.message) {
11592
- await runOneShot(ctx, sessionId, opts.message, isJson, clientId, showTools, timeoutMs);
11603
+ await runOneShot(ctx, sessionId, opts.message, isJson, clientId, agentName, showTools, timeoutMs);
11593
11604
  } else {
11594
11605
  if (isJson) {
11595
11606
  throw new CLIError("The --json flag requires --message for non-interactive mode.", 4 /* Validation */, 'Example: martha --json chat --message "hello"');
11596
11607
  }
11597
- await runRepl(ctx, sessionId, clientId, showTools, timeoutMs);
11608
+ await runRepl(ctx, sessionId, clientId, agentName, showTools, timeoutMs);
11598
11609
  }
11599
11610
  });
11600
11611
  }
@@ -17732,26 +17743,36 @@ Provision an access principal now so calls just work?`, true);
17732
17743
  if (intents.length === 0 && workflows.length === 0 && functions.length === 0) {
17733
17744
  throw new CLIError("Nothing to grant: declare at least one --intent (rag/documents/workflows/custom) or a --workflow/--function.", 4 /* Validation */);
17734
17745
  }
17746
+ let wantAgent = mode === "human" && !!opts.agent;
17747
+ if (mode === "human" && !opts.agent && interactive) {
17748
+ wantAgent = await askYesNo(`
17749
+ Also set up a chat copilot you can talk to (martha chat)?`, true);
17750
+ }
17735
17751
  const ctx = createContext({ profileOverride: profileName });
17736
17752
  if (opts.apiUrl)
17737
17753
  ctx.profile.api_url = opts.apiUrl;
17738
17754
  await ensureAuthenticated(ctx, interactive, jsonMode);
17739
17755
  if (!jsonMode) {
17740
17756
  console.log();
17741
- console.log(source_default.dim(` Provisioning ${mode === "integration" ? "an integration service account" : "your access principal"}…`));
17757
+ console.log(source_default.dim(` Provisioning ${mode === "integration" ? "an integration service account" : "your access principal"}${wantAgent ? " + copilot" : ""}…`));
17742
17758
  }
17743
17759
  const body = {
17744
17760
  mode,
17745
17761
  intents,
17746
17762
  workflows,
17747
17763
  functions,
17748
- ...opts.label ? { label: opts.label } : {}
17764
+ ...opts.label ? { label: opts.label } : {},
17765
+ ...wantAgent ? { agent: true } : {},
17766
+ ...wantAgent && opts.agentName ? { agent_name: opts.agentName } : {},
17767
+ ...wantAgent && opts.model ? { model: opts.model } : {}
17749
17768
  };
17750
17769
  const resp = await ctx.api.post("/api/admin/definitions/principals/bootstrap", body);
17751
17770
  if (mode === "human" && resp.client_key) {
17752
17771
  const cfg = loadConfig();
17753
17772
  if (cfg.profiles[profileName]) {
17754
17773
  cfg.profiles[profileName].default_client = resp.client_key;
17774
+ if (resp.agent)
17775
+ cfg.profiles[profileName].default_agent = resp.agent.name;
17755
17776
  saveConfig(cfg);
17756
17777
  }
17757
17778
  }
@@ -17799,6 +17820,15 @@ function printBootstrapResult(resp, profileName) {
17799
17820
  for (const s of resp.skipped) {
17800
17821
  console.log(source_default.yellow(` Skipped ${s.name} — ${s.reason}`));
17801
17822
  }
17823
+ if (resp.agent) {
17824
+ const a = resp.agent;
17825
+ console.log();
17826
+ const averb = a.adopted ? "Using existing" : "Created";
17827
+ console.log(source_default.green(` ${averb} copilot ${source_default.bold(a.name)}.`));
17828
+ if (a.granted_functions.length > 0) {
17829
+ console.log(` Copilot tools: ${source_default.cyan(a.granted_functions.join(", "))}`);
17830
+ }
17831
+ }
17802
17832
  if (resp.service_account) {
17803
17833
  const sa = resp.service_account;
17804
17834
  console.log();
@@ -17813,6 +17843,9 @@ function printBootstrapResult(resp, profileName) {
17813
17843
  console.log(source_default.dim(` Profile ${source_default.cyan(profileName)} will now run as this client.`));
17814
17844
  console.log();
17815
17845
  console.log(source_default.dim(" Next:"));
17846
+ if (resp.agent) {
17847
+ console.log(source_default.dim(' martha chat "what can you do?"'));
17848
+ }
17816
17849
  console.log(source_default.dim(" martha workflows list"));
17817
17850
  console.log(source_default.dim(" martha workflows execute <name> --inputs '{}'"));
17818
17851
  }
@@ -17869,7 +17902,7 @@ async function askIntents() {
17869
17902
  return parseIntents(raw);
17870
17903
  }
17871
17904
  function registerInitCommand(program2) {
17872
- program2.command("init").description("Create or update a profile, and optionally bootstrap an access principal").option("--preset <name>", "Preset: cloud or local", "cloud").option("--name <name>", "Profile name (defaults to preset name)").option("--force", "Overwrite an existing profile").option("--api-url <url>", "Override the API URL").option("--keycloak-url <url>", "Override the Keycloak URL").option("--keycloak-realm <realm>", "Override the Keycloak realm").option("--no-interactive", "Skip prompts; use defaults / overrides only").option("--bootstrap", "Provision an intent-scoped principal + grants after saving the profile").option("--mode <mode>", "Bootstrap mode: 'human' (your own client) or 'integration' (service account)").option("--intent <intents...>", "Usage buckets to grant: rag, documents, workflows, custom").option("--workflow <names...>", "Workflow name(s) to grant (workflows/custom intent)").option("--function <names...>", "Extra function name(s) to grant (custom intent)").option("--label <name>", "Integration mode: label for the SA client id").option("--yes", "Acknowledge non-interactive bootstrap side effects").action(async (opts, command) => {
17905
+ program2.command("init").description("Create or update a profile, and optionally bootstrap an access principal").option("--preset <name>", "Preset: cloud or local", "cloud").option("--name <name>", "Profile name (defaults to preset name)").option("--force", "Overwrite an existing profile").option("--api-url <url>", "Override the API URL").option("--keycloak-url <url>", "Override the Keycloak URL").option("--keycloak-realm <realm>", "Override the Keycloak realm").option("--no-interactive", "Skip prompts; use defaults / overrides only").option("--bootstrap", "Provision an intent-scoped principal + grants after saving the profile").option("--mode <mode>", "Bootstrap mode: 'human' (your own client) or 'integration' (service account)").option("--intent <intents...>", "Usage buckets to grant: rag, documents, workflows, custom").option("--workflow <names...>", "Workflow name(s) to grant (workflows/custom intent)").option("--function <names...>", "Extra function name(s) to grant (custom intent)").option("--label <name>", "Integration mode: label for the SA client id").option("--agent", "Also provision a personal chat copilot (human mode) and set it as the default").option("--agent-name <name>", "Copilot agent name (defaults to copilot-{user})").option("--model <id>", "Copilot LLM model id (defaults to the platform default)").option("--yes", "Acknowledge non-interactive bootstrap side effects").action(async (opts, command) => {
17873
17906
  const globals = command.parent?.opts() ?? {};
17874
17907
  await initCommand({ ...opts, json: !!globals.json });
17875
17908
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {