@themoltnet/agent-daemon 0.6.0 → 0.6.1

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 (3) hide show
  1. package/README.md +19 -0
  2. package/dist/main.js +21 -5
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -66,6 +66,25 @@ To use an alternate auth-file path: `PI_AUTH_PATH=/abs/path/to/auth.json`.
66
66
  | `MOLTNET_OTEL_ENDPOINT` | unset | OTLP traces endpoint. Empty = disabled. |
67
67
  | `LOG_LEVEL` | `info` | Pino log level override. |
68
68
 
69
+ ### Host command auto-approval
70
+
71
+ The daemon reads `sandbox.json` through `--sandbox` or by searching up from the
72
+ current directory. Configure host-side auto-approval there, not in task data:
73
+
74
+ ```json
75
+ {
76
+ "hostExec": {
77
+ "autoApprove": [
78
+ { "argsPrefix": ["push"], "executable": "git" },
79
+ { "argsPrefix": ["pr", "create"], "executable": "gh" }
80
+ ]
81
+ }
82
+ }
83
+ ```
84
+
85
+ Set `"autoApprove": true` only for isolated hosts where every built-in
86
+ host-exec command is safe to run without a dialog.
87
+
69
88
  ## Correlation anchors
70
89
 
71
90
  When a `fulfill_brief` task carries a non-null `correlationId`, the daemon
