palmier 0.4.5 → 0.4.7

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.
Files changed (62) hide show
  1. package/README.md +29 -31
  2. package/dist/agents/agent-instructions.md +9 -9
  3. package/dist/agents/claude.js +3 -3
  4. package/dist/agents/codex.js +3 -3
  5. package/dist/agents/copilot.js +3 -6
  6. package/dist/agents/gemini.js +4 -5
  7. package/dist/agents/openclaw.js +2 -2
  8. package/dist/agents/shared-prompt.d.ts +2 -4
  9. package/dist/agents/shared-prompt.js +9 -4
  10. package/dist/commands/init.js +31 -2
  11. package/dist/commands/pair.d.ts +1 -1
  12. package/dist/commands/pair.js +12 -15
  13. package/dist/commands/plan-generation.md +12 -15
  14. package/dist/commands/run.js +23 -44
  15. package/dist/commands/serve.d.ts +1 -1
  16. package/dist/commands/serve.js +9 -2
  17. package/dist/events.d.ts +2 -2
  18. package/dist/events.js +15 -16
  19. package/dist/index.js +0 -25
  20. package/dist/pending-requests.d.ts +27 -0
  21. package/dist/pending-requests.js +39 -0
  22. package/dist/rpc-handler.js +18 -10
  23. package/dist/task.d.ts +1 -1
  24. package/dist/task.js +3 -2
  25. package/dist/transports/http-transport.d.ts +4 -2
  26. package/dist/transports/http-transport.js +218 -77
  27. package/dist/types.d.ts +7 -16
  28. package/package.json +1 -1
  29. package/src/agents/agent-instructions.md +9 -9
  30. package/src/agents/claude.ts +3 -3
  31. package/src/agents/codex.ts +3 -3
  32. package/src/agents/copilot.ts +3 -6
  33. package/src/agents/gemini.ts +5 -5
  34. package/src/agents/openclaw.ts +2 -2
  35. package/src/agents/shared-prompt.ts +12 -6
  36. package/src/commands/init.ts +34 -3
  37. package/src/commands/pair.ts +11 -14
  38. package/src/commands/plan-generation.md +12 -15
  39. package/src/commands/run.ts +21 -58
  40. package/src/commands/serve.ts +11 -2
  41. package/src/events.ts +14 -15
  42. package/src/index.ts +0 -26
  43. package/src/pending-requests.ts +55 -0
  44. package/src/rpc-handler.ts +18 -11
  45. package/src/task.ts +3 -1
  46. package/src/transports/http-transport.ts +232 -133
  47. package/src/types.ts +10 -16
  48. package/dist/commands/lan.d.ts +0 -8
  49. package/dist/commands/lan.js +0 -44
  50. package/dist/commands/notify.d.ts +0 -9
  51. package/dist/commands/notify.js +0 -43
  52. package/dist/commands/request-input.d.ts +0 -10
  53. package/dist/commands/request-input.js +0 -49
  54. package/dist/lan-lock.d.ts +0 -7
  55. package/dist/lan-lock.js +0 -18
  56. package/dist/user-input.d.ts +0 -15
  57. package/dist/user-input.js +0 -50
  58. package/src/commands/lan.ts +0 -48
  59. package/src/commands/notify.ts +0 -44
  60. package/src/commands/request-input.ts +0 -51
  61. package/src/lan-lock.ts +0 -16
  62. package/src/user-input.ts +0 -67
@@ -9,7 +9,6 @@ import { getAgent } from "../agents/agent.js";
9
9
  import { getPlatform } from "../platform/index.js";
10
10
  import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, TASK_REPORT_PREFIX, TASK_PERMISSION_PREFIX } from "../agents/shared-prompt.js";
11
11
  import { publishHostEvent } from "../events.js";
