@parall/daemon 1.47.0 → 1.49.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.
@@ -51351,6 +51351,8 @@ var ENDPOINTS = {
51351
51351
  MESSAGE_WATCH: (id) => `${API_BASE}/messages/${id}/watch`,
51352
51352
  MESSAGE_WATCHERS: (id) => `${API_BASE}/messages/${id}/watchers`,
51353
51353
  MESSAGE_WATCHING: (id) => `${API_BASE}/messages/${id}/watching`,
51354
+ MESSAGE_REACTIONS: (id) => `${API_BASE}/messages/${id}/reactions`,
51355
+ MESSAGE_REACTION: (id, emoji) => `${API_BASE}/messages/${id}/reactions/${encodeURIComponent(emoji)}`,
51354
51356
  // Upload (org-scoped)
51355
51357
  UPLOAD_PRESIGN: (orgId) => `${API_BASE}/orgs/${orgId}/upload/presign`,
51356
51358
  UPLOAD_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/upload/complete`,
@@ -51588,11 +51590,13 @@ var ENDPOINTS = {
51588
51590
  UNREAD: `${API_BASE}/me/unread`,
51589
51591
  ORG_UNREAD: (orgId) => `${API_BASE}/orgs/${orgId}/unread`,
51590
51592
  CHAT_READ: (orgId, chatId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
51593
+ CHAT_READ_ALL: (orgId, chatId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/read-all`,
51591
51594
  THREAD_UNREAD: (orgId, chatId, threadRootId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/unread`,
51592
51595
  THREAD_READ: (orgId, chatId, threadRootId) => `${API_BASE}/orgs/${orgId}/chats/${chatId}/threads/${threadRootId}/read`,
51593
51596
  // References (org-scoped)
51594
51597
  REFS_RESOLVE: (orgId) => `${API_BASE}/orgs/${orgId}/refs/resolve`,
51595
51598
  REFS_BACKLINKS: (orgId) => `${API_BASE}/orgs/${orgId}/refs/backlinks`,
51599
+ REFS_OUTBOUND: (orgId) => `${API_BASE}/orgs/${orgId}/refs/outbound`,
51596
51600
  REFS_GRAPH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/graph`,
51597
51601
  REFS_CHECK: (orgId) => `${API_BASE}/orgs/${orgId}/refs/check`,
51598
51602
  // Platform config (agent-scoped, not org-scoped)
@@ -51651,10 +51655,23 @@ var ENDPOINTS = {
51651
51655
  ORG_EDGE: (orgId) => `/api/v1/orgs/${orgId}/edge`,
51652
51656
  ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
51653
51657
  ORG_EDGE_DEVICE: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}`,
51658
+ ORG_EDGE_DEVICE_UNREGISTER: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/unregister`,
51654
51659
  ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
51655
51660
  ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
51661
+ // Per-profile egress proxy (hosted Cloud Profiles; manager-only, human-only).
51662
+ ORG_EDGE_PROFILE_PROXY: (orgId, edgeId, profileName) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles/${encodeURIComponent(profileName)}/proxy`,
51663
+ ORG_EDGE_EXEC: (orgId) => `/api/v1/orgs/${orgId}/edge/exec`,
51664
+ // Cloud Edge live viewer command (V1b) — api-server, gated on cap:edge-viewer.
51665
+ // Same request/reply shape as the v2 browser-profile viewer, on the v3 edge
51666
+ // pipe. Additive: does NOT replace BROWSER_PROFILE_VIEWER_COMMAND.
51667
+ ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
51656
51668
  CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
51657
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
51669
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
51670
+ // Clip registry (v3, org-scoped, served by api-server — `crg_` entries; the
51671
+ // Pinix Hub catalog proxy above is a different, id-less surface)
51672
+ ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
51673
+ ORG_CLIP_INSTALL: (orgId) => `/api/v1/orgs/${orgId}/clips/install`,
51674
+ ORG_CLIPS_INSTALLED: (orgId) => `/api/v1/orgs/${orgId}/clips/installed`
51658
51675
  };
51659
51676
  var WS_EVENTS = {
51660
51677
  // Client -> Server
@@ -51670,6 +51687,7 @@ var WS_EVENTS = {
51670
51687
  MESSAGE_PATCH: "message.patch",
51671
51688
  MESSAGE_EDIT: "message.edit",
51672
51689
  MESSAGE_DELETE: "message.delete",
51690
+ MESSAGE_REACTION_UPDATED: "message.reaction.updated",
51673
51691
  TYPING_UPDATE: "typing.update",
51674
51692
  CHAT_UPDATE: "chat.update",
51675
51693
  CHAT_DELETED: "chat.deleted",
@@ -51711,6 +51729,7 @@ var WS_EVENTS = {
51711
51729
  INBOX_UPDATE: "inbox.update",
51712
51730
  INBOX_BULK_UPDATE: "inbox.bulk_update",
51713
51731
  READ_POSITION_UPDATED: "read_position.updated",
51732
+ THREAD_READ_POSITION_UPDATED: "thread_read_position.updated",
51714
51733
  DISPATCH_NEW: "dispatch.new",
51715
51734
  DISPATCH_RECEIVED: "dispatch.received",
51716
51735
  DISPATCH_RESOLVED: "dispatch.resolved",
@@ -51896,7 +51915,7 @@ var ParallClient = class _ParallClient {
51896
51915
  if (qs)
51897
51916
  url += `?${qs}`;
51898
51917
  }
51899
- const headers = this.buildHeaders(path11);
51918
+ const headers = this.buildHeaders(path11, opts?.headers);
51900
51919
  const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
51901
51920
  const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
51902
51921
  let res;
@@ -52302,6 +52321,14 @@ var ParallClient = class _ParallClient {
52302
52321
  async getMessageReplies(id, params) {
52303
52322
  return this.request("GET", ENDPOINTS.MESSAGE_REPLIES(id), void 0, params);
52304
52323
  }
52324
+ // ---- Reactions ----
52325
+ async toggleReaction(messageId, emoji) {
52326
+ return this.request("PUT", ENDPOINTS.MESSAGE_REACTION(messageId, emoji));
52327
+ }
52328
+ async listReactions(messageId) {
52329
+ const res = await this.request("GET", ENDPOINTS.MESSAGE_REACTIONS(messageId));
52330
+ return res.reactions;
52331
+ }
52305
52332
  // ---- File Upload ----
52306
52333
  async getUploadPresignUrl(orgId, req) {
52307
52334
  return this.request("POST", ENDPOINTS.UPLOAD_PRESIGN(orgId), req);
@@ -52677,6 +52704,15 @@ var ParallClient = class _ParallClient {
52677
52704
  async browserViewerCommand(orgId, profileId, req, opts) {
52678
52705
  return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
52679
52706
  }
52707
+ /**
52708
+ * Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
52709
+ * tab nav for a hosted browser (Cloud Profile). Same request/reply shape as
52710
+ * browserViewerCommand, on the v3 edge pipe; additive — the v2 browser-profile
52711
+ * viewer is unchanged.
52712
+ */
52713
+ async edgeViewerCommand(orgId, edgeId, req, opts) {
52714
+ return this.request("POST", ENDPOINTS.ORG_EDGE_VIEWER_COMMAND(orgId, edgeId), req, void 0, false, opts);
52715
+ }
52680
52716
  async resizeMachine(orgId, machineId, spec) {
52681
52717
  return this.request("PATCH", ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
52682
52718
  }
@@ -52703,14 +52739,23 @@ var ParallClient = class _ParallClient {
52703
52739
  }
52704
52740
  // Wiki mount/token methods removed — wiki-service handles all wiki endpoints.
52705
52741
  // ---- Unread ----
52706
- async getUnreadCounts(orgId) {
52742
+ async getUnreadCounts(orgId, opts) {
52707
52743
  const endpoint = orgId ? ENDPOINTS.ORG_UNREAD(orgId) : ENDPOINTS.UNREAD;
52708
- const res = await this.request("GET", endpoint);
52744
+ const res = await this.request("GET", endpoint, void 0, {
52745
+ include_thread_replies: opts?.includeThreadReplies ? "true" : void 0
52746
+ });
52709
52747
  return res.data;
52710
52748
  }
52711
52749
  async markRead(orgId, chatId, messageId) {
52712
52750
  return this.request("POST", ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
52713
52751
  }
52752
+ /** Mark everything in a chat read: the channel cursor jumps to the latest
52753
+ * top-level message and every thread cursor to its latest reply, in one
52754
+ * idempotent call. The server echoes the same per-cursor WS events the
52755
+ * single-cursor routes emit and auto-clears covered inbox items. */
52756
+ async markAllRead(orgId, chatId) {
52757
+ return this.request("POST", ENDPOINTS.CHAT_READ_ALL(orgId, chatId));
52758
+ }
52714
52759
  async getThreadUnread(orgId, chatId, threadRootId) {
52715
52760
  return this.request("GET", ENDPOINTS.THREAD_UNREAD(orgId, chatId, threadRootId));
52716
52761
  }
@@ -53385,6 +53430,16 @@ var ParallClient = class _ParallClient {
53385
53430
  async getBacklinks(orgId, params) {
53386
53431
  return this.request("GET", ENDPOINTS.REFS_BACKLINKS(orgId), void 0, params);
53387
53432
  }
53433
+ /**
53434
+ * Outbound prll:// refs authored in a set of sources, as raw ref_links rows
53435
+ * (dedupe/group client-side). Pass `{ thread_root_id }` to list a whole
53436
+ * thread's refs (root + all replies, resolved server-side — the client's
53437
+ * reply window may be partial), or `{ source_type, source_ids }` for
53438
+ * explicit sources (max 500; v1 accepts only message sources).
53439
+ */
53440
+ async listOutboundRefs(orgId, req) {
53441
+ return this.request("POST", ENDPOINTS.REFS_OUTBOUND(orgId), req);
53442
+ }
53388
53443
  /**
53389
53444
  * Bounded multi-hop walk of the prll:// reference graph around `uri`. `uri`
53390
53445
  * must be an entity-level prll:// URI — a refined URI (path/query/fragment) is
@@ -53595,6 +53650,31 @@ var ParallClient = class _ParallClient {
53595
53650
  const resp = await this.request("GET", url);
53596
53651
  return resp.data;
53597
53652
  }
53653
+ // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
53654
+ /**
53655
+ * List registry clips visible to the org: its own plus public+approved
53656
+ * cross-org entries. This is the surface `installRegistryClip` and clip
53657
+ * connections operate on — NOT the Pinix catalog proxy
53658
+ * ({@link listRegistryClips}), whose entries carry no `crg_` id.
53659
+ */
53660
+ async listOrgRegistryClips(orgId) {
53661
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
53662
+ return resp ?? [];
53663
+ }
53664
+ /**
53665
+ * Install a registry clip into the org (a reference in `clip_installs`, not a
53666
+ * copy). Idempotent: installing an already-installed clip returns the same
53667
+ * `200 {ok:true}`. Fails closed with `403 CLIP_NOT_APPROVED` when the clip is
53668
+ * not eligible (cross-org requires public + approved).
53669
+ */
53670
+ async installRegistryClip(orgId, clipId) {
53671
+ return this.request("POST", ENDPOINTS.ORG_CLIP_INSTALL(orgId), { clip_id: clipId });
53672
+ }
53673
+ /** List the org's installed registry clips (full entries). */
53674
+ async listInstalledRegistryClips(orgId) {
53675
+ const resp = await this.request("GET", ENDPOINTS.ORG_CLIPS_INSTALLED(orgId));
53676
+ return resp ?? [];
53677
+ }
53598
53678
  // ---- Edge devices ----
53599
53679
  async listEdgeDevices(orgId) {
53600
53680
  return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
@@ -53612,8 +53692,8 @@ var ParallClient = class _ParallClient {
53612
53692
  return this.request("POST", ENDPOINTS.ORG_EDGE(orgId), input);
53613
53693
  }
53614
53694
  /**
53615
- * Delete a hosted Cloud Profile. Hosted only — a BYOC device is removed by
53616
- * uninstalling Parall Clip on that machine (`400 EDGE_PLACEMENT_UNSUPPORTED`).
53695
+ * Delete a hosted Cloud Profile. Hosted only — use {@link unregisterEdgeDevice}
53696
+ * for an offline BYOC registration (`400 EDGE_PLACEMENT_UNSUPPORTED` here).
53617
53697
  *
53618
53698
  * Idempotent and ASYNC: returns `202` with `hosted_state: 'deleting'` on the first
53619
53699
  * call and on every repeat. The device stops being usable immediately (no exec, no
@@ -53623,12 +53703,105 @@ var ParallClient = class _ParallClient {
53623
53703
  async deleteEdgeDevice(orgId, edgeId) {
53624
53704
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
53625
53705
  }
53706
+ /**
53707
+ * Remove the interactive human caller's own offline BYOC registration.
53708
+ *
53709
+ * Synchronous and idempotent: a committed removal and a repeat after removal
53710
+ * both resolve with no response body. A live connection returns `EDGE_ONLINE`;
53711
+ * callers must not clear local device identity until this method resolves.
53712
+ */
53713
+ async unregisterEdgeDevice(orgId, edgeId) {
53714
+ await this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE_UNREGISTER(orgId, edgeId));
53715
+ }
53626
53716
  async getEdgeOnboarding(orgId) {
53627
53717
  return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
53628
53718
  }
53629
53719
  async listEdgeProfiles(orgId, edgeId) {
53630
53720
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
53631
53721
  }
53722
+ /**
53723
+ * Read a hosted Cloud Profile's egress-proxy status (manager-only: hosted
53724
+ * human maintainer or org admin). Sanitized — the password never comes back.
53725
+ * Typed errors: `EDGE_NOT_HOSTED` (BYOC device), `PROXY_CONFIG_CORRUPT` /
53726
+ * validation codes as 422 when a stored config no longer passes current rules.
53727
+ * `can_mutate` and `lease_status` are the authoritative idle gate; device
53728
+ * list status is not a substitute.
53729
+ */
53730
+ async getEdgeProfileProxy(orgId, edgeId, profileName) {
53731
+ return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName));
53732
+ }
53733
+ /**
53734
+ * Set/replace the profile's egress proxy (full triple every time) — IDLE
53735
+ * ONLY: while the profile's hosted browser is running (a viewer session is
53736
+ * open or a pod is otherwise live) the server answers 409
53737
+ * `EDGE_PROFILE_IN_USE`; close the viewer, wait for idle scale-to-zero, and
53738
+ * retry. The next cold start uses the new egress; browser login state is
53739
+ * preserved across it. Other typed errors: the validation vocabulary
53740
+ * (`INVALID_PROXY_SERVER`, `PROXY_AUTH_INCOMPLETE`,
53741
+ * `PROXY_SERVER_FORBIDDEN_TARGET`, …), `EDGE_DELETING` (409), and
53742
+ * `SECRETBOX_UNCONFIGURED` (503 — server cannot store credentials safely),
53743
+ * and `EDGE_PROFILE_PROXY_STALE` (409 — expectedVersion lost a tab race).
53744
+ */
53745
+ async setEdgeProfileProxy(orgId, edgeId, profileName, req, expectedVersion) {
53746
+ return this.request("PUT", ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName), req, void 0, false, {
53747
+ headers: expectedVersion ? { "If-Match": `"proxy-${expectedVersion}"` } : void 0
53748
+ });
53749
+ }
53750
+ /**
53751
+ * Clear the profile's egress proxy; the next pod start egresses directly.
53752
+ * Idle-only like set — 409 `EDGE_PROFILE_IN_USE` while the browser is live.
53753
+ */
53754
+ async clearEdgeProfileProxy(orgId, edgeId, profileName, expectedVersion) {
53755
+ return this.request("DELETE", ENDPOINTS.ORG_EDGE_PROFILE_PROXY(orgId, edgeId, profileName), void 0, void 0, false, {
53756
+ headers: expectedVersion ? { "If-Match": `"proxy-${expectedVersion}"` } : void 0
53757
+ });
53758
+ }
53759
+ /**
53760
+ * Execute a registry clip command on an Edge device.
53761
+ *
53762
+ * A hosted (Cloud Profile) device is reachable ONLY through an explicit
53763
+ * `connection` (id `ccn_…` or alias) — there is no implicit route to an
53764
+ * org-shared browser login. BYOC keeps its legacy selectors (`edge_id`, or
53765
+ * nothing for the caller's own online device).
53766
+ *
53767
+ * Returns the result envelope on completion (`success` may be false when the
53768
+ * command RAN and failed — `error`/`error_code` describe why). Everything
53769
+ * else throws a typed {@link ApiError}; match on `err.code`:
53770
+ *
53771
+ * Safe to retry (guaranteed nothing was dispatched):
53772
+ * - `EDGE_ACTIVATING` 503 + `Retry-After` — cold cloud profile is starting.
53773
+ * Bounded backoff, same `correlation_id` across the loop.
53774
+ * - `EDGE_BUSY` 409 — the device is executing another request.
53775
+ * - `EDGE_CONCURRENCY_LIMIT` 429 — org at its concurrent-session limit.
53776
+ * - `EDGE_UNAVAILABLE` 503 — session torn down / replaced mid-dispatch.
53777
+ *
53778
+ * NOT retryable:
53779
+ * - `OUTCOME_UNKNOWN` 504 — dispatched, but no result arrived. The command
53780
+ * MAY HAVE EXECUTED (posted, ordered, deleted…). Never retry
53781
+ * automatically: verify the effect first, then decide. The message carries
53782
+ * the request id for audit.
53783
+ * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
53784
+ * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
53785
+ * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
53786
+ * - Routing errors: `HOSTED_CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
53787
+ * `CONNECTION_CLIP_MISMATCH`, `CONNECTION_TARGET_GONE`,
53788
+ * `CONNECTION_PROFILE_MISMATCH`, `EDGE_DELETING`, `DEVICE_OFFLINE`.
53789
+ */
53790
+ async execEdgeClip(orgId, req) {
53791
+ const t = req.timeout;
53792
+ const serverTimeout = t !== void 0 && t > 0 && t <= 12e4 ? t : 3e4;
53793
+ const timeoutMs = serverTimeout + 1e4;
53794
+ try {
53795
+ return await this.request("POST", ENDPOINTS.ORG_EDGE_EXEC(orgId), req, void 0, false, {
53796
+ timeoutMs
53797
+ });
53798
+ } catch (err) {
53799
+ if (err instanceof ApiError && err.status === 422 && !err.code && typeof err.extras?.error_code === "string") {
53800
+ err.code = err.extras.error_code;
53801
+ }
53802
+ throw err;
53803
+ }
53804
+ }
53632
53805
  // ---- Clip connections ----
53633
53806
  async listClipConnections(orgId, clipId) {
53634
53807
  return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
@@ -53651,6 +53824,8 @@ var ApiError = class extends Error {
53651
53824
  status;
53652
53825
  code;
53653
53826
  extras;
53827
+ /** Retry-After delta seconds when the server supplies one. */
53828
+ retryAfterSeconds;
53654
53829
  /** Attempted action (authorization denials) — e.g. "chat.add_member". */
53655
53830
  action;
53656
53831
  /** Target resource URI that was evaluated — e.g. "prll://cht_…". */
@@ -53670,6 +53845,9 @@ function buildApiError(res, rawErrorBody) {
53670
53845
  const errMsg = (typeof errorObj?.message === "string" ? errorObj.message : void 0) ?? (typeof errorBody.message === "string" ? errorBody.message : void 0) ?? (typeof errorBody.error === "string" ? errorBody.error : void 0);
53671
53846
  const errCode = (typeof errorObj?.code === "string" ? errorObj.code : void 0) ?? (typeof errorBody.code === "string" ? errorBody.code : void 0);
53672
53847
  const apiError = new ApiError(res.status, errMsg ?? res.statusText, errCode);
53848
+ const retryAfter = Number(res.headers?.get("Retry-After"));
53849
+ if (Number.isFinite(retryAfter) && retryAfter > 0)
53850
+ apiError.retryAfterSeconds = retryAfter;
53673
53851
  const anchors = errorObj ?? errorBody;
53674
53852
  if (typeof anchors.action === "string")
53675
53853
  apiError.action = anchors.action;
@@ -59244,7 +59422,7 @@ parall tasks list --assignee-id prll://usr_xxx # first page only (default 20)
59244
59422
  parall tasks subtasks prll://tsk_xxx # children of a single parent task
59245
59423
 
59246
59424
  # Create a task (add --parent-id to make it a SUBTASK of another task)
59247
- parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--parent-id prll://tsk_xxx] [--project-id prll://prj_xxx] [--due-date 2026-08-01]
59425
+ parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--parent-id prll://tsk_xxx] [--project-id prll://prj_xxx] [--planned-start 2026-07-20] [--due-date 2026-08-01]
59248
59426
 
59249
59427
  # Update task status \u2014 add --placement end so the task lands at the end of
59250
59428
  # its NEW status column (a bare --status keeps the old column's sort_order)
@@ -59255,6 +59433,12 @@ parall tasks update prll://tsk_xxx --status done --placement end
59255
59433
  parall tasks update prll://tsk_xxx --due-date 2026-08-01
59256
59434
  parall tasks update prll://tsk_xxx --due-date none
59257
59435
 
59436
+ # Planned start \u2014 same grammar as --due-date; must be on or before the due
59437
+ # date when both are set. This is the planned schedule's left-edge date,
59438
+ # NOT when work actually began (that stays on lifecycle timestamps)
59439
+ parall tasks update prll://tsk_xxx --planned-start 2026-07-20
59440
+ parall tasks update prll://tsk_xxx --planned-start none
59441
+
59258
59442
  # Move a task to the end of its status column
59259
59443
  parall tasks update prll://tsk_xxx --placement end
59260
59444
 
@@ -59715,6 +59899,27 @@ parall clip invoke github-tools list-repos '{"org":"acme"}'
59715
59899
 
59716
59900
  Results are JSON on stdout; failures print an error.
59717
59901
 
59902
+ ## Execute on an Edge device (registry clips)
59903
+
59904
+ Registry clips run on an Edge \u2014 a member's desktop, or an org-shared cloud
59905
+ profile. **Name the target explicitly.** A cloud profile has NO implicit
59906
+ route; omitting the target entirely is a desktop-only legacy form that
59907
+ reaches just YOUR OWN online desktop device \u2014 never a shared cloud profile.
59908
+
59909
+ \`\`\`bash
59910
+ parall clip exec <clip> <command> [args] --connection <id|alias> # the normal form
59911
+ parall clip exec browser-tools screenshot '{"url":"\u2026"}' --connection cloud-main
59912
+ \`\`\`
59913
+
59914
+ - A cloud (hosted) profile is reachable ONLY via \`--connection\` \u2014 the clip
59915
+ connection its maintainer bound (\`ccn_\u2026\` id or alias). That binding IS your
59916
+ authorization; without one the server answers \`HOSTED_CONNECTION_REQUIRED\`
59917
+ and the fix is to ask an owner/admin to bind the clip, never to retry.
59918
+ - \`--edge <edgeId>\` targets only a desktop device YOU own.
59919
+ - Cold cloud profiles are handled by the CLI: it absorbs \`EDGE_ACTIVATING\`
59920
+ with a bounded wait (~60s) while the profile starts. If the command still
59921
+ fails, report the error \u2014 do not blind-retry in a loop.
59922
+
59718
59923
  ## Behavior rules
59719
59924
 
59720
59925
  - An authorization error (clip not bound to you) is a fail-fast: ask the
@@ -59724,6 +59929,12 @@ Results are JSON on stdout; failures print an error.
59724
59929
  - Hosted browser activation is handled by the CLI: it waits (bounded) while a
59725
59930
  cold hosted browser starts, so if the invoke still fails, report the error \u2014
59726
59931
  do not blind-retry in a loop.
59932
+ - **\`OUTCOME_UNKNOWN\` is never retryable.** It means the command was
59933
+ dispatched and MAY HAVE EXECUTED even though no result came back. Retrying
59934
+ could post, order or delete twice. Verify the effect through the system you
59935
+ acted on (or tell the human, quoting the request id from the error) before
59936
+ ever re-running. \`EDGE_BUSY\` is the opposite: guaranteed-unexecuted \u2014 wait
59937
+ briefly, then one retry is safe.
59727
59938
  - A clip may act through a person's real logged-in account \u2014 outward,
59728
59939
  irreversible, or spending actions (post, order, delete, pay) get the same
59729
59940
  caution as any shared-state change: confirm when intent isn't explicit.