@awak-app/simy-cli 0.5.18 → 0.5.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awak-app/simy-cli",
3
- "version": "0.5.18",
3
+ "version": "0.5.20",
4
4
  "description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -53,6 +53,7 @@ import {
53
53
  createConnectionStatusTracker,
54
54
  desktopAuthStatus,
55
55
  desktopTaskStatus,
56
+ desktopTaskDetail,
56
57
  } from "./desktop-status.js";
57
58
  import { recoveryContractForPreflight } from "./orchestrator/recovery.js";
58
59
  import {
@@ -1661,6 +1662,7 @@ export async function startAgent({
1661
1662
  },
1662
1663
  requestDesktopLogin,
1663
1664
  getDesktopStatus,
1665
+ getDesktopTask: (key) => desktopTaskDetail([registry, directRegistry, localTaskRegistry], key),
1664
1666
  async refreshDesktopStatus() {
1665
1667
  if (isSessionValid(session, Date.now(), apiOrigin)) {
1666
1668
  await heartbeat().catch((error) => reportHeartbeatError(error, quiet));
@@ -13,7 +13,7 @@ import {
13
13
  } from "./durable-local-task-steps-contract.js";
14
14
  import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
15
15
 
16
- export const CLI_VERSION = "0.5.18";
16
+ export const CLI_VERSION = "0.5.20";
17
17
  export const CLI_API_CONTRACT_VERSION = 10;
18
18
 
19
19
  export function withCliContract(
@@ -144,6 +144,12 @@ export async function startDesktopHost({ input = process.stdin, output = process
144
144
  queue = queue.then(async () => {
145
145
  try {
146
146
  const action = command.action;
147
+ if (action === "task_detail") {
148
+ const task = await agent.getDesktopTask?.(command.taskKey);
149
+ emit({ type: "task_detail", id: command.id, taskKey: command.taskKey, task: task ?? null });
150
+ emit({ type: "result", id: command.id, ok: Boolean(task) });
151
+ return;
152
+ }
147
153
  if (action === "refresh" || action === "reconnect") {
148
154
  await agent.refreshCodexStatus?.();
149
155
  await agent.refreshDesktopStatus();
@@ -83,6 +83,42 @@ export function desktopTaskStatus(registries, { maxItems = MAX_STATUS_ITEMS } =
83
83
  };
84
84
  }
85
85
 
86
+ // Detail is sent only on selection, never in the periodic metadata broadcast.
87
+ export function desktopTaskDetail(registries, key) {
88
+ if (typeof key !== "string" || key.length > 256) return null;
89
+ const match = /^(\d+):(.+)$/.exec(key);
90
+ if (!match) return null;
91
+ const registryIndex = Number(match[1]);
92
+ const task = registries[registryIndex]?.list?.().find((item) => item.id === match[2]);
93
+ if (!task) return null;
94
+ const attempt = task.snapshot?.attempts?.at(-1);
95
+ const request = task.request?.requirement || task.instruction || task.snapshot?.charter?.requirement;
96
+ const result = typeof task.result === "string" ? task.result : task.result?.summary || attempt?.summary;
97
+ const links = [];
98
+ const pr = safeDesktopLink(attempt?.pr_url);
99
+ if (pr) links.push({ label: "Pull request", url: pr });
100
+ return {
101
+ key,
102
+ instruction: desktopContent(request),
103
+ result: desktopContent(result),
104
+ error: desktopContent(task.error, 1000),
105
+ truncated: [request, result].some((value) => typeof value === "string" && value.length > 16000),
106
+ links,
107
+ };
108
+ }
109
+
110
+ function desktopContent(value, limit = 16000) {
111
+ // Preserve paragraphs/Markdown, but never serialize runtime objects or logs.
112
+ return typeof value === "string" ? value.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").slice(0, limit) : "";
113
+ }
114
+
115
+ function safeDesktopLink(value) {
116
+ try {
117
+ const url = new URL(value);
118
+ return url.protocol === "https:" && !url.username && !url.password ? url.href : null;
119
+ } catch { return null; }
120
+ }
121
+
86
122
  export function createCodexStatusInspector({
87
123
  cacheMs = CODEX_STATUS_CACHE_MS,
88
124
  inspect = inspectCodexStatus,
@@ -178,6 +214,7 @@ function taskStatusItem(task, registryIndex) {
178
214
  48,
179
215
  );
180
216
  return {
217
+ key: `${registryIndex}:${bounded(task?.id || "", 240)}`,
181
218
  id: bounded(String(task?.id || `unknown-${registryIndex}`), MAX_LABEL_LENGTH),
182
219
  label: bounded(
183
220
  String(