12
- import { waitForUserInput } from "../user-input.js";
13
12
  /**
14
13
  * Invoke the agent CLI with a continuation loop for permissions and user input.
15
14
  *
@@ -24,7 +23,7 @@ async function invokeAgentWithContinuation(ctx, invokeTask) {
24
23
  const { command, args, stdin } = ctx.agent.getTaskRunCommandLine(invokeTask, followupPrompt, ctx.transientPermissions);
25
24
  const result = await spawnCommand(command, args, {
26
25
  cwd: getRunDir(ctx.taskDir, ctx.runId),
27
- env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId) },
26
+ env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 7400) },
28
27
  echoStdout: true,
29
28
  resolveOnFailure: true,
30
29
  stdin,
@@ -41,8 +40,7 @@ async function invokeAgentWithContinuation(ctx, invokeTask) {
41
40
  });
42
41
  // Permission handling — agent requested permissions
43
42
  if (requiredPermissions.length > 0) {
44
- const response = await requestPermission(ctx.nc, ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
45
- await publishPermissionResolved(ctx.nc, ctx.config, ctx.taskId, response);
43
+ const response = await requestPermission(ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
46
44
  if (response === "aborted") {
47
45
  await appendAndNotify(ctx, {
48
46
  role: "user",
@@ -128,7 +126,7 @@ export async function runCommand(taskId) {
128
126
  const taskName = task.frontmatter.name;
129
127
  // Use existing run dir if just created by RPC, otherwise create a new one
130
128
  const existingRunId = findLatestPendingRunId(taskDir);
131
- const runId = existingRunId ?? createRunDir(taskDir, taskName, Date.now());
129
+ const runId = existingRunId ?? createRunDir(taskDir, taskName, Date.now(), task.frontmatter.agent);
132
130
  if (!existingRunId) {
133
131
  appendHistory(config.projectRoot, { task_id: taskId, run_id: runId });
134
132
  }
@@ -144,9 +142,7 @@ export async function runCommand(taskId) {
144
142
  await publishHostEvent(nc, config.hostId, taskId, { event_type: "result-updated", run_id: runId });
145
143
  // If requires_confirmation, notify clients and wait
146
144
  if (task.frontmatter.requires_confirmation) {
147
- const confirmed = await requestConfirmation(nc, config, task, taskDir);
148
- const resolvedStatus = confirmed ? "confirmed" : "aborted";
149
- await publishConfirmResolved(nc, config, taskId, resolvedStatus);
145
+ const confirmed = await requestConfirmation(config, task, taskDir);
150
146
  if (!confirmed) {
151
147
  console.log("Task aborted by user.");
152
148
  appendRunMessage(taskDir, runId, { role: "status", time: Date.now(), content: "", type: "aborted" });
@@ -220,7 +216,7 @@ async function runCommandTriggeredMode(ctx) {
220
216
  console.log(`[command-triggered] Spawning: ${commandStr}`);
221
217
  const child = spawnStreamingCommand(commandStr, {
222
218
  cwd: getRunDir(ctx.taskDir, ctx.runId),
223
- env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId) },
219
+ env: { ...ctx.guiEnv, PALMIER_TASK_ID: ctx.task.frontmatter.id, PALMIER_RUN_DIR: getRunDir(ctx.taskDir, ctx.runId), PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 7400) },
224
220
  });
225
221
  let linesProcessed = 0;
226
222
  let invocationsSucceeded = 0;
@@ -336,49 +332,32 @@ async function publishTaskEvent(nc, config, taskDir, taskId, eventType, taskName
336
332
  payload.run_id = runId;
337
333
  await publishHostEvent(nc, config.hostId, taskId, payload);
338
334
  }
339
- /**
340
- * Notify clients that a confirmation request has been resolved.
341
- */
342
- async function publishConfirmResolved(nc, config, taskId, status) {
343
- await publishHostEvent(nc, config.hostId, taskId, {
344
- event_type: "confirm-resolved",
345
- host_id: config.hostId,
346
- status,
347
- });
348
- }
349
- async function requestPermission(nc, config, task, taskDir, requiredPermissions) {
350
- const currentStatus = readTaskStatus(taskDir);
351
- writeTaskStatus(taskDir, { ...currentStatus, pending_permission: requiredPermissions });
352
- await publishHostEvent(nc, config.hostId, task.frontmatter.id, {
353
- event_type: "permission-request",
354
- host_id: config.hostId,
355
- required_permissions: requiredPermissions,
356
- name: task.frontmatter.name,
335
+ async function requestPermission(config, task, taskDir, requiredPermissions) {
336
+ const port = config.httpPort ?? 7400;
337
+ const res = await fetch(`http://localhost:${port}/request-permission`, {
338
+ method: "POST",
339
+ headers: { "Content-Type": "application/json" },
340
+ body: JSON.stringify({
341
+ taskId: task.frontmatter.id,
342
+ taskName: task.frontmatter.name,
343
+ permissions: requiredPermissions,
344
+ }),
357
345
  });
358
- const userInput = await waitForUserInput(taskDir);
359
- const response = userInput[0];
346
+ const { response } = await res.json();
360
347
  writeTaskStatus(taskDir, {
361
348
  running_state: response === "aborted" ? "aborted" : "started",
362
349
  time_stamp: Date.now(),
363
350
  });
364
351
  return response;
365
352
  }
366
- async function publishPermissionResolved(nc, config, taskId, status) {
367
- await publishHostEvent(nc, config.hostId, taskId, {
368
- event_type: "permission-resolved",
369
- host_id: config.hostId,
370
- status,
371
- });
372
- }
373
- async function requestConfirmation(nc, config, task, taskDir) {
374
- const currentStatus = readTaskStatus(taskDir);
375
- writeTaskStatus(taskDir, { ...currentStatus, pending_confirmation: true });
376
- await publishHostEvent(nc, config.hostId, task.frontmatter.id, {
377
- event_type: "confirm-request",
378
- host_id: config.hostId,
353
+ async function requestConfirmation(config, task, taskDir) {
354
+ const port = config.httpPort ?? 7400;
355
+ const res = await fetch(`http://localhost:${port}/request-confirmation`, {
356
+ method: "POST",
357
+ headers: { "Content-Type": "application/json" },
358
+ body: JSON.stringify({ taskId: task.frontmatter.id, taskName: task.frontmatter.name }),
379
359
  });
380
- const userInput = await waitForUserInput(taskDir);
381
- const confirmed = userInput[0] === "confirmed";
360
+ const { confirmed } = await res.json();
382
361
  writeTaskStatus(taskDir, {
383
362
  running_state: confirmed ? "started" : "aborted",
384
363
  time_stamp: Date.now(),
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Start the persistent RPC handler (NATS only).
2
+ * Start the persistent RPC handler (NATS + HTTP).
3
3
  */
4
4
  export declare function serveCommand(): Promise<void>;
5
5
  //# sourceMappingURL=serve.d.ts.map
@@ -4,6 +4,7 @@ import { loadConfig } from "../config.js";
4
4
  import { connectNats } from "../nats-client.js";
5
5
  import { createRpcHandler } from "../rpc-handler.js";
6
6
  import { startNatsTransport } from "../transports/nats-transport.js";
7
+ import { startHttpTransport } from "../transports/http-transport.js";
7
8
  import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMessage } from "../task.js";
8
9
  import { publishHostEvent } from "../events.js";
9
10
  import { getPlatform } from "../platform/index.js";
@@ -69,7 +70,7 @@ async function checkStaleTasks(config, nc) {
69
70
  }
70
71
  }
71
72
  /**
72
- * Start the persistent RPC handler (NATS only).
73
+ * Start the persistent RPC handler (NATS + HTTP).
73
74
  */
74
75
  export async function serveCommand() {
75
76
  const config = loadConfig();
@@ -91,6 +92,12 @@ export async function serveCommand() {
91
92
  });
92
93
  }, POLL_INTERVAL_MS);
93
94
  const handleRpc = createRpcHandler(config, nc);
94
- await startNatsTransport(config, handleRpc, nc);
95
+ const httpPort = config.httpPort ?? 7400;
96
+ // Start NATS transport (loops forever, fire-and-forget)
97
+ if (nc) {
98
+ startNatsTransport(config, handleRpc, nc);
99
+ }
100
+ // Start HTTP transport (loops forever)
101
+ await startHttpTransport(config, handleRpc, httpPort, nc);
95
102
  }
96
103
  //# sourceMappingURL=serve.js.map
package/dist/events.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { type NatsConnection } from "nats";
2
2
  /**
3
- * Broadcast an event to connected clients via NATS and HTTP SSE (if LAN server is running).
3
+ * Broadcast an event to connected clients via NATS and HTTP SSE.
4
4
  *
5
5
  * - NATS: publishes to `host-event.{hostId}.{taskId}`
6
- * - HTTP: POSTs to the LAN server's `/internal/event` endpoint (auto-detected via lockfile)
6
+ * - HTTP: POSTs to the serve daemon's `/event` endpoint
7
7
  */
8
8
  export declare function publishHostEvent(nc: NatsConnection | undefined, hostId: string, taskId: string, payload: Record<string, unknown>): Promise<void>;
9
9
  //# sourceMappingURL=events.d.ts.map
package/dist/events.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { StringCodec } from "nats";
2
- import { getLanPort } from "./lan-lock.js";
2
+ import { loadConfig } from "./config.js";
3
3
  const sc = StringCodec();
4
4
  /**
5
- * Broadcast an event to connected clients via NATS and HTTP SSE (if LAN server is running).
5
+ * Broadcast an event to connected clients via NATS and HTTP SSE.
6
6
  *
7
7
  * - NATS: publishes to `host-event.{hostId}.{taskId}`
8
- * - HTTP: POSTs to the LAN server's `/internal/event` endpoint (auto-detected via lockfile)
8
+ * - HTTP: POSTs to the serve daemon's `/event` endpoint
9
9
  */
10
10
  export async function publishHostEvent(nc, hostId, taskId, payload) {
11
11
  const subject = `host-event.${hostId}.${taskId}`;
@@ -13,19 +13,18 @@ export async function publishHostEvent(nc, hostId, taskId, payload) {
13
13
  nc.publish(subject, sc.encode(JSON.stringify(payload)));
14
14
  console.log(`[nats] ${subject} →`, payload);
15
15
  }
16
- const lanPort = getLanPort();
17
- if (lanPort) {
18
- try {
19
- await fetch(`http://localhost:${lanPort}/internal/event`, {
20
- method: "POST",
21
- headers: { "Content-Type": "application/json" },
22
- body: JSON.stringify({ task_id: taskId, ...payload }),
23
- });
24
- console.log(`[http] host-event: ${taskId} →`, payload);
25
- }
26
- catch {
27
- // LAN server may have shut down — ignore
28
- }
16
+ const config = loadConfig();
17
+ const port = config.httpPort ?? 7400;
18
+ try {
19
+ await fetch(`http://localhost:${port}/event`, {
20
+ method: "POST",
21
+ headers: { "Content-Type": "application/json" },
22
+ body: JSON.stringify({ task_id: taskId, ...payload }),
23
+ });
24
+ console.log(`[http] host-event: ${taskId} →`, payload);
25
+ }
26
+ catch {
27
+ // Serve HTTP may not be ready yet — ignore
29
28
  }
30
29
  }
31
30
  //# sourceMappingURL=events.js.map
package/dist/index.js CHANGED
@@ -8,10 +8,7 @@ import { initCommand } from "./commands/init.js";
8
8
  import { infoCommand } from "./commands/info.js";
9
9
  import { runCommand } from "./commands/run.js";
10
10
  import { serveCommand } from "./commands/serve.js";
11
- import { notifyCommand } from "./commands/notify.js";
12
- import { requestInputCommand } from "./commands/request-input.js";
13
11
  import { pairCommand } from "./commands/pair.js";
14
- import { lanCommand } from "./commands/lan.js";
15
12
  import { restartCommand } from "./commands/restart.js";
16
13
  import { sessionsListCommand, sessionsRevokeCommand, sessionsRevokeAllCommand } from "./commands/sessions.js";
17
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -51,34 +48,12 @@ program
51
48
  .action(async () => {
52
49
  await restartCommand();
53
50
  });
54
- program
55
- .command("notify")
56
- .description("Send a push notification to the user")
57
- .requiredOption("--title <title>", "Notification title")
58
- .requiredOption("--body <body>", "Notification body text")
59
- .action(async (opts) => {
60
- await notifyCommand(opts);
61
- });
62
- program
63
- .command("request-input")
64
- .description("Request input from the user (requires PALMIER_TASK_ID env var)")
65
- .requiredOption("--description <desc...>", "Input descriptions to show the user")
66
- .action(async (opts) => {
67
- await requestInputCommand(opts);
68
- });
69
51
  program
70
52
  .command("pair")
71
53
  .description("Generate a pairing code for connecting a PWA client")
72
54
  .action(async () => {
73
55
  await pairCommand();
74
56
  });
75
- program
76
- .command("lan")
77
- .description("Start an on-demand LAN server for direct HTTP connections")
78
- .option("-p, --port <port>", "Port to listen on", "7400")
79
- .action(async (opts) => {
80
- await lanCommand({ port: parseInt(opts.port, 10) });
81
- });
82
57
  const sessionsCmd = program
83
58
  .command("sessions")
84
59
  .description("Manage paired client sessions");
@@ -0,0 +1,27 @@
1
+ import type { RequiredPermission } from "./types.js";
2
+ export interface PendingRequest {
3
+ type: "confirmation" | "permission" | "input";
4
+ resolve: (value: string[]) => void;
5
+ /** Permission list (for 'permission') or input descriptions (for 'input'). */
6
+ params?: RequiredPermission[] | string[];
7
+ }
8
+ /**
9
+ * Register a pending request for a task. Returns a Promise that resolves
10
+ * when `resolvePending` is called with the user's response.
11
+ * Only one pending request per task at a time.
12
+ */
13
+ export declare function registerPending(taskId: string, type: PendingRequest["type"], params?: PendingRequest["params"]): Promise<string[]>;
14
+ /**
15
+ * Resolve a pending request with the user's response.
16
+ * Returns true if a pending request was found and resolved.
17
+ */
18
+ export declare function resolvePending(taskId: string, value: string[]): boolean;
19
+ /**
20
+ * Get the current pending request for a task (if any).
21
+ */
22
+ export declare function getPending(taskId: string): PendingRequest | undefined;
23
+ /**
24
+ * Remove a pending request without resolving it.
25
+ */
26
+ export declare function removePending(taskId: string): void;
27
+ //# sourceMappingURL=pending-requests.d.ts.map
@@ -0,0 +1,39 @@
1
+ const pending = new Map();
2
+ /**
3
+ * Register a pending request for a task. Returns a Promise that resolves
4
+ * when `resolvePending` is called with the user's response.
5
+ * Only one pending request per task at a time.
6
+ */
7
+ export function registerPending(taskId, type, params) {
8
+ if (pending.has(taskId)) {
9
+ return Promise.reject(new Error(`Task ${taskId} already has a pending request`));
10
+ }
11
+ return new Promise((resolve) => {
12
+ pending.set(taskId, { type, resolve, params });
13
+ });
14
+ }
15
+ /**
16
+ * Resolve a pending request with the user's response.
17
+ * Returns true if a pending request was found and resolved.
18
+ */
19
+ export function resolvePending(taskId, value) {
20
+ const entry = pending.get(taskId);
21
+ if (!entry)
22
+ return false;
23
+ pending.delete(taskId);
24
+ entry.resolve(value);
25
+ return true;
26
+ }
27
+ /**
28
+ * Get the current pending request for a task (if any).
29
+ */
30
+ export function getPending(taskId) {
31
+ return pending.get(taskId);
32
+ }
33
+ /**
34
+ * Remove a pending request without resolving it.
35
+ */
36
+ export function removePending(taskId) {
37
+ pending.delete(taskId);
38
+ }
39
+ //# sourceMappingURL=pending-requests.js.map
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
5
5
  import { spawn } from "child_process";
6
6
  import { parse as parseYaml } from "yaml";
7
7
  import { listTasks, parseTaskFile, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, appendHistory, createRunDir, appendRunMessage, getRunDir } from "./task.js";
8
+ import { resolvePending, getPending } from "./pending-requests.js";
8
9
  import { getPlatform } from "./platform/index.js";
9
10
  import { spawnCommand } from "./spawn-command.js";
10
11
  import crossSpawn from "cross-spawn";
@@ -47,6 +48,7 @@ function parseResultFrontmatter(raw) {
47
48
  return {
48
49
  messages,
49
50
  task_name: meta.task_name,
51
+ agent: meta.agent,
50
52
  running_state: runningState,
51
53
  start_time: startedMsg?.time || undefined,
52
54
  end_time: terminalMsg?.time || undefined,
@@ -131,8 +133,8 @@ export function createRpcHandler(config, nc) {
131
133
  };
132
134
  }
133
135
  async function handleRpc(request) {
134
- // Session token validation: always require a valid session token
135
- if (!request.sessionToken || !validateSession(request.sessionToken)) {
136
+ // Session token validation: skip for trusted localhost requests
137
+ if (!request.localhost && (!request.sessionToken || !validateSession(request.sessionToken))) {
136
138
  return { error: "Unauthorized" };
137
139
  }
138
140
  switch (request.method) {
@@ -259,7 +261,7 @@ export function createRpcHandler(config, nc) {
259
261
  writeTaskFile(taskDir, task);
260
262
  // Do NOT append to tasks.jsonl — this is a one-off run
261
263
  // Create initial result file so it appears in runs list immediately
262
- const runId = createRunDir(taskDir, name, Date.now());
264
+ const runId = createRunDir(taskDir, name, Date.now(), params.agent);
263
265
  appendHistory(config.projectRoot, { task_id: id, run_id: runId });
264
266
  // Spawn `palmier run <id>` directly as a detached process
265
267
  const script = process.argv[1] || "palmier";
@@ -277,7 +279,7 @@ export function createRpcHandler(config, nc) {
277
279
  // Create initial result file so it appears in runs list immediately
278
280
  const runTaskDir = getTaskDir(config.projectRoot, params.id);
279
281
  const runTask = parseTaskFile(runTaskDir);
280
- const taskRunId = createRunDir(runTaskDir, runTask.frontmatter.name, Date.now());
282
+ const taskRunId = createRunDir(runTaskDir, runTask.frontmatter.name, Date.now(), runTask.frontmatter.agent);
281
283
  appendHistory(config.projectRoot, { task_id: params.id, run_id: taskRunId });
282
284
  await getPlatform().startTask(params.id);
283
285
  return { ok: true, task_id: params.id, run_id: taskRunId };
@@ -447,7 +449,14 @@ export function createRpcHandler(config, nc) {
447
449
  if (!status) {
448
450
  return { task_id: params.id, error: "No status found" };
449
451
  }
450
- return { task_id: params.id, ...status };
452
+ const pending = getPending(params.id);
453
+ return {
454
+ task_id: params.id,
455
+ ...status,
456
+ ...(pending?.type === "confirmation" ? { pending_confirmation: true } : {}),
457
+ ...(pending?.type === "permission" ? { pending_permission: pending.params } : {}),
458
+ ...(pending?.type === "input" ? { pending_input: pending.params } : {}),
459
+ };
451
460
  }
452
461
  case "task.result": {
453
462
  const params = request.params;
@@ -494,14 +503,13 @@ export function createRpcHandler(config, nc) {
494
503
  }
495
504
  case "task.user_input": {
496
505
  const params = request.params;
497
- const taskDir = getTaskDir(config.projectRoot, params.id);
498
- const currentStatus = readTaskStatus(taskDir);
499
- if (!currentStatus?.pending_confirmation && !currentStatus?.pending_permission?.length && !currentStatus?.pending_input?.length) {
506
+ const pending = getPending(params.id);
507
+ if (!pending) {
500
508
  return { ok: false, error: "not pending" };
501
509
  }
502
- writeTaskStatus(taskDir, { ...currentStatus, user_input: params.value });
510
+ const resolved = resolvePending(params.id, params.value);
503
511
  console.log(`[task.user_input] ${params.id} → ${params.value}`);
504
- return { ok: true };
512
+ return { ok: resolved };
505
513
  }
506
514
  case "taskrun.list": {
507
515
  const params = request.params;
package/dist/task.d.ts CHANGED
@@ -42,7 +42,7 @@ export declare function readTaskStatus(taskDir: string): TaskStatus | undefined;
42
42
  * Create a run directory with an initial TASKRUN.md file.
43
43
  * Returns the run ID (timestamp string used as directory name).
44
44
  */
45
- export declare function createRunDir(taskDir: string, taskName: string, startTime: number): string;
45
+ export declare function createRunDir(taskDir: string, taskName: string, startTime: number, agent?: string): string;
46
46
  /**
47
47
  * Get the path to a run directory.
48
48
  */
package/dist/task.js CHANGED
@@ -133,11 +133,12 @@ export function readTaskStatus(taskDir) {
133
133
  * Create a run directory with an initial TASKRUN.md file.
134
134
  * Returns the run ID (timestamp string used as directory name).
135
135
  */
136
- export function createRunDir(taskDir, taskName, startTime) {
136
+ export function createRunDir(taskDir, taskName, startTime, agent) {
137
137
  const runId = String(startTime);
138
138
  const runDir = path.join(taskDir, runId);
139
139
  fs.mkdirSync(runDir, { recursive: true });
140
- const content = `---\ntask_name: ${taskName}\n---\n\n`;
140
+ const agentLine = agent ? `\nagent: ${agent}` : "";
141
+ const content = `---\ntask_name: ${taskName}${agentLine}\n---\n\n`;
141
142
  fs.writeFileSync(path.join(runDir, "TASKRUN.md"), content, "utf-8");
142
143
  return runId;
143
144
  }
@@ -1,7 +1,9 @@
1
+ import { type NatsConnection } from "nats";
1
2
  import type { HostConfig, RpcMessage } from "../types.js";
2
3
  export declare function detectLanIp(): string;
3
4
  /**
4
- * Start the HTTP transport: Express-like server with RPC, SSE, and health endpoints.
5
+ * Start the HTTP transport: server with RPC, SSE, PWA proxy, pairing, and
6
+ * localhost-only agent endpoints (notify, request-input, confirmation, permission).
5
7
  */
6
- export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, pairingCode?: string, onReady?: () => void): Promise<void>;
8
+ export declare function startHttpTransport(config: HostConfig, handleRpc: (req: RpcMessage) => Promise<unknown>, port: number, nc: NatsConnection | undefined, pairingCode?: string, onReady?: () => void): Promise<void>;
7
9
  //# sourceMappingURL=http-transport.d.ts.map