palmier 1.1.23 → 1.1.24
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/DISCLAIMER.md +4 -2
- package/dist/agents/agent-instructions.md +0 -5
- package/dist/agents/agent.d.ts +0 -4
- package/dist/agents/agent.js +0 -1
- package/dist/agents/claude.js +0 -1
- package/dist/agents/shared-prompt.d.ts +0 -1
- package/dist/agents/shared-prompt.js +1 -3
- package/dist/agents/wizard.js +0 -1
- package/dist/commands/run.d.ts +1 -2
- package/dist/commands/run.js +97 -180
- package/dist/pending-requests.d.ts +3 -5
- package/dist/pwa/assets/{index-De8hDN5I.js → index-CXiDfnsf.js} +41 -41
- package/dist/pwa/assets/index-DWgTuvMe.css +1 -0
- package/dist/pwa/assets/{web-miCfAJBc.js → web-BIh8c8oo.js} +1 -1
- package/dist/pwa/assets/{web-BQ0rMZ_X.js → web-DTphq1C9.js} +1 -1
- package/dist/pwa/assets/{web-o_D8jqAS.js → web-durUDXGk.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/rpc-handler.js +0 -3
- package/dist/transports/http-transport.js +0 -37
- package/dist/types.d.ts +2 -8
- package/package.json +1 -1
- package/dist/pwa/assets/index-BPB3G_2J.css +0 -1
package/DISCLAIMER.md
CHANGED
|
@@ -10,7 +10,7 @@ Palmier spawns third-party AI agent CLIs (such as Claude Code, Codex CLI, GitHub
|
|
|
10
10
|
- **Execute arbitrary shell commands** with your user permissions
|
|
11
11
|
- **Make network requests** and interact with external services
|
|
12
12
|
|
|
13
|
-
AI agents may produce unexpected, incorrect, or harmful outputs. **You are solely responsible for
|
|
13
|
+
AI agents may produce unexpected, incorrect, or harmful outputs. **You are solely responsible for all actions taken by AI agents on your system, including actions taken without per-action approval.** The authors of Palmier have no control over the behavior of third-party AI agents and accept no liability for their actions.
|
|
14
14
|
|
|
15
15
|
## Agent CLI Installation
|
|
16
16
|
|
|
@@ -20,10 +20,12 @@ Agent CLIs that Palmier installed (those carrying a Palmier-managed version stam
|
|
|
20
20
|
|
|
21
21
|
## Unattended and Scheduled Execution
|
|
22
22
|
|
|
23
|
+
Palmier runs agents with tool approvals bypassed whenever the agent CLI supports it. Agents can act without asking first.
|
|
24
|
+
|
|
23
25
|
Tasks can be configured to run on schedules (cron) or in response to events without active supervision. You should:
|
|
24
26
|
|
|
25
27
|
- Use the **confirmation** feature for sensitive tasks
|
|
26
|
-
-
|
|
28
|
+
- Limit what the **host's user account** can access, for example by using a dedicated account, container or VM
|
|
27
29
|
- Regularly review **task history and results**
|
|
28
30
|
- Maintain **backups** of any important data in directories where agents operate
|
|
29
31
|
|
|
@@ -22,11 +22,6 @@ On the line right before that marker, in the same final message, print the model
|
|
|
22
22
|
|
|
23
23
|
Do this at the end of every response, including responses to follow-up messages.
|
|
24
24
|
|
|
25
|
-
## Permissions
|
|
26
|
-
|
|
27
|
-
Whenever a tool you are trying to use is denied or you lack the required permissions, print each required permission on its own line to stdout using this exact format:
|
|
28
|
-
[PALMIER_PERMISSION] <tool_name> | <description>
|
|
29
|
-
|
|
30
25
|
## Browsers
|
|
31
26
|
|
|
32
27
|
Whenever the task needs a browser, launch it with the Playwright CLI (`playwright-cli open`). Do not use any other browser tool, automation library, or launch method.
|
package/dist/agents/agent.d.ts
CHANGED
|
@@ -25,9 +25,6 @@ export interface AgentTool {
|
|
|
25
25
|
* can sign in before configuration continues. Leave undefined for agents that auth on
|
|
26
26
|
* first run with no separate command. */
|
|
27
27
|
authArgs?: string[];
|
|
28
|
-
/** Whether this agent supports permission overrides (e.g. --allowedTools).
|
|
29
|
-
* When falsy, the permissions section is omitted from agent instructions. */
|
|
30
|
-
supportsPermissions?: boolean;
|
|
31
28
|
/** When true, the run loop persists the agent's stderr output only if the invocation fails. */
|
|
32
29
|
suppressStdErr?: boolean;
|
|
33
30
|
/** Matches the model name (capture group 1) in the banner the CLI prints on stderr.
|
|
@@ -51,7 +48,6 @@ export declare function getNpmInstalledVersion(npmPackage: string): string | nul
|
|
|
51
48
|
export interface DetectedAgent {
|
|
52
49
|
key: string;
|
|
53
50
|
label: string;
|
|
54
|
-
supportsPermissions?: boolean;
|
|
55
51
|
npmPackage?: string;
|
|
56
52
|
/** Runtime marker for "managed by Palmier" — present iff Palmier installed/manages this agent. */
|
|
57
53
|
version?: string;
|
package/dist/agents/agent.js
CHANGED
|
@@ -120,7 +120,6 @@ export async function detectAgents(previous, newlyInstalled) {
|
|
|
120
120
|
detected.push({
|
|
121
121
|
key,
|
|
122
122
|
label: agent.label,
|
|
123
|
-
supportsPermissions: agent.supportsPermissions,
|
|
124
123
|
...(agent.npmPackage ? { npmPackage: agent.npmPackage } : {}),
|
|
125
124
|
...(version ? { version } : {}),
|
|
126
125
|
});
|
package/dist/agents/claude.js
CHANGED
|
@@ -2,5 +2,4 @@ import type { ParsedTask } from "../types.js";
|
|
|
2
2
|
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
|
-
export declare const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
|
|
6
5
|
export declare const AGENT_MODEL_PREFIX = "[PALMIER_AGENT_MODEL]";
|
|
@@ -10,10 +10,8 @@ export function getAgentInstructions(task) {
|
|
|
10
10
|
const taskDescription = task.frontmatter.user_prompt;
|
|
11
11
|
return AGENT_INSTRUCTIONS_TEMPLATE
|
|
12
12
|
.replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(port, task.frontmatter.id))
|
|
13
|
-
.replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription)
|
|
14
|
-
.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
|
|
13
|
+
.replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription);
|
|
15
14
|
}
|
|
16
15
|
export const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
|
|
17
16
|
export const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
|
|
18
|
-
export const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
|
|
19
17
|
export const AGENT_MODEL_PREFIX = "[PALMIER_AGENT_MODEL]";
|
package/dist/agents/wizard.js
CHANGED
|
@@ -60,7 +60,6 @@ export async function pickAndInstallAgent(current, options = {}) {
|
|
|
60
60
|
const record = {
|
|
61
61
|
key: choice.key,
|
|
62
62
|
label: choice.label,
|
|
63
|
-
...(tool.supportsPermissions ? { supportsPermissions: true } : {}),
|
|
64
63
|
npmPackage: choice.npmPackage,
|
|
65
64
|
...(version ? { version } : {}),
|
|
66
65
|
};
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { AgentTool } from "../agents/agent.js";
|
|
2
|
-
import type { TaskRunningState
|
|
2
|
+
import type { TaskRunningState } from "../types.js";
|
|
3
3
|
export declare function stripPalmierMarkers(output: string): string;
|
|
4
4
|
export declare function runCommand(taskId: string): Promise<void>;
|
|
5
5
|
/** Only links to files that exist count, so example links echoed from the prompt are ignored. */
|
|
6
6
|
export declare function listLinkedTaskFiles(taskDir: string, runId: string, output: string): string[];
|
|
7
|
-
export declare function parsePermissions(output: string): RequiredPermission[];
|
|
8
7
|
export declare function parseAgentModel(output: string): string | undefined;
|
|
9
8
|
/** Returns a function that accumulates stderr chunks and yields the model once the CLI banner reveals it. */
|
|
10
9
|
export declare function createCliModelCapture(agent: AgentTool): (stderrChunk: string) => string | undefined;
|
package/dist/commands/run.js
CHANGED
|
@@ -2,157 +2,116 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { spawnCommand } from "../spawn-command.js";
|
|
4
4
|
import { loadConfig } from "../config.js";
|
|
5
|
-
import { parseTaskFile, taskDisplayName, getTaskDir,
|
|
5
|
+
import { parseTaskFile, taskDisplayName, getTaskDir, writeTaskStatus, readTaskStatus, appendHistory, createRunDir, claimPendingRun, appendRunMessage, getRunDir, beginStreamingMessage, formatAgentModelComment } from "../task.js";
|
|
6
6
|
import { getAgent } from "../agents/agent.js";
|
|
7
7
|
import { getPlatform } from "../platform/index.js";
|
|
8
|
-
import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER,
|
|
8
|
+
import { TASK_SUCCESS_MARKER, TASK_FAILURE_MARKER, AGENT_MODEL_PREFIX } from "../agents/shared-prompt.js";
|
|
9
9
|
import { extractFileLinks, resolveTaskFile } from "../task-files.js";
|
|
10
10
|
import { publishHostEvent } from "../events.js";
|
|
11
11
|
/**
|
|
12
|
-
* Invoke the agent CLI in a continuation loop to handle permission requests.
|
|
13
12
|
* `invokeTask` is the ParsedTask whose prompt is passed to the agent (for
|
|
14
13
|
* triggered tasks this is the per-trigger augmented task).
|
|
15
14
|
*/
|
|
16
|
-
async function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
notifyPending =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function ensureWriter(stream) {
|
|
38
|
-
if (writer && activeStream === stream)
|
|
39
|
-
return writer;
|
|
40
|
-
if (writer)
|
|
41
|
-
writer.end();
|
|
42
|
-
writer = beginStreamingMessage(ctx.taskDir, ctx.runId, Date.now(), stream);
|
|
43
|
-
activeStream = stream;
|
|
15
|
+
async function invokeAgent(ctx, invokeTask) {
|
|
16
|
+
let writer;
|
|
17
|
+
let activeStream;
|
|
18
|
+
const lineBufs = { stdout: "", stderr: "" };
|
|
19
|
+
let suppressedStderr = "";
|
|
20
|
+
let notifyPending = false;
|
|
21
|
+
let notifyTimer;
|
|
22
|
+
let agentModel;
|
|
23
|
+
let agentModelWritten = false;
|
|
24
|
+
const captureCliModel = createCliModelCapture(ctx.agent);
|
|
25
|
+
function throttledNotify() {
|
|
26
|
+
if (notifyPending)
|
|
27
|
+
return;
|
|
28
|
+
notifyPending = true;
|
|
29
|
+
notifyTimer = setTimeout(() => {
|
|
30
|
+
notifyPending = false;
|
|
31
|
+
publishHostEvent(ctx.taskId, { event_type: "result-updated", run_id: ctx.runId });
|
|
32
|
+
}, 500);
|
|
33
|
+
}
|
|
34
|
+
function ensureWriter(stream) {
|
|
35
|
+
if (writer && activeStream === stream)
|
|
44
36
|
return writer;
|
|
37
|
+
if (writer)
|
|
38
|
+
writer.end();
|
|
39
|
+
writer = beginStreamingMessage(ctx.taskDir, ctx.runId, Date.now(), stream);
|
|
40
|
+
activeStream = stream;
|
|
41
|
+
return writer;
|
|
42
|
+
}
|
|
43
|
+
function flushAgentModel() {
|
|
44
|
+
if (!agentModel || agentModelWritten || !writer)
|
|
45
|
+
return;
|
|
46
|
+
writer.write(formatAgentModelComment(agentModel));
|
|
47
|
+
agentModelWritten = true;
|
|
48
|
+
throttledNotify();
|
|
49
|
+
}
|
|
50
|
+
function onStderr(chunk) {
|
|
51
|
+
if (ctx.agent.suppressStdErr)
|
|
52
|
+
suppressedStderr += chunk;
|
|
53
|
+
else
|
|
54
|
+
emit("stderr", chunk);
|
|
55
|
+
const cliModel = captureCliModel(chunk);
|
|
56
|
+
if (cliModel && !agentModelWritten)
|
|
57
|
+
agentModel = cliModel;
|
|
58
|
+
flushAgentModel();
|
|
59
|
+
}
|
|
60
|
+
function emit(stream, chunk) {
|
|
61
|
+
lineBufs[stream] += chunk;
|
|
62
|
+
const lines = lineBufs[stream].split("\n");
|
|
63
|
+
lineBufs[stream] = lines.pop() ?? "";
|
|
64
|
+
if (stream === "stdout" && !agentModel) {
|
|
65
|
+
agentModel = parseAgentModel(lines.join("\n"));
|
|
45
66
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
writer.write(formatAgentModelComment(agentModel));
|
|
50
|
-
agentModelWritten = true;
|
|
67
|
+
const filtered = lines.filter((l) => !l.startsWith("[PALMIER"));
|
|
68
|
+
if (filtered.length > 0) {
|
|
69
|
+
ensureWriter(stream).write(filtered.join("\n") + "\n");
|
|
51
70
|
throttledNotify();
|
|
52
71
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (cliModel && !agentModelWritten)
|
|
60
|
-
agentModel = cliModel;
|
|
61
|
-
flushAgentModel();
|
|
62
|
-
}
|
|
63
|
-
function emit(stream, chunk) {
|
|
64
|
-
lineBufs[stream] += chunk;
|
|
65
|
-
const lines = lineBufs[stream].split("\n");
|
|
66
|
-
lineBufs[stream] = lines.pop() ?? "";
|
|
67
|
-
if (stream === "stdout" && !agentModel) {
|
|
68
|
-
agentModel = parseAgentModel(lines.join("\n"));
|
|
69
|
-
}
|
|
70
|
-
const filtered = lines.filter((l) => !l.startsWith("[PALMIER"));
|
|
71
|
-
if (filtered.length > 0) {
|
|
72
|
-
ensureWriter(stream).write(filtered.join("\n") + "\n");
|
|
73
|
-
throttledNotify();
|
|
74
|
-
}
|
|
75
|
-
flushAgentModel();
|
|
76
|
-
}
|
|
77
|
-
function flushTrailingLine(stream) {
|
|
78
|
-
const trailing = lineBufs[stream];
|
|
79
|
-
if (trailing && !trailing.startsWith("[PALMIER")) {
|
|
80
|
-
ensureWriter(stream).write(trailing);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const { args, stdin, env: agentEnv, files } = ctx.agent.getTaskRunCommandLine(invokeTask);
|
|
84
|
-
const command = ctx.agent.command;
|
|
85
|
-
const runDir = getRunDir(ctx.taskDir, ctx.runId);
|
|
86
|
-
if (files) {
|
|
87
|
-
for (const f of files)
|
|
88
|
-
fs.writeFileSync(path.join(runDir, f.path), f.content, "utf-8");
|
|
72
|
+
flushAgentModel();
|
|
73
|
+
}
|
|
74
|
+
function flushTrailingLine(stream) {
|
|
75
|
+
const trailing = lineBufs[stream];
|
|
76
|
+
if (trailing && !trailing.startsWith("[PALMIER")) {
|
|
77
|
+
ensureWriter(stream).write(trailing);
|
|
89
78
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
}
|
|
80
|
+
const { args, stdin, env: agentEnv, files } = ctx.agent.getTaskRunCommandLine(invokeTask);
|
|
81
|
+
const command = ctx.agent.command;
|
|
82
|
+
const runDir = getRunDir(ctx.taskDir, ctx.runId);
|
|
83
|
+
if (files) {
|
|
84
|
+
for (const f of files)
|
|
85
|
+
fs.writeFileSync(path.join(runDir, f.path), f.content, "utf-8");
|
|
86
|
+
}
|
|
87
|
+
const result = await spawnCommand(command, args, {
|
|
88
|
+
cwd: runDir,
|
|
89
|
+
env: { ...ctx.guiEnv, ...agentEnv, PALMIER_RUN_DIR: runDir, PALMIER_HTTP_PORT: String(ctx.config.httpPort ?? 7256) },
|
|
90
|
+
echoStdout: true,
|
|
91
|
+
resolveOnFailure: true,
|
|
92
|
+
stdin,
|
|
93
|
+
onStdout: (chunk) => emit("stdout", chunk),
|
|
94
|
+
onStderr,
|
|
95
|
+
});
|
|
96
|
+
const outcome = result.exitCode !== 0 ? "failed" : parseTaskOutcome(result.output);
|
|
97
|
+
const linkedFiles = listLinkedTaskFiles(ctx.taskDir, ctx.runId, result.output);
|
|
98
|
+
flushTrailingLine("stdout");
|
|
99
|
+
if (outcome === "failed")
|
|
100
|
+
emit("stderr", suppressedStderr);
|
|
101
|
+
flushTrailingLine("stderr");
|
|
102
|
+
if (notifyTimer)
|
|
103
|
+
clearTimeout(notifyTimer);
|
|
104
|
+
writer?.end();
|
|
105
|
+
await publishHostEvent(ctx.taskId, { event_type: "result-updated", run_id: ctx.runId });
|
|
106
|
+
if (linkedFiles.length > 0) {
|
|
107
|
+
await publishHostEvent(ctx.taskId, {
|
|
108
|
+
event_type: "report-generated",
|
|
109
|
+
run_id: ctx.runId,
|
|
110
|
+
name: taskDisplayName(ctx.task.frontmatter),
|
|
111
|
+
report_files: linkedFiles,
|
|
98
112
|
});
|
|
99
|
-
const outcome = result.exitCode !== 0 ? "failed" : parseTaskOutcome(result.output);
|
|
100
|
-
const linkedFiles = listLinkedTaskFiles(ctx.taskDir, ctx.runId, result.output);
|
|
101
|
-
const requiredPermissions = parsePermissions(result.output);
|
|
102
|
-
flushTrailingLine("stdout");
|
|
103
|
-
if (outcome === "failed")
|
|
104
|
-
emit("stderr", suppressedStderr);
|
|
105
|
-
flushTrailingLine("stderr");
|
|
106
|
-
if (requiredPermissions.length > 0) {
|
|
107
|
-
const permLines = requiredPermissions.map((p) => `- **${p.name}** ${p.description}`).join("\n");
|
|
108
|
-
ensureWriter("stdout").write(`\n\n**Permissions requested:**\n${permLines}\n`);
|
|
109
|
-
}
|
|
110
|
-
if (notifyTimer)
|
|
111
|
-
clearTimeout(notifyTimer);
|
|
112
|
-
writer?.end();
|
|
113
|
-
await publishHostEvent(ctx.taskId, { event_type: "result-updated", run_id: ctx.runId });
|
|
114
|
-
if (linkedFiles.length > 0) {
|
|
115
|
-
await publishHostEvent(ctx.taskId, {
|
|
116
|
-
event_type: "report-generated",
|
|
117
|
-
run_id: ctx.runId,
|
|
118
|
-
name: taskDisplayName(ctx.task.frontmatter),
|
|
119
|
-
report_files: linkedFiles,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
if (requiredPermissions.length > 0) {
|
|
123
|
-
const response = await requestPermission(ctx.config, ctx.task, ctx.taskDir, requiredPermissions);
|
|
124
|
-
if (response === "aborted") {
|
|
125
|
-
await appendAndNotify(ctx, {
|
|
126
|
-
role: "user",
|
|
127
|
-
time: Date.now(),
|
|
128
|
-
content: "Deny & Abort Task",
|
|
129
|
-
type: "permission",
|
|
130
|
-
});
|
|
131
|
-
return { outcome: "failed" };
|
|
132
|
-
}
|
|
133
|
-
const newPerms = requiredPermissions.filter((rp) => !ctx.task.frontmatter.permissions?.some((ep) => ep.name === rp.name)
|
|
134
|
-
&& !ctx.transientPermissions.some((ep) => ep.name === rp.name));
|
|
135
|
-
await appendAndNotify(ctx, {
|
|
136
|
-
role: "user",
|
|
137
|
-
time: Date.now(),
|
|
138
|
-
content: response === "granted_all" ? "Allow Always" : "Allow Once",
|
|
139
|
-
type: "permission",
|
|
140
|
-
});
|
|
141
|
-
if (response === "granted_all") {
|
|
142
|
-
ctx.task.frontmatter.permissions = [...(ctx.task.frontmatter.permissions ?? []), ...newPerms];
|
|
143
|
-
invokeTask.frontmatter.permissions = ctx.task.frontmatter.permissions;
|
|
144
|
-
writeTaskFile(ctx.taskDir, ctx.task);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
ctx.transientPermissions = [...ctx.transientPermissions, ...newPerms];
|
|
148
|
-
}
|
|
149
|
-
// Retry with the new permissions if the agent failed.
|
|
150
|
-
if (outcome === "failed") {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return { outcome };
|
|
155
113
|
}
|
|
114
|
+
return { outcome };
|
|
156
115
|
}
|
|
157
116
|
export function stripPalmierMarkers(output) {
|
|
158
117
|
return output.split("\n").filter((l) => !l.startsWith("[PALMIER")).join("\n").trim();
|
|
@@ -217,7 +176,6 @@ export async function runCommand(taskId) {
|
|
|
217
176
|
const agent = getAgent(task.frontmatter.agent);
|
|
218
177
|
const ctx = {
|
|
219
178
|
agent, task, taskDir, runId, guiEnv, config, taskId,
|
|
220
|
-
transientPermissions: [],
|
|
221
179
|
};
|
|
222
180
|
// Command-output and on_new_* tasks share the same trigger machinery: the
|
|
223
181
|
// daemon owns the trigger source (the shell command's stdout / a NATS
|
|
@@ -238,7 +196,7 @@ export async function runCommand(taskId) {
|
|
|
238
196
|
time: Date.now(),
|
|
239
197
|
content: task.frontmatter.user_prompt,
|
|
240
198
|
});
|
|
241
|
-
const result = await
|
|
199
|
+
const result = await invokeAgent(ctx, task);
|
|
242
200
|
const outcome = resolveOutcome(taskDir, result.outcome);
|
|
243
201
|
appendRunMessage(taskDir, runId, { role: "status", time: Date.now(), content: "", type: outcome });
|
|
244
202
|
await publishTaskEvent(taskDir, taskId, outcome, taskName, runId);
|
|
@@ -299,7 +257,7 @@ async function runEventTriggeredMode(ctx) {
|
|
|
299
257
|
const perEventTask = {
|
|
300
258
|
frontmatter: { ...ctx.task.frontmatter, user_prompt: perEventPrompt },
|
|
301
259
|
};
|
|
302
|
-
const result = await
|
|
260
|
+
const result = await invokeAgent(ctx, perEventTask);
|
|
303
261
|
lastOutcome = result.outcome;
|
|
304
262
|
}
|
|
305
263
|
}
|
|
@@ -324,28 +282,6 @@ async function publishTaskEvent(taskDir, taskId, eventType, taskName, runId) {
|
|
|
324
282
|
payload.run_id = runId;
|
|
325
283
|
await publishHostEvent(taskId, payload);
|
|
326
284
|
}
|
|
327
|
-
async function requestPermission(config, task, taskDir, requiredPermissions) {
|
|
328
|
-
const port = config.httpPort ?? 7256;
|
|
329
|
-
const res = await fetch(`http://localhost:${port}/request-permission`, {
|
|
330
|
-
method: "POST",
|
|
331
|
-
headers: { "Content-Type": "application/json" },
|
|
332
|
-
body: JSON.stringify({
|
|
333
|
-
taskId: task.frontmatter.id,
|
|
334
|
-
taskName: taskDisplayName(task.frontmatter),
|
|
335
|
-
permissions: requiredPermissions,
|
|
336
|
-
}),
|
|
337
|
-
});
|
|
338
|
-
const body = await res.json();
|
|
339
|
-
const response = body.response;
|
|
340
|
-
if (!response || !["granted", "granted_all", "aborted"].includes(response)) {
|
|
341
|
-
throw new Error(`Permission request failed: ${body.error ?? `unexpected response: ${JSON.stringify(body)}`}`);
|
|
342
|
-
}
|
|
343
|
-
writeTaskStatus(taskDir, {
|
|
344
|
-
running_state: response === "aborted" ? "aborted" : "started",
|
|
345
|
-
time_stamp: Date.now(),
|
|
346
|
-
});
|
|
347
|
-
return response;
|
|
348
|
-
}
|
|
349
285
|
async function requestConfirmation(config, task, taskDir) {
|
|
350
286
|
const port = config.httpPort ?? 7256;
|
|
351
287
|
const res = await fetch(`http://localhost:${port}/request-confirmation?taskId=${encodeURIComponent(task.frontmatter.id)}`, {
|
|
@@ -376,25 +312,6 @@ export function listLinkedTaskFiles(taskDir, runId, output) {
|
|
|
376
312
|
}
|
|
377
313
|
});
|
|
378
314
|
}
|
|
379
|
-
export function parsePermissions(output) {
|
|
380
|
-
const regex = new RegExp(`^\\${TASK_PERMISSION_PREFIX}\\s+(.+)$`, "gm");
|
|
381
|
-
const perms = [];
|
|
382
|
-
let match;
|
|
383
|
-
while ((match = regex.exec(output)) !== null) {
|
|
384
|
-
const raw = match[1].trim();
|
|
385
|
-
// Skip placeholder examples echoed from the prompt (e.g. "<tool_name> | <description>").
|
|
386
|
-
if (raw.startsWith("<"))
|
|
387
|
-
continue;
|
|
388
|
-
const sep = raw.indexOf("|");
|
|
389
|
-
if (sep !== -1) {
|
|
390
|
-
perms.push({ name: raw.slice(0, sep).trim(), description: raw.slice(sep + 1).trim() });
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
perms.push({ name: raw, description: "" });
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return perms;
|
|
397
|
-
}
|
|
398
315
|
export function parseAgentModel(output) {
|
|
399
316
|
const regex = new RegExp(`^\\${AGENT_MODEL_PREFIX}[ \\t]+(.+)$`, "gm");
|
|
400
317
|
let match;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type { RequiredPermission } from "./types.js";
|
|
2
1
|
export interface PendingRequestMeta {
|
|
3
2
|
/** The task the request belongs to. */
|
|
4
3
|
session_id?: string;
|
|
5
|
-
/** Task name, set for permission requests. */
|
|
6
4
|
session_name?: string;
|
|
7
5
|
description?: string;
|
|
8
6
|
input_questions?: string[];
|
|
@@ -10,10 +8,10 @@ export interface PendingRequestMeta {
|
|
|
10
8
|
secret?: boolean;
|
|
11
9
|
}
|
|
12
10
|
export interface PendingRequest {
|
|
13
|
-
type: "confirmation" | "
|
|
11
|
+
type: "confirmation" | "input" | "choice";
|
|
14
12
|
resolve: (value: string[]) => void;
|
|
15
|
-
/**
|
|
16
|
-
params?:
|
|
13
|
+
/** Input descriptions (for 'input') or option labels (for 'choice'). */
|
|
14
|
+
params?: string[];
|
|
17
15
|
/** Display context for PWAs that connect while this request is already open. */
|
|
18
16
|
meta?: PendingRequestMeta;
|
|
19
17
|
}
|