@arbidocs/cli 0.4.31 → 0.4.33

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
@@ -1,5 +1,125 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.4.33
4
+
5
+ [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.4.31...HEAD)
6
+
7
+ ### 🚀 Enhancements
8
+
9
+ - **automations:** Separate editor and history with concise run results ([ececfaf59](https://github.com/arbicity/ARBI-frontend/commit/ececfaf59))
10
+ - **ui:** Unify automation tabs and sidebar with compact rows and inbox previews ([75b020457](https://github.com/arbicity/ARBI-frontend/commit/75b020457))
11
+ - **home:** Group workspaces under project headings in the table view ([#1391](https://github.com/arbicity/ARBI-frontend/pull/1391))
12
+ - **theme:** Premium Midnight & Cyan tokens - serif display, paper canvas, navy sidebar, ink shadows, tighter radius ([7743678ce](https://github.com/arbicity/ARBI-frontend/commit/7743678ce))
13
+ - **theme:** Raised white cards, eyebrow section labels, cyan active nav marker ([9d5a25f73](https://github.com/arbicity/ARBI-frontend/commit/9d5a25f73))
14
+ - **theme:** Finish premium look; session works without Web Crypto (memory-only) with visible warning ([182b9eb5d](https://github.com/arbicity/ARBI-frontend/commit/182b9eb5d))
15
+ - **theme:** Redesign for lawyers - Office/Fluent familiarity, brand arch on the login hero ([c584e0e4b](https://github.com/arbicity/ARBI-frontend/commit/c584e0e4b))
16
+ - **files:** Wider Title, inline status glyph, visible column resize ticks; square sidebar boxes and toasts ([368b03717](https://github.com/arbicity/ARBI-frontend/commit/368b03717))
17
+ - Mail folders use the Files folder control in the tab header; migrate saved layouts to hide the Status column ([ff205f57e](https://github.com/arbicity/ARBI-frontend/commit/ff205f57e))
18
+ - Automations and Mail behind developer-only Experimental switches, off by default ([a817df1f7](https://github.com/arbicity/ARBI-frontend/commit/a817df1f7))
19
+ - **e2e:** Persona think-aloud evals — simulated users in real browsers, recorded, with fixer hand-off ([1786856ff](https://github.com/arbicity/ARBI-frontend/commit/1786856ff))
20
+ - **personas:** Any agent harness (claude ([ external ](https://github.com/arbicity/ARBI-frontend/commit/ external ))
21
+ - **personas:** Six-persona cast + round 1 results in handoff ([1ce966912](https://github.com/arbicity/ARBI-frontend/commit/1ce966912))
22
+ - **personas:** Run on the Claude subscription, not API keys; label $ as list-price equivalents ([b914b6cb4](https://github.com/arbicity/ARBI-frontend/commit/b914b6cb4))
23
+ - **mail:** Mail header matches Files and Facts - icon + count, shared filter box with dropdown, no refresh ([d58156a3d](https://github.com/arbicity/ARBI-frontend/commit/d58156a3d))
24
+ - **mail:** Header button reads '+ Email' ([d048c885e](https://github.com/arbicity/ARBI-frontend/commit/d048c885e))
25
+ - **mail:** First-run setup in the main screen, ready greeting, refresh on new mail ([3d9ea7bb5](https://github.com/arbicity/ARBI-frontend/commit/3d9ea7bb5))
26
+ - **personas:** --baseline <run> compares a re-run against an earlier round ([f4e49ef22](https://github.com/arbicity/ARBI-frontend/commit/f4e49ef22))
27
+ - **sdk:** Typed mail module and arbi mail CLI (agents can draft) ([ed5366654](https://github.com/arbicity/ARBI-frontend/commit/ed5366654))
28
+
29
+ ### 🩹 Fixes
30
+
31
+ - **react:** Show fractional credits and route all credit displays through formatCredits ([bcf12acbe](https://github.com/arbicity/ARBI-frontend/commit/bcf12acbe))
32
+ - **react:** Render italic and list markers when a citation link wraps them ([5fd043cf8](https://github.com/arbicity/ARBI-frontend/commit/5fd043cf8))
33
+ - **react:** Keep a citation link from ending inside an italic source note ([98b77be7f](https://github.com/arbicity/ARBI-frontend/commit/98b77be7f))
34
+ - **automations:** Use standard grids for rules and run history ([94f9da431](https://github.com/arbicity/ARBI-frontend/commit/94f9da431))
35
+ - **ui:** Compact sidebar menus and link run result badges to history ([381d63a42](https://github.com/arbicity/ARBI-frontend/commit/381d63a42))
36
+ - **automations:** Improve trigger badge legibility ([f2e247364](https://github.com/arbicity/ARBI-frontend/commit/f2e247364))
37
+ - **automations:** Simplify trigger labels to text and icon ([64929ee96](https://github.com/arbicity/ARBI-frontend/commit/64929ee96))
38
+ - **automations:** Preserve document rows and validate shared UI and clients ([b6b98bd18](https://github.com/arbicity/ARBI-frontend/commit/b6b98bd18))
39
+ - **notifications:** Open task alerts in their originating workspace ([7c8c05c9b](https://github.com/arbicity/ARBI-frontend/commit/7c8c05c9b))
40
+ - **theme:** Website skyline on the login hero, drop sparkle art and nav accent bar ([0ec8eeb07](https://github.com/arbicity/ARBI-frontend/commit/0ec8eeb07))
41
+ - **theme:** Consistency pass - one rule for fields, cards and chips; drop row accent bar ([3f34159bb](https://github.com/arbicity/ARBI-frontend/commit/3f34159bb))
42
+ - **theme:** Keep the open-document row accent bar in the files grid ([0da8b82f5](https://github.com/arbicity/ARBI-frontend/commit/0da8b82f5))
43
+ - **theme:** Square sidebar nav rows with a straight leading bar on the selected row ([7ca5777c5](https://github.com/arbicity/ARBI-frontend/commit/7ca5777c5))
44
+ - **theme:** Quiet entity labels - plain icon + text at rest, fill only on hover ([eeabe8e57](https://github.com/arbicity/ARBI-frontend/commit/eeabe8e57))
45
+ - Fresh browsers crash opening a workspace (grid migration clobbered gridStates) ([7e114ab08](https://github.com/arbicity/ARBI-frontend/commit/7e114ab08))
46
+ - **personas:** Count fixer commits against the resolved base, not the fix branch's HEAD ([283a8bfbe](https://github.com/arbicity/ARBI-frontend/commit/283a8bfbe))
47
+ - Fresh browsers crash opening a workspace (grid migration clobbered gridStates) ([4acdaaa6d](https://github.com/arbicity/ARBI-frontend/commit/4acdaaa6d))
48
+ - **ci:** Trust Let's Encrypt staging CA for the SDK/CLI integration tests ([103a4b20f](https://github.com/arbicity/ARBI-frontend/commit/103a4b20f))
49
+ - **react:** Text, Word, sheet and HTML files no longer get the PDF icon (persona smoke I01) ([c5d76f775](https://github.com/arbicity/ARBI-frontend/commit/c5d76f775))
50
+ - **mail:** Drop the header's own bottom border (the tab header already draws one) ([e330b2aa8](https://github.com/arbicity/ARBI-frontend/commit/e330b2aa8))
51
+ - **files:** Add menu no longer closes right after a hover-opened click (persona smoke I02) ([55066376b](https://github.com/arbicity/ARBI-frontend/commit/55066376b))
52
+ - **react:** Citation popover re-docks when the chat column narrows (persona smoke I05) ([24a262151](https://github.com/arbicity/ARBI-frontend/commit/24a262151))
53
+ - **viewer:** Show the zoom button on desktop so documents can be enlarged (persona ken-low-vision I01) ([ac4a0588c](https://github.com/arbicity/ARBI-frontend/commit/ac4a0588c))
54
+ - **messages:** Confirm copy of an answer with a toast, and report failures (persona maya-paralegal I05) ([22f4c5d8d](https://github.com/arbicity/ARBI-frontend/commit/22f4c5d8d))
55
+ - **search:** Label Smart Search passages found by meaning, not words (persona lena-legal-ops I02) ([7e5a79d53](https://github.com/arbicity/ARBI-frontend/commit/7e5a79d53))
56
+ - **search:** Explain quotes and relevance threshold in Smart Search results (persona lena-legal-ops I03) ([ed64d06fe](https://github.com/arbicity/ARBI-frontend/commit/ed64d06fe))
57
+ - **upload:** Default visibility to private and say who can see files (persona tom-first-timer I01) ([abfdec1ff](https://github.com/arbicity/ARBI-frontend/commit/abfdec1ff))
58
+ - **citations:** Readable hover passage preview (persona ken-low-vision I02) ([bf64b7a0a](https://github.com/arbicity/ARBI-frontend/commit/bf64b7a0a))
59
+ - **chat:** Offer backend follow-up as a chip instead of pre-filled ghost text (persona smoke I06) ([8859cbb4a](https://github.com/arbicity/ARBI-frontend/commit/8859cbb4a))
60
+ - **facts:** Chronology tables save as dated facts (persona maya-paralegal I07) ([a76dad930](https://github.com/arbicity/ARBI-frontend/commit/a76dad930))
61
+ - **ui:** Citation card closes on any press outside, even one that opens another menu (persona priya-mobile I05) ([4f99f2aff](https://github.com/arbicity/ARBI-frontend/commit/4f99f2aff))
62
+ - **upload:** Keep uploads shared with the workspace by default ([d62538b0c](https://github.com/arbicity/ARBI-frontend/commit/d62538b0c))
63
+ - **ci:** Trust the staging CA inside the deployment container too ([ede715fc2](https://github.com/arbicity/ARBI-frontend/commit/ede715fc2))
64
+ - **conversation:** Show a new chat's question while its answer generates (persona raj-partner I01) ([f05e2d0fd](https://github.com/arbicity/ARBI-frontend/commit/f05e2d0fd))
65
+ - **chat:** Show the passages an uncited answer read as its sources (persona tom-first-timer I03) ([3f1833640](https://github.com/arbicity/ARBI-frontend/commit/3f1833640))
66
+ - **a11y:** Thicker keyboard focus ring on controls (persona ken-low-vision I04) ([f40403d78](https://github.com/arbicity/ARBI-frontend/commit/f40403d78))
67
+ - **viewer:** Name a text file's preview tab Text, not PDF (persona lena-legal-ops I01) ([0195e80f6](https://github.com/arbicity/ARBI-frontend/commit/0195e80f6))
68
+ - **chronology:** State the sort key and how to date ranges (persona maya-paralegal I02) ([9a63cb6cb](https://github.com/arbicity/ARBI-frontend/commit/9a63cb6cb))
69
+ - **facts:** Empty Facts page offers a route to pull dates from documents (persona maya-paralegal I01) ([22124287e](https://github.com/arbicity/ARBI-frontend/commit/22124287e))
70
+ - **facts:** Saved answer fact no longer titled with the chat lead-in (persona maya-paralegal I06) ([3a6a7d701](https://github.com/arbicity/ARBI-frontend/commit/3a6a7d701))
71
+ - **sharing:** Close member suggestion list after adding an email (persona raj-partner I04) ([ed22db368](https://github.com/arbicity/ARBI-frontend/commit/ed22db368))
72
+ - **viewer:** Keep the HTML tab labelled on phones (persona priya-mobile I03) ([e153cac99](https://github.com/arbicity/ARBI-frontend/commit/e153cac99))
73
+ - **documents:** Larger default file tile so titles and thumbnails are readable (persona raj-partner I06) ([c83f28416](https://github.com/arbicity/ARBI-frontend/commit/c83f28416))
74
+ - **react:** Citation menus no longer pop up while scrolling (persona priya-mobile I04) ([c21a7bccc](https://github.com/arbicity/ARBI-frontend/commit/c21a7bccc))
75
+ - **personas:** Mask the demo cursor from agent screenshots; tell personas their OS ([48d6a1938](https://github.com/arbicity/ARBI-frontend/commit/48d6a1938))
76
+ - **workflows:** Name the agent picker 'Who this runs for' (persona tom-first-timer I04) ([666a9d03d](https://github.com/arbicity/ARBI-frontend/commit/666a9d03d))
77
+ - **markdown:** Table cells wrap between words, not mid-word (persona maya-paralegal I03) ([79196bbe9](https://github.com/arbicity/ARBI-frontend/commit/79196bbe9))
78
+ - **personas:** Retry seed documents that fail backend processing instead of testing on them ([ea793d446](https://github.com/arbicity/ARBI-frontend/commit/ea793d446))
79
+ - **chat:** Explain held messages in a task awaiting review (persona priya-mobile I03) ([fc723248c](https://github.com/arbicity/ARBI-frontend/commit/fc723248c))
80
+ - **messages:** Show backticked doc ids as document pills (persona lena-legal-ops I10) ([277501761](https://github.com/arbicity/ARBI-frontend/commit/277501761))
81
+ - **shell:** Bold navy focus ring on the Ask or search field (persona ken-low-vision I04) ([4d78a2480](https://github.com/arbicity/ARBI-frontend/commit/4d78a2480))
82
+ - **workspace:** Explain the common/members-only switch in plain words (persona tom-first-timer I03) ([9e97ceb8f](https://github.com/arbicity/ARBI-frontend/commit/9e97ceb8f))
83
+ - **annotations:** Say the AI has not run after creating an annotation (persona lena-legal-ops I02) ([7ab8cbeef](https://github.com/arbicity/ARBI-frontend/commit/7ab8cbeef))
84
+ - **documents:** Keep a newly created folder listed and selected in Move dialog (persona lena-legal-ops I08) ([3b02f0739](https://github.com/arbicity/ARBI-frontend/commit/3b02f0739))
85
+ - **facts:** Ask the assistant to report the count it actually saved (persona maya-paralegal I03) ([4bbd34e99](https://github.com/arbicity/ARBI-frontend/commit/4bbd34e99))
86
+ - **facts:** Keep the Find-dates prompt in plain words; handoff: round-2 fixes + drag-selection call ([10caf467c](https://github.com/arbicity/ARBI-frontend/commit/10caf467c))
87
+ - **thread:** Keep 'Started Task' / 'Answered' activity in the header, not the chat ([15ca6823b](https://github.com/arbicity/ARBI-frontend/commit/15ca6823b))
88
+ - **auth:** Hero text no longer jumps when switching Login/Register ([1cf652104](https://github.com/arbicity/ARBI-frontend/commit/1cf652104))
89
+ - **connectors:** Show Google (Gmail + Drive) connectors by default ([#1398](https://github.com/arbicity/ARBI-frontend/pull/1398))
90
+
91
+ ### 📖 Documentation
92
+
93
+ - **changeset:** Include automation clients and final shared UI ([0dab73760](https://github.com/arbicity/ARBI-frontend/commit/0dab73760))
94
+ - **personas:** Handoff — fix pass, round 2 re-test, UX calls for a human ([fef6b99c1](https://github.com/arbicity/ARBI-frontend/commit/fef6b99c1))
95
+
96
+ ### 🏡 Chore
97
+
98
+ - Drop incidental version and lockfile changes ([a1e735dca](https://github.com/arbicity/ARBI-frontend/commit/a1e735dca))
99
+ - **examples:** Let the react-chat example name the deployment domain behind a proxy ([c31409b35](https://github.com/arbicity/ARBI-frontend/commit/c31409b35))
100
+
101
+ ### ✅ Tests
102
+
103
+ - **e2e:** Login specs follow the verified-link signup (code field hidden when the link carries a code) ([#1387](https://github.com/arbicity/ARBI-frontend/pull/1387))
104
+ - **e2e:** The saved workflow is found on the Active tab, not below 21 skills ([#1388](https://github.com/arbicity/ARBI-frontend/pull/1388))
105
+ - **e2e:** Goal task demo follows the one Threads list ([#1389](https://github.com/arbicity/ARBI-frontend/pull/1389))
106
+
107
+ ### 🎨 Styles
108
+
109
+ - **upload:** Add button is white on cyan when the upload is shared ([72e1e810d](https://github.com/arbicity/ARBI-frontend/commit/72e1e810d))
110
+ - **upload:** Shared Add button uses the brand cyan (--secondary), matching the Shared switch ([f55aa8388](https://github.com/arbicity/ARBI-frontend/commit/f55aa8388))
111
+ - **upload:** Keep the Add button primary whether the upload is shared or private ([7ee51e2b2](https://github.com/arbicity/ARBI-frontend/commit/7ee51e2b2))
112
+
113
+ ### 🤖 CI
114
+
115
+ - Give the SDK and CLI integration job 35 minutes ([#1384](https://github.com/arbicity/ARBI-frontend/pull/1384))
116
+ - Route remaining general jobs to disposable CPU runners ([d99a7d5df](https://github.com/arbicity/ARBI-frontend/commit/d99a7d5df))
117
+ - Configure software signing and public endpoints for CPU guests ([79fcac6b0](https://github.com/arbicity/ARBI-frontend/commit/79fcac6b0))
118
+ - Use isolated Redis for full-stack jobs ([484a2d6bc](https://github.com/arbicity/ARBI-frontend/commit/484a2d6bc))
119
+ - Isolate deployment identities across parallel VM jobs ([b4834b72a](https://github.com/arbicity/ARBI-frontend/commit/b4834b72a))
120
+ - Use the deployment CLI domain option ([535c3a208](https://github.com/arbicity/ARBI-frontend/commit/535c3a208))
121
+ - Preserve the remote parser queue endpoint ([d9275d599](https://github.com/arbicity/ARBI-frontend/commit/d9275d599))
122
+
3
123
  ## v0.4.31
4
124
 
5
125
  [compare changes](https://github.com/arbicity/ARBI-frontend/compare/v0.4.28...HEAD)
package/SKILL.md CHANGED
@@ -147,3 +147,23 @@ Footnote-style rendering at the end of the answer is currently not supported —
147
147
  - **Listen side-effects**: `arbi listen --agent claude` writes `~/.claude/commands/arbi/SKILL.md` and sets a `defaultAgent` in the CLI config. Run it only on a machine you control.
148
148
  - **Persistent agents**: `arbi agent create --name <n> -w <ws-id>` creates a child identity the parent can delegate to (printed email+password). Share workspace IDs via `-w` rather than the interactive picker when running non-interactively.
149
149
  - **Agent-create errors**: `agent-create` rejects non-`agent-*` emails with a clear message. If you see "No verification record found" the account probably already exists — use `arbi login` instead.
150
+
151
+ ## Workspace automations
152
+
153
+ `arbi automation` is an alias for `arbi workflow`. Triggers are run now (no trigger flags), a schedule (`--run-at` or `--rrule`), or a notification (`--event-kind`, optional `--event-filter` JSON). `run <wfl-id>` starts any saved action now. Scheduled document actions require an assistant with standing workspace access (`--agent <agt-id> --auth-mode scheduled_permanent`). Do not silently grant that access.
154
+
155
+ ```bash
156
+ # Daily annotation review of documents currently carrying a label.
157
+ arbi automation create --name 'Daily relevance review' --rrule 'FREQ=DAILY;BYHOUR=9;BYMINUTE=0;BYSECOND=0' --timezone Europe/London --action annotate_documents --labels lbl-needs-review --tags tag-relevance --agent agt-reviewer --auth-mode scheduled_permanent
158
+ # Daily email digest of documents uploaded since the last successful run.
159
+ arbi automation create --name 'Latest documents' --rrule 'FREQ=DAILY;BYHOUR=17;BYMINUTE=0;BYSECOND=0' --timezone Europe/London --action email_documents --latest --agent agt-reviewer --auth-mode scheduled_permanent
160
+ arbi automation run wfl-example --json
161
+ arbi automation runs wfl-example --json
162
+ arbi automation update wfl-example --file changes.json
163
+ ```
164
+
165
+ Document actions return `execution_id` and do not require a conversation. Execution history includes status, selected documents, a run label, errors and result metadata. Annotation answers/citations remain document annotation fields; emails report delivery metadata. Chat/prompt actions retain their linked conversation history. `--overwrite` regenerates existing annotation answers; the default preserves them. Label selection is resolved at execution time rather than frozen when creating the rule. `arbi task` still refers to local background ask requests, not workspace automations.
166
+
167
+ In the terminal UI: `/automations` lists rules; `/automation view|run|runs|pause|resume|delete <wfl-id>` manages them; `/automation create <JSON>` and `/automation update <wfl-id> <JSON>` accept the same request bodies as the SDK.
168
+
169
+ For latest-document digests, the first run covers the preceding 24 hours; later runs use the previous successful run cutoff. Failed delivery does not advance it. Emails contain document names and authenticated platform links, without attachments.
package/dist/index.js CHANGED
@@ -3694,7 +3694,7 @@ function getLatestVersion(skipCache = false) {
3694
3694
  }
3695
3695
  }
3696
3696
  function getCurrentVersion() {
3697
- return "0.4.31";
3697
+ return "0.4.33";
3698
3698
  }
3699
3699
  function readChangelog(fromVersion, toVersion) {
3700
3700
  try {
@@ -3747,17 +3747,17 @@ function showChangelog(fromVersion, toVersion) {
3747
3747
  async function checkForUpdates(autoUpdate) {
3748
3748
  try {
3749
3749
  const latest = getLatestVersion();
3750
- if (!latest || latest === "0.4.31") return;
3750
+ if (!latest || latest === "0.4.33") return;
3751
3751
  if (autoUpdate) {
3752
3752
  warn(`
3753
- Your arbi version is out of date (${"0.4.31"} \u2192 ${latest}). Updating...`);
3753
+ Your arbi version is out of date (${"0.4.33"} \u2192 ${latest}). Updating...`);
3754
3754
  child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
3755
- showChangelog("0.4.31", latest);
3755
+ showChangelog("0.4.33", latest);
3756
3756
  console.log(`Updated to ${latest}.`);
3757
3757
  } else {
3758
3758
  warn(
3759
3759
  `
3760
- Your arbi version is out of date (${"0.4.31"} \u2192 ${latest}).
3760
+ Your arbi version is out of date (${"0.4.33"} \u2192 ${latest}).
3761
3761
  Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
3762
3762
  );
3763
3763
  }
@@ -3789,10 +3789,10 @@ function markNagShown(latest) {
3789
3789
  function hintUpdateOnError() {
3790
3790
  try {
3791
3791
  const cached = readCache();
3792
- if (!cached || cached.latest === "0.4.31") return;
3792
+ if (!cached || cached.latest === "0.4.33") return;
3793
3793
  if (!shouldShowNag(cached.latest)) return;
3794
3794
  warn(
3795
- `Your arbi version is out of date (${"0.4.31"} \u2192 ${cached.latest}). Run "arbi update".`
3795
+ `Your arbi version is out of date (${"0.4.33"} \u2192 ${cached.latest}). Run "arbi update".`
3796
3796
  );
3797
3797
  markNagShown(cached.latest);
3798
3798
  } catch {
@@ -9583,7 +9583,7 @@ function resolveTaskId(taskIdArg) {
9583
9583
  }
9584
9584
  function statusColor2(s) {
9585
9585
  if (s === "completed") return chalk2__default.default.green(s);
9586
- if (s === "failed") return chalk2__default.default.red(s);
9586
+ if (s === "failed" || s === "cancelled") return chalk2__default.default.red(s);
9587
9587
  if (s === "queued" || s === "in_progress") return chalk2__default.default.yellow(s);
9588
9588
  return s;
9589
9589
  }
@@ -9633,7 +9633,7 @@ function registerTaskCommand(program2) {
9633
9633
  const id = resolveTaskId(taskId);
9634
9634
  const { accessToken, config } = await resolveWorkspace(opts?.workspace);
9635
9635
  const result = await sdk.responses.getResponse({ baseUrl: config.baseUrl, accessToken }, id);
9636
- if (result.status === "completed" || result.status === "failed") {
9636
+ if (result.status === "completed" || result.status === "failed" || result.status === "cancelled") {
9637
9637
  updateTaskStatus(id, result.status);
9638
9638
  }
9639
9639
  if (opts?.json) {
@@ -9653,7 +9653,7 @@ function registerTaskCommand(program2) {
9653
9653
  const id = resolveTaskId(taskId);
9654
9654
  const { accessToken, config } = await resolveWorkspace(opts?.workspace);
9655
9655
  const result = await sdk.responses.getResponse({ baseUrl: config.baseUrl, accessToken }, id);
9656
- if (result.status === "completed" || result.status === "failed") {
9656
+ if (result.status === "completed" || result.status === "failed" || result.status === "cancelled") {
9657
9657
  updateTaskStatus(id, result.status);
9658
9658
  }
9659
9659
  if (opts?.json) {
@@ -9675,8 +9675,12 @@ function registerTaskCommand(program2) {
9675
9675
  );
9676
9676
  }
9677
9677
  function registerWorkflowCommand(program2) {
9678
- const workflow = program2.command("workflow").description("Workflows (/v1/workflow): create, list, resume, pause, run, delete");
9679
- workflow.command("create").description("Create a workflow with exactly one trigger").requiredOption("--name <name>", "Workflow name (required)").option("--rrule <RRULE>", "Trigger: RFC-5545 recurrence rule").option("--run-at <ISO>", "Trigger: run once at this time (ISO-8601)").option("--event-kind <kind>", "Trigger: a notification type to react to").option("--after <con-id>", "Trigger: when this task (conversation) is done").option("--timezone <tz>", "IANA timezone for --rrule", "UTC").option("--prompt <text>", "Instruction the agent runs").option("--skill <ref>", "Skill reference to run").option("--agent <agt-id>", "Agent to run it as (external id)").option("--workspace <wrk-id>", "Workspace to scope the workflow to (default: the current one)").option("--shared", "Share it with the workspace").option("--json", "Output as JSON").action(
9678
+ const workflow = program2.command("workflow").alias("automation").description("Workflows (/v1/workflow): create, list, resume, pause, run, delete");
9679
+ workflow.command("create").description("Create a workflow with exactly one trigger").requiredOption("--name <name>", "Workflow name (required)").option("--rrule <RRULE>", "Trigger: RFC-5545 recurrence rule").option("--run-at <ISO>", "Trigger: run once at this time (ISO-8601)").option("--event-kind <kind>", "Trigger: a notification type to react to").option("--after <con-id>", "Trigger: when this task (conversation) is done").option("--timezone <tz>", "IANA timezone for --rrule", "UTC").option("--action <type>", "Action: annotate_documents or email_documents").option("--documents <ids>", "Comma-separated document IDs").option("--labels <ids>", "Select documents matching these label IDs at execution time").option("--latest", "Select documents uploaded since the previous successful run").option("--tags <ids>", "Annotation field IDs for AI review").option("--overwrite", "Regenerate existing annotation answers").option(
9680
+ "--auth-mode <mode>",
9681
+ "scheduled_require_auth or scheduled_permanent",
9682
+ "scheduled_require_auth"
9683
+ ).option("--event-filter <json>", "Notification attribute filters as a JSON object").option("--prompt <text>", "Instruction the agent runs").option("--skill <ref>", "Skill reference to run").option("--agent <agt-id>", "Agent to run it as (external id)").option("--workspace <wrk-id>", "Workspace to scope the workflow to (default: the current one)").option("--shared", "Share it with the workspace").option("--json", "Output as JSON").action(
9680
9684
  (opts) => runAction(async () => {
9681
9685
  const { arbi, workspaceId } = await resolveWorkspace();
9682
9686
  const body = {
@@ -9688,9 +9692,31 @@ function registerWorkflowCommand(program2) {
9688
9692
  timezone: opts.timezone,
9689
9693
  prompt: opts.prompt ?? "",
9690
9694
  // Unattended runs ask the owner for access each time unless the workflow is made permanent.
9691
- auth_mode: "scheduled_require_auth",
9695
+ auth_mode: opts.authMode,
9692
9696
  workspace_ext_id: opts.workspace ?? workspaceId
9693
9697
  };
9698
+ if (!["scheduled_require_auth", "scheduled_permanent"].includes(opts.authMode)) {
9699
+ throw new Error(
9700
+ "Invalid --auth-mode: use scheduled_require_auth or scheduled_permanent"
9701
+ );
9702
+ }
9703
+ if (opts.action) {
9704
+ if (!["annotate_documents", "email_documents"].includes(opts.action)) {
9705
+ throw new Error("Invalid --action: use annotate_documents or email_documents");
9706
+ }
9707
+ const ids = (value) => value?.split(",").map((id) => id.trim()).filter(Boolean) ?? [];
9708
+ body.action = {
9709
+ type: opts.action,
9710
+ documents: {
9711
+ doc_ext_ids: ids(opts.documents),
9712
+ label_ext_ids: ids(opts.labels),
9713
+ latest: !!opts.latest
9714
+ },
9715
+ tag_ext_ids: ids(opts.tags),
9716
+ overwrite: !!opts.overwrite
9717
+ };
9718
+ }
9719
+ if (opts.eventFilter) body.event_filter = JSON.parse(opts.eventFilter);
9694
9720
  if (opts.rrule) body.rrule = opts.rrule;
9695
9721
  if (opts.runAt) body.run_at = opts.runAt;
9696
9722
  if (opts.eventKind) body.event_kind = opts.eventKind;
@@ -9735,7 +9761,7 @@ function registerWorkflowCommand(program2) {
9735
9761
  })()
9736
9762
  );
9737
9763
  }
9738
- workflow.command("run <id>").description("Run a workflow now (creates a task like its trigger would)").option("--json", "Output as JSON").action(
9764
+ workflow.command("run <id>").description("Start the saved action now (document actions do not create a thread)").option("--json", "Output as JSON").action(
9739
9765
  (id, opts) => runAction(async () => {
9740
9766
  const { arbi } = await resolveAuth();
9741
9767
  const data = await sdk.workflows.runWorkflow(arbi, id);
@@ -9743,7 +9769,47 @@ function registerWorkflowCommand(program2) {
9743
9769
  printJson(data);
9744
9770
  return;
9745
9771
  }
9746
- success(`Started workflow ${ref(id)}`);
9772
+ if (data.error) throw new Error(data.error);
9773
+ success(
9774
+ `${data.started ? "Started" : "Submitted"} automation ${ref(id)}${data.execution_id ? ` \u2014 execution ${data.execution_id}` : ""}${data.conversation_ext_id ? ` \u2014 thread ${ref(data.conversation_ext_id)}` : ""}`
9775
+ );
9776
+ })()
9777
+ );
9778
+ workflow.command("get <id>").description("Read the complete saved automation configuration").action(
9779
+ (id) => runAction(async () => {
9780
+ const { arbi } = await resolveAuth();
9781
+ printJson(await sdk.workflows.getWorkflow(arbi, id));
9782
+ })()
9783
+ );
9784
+ workflow.command("runs <id>").alias("executions").description("Show persisted document-action runs, results and failures").option("--json", "Output as JSON").action(
9785
+ (id, opts) => runAction(async () => {
9786
+ const { arbi } = await resolveAuth();
9787
+ const data = await sdk.workflows.listWorkflowRuns(arbi, id);
9788
+ if (!emitListOrTable(data, opts, "This automation has not run yet.")) return;
9789
+ printTable(
9790
+ [
9791
+ { header: "RUN", width: 10, value: (r) => String(r.id) },
9792
+ { header: "STATUS", width: 14, value: (r) => String(r.status) },
9793
+ { header: "ACTION", width: 22, value: (r) => String(r.action_type) },
9794
+ {
9795
+ header: "DOCUMENTS",
9796
+ width: 12,
9797
+ value: (r) => String(r.document_ext_ids.length)
9798
+ },
9799
+ { header: "CREATED", width: 26, value: (r) => String(r.created_at) },
9800
+ { header: "ERROR", width: 40, value: (r) => String(r.error ?? "") }
9801
+ ],
9802
+ data
9803
+ );
9804
+ })()
9805
+ );
9806
+ workflow.command("update <id>").description("Update any saved automation settings from a JSON file").requiredOption("--file <path>", "JSON UpdateWorkflowRequest file").option("--json", "Output as JSON").action(
9807
+ (id, opts) => runAction(async () => {
9808
+ const { arbi } = await resolveAuth();
9809
+ const body = JSON.parse(fs5.readFileSync(opts.file, "utf8"));
9810
+ const data = await sdk.workflows.updateWorkflow(arbi, id, body);
9811
+ if (opts.json) printJson(data);
9812
+ else success(`Updated automation ${ref(id)}`);
9747
9813
  })()
9748
9814
  );
9749
9815
  workflow.command("delete <id>").description("Delete a workflow (tasks it already created stay)").action(
@@ -10045,6 +10111,204 @@ function registerSessionCommand(program2) {
10045
10111
  await session.commands.find((c) => c.name() === "list").parseAsync([], { from: "user" });
10046
10112
  });
10047
10113
  }
10114
+ function parseAddress(value) {
10115
+ const match = /^\s*(.*?)\s*<([^<>\s]+)>\s*$/.exec(value);
10116
+ const email = (match ? match[2] : value).trim();
10117
+ if (!/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(email)) {
10118
+ throw new Error(`Not an email address: ${value}`);
10119
+ }
10120
+ const name = match?.[1].replace(/^"|"$/g, "");
10121
+ return name ? { email, name } : { email };
10122
+ }
10123
+ function collect(value, previous = []) {
10124
+ return [...previous, ...value.split(",").filter((v) => v.trim())];
10125
+ }
10126
+ var readTextFile = (path7) => fs5__default.default.readFileSync(path7 === "-" ? 0 : path7, "utf8");
10127
+ function buildCompose(options) {
10128
+ const read = options.readText ?? readTextFile;
10129
+ if (options.text !== void 0 && options.textFile) {
10130
+ throw new Error("Use either --text or --text-file, not both");
10131
+ }
10132
+ if (options.html !== void 0 && options.htmlFile) {
10133
+ throw new Error("Use either --html or --html-file, not both");
10134
+ }
10135
+ const html = options.html ?? (options.htmlFile ? read(options.htmlFile) : void 0);
10136
+ return {
10137
+ to: (options.to ?? []).map(parseAddress),
10138
+ cc: (options.cc ?? []).map(parseAddress),
10139
+ bcc: (options.bcc ?? []).map(parseAddress),
10140
+ subject: options.subject ?? "",
10141
+ text: options.text ?? (options.textFile ? read(options.textFile) : ""),
10142
+ ...html ? { html } : {},
10143
+ secure_delivery: false,
10144
+ attachments: (options.attach ?? []).map((file) => sdk.mailNode.attachmentFromFile(file))
10145
+ };
10146
+ }
10147
+ function addComposeOptions(command) {
10148
+ return command.option("--to <addresses>", "Recipient(s), comma-separated or repeated", collect).option("--cc <addresses>", "Cc recipient(s)", collect).option("--bcc <addresses>", "Bcc recipient(s)", collect).option("-s, --subject <subject>", "Subject").option("--text <text>", "Plain-text body").option("--text-file <path>", "Read the plain-text body from a file (- for stdin)").option("--html <html>", "HTML body (the plain text stays as the fallback)").option("--html-file <path>", "Read the HTML body from a file (- for stdin)").option("-a, --attach <path>", "Attach a file (repeatable)", (v, prev = []) => [
10149
+ ...prev,
10150
+ v
10151
+ ]).option("--json", "Output as JSON (recommended for scripting / agents)");
10152
+ }
10153
+ function registerMailCommand(program2) {
10154
+ const mail = program2.command("mail").enablePositionalOptions().description("Your ARBI mailbox: read, draft and send (an agent can only draft)");
10155
+ async function connect() {
10156
+ const { loginResult, config } = await resolveAuth();
10157
+ return {
10158
+ fetch: sdk.mail.createMailFetch({
10159
+ baseUrl: config.baseUrl,
10160
+ accessToken: loginResult.accessToken
10161
+ }),
10162
+ login: loginResult,
10163
+ isAgent: Boolean(loginResult.parentExtId)
10164
+ };
10165
+ }
10166
+ async function connectToRead() {
10167
+ const ctx = await connect();
10168
+ if (ctx.isAgent) {
10169
+ error("Agents can only create drafts. Use: arbi mail draft --to <address> ...");
10170
+ process.exit(1);
10171
+ }
10172
+ await sdk.mail.unlockNativeMail(ctx.fetch, {
10173
+ signingPrivateKey: ctx.login.signingPrivateKey,
10174
+ serverSessionKey: ctx.login.serverSessionKey
10175
+ });
10176
+ return ctx;
10177
+ }
10178
+ mail.command("alias <name>").description("Claim your ARBI address, name@<deployment domain> (one per user)").action(
10179
+ (name) => runAction(async () => {
10180
+ const { fetch: fetch2 } = await connect();
10181
+ success(`Your ARBI email is ${await sdk.mail.claimNativeAlias(fetch2, name.toLowerCase())}`);
10182
+ })()
10183
+ );
10184
+ mail.command("folders").description("List folders with unread counts").option("--json", "Output as JSON").action(
10185
+ (opts) => runAction(async () => {
10186
+ const { fetch: fetch2 } = await connectToRead();
10187
+ const folders = await sdk.mail.listFolders(fetch2);
10188
+ if (opts.json) return printJson(folders);
10189
+ printTable(
10190
+ [
10191
+ { header: "ID", width: 14, value: (f) => f.id },
10192
+ { header: "NAME", width: 20, value: (f) => f.name },
10193
+ { header: "UNREAD", width: 8, value: (f) => String(f.unread ?? 0) }
10194
+ ],
10195
+ folders
10196
+ );
10197
+ })()
10198
+ );
10199
+ mail.command("list").description("List messages in a folder (default: inbox)").option("-f, --folder <id>", "Folder id: inbox, sent, drafts, archive, trash", "inbox").option("-q, --query <text>", "Filter by subject, sender or recipient").option("-l, --limit <n>", "Messages per page", (v) => parseInt(v, 10), 30).option("--cursor <cursor>", "Continue from a previous page (its next_cursor)").option("--json", "Output as JSON (includes next_cursor)").action(
10200
+ (opts) => runAction(async () => {
10201
+ const { fetch: fetch2 } = await connectToRead();
10202
+ const page = await sdk.mail.listMessages(fetch2, {
10203
+ folderId: opts.folder,
10204
+ query: opts.query,
10205
+ limit: opts.limit,
10206
+ cursor: opts.cursor
10207
+ });
10208
+ if (opts.json) return printJson(page);
10209
+ if (page.messages.length === 0) {
10210
+ process.stderr.write("No messages.\n");
10211
+ return;
10212
+ }
10213
+ const people = (m) => (["sent", "drafts"].includes(opts.folder) ? m.to : m.sender).map((a) => a.name || a.email).join(", ");
10214
+ printTable(
10215
+ [
10216
+ { header: "ID", width: 38, value: (m) => m.id },
10217
+ { header: "WHO", width: 24, value: (m) => truncate(people(m), 23) },
10218
+ {
10219
+ header: "SUBJECT",
10220
+ width: 40,
10221
+ value: (m) => truncate(m.subject || "(no subject)", 39)
10222
+ },
10223
+ {
10224
+ header: "FLAGS",
10225
+ width: 14,
10226
+ value: (m) => [
10227
+ m.unread ? "unread" : "",
10228
+ m.attachments.length ? "attach" : "",
10229
+ m.drafted_by ? `by ${m.drafted_by}` : ""
10230
+ ].filter(Boolean).join(" ")
10231
+ }
10232
+ ],
10233
+ page.messages
10234
+ );
10235
+ if (page.next_cursor) dim(`More: --cursor ${page.next_cursor}`);
10236
+ })()
10237
+ );
10238
+ mail.command("read <id>").description("Read a message, or save one of its attachments with --attachment").option("--attachment <id>", "Save this attachment (id from the attachment list) instead").option("-o, --output <path>", "Where to save the attachment (default: its file name)").option("--json", "Output the full message as JSON").action(
10239
+ (id, opts) => runAction(async () => {
10240
+ const { fetch: fetch2 } = await connectToRead();
10241
+ const message = await sdk.mail.getMessage(fetch2, id);
10242
+ if (opts.attachment !== void 0) {
10243
+ const meta = message.attachments.find((a) => a.id === opts.attachment);
10244
+ if (!meta) throw new Error(`No attachment ${opts.attachment} on this message`);
10245
+ const bytes = await sdk.mail.downloadAttachment(fetch2, id, meta.id);
10246
+ const target = opts.output ?? meta.name;
10247
+ fs5__default.default.writeFileSync(target, bytes);
10248
+ success(`Saved ${target} (${bytes.length} bytes)`);
10249
+ return;
10250
+ }
10251
+ if (opts.json) return printJson(message);
10252
+ const who = (list) => list.map((a) => a.name ? `${a.name} <${a.email}>` : a.email).join(", ");
10253
+ console.log(`From: ${who(message.sender)}`);
10254
+ console.log(`To: ${who(message.to)}`);
10255
+ if (message.cc.length) console.log(`Cc: ${who(message.cc)}`);
10256
+ console.log(`Subject: ${message.subject}`);
10257
+ if (message.received_at) console.log(`Date: ${message.received_at}`);
10258
+ if (message.drafted_by) console.log(`Drafted by: ${message.drafted_by}`);
10259
+ if (message.attachments.length) {
10260
+ console.log(
10261
+ `Attachments: ${message.attachments.map((a) => `${a.name} (id ${a.id}, ${a.size} bytes)`).join("; ")}`
10262
+ );
10263
+ }
10264
+ console.log(`
10265
+ ${message.text}`);
10266
+ })()
10267
+ );
10268
+ addComposeOptions(
10269
+ mail.command("draft").description(
10270
+ "Save a draft. Run by an agent, it goes to its owner's Drafts, from their address, for them to review and send"
10271
+ )
10272
+ ).action(
10273
+ (opts) => runAction(async () => {
10274
+ const compose = buildCompose(opts);
10275
+ const { fetch: fetch2, isAgent } = await connect();
10276
+ const saved = await sdk.mail.saveDraft(fetch2, compose);
10277
+ if (opts.json) return printJson(saved);
10278
+ success(
10279
+ isAgent ? `Draft ${saved.id} saved to your owner's Drafts. They review it and send.` : `Draft ${saved.id} saved to Drafts.`
10280
+ );
10281
+ })()
10282
+ );
10283
+ addComposeOptions(
10284
+ mail.command("send").description("Send a message from your ARBI address (not available to agents)")
10285
+ ).option("--request-id <id>", "Idempotency key: retrying with the same id never sends twice").action(
10286
+ (opts) => runAction(async () => {
10287
+ const compose = buildCompose(opts);
10288
+ if (compose.to?.length === 0 && compose.cc?.length === 0 && compose.bcc?.length === 0) {
10289
+ throw new Error("Add at least one recipient with --to, --cc or --bcc");
10290
+ }
10291
+ const { fetch: fetch2, isAgent } = await connect();
10292
+ if (isAgent) {
10293
+ error("Agents cannot send mail. Save a draft instead: arbi mail draft ...");
10294
+ process.exit(1);
10295
+ }
10296
+ const result = await sdk.mail.sendMessage(fetch2, { ...compose, request_id: opts.requestId });
10297
+ if (opts.json) return printJson(result);
10298
+ success(`Sent (${result.id}).`);
10299
+ })()
10300
+ );
10301
+ mail.arguments("[maybeSubcommand]").action((maybe) => {
10302
+ if (maybe) {
10303
+ suggestSubcommandAndExit(
10304
+ "mail",
10305
+ maybe,
10306
+ mail.commands.map((c) => c.name())
10307
+ );
10308
+ }
10309
+ mail.outputHelp();
10310
+ });
10311
+ }
10048
10312
  function registerNotificationsCommand(program2) {
10049
10313
  const notif = program2.command("notifications").enablePositionalOptions().description("Non-DM notifications");
10050
10314
  notif.command("list").description("List non-DM notifications (workspace invites, contact accepts, \u2026)").option("--unread", "Only show unread notifications").option("--type <comma-list>", "Filter to specific type(s), e.g. workspaceuser_invited").option("-l, --limit <n>", "Cap to N most-recent", (v) => parseInt(v, 10)).option("--json", "Output as JSON (recommended for scripting / agents)").action(
@@ -10610,7 +10874,7 @@ console.info = (...args) => {
10610
10874
  _origInfo(...args);
10611
10875
  };
10612
10876
  var program = new commander.Command();
10613
- program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.31").showHelpAfterError(true).showSuggestionAfterError(true);
10877
+ program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.4.33").showHelpAfterError(true).showSuggestionAfterError(true);
10614
10878
  registerConfigCommand(program);
10615
10879
  registerLoginCommand(program);
10616
10880
  registerRegisterCommand(program);
@@ -10645,6 +10909,7 @@ registerListenCommand(program);
10645
10909
  registerSessionCommand(program);
10646
10910
  registerLocalCommand(program);
10647
10911
  registerNotificationsCommand(program);
10912
+ registerMailCommand(program);
10648
10913
  registerProjectCommand(program);
10649
10914
  registerFilesCommand(program);
10650
10915
  registerUsageCommand(program);