palmier 1.1.8 → 1.1.10

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.
@@ -2,6 +2,12 @@ You are an AI agent executing a task on behalf of the user. Follow these instruc
2
2
 
3
3
  All `[PALMIER_*]` markers below are control signals parsed by the host. They MUST be written to **stdout** (not stderr). Markers on stderr are ignored.
4
4
 
5
+ ## Model
6
+
7
+ At the start of every response (including responses to follow-up messages), before any other output, print the model you are running as on its own line, using the exact model name or ID from your system prompt:
8
+ [PALMIER_AGENT_MODEL] <model>
9
+ If you do not know your exact model, skip this line rather than guessing.
10
+
5
11
  ## Linking Files
6
12
 
7
13
  The user reads your output on a different device and can only open files you link. When you refer to a file you created (report, image, data export, etc.):
@@ -3,3 +3,4 @@ export declare function getAgentInstructions(task: ParsedTask): string;
3
3
  export declare const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
4
4
  export declare const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
5
5
  export declare const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
6
+ export declare const AGENT_MODEL_PREFIX = "[PALMIER_AGENT_MODEL]";
@@ -21,3 +21,4 @@ export function getAgentInstructions(task) {
21
21
  export const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
22
22
  export const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
23
23
  export const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
24
+ export const AGENT_MODEL_PREFIX = "[PALMIER_AGENT_MODEL]";
@@ -9,7 +9,7 @@ import { installPlaywrightCli, installPlaywrightSkills, getPlaywrightCliVersion,
9
9
  import { getPlatform } from "../platform/index.js";
10
10
  import { pairCommand } from "./pair.js";
11
11
  import { detectDefaultInterface, getInterfaceIpv4 } from "../network.js";
12
- import { listTasks } from "../task.js";
12
+ import { listTasks, taskDisplayName } from "../task.js";
13
13
  const { bold, dim, green, cyan, red } = colors;
14
14
  export async function initCommand() {
15
15
  console.log(`\n${bold("=== Palmier Host Setup ===")}\n`);
@@ -98,7 +98,7 @@ export async function initCommand() {
98
98
  if (existingTasks.length > 0) {
99
99
  console.log(` ${dim("Recover tasks:")} ${existingTasks.length} existing task(s) found:`);
100
100
  for (const t of existingTasks) {
101
- console.log(` - ${t.frontmatter.name || t.frontmatter.user_prompt.slice(0, 50)}`);
101
+ console.log(` - ${taskDisplayName(t.frontmatter) || t.frontmatter.user_prompt.slice(0, 50)}`);
102
102
  }
103
103
  console.log();
104
104
  }
@@ -4,5 +4,6 @@ export declare function runCommand(taskId: string): Promise<void>;
4
4
  /** Only links to files that exist count, so example links echoed from the prompt are ignored. */
5
5
  export declare function listLinkedTaskFiles(taskDir: string, runId: string, output: string): string[];
6
6
  export declare function parsePermissions(output: string): RequiredPermission[];
7
+ export declare function parseAgentModel(output: string): string | undefined;
7
8
  /** Falls back to "finished" if no success/failure marker is found. */
8
9
  export declare function parseTaskOutcome(output: string): TaskRunningState;
@@ -3,10 +3,10 @@ import * as path from "path";
3
3
  import { spawnCommand } from "../spawn-command.js";
4
4
  import { loadConfig } from "../config.js";
5
5
  import { connectNats } from "../nats-client.js";
6
- import { parseTaskFile, getTaskDir, writeTaskFile, writeTaskStatus, readTaskStatus, appendHistory, createRunDir, appendRunMessage, readRunMessages, getRunDir, beginStreamingMessage } from "../task.js";
6
+ import { parseTaskFile, taskDisplayName, getTaskDir, writeTaskFile, writeTaskStatus, readTaskStatus, appendHistory, createRunDir, appendRunMessage, readRunMessages, getRunDir, beginStreamingMessage, formatAgentModelComment } from "../task.js";
7
7
  import { getAgent } from "../agents/agent.js";
8
8
  import { getPlatform } from "../platform/index.js";
9
- import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, TASK_PERMISSION_PREFIX } from "../agents/shared-prompt.js";
9
+ import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, TASK_PERMISSION_PREFIX, AGENT_MODEL_PREFIX } from "../agents/shared-prompt.js";
10
10
  import { extractFileLinks, resolveTaskFile } from "../task-files.js";
11
11
  import { publishHostEvent } from "../events.js";
12
12
  /**
@@ -22,6 +22,8 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
22
22
  const lineBufs = { stdout: "", stderr: "" };
23
23
  let notifyPending = false;
24
24
  let notifyTimer;
25
+ let agentModelSeen = false;
26
+ let pendingAgentModel;
25
27
  function throttledNotify() {
26
28
  if (notifyPending)
27
29
  return;
@@ -40,15 +42,27 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
40
42
  activeStream = stream;
41
43
  return writer;
42
44
  }
45
+ function flushAgentModel() {
46
+ if (!pendingAgentModel || !writer)
47
+ return;
48
+ writer.write(formatAgentModelComment(pendingAgentModel));
49
+ pendingAgentModel = undefined;
50
+ throttledNotify();
51
+ }
43
52
  function emit(stream, chunk) {
44
53
  lineBufs[stream] += chunk;
45
54
  const lines = lineBufs[stream].split("\n");
46
55
  lineBufs[stream] = lines.pop() ?? "";
56
+ if (stream === "stdout" && !agentModelSeen) {
57
+ pendingAgentModel = parseAgentModel(lines.join("\n"));
58
+ agentModelSeen = !!pendingAgentModel;
59
+ }
47
60
  const filtered = lines.filter((l) => !l.startsWith("[PALMIER"));
48
- if (filtered.length === 0)
49
- return;
50
- ensureWriter(stream).write(filtered.join("\n") + "\n");
51
- throttledNotify();
61
+ if (filtered.length > 0) {
62
+ ensureWriter(stream).write(filtered.join("\n") + "\n");
63
+ throttledNotify();
64
+ }
65
+ flushAgentModel();
52
66
  }
53
67
  const { args, stdin, env: agentEnv, files } = ctx.agent.getTaskRunCommandLine(invokeTask, undefined, ctx.task.frontmatter.yolo_mode ? "yolo" : ctx.transientPermissions);
54
68
  const command = ctx.agent.command;
@@ -87,7 +101,7 @@ async function invokeAgentWithRetries(ctx, invokeTask) {
87
101
  await publishHostEvent(ctx.nc, ctx.config.hostId, ctx.taskId, {
88
102
  event_type: "report-generated",
89
103
  run_id: ctx.runId,
90
- name: ctx.task.frontmatter.name,
104
+ name: taskDisplayName(ctx.task.frontmatter),
91
105
  report_files: linkedFiles,
92
106
  });
93
107
  }
@@ -161,7 +175,7 @@ export async function runCommand(taskId) {
161
175
  const task = parseTaskFile(taskDir);
162
176
  console.log(`Running task: ${taskId}`);
163
177
  let nc;
164
- const taskName = task.frontmatter.name;
178
+ const taskName = taskDisplayName(task.frontmatter);
165
179
  const existingRunId = findLatestPendingRunId(taskDir);
166
180
  const agentVersion = config.agents?.find((a) => a.key === task.frontmatter.agent)?.version;
167
181
  const runId = existingRunId ?? createRunDir(taskDir, taskName, Date.now(), task.frontmatter.agent, agentVersion);
@@ -320,7 +334,7 @@ async function requestPermission(config, task, taskDir, requiredPermissions) {
320
334
  headers: { "Content-Type": "application/json" },
321
335
  body: JSON.stringify({
322
336
  taskId: task.frontmatter.id,
323
- taskName: task.frontmatter.name,
337
+ taskName: taskDisplayName(task.frontmatter),
324
338
  permissions: requiredPermissions,
325
339
  }),
326
340
  });
@@ -340,7 +354,7 @@ async function requestConfirmation(config, task, taskDir) {
340
354
  const res = await fetch(`http://localhost:${port}/request-confirmation?taskId=${encodeURIComponent(task.frontmatter.id)}`, {
341
355
  method: "POST",
342
356
  headers: { "Content-Type": "application/json" },
343
- body: JSON.stringify({ description: `Run task "${task.frontmatter.name || task.frontmatter.id}"?` }),
357
+ body: JSON.stringify({ description: `Run task "${taskDisplayName(task.frontmatter) || task.frontmatter.id}"?` }),
344
358
  });
345
359
  const body = await res.json();
346
360
  if (typeof body.confirmed !== "boolean") {
@@ -384,6 +398,18 @@ export function parsePermissions(output) {
384
398
  }
385
399
  return perms;
386
400
  }
401
+ export function parseAgentModel(output) {
402
+ const regex = new RegExp(`^\\${AGENT_MODEL_PREFIX}[ \\t]+(.+)$`, "gm");
403
+ let match;
404
+ while ((match = regex.exec(output)) !== null) {
405
+ const model = match[1].trim();
406
+ // Skip the "<model>" placeholder echoed from the prompt, and values that would break the run-file comment.
407
+ if (!model || model.startsWith("<") || model.includes("-->") || model.length > 100)
408
+ continue;
409
+ return model;
410
+ }
411
+ return undefined;
412
+ }
387
413
  /** Falls back to "finished" if no success/failure marker is found. */
388
414
  export function parseTaskOutcome(output) {
389
415
  const lastChunk = output.slice(-500);
@@ -5,7 +5,7 @@ import { connectNats } from "../nats-client.js";
5
5
  import { createRpcHandler } from "../rpc-handler.js";
6
6
  import { startNatsTransport } from "../transports/nats-transport.js";
7
7
  import { startHttpTransport } from "../transports/http-transport.js";
8
- import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, appendRunMessage, listTasks, readFollowupStatus, deleteFollowupStatus } from "../task.js";
8
+ import { getTaskDir, readTaskStatus, writeTaskStatus, parseTaskFile, taskDisplayName, appendRunMessage, listTasks, readFollowupStatus, deleteFollowupStatus } from "../task.js";
9
9
  import { publishHostEvent } from "../events.js";
10
10
  import { getPlatform } from "../platform/index.js";
11
11
  import { detectAgents } from "../agents/agent.js";
@@ -62,7 +62,7 @@ async function checkStaleTasks(config, nc) {
62
62
  await publishHostEvent(nc, config.hostId, taskId, {
63
63
  event_type: "running-state",
64
64
  running_state: "failed",
65
- name: task.frontmatter.name || taskId,
65
+ name: taskDisplayName(task.frontmatter) || taskId,
66
66
  });
67
67
  }
68
68
  if (task.frontmatter.one_off && status.running_state !== "started") {