@amalgm/automations 0.2.5 → 0.3.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.
- package/AXIOMS.md +52 -12
- package/PURPOSE.md +50 -15
- package/README.md +119 -14
- package/dist/src/automations.js +6 -0
- package/dist/src/cli/arguments.d.ts +20 -0
- package/dist/src/cli/arguments.js +49 -0
- package/dist/src/cli/input.d.ts +6 -0
- package/dist/src/cli/input.js +48 -0
- package/dist/src/cli/main.d.ts +7 -0
- package/dist/src/cli/main.js +39 -0
- package/dist/src/cli/run.d.ts +13 -0
- package/dist/src/cli/run.js +51 -0
- package/dist/src/cli/shell-runtime.d.ts +9 -0
- package/dist/src/cli/shell-runtime.js +102 -0
- package/dist/src/cli-main.js +2 -20
- package/dist/src/cli.d.ts +3 -10
- package/dist/src/cli.js +3 -186
- package/dist/src/command-result.d.ts +8 -0
- package/dist/src/command-result.js +13 -0
- package/dist/src/command-surface.d.ts +25 -0
- package/dist/src/command-surface.js +125 -0
- package/dist/src/contract.d.ts +1 -1
- package/dist/src/execution-contract.d.ts +28 -0
- package/dist/src/execution-contract.js +1 -0
- package/dist/src/execution-errors.d.ts +6 -0
- package/dist/src/execution-errors.js +48 -0
- package/dist/src/executor.d.ts +3 -16
- package/dist/src/executor.js +31 -36
- package/dist/src/index.d.ts +7 -3
- package/dist/src/index.js +6 -2
- package/dist/src/input-template.d.ts +2 -2
- package/dist/src/input-template.js +4 -4
- package/dist/src/mcp-main.js +0 -0
- package/dist/src/mcp.js +15 -79
- package/dist/src/node-process-host.d.ts +13 -0
- package/dist/src/node-process-host.js +116 -0
- package/dist/src/node-process-runner.d.ts +23 -0
- package/dist/src/node-process-runner.js +140 -0
- package/dist/src/plan.js +125 -9
- package/dist/src/run-contract.d.ts +41 -1
- package/dist/src/run-journal.d.ts +2 -2
- package/dist/src/schema.d.ts +4 -4
- package/package.json +3 -3
- package/skills/automations/SKILL.md +69 -6
package/dist/src/plan.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { ValidationError } from './errors.js';
|
|
2
|
+
const MAX_STEPS = 100;
|
|
3
|
+
const MAX_ARGUMENTS = 1_000;
|
|
4
|
+
const MAX_ARGUMENT_LENGTH = 100_000;
|
|
5
|
+
const MAX_SOURCE_LENGTH = 1_000_000;
|
|
6
|
+
const MAX_TIMEOUT_MS = 86_400_000;
|
|
7
|
+
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
const RESERVED_PROCESS_ENV = new Set([
|
|
9
|
+
'AMALGM_AUTOMATION_IDEMPOTENCY_KEY',
|
|
10
|
+
'AMALGM_AUTOMATIONS_AUTHORIZATION',
|
|
11
|
+
'AMALGM_COMPUTER_AUTH_TOKEN',
|
|
12
|
+
'AMALGM_RUNTIME_TOKEN',
|
|
13
|
+
'AMALGM_TUNNEL_TOKEN',
|
|
14
|
+
]);
|
|
2
15
|
export function automationPlan(snapshot) {
|
|
3
16
|
const root = record(snapshot, 'Automation snapshot');
|
|
4
17
|
const workflow = record(root.workflow, 'Workflow snapshot');
|
|
@@ -6,28 +19,72 @@ export function automationPlan(snapshot) {
|
|
|
6
19
|
}
|
|
7
20
|
export function compiledAutomationPlan(value) {
|
|
8
21
|
const compiled = record(value, 'Compiled workflow');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
const version = compiled.version;
|
|
23
|
+
if ((version !== 1 && version !== 2) || !Array.isArray(compiled.steps)
|
|
24
|
+
|| compiled.steps.length < 1 || compiled.steps.length > MAX_STEPS) {
|
|
25
|
+
throw new ValidationError(`Compiled workflow must be a version 1 or 2 plan with 1 to ${MAX_STEPS} steps`);
|
|
12
26
|
}
|
|
13
|
-
const steps = compiled.steps.map((item, index) => step(item, index));
|
|
27
|
+
const steps = compiled.steps.map((item, index) => step(item, index, version));
|
|
14
28
|
const ids = new Set();
|
|
15
29
|
for (const item of steps) {
|
|
16
30
|
if (ids.has(item.id))
|
|
17
31
|
throw new ValidationError(`Workflow step id is duplicated: ${item.id}`);
|
|
18
32
|
ids.add(item.id);
|
|
19
33
|
}
|
|
20
|
-
return
|
|
34
|
+
return version === 1
|
|
35
|
+
? { version: 1, steps: steps }
|
|
36
|
+
: { version: 2, steps };
|
|
21
37
|
}
|
|
22
|
-
function step(value, index) {
|
|
38
|
+
function step(value, index, version) {
|
|
23
39
|
const item = record(value, `Workflow step ${index + 1}`);
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
const id = requiredText(item.id, `Workflow step ${index + 1} needs an id`, 500);
|
|
41
|
+
if (version === 1 && item.kind !== undefined) {
|
|
42
|
+
throw new ValidationError(`Workflow step ${index + 1} requires plan version 2`);
|
|
43
|
+
}
|
|
44
|
+
if (item.kind === 'command')
|
|
45
|
+
return commandStep(item, id, index);
|
|
46
|
+
if (item.kind === 'script')
|
|
47
|
+
return scriptStep(item, id, index);
|
|
48
|
+
if (item.kind !== undefined) {
|
|
49
|
+
throw new ValidationError(`Workflow step ${index + 1} has an unsupported kind`);
|
|
50
|
+
}
|
|
26
51
|
if (typeof item.actionId !== 'string' || !item.actionId.includes('.')) {
|
|
27
52
|
throw new ValidationError(`Workflow step ${index + 1} needs a qualified actionId`);
|
|
28
53
|
}
|
|
29
54
|
assertJson(item.input);
|
|
30
|
-
return { id
|
|
55
|
+
return { id, actionId: item.actionId.trim(), input: item.input };
|
|
56
|
+
}
|
|
57
|
+
function commandStep(item, id, index) {
|
|
58
|
+
const label = `Workflow command step ${index + 1}`;
|
|
59
|
+
return compact({
|
|
60
|
+
id,
|
|
61
|
+
kind: 'command',
|
|
62
|
+
command: requiredText(item.command, `${label} needs a command`, 10_000),
|
|
63
|
+
args: argumentsList(item.args, label),
|
|
64
|
+
cwd: workingDirectory(item.cwd, label),
|
|
65
|
+
env: environment(item.env, label),
|
|
66
|
+
stdin: jsonInput(item.stdin, label),
|
|
67
|
+
timeoutMs: boundedInteger(item.timeoutMs, 1, MAX_TIMEOUT_MS, `${label} timeoutMs`),
|
|
68
|
+
maxOutputBytes: boundedInteger(item.maxOutputBytes, 1, MAX_OUTPUT_BYTES, `${label} maxOutputBytes`),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function scriptStep(item, id, index) {
|
|
72
|
+
const label = `Workflow script step ${index + 1}`;
|
|
73
|
+
if (item.runtime !== 'shell' && item.runtime !== 'node' && item.runtime !== 'python') {
|
|
74
|
+
throw new ValidationError(`${label} runtime must be shell, node, or python`);
|
|
75
|
+
}
|
|
76
|
+
return compact({
|
|
77
|
+
id,
|
|
78
|
+
kind: 'script',
|
|
79
|
+
runtime: item.runtime,
|
|
80
|
+
source: requiredText(item.source, `${label} needs source`, MAX_SOURCE_LENGTH, false),
|
|
81
|
+
args: argumentsList(item.args, label),
|
|
82
|
+
cwd: workingDirectory(item.cwd, label),
|
|
83
|
+
env: environment(item.env, label),
|
|
84
|
+
stdin: jsonInput(item.stdin, label),
|
|
85
|
+
timeoutMs: boundedInteger(item.timeoutMs, 1, MAX_TIMEOUT_MS, `${label} timeoutMs`),
|
|
86
|
+
maxOutputBytes: boundedInteger(item.maxOutputBytes, 1, MAX_OUTPUT_BYTES, `${label} maxOutputBytes`),
|
|
87
|
+
});
|
|
31
88
|
}
|
|
32
89
|
function record(value, name) {
|
|
33
90
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -46,3 +103,62 @@ function assertJson(value) {
|
|
|
46
103
|
}
|
|
47
104
|
throw new ValidationError('Workflow step input must be JSON');
|
|
48
105
|
}
|
|
106
|
+
function jsonInput(value, label) {
|
|
107
|
+
if (value === undefined)
|
|
108
|
+
return undefined;
|
|
109
|
+
try {
|
|
110
|
+
assertJson(value);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
throw new ValidationError(`${label} stdin must be JSON`);
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
function argumentsList(value, label) {
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
return undefined;
|
|
120
|
+
if (!Array.isArray(value) || value.length > MAX_ARGUMENTS
|
|
121
|
+
|| value.some((argument) => typeof argument !== 'string' || argument.length > MAX_ARGUMENT_LENGTH)) {
|
|
122
|
+
throw new ValidationError(`${label} args must contain at most ${MAX_ARGUMENTS} strings`);
|
|
123
|
+
}
|
|
124
|
+
return [...value];
|
|
125
|
+
}
|
|
126
|
+
function workingDirectory(value, label) {
|
|
127
|
+
if (value === undefined)
|
|
128
|
+
return undefined;
|
|
129
|
+
if (typeof value !== 'string' || !absolutePath(value)) {
|
|
130
|
+
throw new ValidationError(`${label} cwd must be an absolute path`);
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
function environment(value, label) {
|
|
135
|
+
if (value === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
138
|
+
|| Object.entries(value).some(([name, content]) => (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)
|
|
139
|
+
|| RESERVED_PROCESS_ENV.has(name)
|
|
140
|
+
|| typeof content !== 'string'))) {
|
|
141
|
+
throw new ValidationError(`${label} env must map non-reserved environment variable names to strings`);
|
|
142
|
+
}
|
|
143
|
+
return { ...value };
|
|
144
|
+
}
|
|
145
|
+
function boundedInteger(value, minimum, maximum, label) {
|
|
146
|
+
if (value === undefined)
|
|
147
|
+
return undefined;
|
|
148
|
+
if (!Number.isInteger(value) || Number(value) < minimum || Number(value) > maximum) {
|
|
149
|
+
throw new ValidationError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
150
|
+
}
|
|
151
|
+
return Number(value);
|
|
152
|
+
}
|
|
153
|
+
function requiredText(value, message, maximum, trim = true) {
|
|
154
|
+
if (typeof value !== 'string' || !value.trim() || value.length > maximum) {
|
|
155
|
+
throw new ValidationError(message);
|
|
156
|
+
}
|
|
157
|
+
return trim ? value.trim() : value;
|
|
158
|
+
}
|
|
159
|
+
function absolutePath(value) {
|
|
160
|
+
return value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith('\\\\');
|
|
161
|
+
}
|
|
162
|
+
function compact(value) {
|
|
163
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
164
|
+
}
|
|
@@ -5,10 +5,50 @@ export interface ToolActionStep {
|
|
|
5
5
|
actionId: string;
|
|
6
6
|
input: Json;
|
|
7
7
|
}
|
|
8
|
-
export interface
|
|
8
|
+
export interface DirectCommandStep {
|
|
9
|
+
id: string;
|
|
10
|
+
kind: 'command';
|
|
11
|
+
/** Executable name resolved by the machine PATH, or an absolute path. */
|
|
12
|
+
command: string;
|
|
13
|
+
/** Passed directly to the executable. No shell parsing occurs. */
|
|
14
|
+
args?: string[];
|
|
15
|
+
/** Absolute directory on the selected machine. */
|
|
16
|
+
cwd?: string;
|
|
17
|
+
/** Persisted, non-secret environment additions. */
|
|
18
|
+
env?: Record<string, string>;
|
|
19
|
+
/** String input is passed verbatim; other JSON values are serialized with a trailing newline. */
|
|
20
|
+
stdin?: Json;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
maxOutputBytes?: number;
|
|
23
|
+
}
|
|
24
|
+
export type AutomationScriptRuntime = 'shell' | 'node' | 'python';
|
|
25
|
+
export interface CodeScriptStep {
|
|
26
|
+
id: string;
|
|
27
|
+
kind: 'script';
|
|
28
|
+
runtime: AutomationScriptRuntime;
|
|
29
|
+
source: string;
|
|
30
|
+
args?: string[];
|
|
31
|
+
/** Absolute directory on the selected machine. */
|
|
32
|
+
cwd?: string;
|
|
33
|
+
/** Persisted, non-secret environment additions. */
|
|
34
|
+
env?: Record<string, string>;
|
|
35
|
+
/** String input is passed verbatim; other JSON values are serialized with a trailing newline. */
|
|
36
|
+
stdin?: Json;
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
maxOutputBytes?: number;
|
|
39
|
+
}
|
|
40
|
+
export type AutomationStep = ToolActionStep | DirectCommandStep | CodeScriptStep;
|
|
41
|
+
/** Original workflow contract. Its meaning remains action-only forever. */
|
|
42
|
+
export interface AutomationActionPlan {
|
|
9
43
|
version: 1;
|
|
10
44
|
steps: ToolActionStep[];
|
|
11
45
|
}
|
|
46
|
+
/** Typed workflow contract for Amalgm actions, native commands, and code. */
|
|
47
|
+
export interface AutomationTypedPlan {
|
|
48
|
+
version: 2;
|
|
49
|
+
steps: AutomationStep[];
|
|
50
|
+
}
|
|
51
|
+
export type AutomationPlan = AutomationActionPlan | AutomationTypedPlan;
|
|
12
52
|
export type AutomationStepStatus = 'running' | 'completed' | 'failed';
|
|
13
53
|
export interface AutomationStepRun {
|
|
14
54
|
id: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { AutomationRunJournal, AutomationStepRun, Json
|
|
1
|
+
import type { AutomationRunJournal, AutomationStep, AutomationStepRun, Json } from './contract.js';
|
|
2
2
|
export declare function emptyJournal(): AutomationRunJournal;
|
|
3
|
-
export declare function runJournal(value: Json | undefined, plan: readonly
|
|
3
|
+
export declare function runJournal(value: Json | undefined, plan: readonly AutomationStep[]): AutomationRunJournal;
|
|
4
4
|
export declare function completed(journal: AutomationRunJournal, stepId: string): boolean;
|
|
5
5
|
export declare function stepAttempt(journal: AutomationRunJournal, stepId: string): number;
|
|
6
6
|
export declare function putStep(journal: AutomationRunJournal, step: AutomationStepRun): AutomationRunJournal;
|
package/dist/src/schema.d.ts
CHANGED
|
@@ -197,27 +197,27 @@ export declare const updateWorkflowSchema: z.ZodEffects<z.ZodObject<{
|
|
|
197
197
|
allowlist: z.ZodOptional<z.ZodNullable<z.ZodType<Json, z.ZodTypeDef, Json>>>;
|
|
198
198
|
limits: z.ZodOptional<z.ZodNullable<z.ZodType<Json, z.ZodTypeDef, Json>>>;
|
|
199
199
|
}, "strict", z.ZodTypeAny, {
|
|
200
|
+
script?: string | undefined;
|
|
200
201
|
name?: string | null | undefined;
|
|
201
202
|
compiled?: Json | undefined;
|
|
202
|
-
script?: string | undefined;
|
|
203
203
|
allowlist?: Json | undefined;
|
|
204
204
|
limits?: Json | undefined;
|
|
205
205
|
}, {
|
|
206
|
+
script?: string | undefined;
|
|
206
207
|
name?: string | null | undefined;
|
|
207
208
|
compiled?: Json | undefined;
|
|
208
|
-
script?: string | undefined;
|
|
209
209
|
allowlist?: Json | undefined;
|
|
210
210
|
limits?: Json | undefined;
|
|
211
211
|
}>, {
|
|
212
|
+
script?: string | undefined;
|
|
212
213
|
name?: string | null | undefined;
|
|
213
214
|
compiled?: Json | undefined;
|
|
214
|
-
script?: string | undefined;
|
|
215
215
|
allowlist?: Json | undefined;
|
|
216
216
|
limits?: Json | undefined;
|
|
217
217
|
}, {
|
|
218
|
+
script?: string | undefined;
|
|
218
219
|
name?: string | null | undefined;
|
|
219
220
|
compiled?: Json | undefined;
|
|
220
|
-
script?: string | undefined;
|
|
221
221
|
allowlist?: Json | undefined;
|
|
222
222
|
limits?: Json | undefined;
|
|
223
223
|
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/automations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Amalgm's automation SDK: durable trigger admission and target-machine execution.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"README.md"
|
|
46
46
|
],
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
48
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && tsx scripts/mark-executables.ts",
|
|
49
49
|
"check": "tsx scripts/check-tree.ts && tsc -p tsconfig.json --noEmit",
|
|
50
50
|
"test": "tsx --test test/*.test.ts",
|
|
51
51
|
"test:supabase": "tsx --test test/integration/postgres.test.ts",
|
|
52
|
-
"verify": "npm run check && npm test && npm run build",
|
|
52
|
+
"verify": "npm run check && npm test && npm run build && tsx scripts/check-cli-artifact.ts",
|
|
53
53
|
"release:check": "npm run verify && npm run test:supabase",
|
|
54
54
|
"prepack": "npm run build",
|
|
55
55
|
"start": "node dist/host/main.js"
|
|
@@ -8,6 +8,67 @@ description: Create, inspect, change, delete, or manually run complete Amalgm au
|
|
|
8
8
|
Use the Automations tools for scheduled or webhook-driven work. The tools are
|
|
9
9
|
the agent adapter over the same hosted SDK used by the UI.
|
|
10
10
|
|
|
11
|
+
When MCP is unavailable but the authenticated Amalgm Shell CLI is available,
|
|
12
|
+
use the command with the matching suffix (`create`, `list`, `get`, `update`,
|
|
13
|
+
`delete`, or `run-now`). Pass the exact MCP input object as JSON, preferably on
|
|
14
|
+
stdin when it contains a provider secret:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
printf '%s\n' '{"name":"Call Mom","schedules":[{"cron":"* * * * *","timezone":"America/Los_Angeles","maxOccurrences":10}],"workflow":{"script":"Notify me to call Mom.","compiled":{"version":1,"steps":[{"id":"notify","actionId":"channels.notify_user","input":{"title":"Reminder","message":"Call Mom"}}]}}}' \
|
|
18
|
+
| amalgm automations create --stdin
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The CLI returns `{ "result": ... }` on stdout or a structured `error` object
|
|
22
|
+
on stderr with a nonzero exit. It has no separate low-level trigger or workflow
|
|
23
|
+
commands; complete definitions and grouped changes use the same task-level
|
|
24
|
+
surface as MCP.
|
|
25
|
+
|
|
26
|
+
The CLI is global. Its current directory does not scope or modify the
|
|
27
|
+
automation. Process location belongs in an absolute workflow-step `cwd`; when
|
|
28
|
+
it is omitted, Shell uses the selected machine user's OS home.
|
|
29
|
+
|
|
30
|
+
## Choose the execution lane deliberately
|
|
31
|
+
|
|
32
|
+
Use exactly one of these shapes for each compiled workflow step:
|
|
33
|
+
|
|
34
|
+
Version 1 remains action-only. Use plan version 2 for every workflow containing
|
|
35
|
+
a native command or code script; version 2 may mix all three lanes.
|
|
36
|
+
|
|
37
|
+
- **Amalgm action** — `{ "id", "actionId", "input" }`. Use this for Amalgm
|
|
38
|
+
tools, Channels, or the Amalgm agent path. `chat.chat_agent_run` is an Amalgm
|
|
39
|
+
agent, not the user's native CLI agent.
|
|
40
|
+
- **Native command** — `{ "id", "kind":"command", "command", "args"?,
|
|
41
|
+
"cwd"?, "env"?, "stdin"?, "timeoutMs"?, "maxOutputBytes"? }`. Use this for
|
|
42
|
+
installed executables such as `codex exec`, `claude -p`, `opencode`, or a
|
|
43
|
+
script file. Args are literal argv and never pass through a shell.
|
|
44
|
+
- **Code script** — `{ "id", "kind":"script", "runtime":"shell"|"node"|
|
|
45
|
+
"python", "source", "args"?, "cwd"?, "env"?, "stdin"?, "timeoutMs"?,
|
|
46
|
+
"maxOutputBytes"? }`. Use this only when inline code or shell semantics are
|
|
47
|
+
desired explicitly.
|
|
48
|
+
|
|
49
|
+
For a native Codex run on the selected machine:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"version": 2,
|
|
54
|
+
"steps": [{
|
|
55
|
+
"id": "native-review",
|
|
56
|
+
"kind": "command",
|
|
57
|
+
"command": "codex",
|
|
58
|
+
"args": ["exec", "Review this repository and write REVIEW.md"],
|
|
59
|
+
"cwd": "/absolute/path/to/repository",
|
|
60
|
+
"timeoutMs": 1800000
|
|
61
|
+
}]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Installed CLI authentication comes from that CLI's own user setup on the
|
|
66
|
+
selected machine. Never put Shell runtime, tunnel, or machine credentials in a
|
|
67
|
+
workflow. `env` is persisted ordinary configuration, not a secret store.
|
|
68
|
+
Process results and bounded stdout/stderr appear in run history. A native
|
|
69
|
+
process may repeat after an uncertain machine crash; it receives
|
|
70
|
+
`AMALGM_AUTOMATION_IDEMPOTENCY_KEY` for durable deduplication when needed.
|
|
71
|
+
|
|
11
72
|
## Create a scheduled notification
|
|
12
73
|
|
|
13
74
|
For a request such as “remind me to call my mom every minute for the next ten
|
|
@@ -52,13 +113,15 @@ request. The result is a durable `pending` run, not proof that its selected
|
|
|
52
113
|
machine has finished it; use `amalgm_automations_get` with run history when the
|
|
53
114
|
user asks for the outcome.
|
|
54
115
|
|
|
55
|
-
|
|
116
|
+
Amalgm action discovery belongs to the Tools product. Native command discovery
|
|
117
|
+
belongs to the selected machine's PATH and installed user environment.
|
|
56
118
|
|
|
57
|
-
For webhook-driven work, pass the admitted provider payload into an action
|
|
58
|
-
placing the exact value `{ "$runInput": true }` at the desired point
|
|
59
|
-
|
|
60
|
-
and set `context` to `{ "$runInput": true }`. Never paste
|
|
61
|
-
into the workflow: every admitted run must use its own
|
|
119
|
+
For webhook-driven work, pass the admitted provider payload into an action or
|
|
120
|
+
process by placing the exact value `{ "$runInput": true }` at the desired point
|
|
121
|
+
in action `input` or process `stdin`. For example, an agent-review action can
|
|
122
|
+
use a static `message` and set `context` to `{ "$runInput": true }`. Never paste
|
|
123
|
+
an example delivery into the workflow: every admitted run must use its own
|
|
124
|
+
durable input.
|
|
62
125
|
|
|
63
126
|
`pending` means a run is durable and waiting for its selected machine. `sent`
|
|
64
127
|
or `running` means that machine holds a lease. `completed` and `failed` are
|