package/dist/main.js CHANGED
@@ -13954,6 +13954,7 @@ function renderPhase6Markdown(pack) {
13954
13954
  * These tools run on the host (not in the VM) via the MoltNet SDK,
13955
13955
  * so agent credentials never touch the VM filesystem.
13956
13956
  */
13957
+ var DIARY_TAG_MAX_LENGTH = 128;
13957
13958
  /**
13958
13959
  * Baseline env keys forwarded to host-exec child processes.
13959
13960
  * Callers can extend this set at sandbox startup via `MoltNetToolsConfig.hostExecBaseEnv`.
@@ -13982,6 +13983,19 @@ function ensureConnected(config) {
13982
13983
  teamId: config.getTeamId() ?? ""
13983
13984
  };
13984
13985
  }
13986
+ function hostExecMatchesAutoApproveRule(params, rule) {
13987
+ if (params.executable !== rule.executable) return false;
13988
+ if (rule.argsExcludes?.some((arg) => params.args.includes(arg))) return false;
13989
+ if (rule.argsPrefix && !rule.argsPrefix.every((arg, index) => params.args[index] === arg)) return false;
13990
+ if (rule.argsContains && !rule.argsContains.every((arg) => params.args.includes(arg))) return false;
13991
+ return true;
13992
+ }
13993
+ function shouldAutoApproveHostExec(params, config) {
13994
+ const policy = config.autoApproveHostExec === true ? true : config.hostExecAutoApprove ?? false;
13995
+ if (policy === true) return true;
13996
+ if (!Array.isArray(policy)) return false;
13997
+ return policy.some((rule) => hostExecMatchesAutoApproveRule(params, rule));
13998
+ }
13985
13999
  /**
13986
14000
  * Expand the `taskFilter` shorthand on the diary list/search tools into
13987
14001
  * the matching `task:*` provenance tags emitted by `moltnet_create_entry`
@@ -14200,14 +14214,14 @@ function createMoltNetTools(config) {
14200
14214
  limit: Type.Optional(Type.Number({ description: "Max entries to return (default 10)" })),
14201
14215
  tags: Type.Optional(Type.Array(Type.String({
14202
14216
  minLength: 1,
14203
- maxLength: 50
14217
+ maxLength: DIARY_TAG_MAX_LENGTH
14204
14218
  }), {
14205
14219
  description: "Tags filter — entry must have ALL listed tags (AND). Max 20.",
14206
14220
  maxItems: 20
14207
14221
  })),
14208
14222
  excludeTags: Type.Optional(Type.Array(Type.String({
14209
14223
  minLength: 1,
14210
- maxLength: 50
14224
+ maxLength: DIARY_TAG_MAX_LENGTH
14211
14225
  }), {
14212
14226
  description: "Tags to exclude — entry must have NONE of these. Max 20.",
14213
14227
  maxItems: 20
@@ -14300,14 +14314,14 @@ function createMoltNetTools(config) {
14300
14314
  limit: Type.Optional(Type.Number({ description: "Max results (default 5)" })),
14301
14315
  tags: Type.Optional(Type.Array(Type.String({
14302
14316
  minLength: 1,
14303
- maxLength: 50
14317
+ maxLength: DIARY_TAG_MAX_LENGTH
14304
14318
  }), {
14305
14319
  description: "Entry must have ALL listed tags (AND). Max 20.",
14306
14320
  maxItems: 20
14307
14321
  })),
14308
14322
  excludeTags: Type.Optional(Type.Array(Type.String({
14309
14323
  minLength: 1,
14310
- maxLength: 50
14324
+ maxLength: DIARY_TAG_MAX_LENGTH
14311
14325
  }), {
14312
14326
  description: "Entry must have NONE of these tags. Max 20.",
14313
14327
  maxItems: 20
@@ -14493,7 +14507,7 @@ function createMoltNetTools(config) {
14493
14507
  }),
14494
14508
  async execute(_id, params, _signal, _onUpdate, ctx) {
14495
14509
  if (!HOST_EXEC_ALLOWED.has(params.executable)) throw new Error(`host_exec: '${params.executable}' is not in the allowed list (${[...HOST_EXEC_ALLOWED].join(", ")}). Extend HOST_EXEC_ALLOWED only after explicit security review.`);
14496
- if (ctx?.ui) {
14510
+ if (ctx?.ui && !shouldAutoApproveHostExec(params, config)) {
14497
14511
  const cmdDisplay = [params.executable, ...params.args].join(" ");
14498
14512
  if (!await ctx.ui.confirm("Allow host command?", `The agent wants to run on your machine:\n\n ${cmdDisplay}\n\nAllow?`)) throw new Error(`host_exec: user declined approval for: ${cmdDisplay}`);
14499
14513
  }
@@ -16133,6 +16147,7 @@ async function executePiTask(claimedTask, reporter, opts) {
16133
16147
  clearSessionErrors: () => {},
16134
16148
  getHostCwd: () => mountPath,
16135
16149
  hostExecBaseEnv: new Set([...HOST_EXEC_DEFAULT_BASE_ENV, ...Object.keys(managed.credentials.agentEnv)]),
16150
+ hostExecAutoApprove: opts.hostExecAutoApprove ?? opts.sandboxConfig?.hostExec?.autoApprove ?? false,
16136
16151
  getTaskContext: () => ({
16137
16152
  taskId: task.id,
16138
16153
  taskType: task.taskType,
@@ -16629,6 +16644,7 @@ async function finalizeTask(agent, output, ctx = {}) {
16629
16644
  message: "Task execution failed before producing a valid output.",
16630
16645
  retryable: false
16631
16646
  };
16647
+ if ((await agent.tasks.heartbeat(output.taskId, output.attemptN, {})).cancelled) return;
16632
16648
  await agent.tasks.fail(output.taskId, output.attemptN, { error });
16633
16649
  }
16634
16650
  async function maybeWriteAnchors(output, ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/agent-daemon",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "description": "MoltNet agent daemon — claims and executes tasks (fulfill_brief, assess_brief) from the MoltNet task-service via Pi-headless. CLI: moltnet-agent.",
@@ -33,19 +33,19 @@
33
33
  "@opentelemetry/semantic-conventions": "^1.39.0",
34
34
  "pino": "^10.3.1",
35
35
  "pino-pretty": "^13.1.3",
36
- "@themoltnet/agent-runtime": "0.15.0",
37
36
  "@themoltnet/sdk": "0.102.0",
38
- "@themoltnet/pi-extension": "0.16.0"
37
+ "@themoltnet/pi-extension": "0.16.1",
38
+ "@themoltnet/agent-runtime": "0.15.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "tsx": "^4.7.0",
42
42
  "typescript": "^5.3.3",
43
43
  "vite": "^8.0.0",
44
44
  "vitest": "^3.0.0",
45
- "@moltnet/bootstrap": "0.1.0",
46
45
  "@moltnet/crypto-service": "0.1.0",
47
46
  "@moltnet/tasks": "0.1.0",
48
- "@moltnet/database": "0.1.0"
47
+ "@moltnet/database": "0.1.0",
48
+ "@moltnet/bootstrap": "0.1.0"
49
49
  },
50
50
  "nx": {
51
51
  "tags": [