@superjackfruitlabs/salaam 0.0.3 → 0.0.5

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 (172) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +12 -11
  3. package/dist/src/acp-desk.js +140 -0
  4. package/dist/src/acp-outcome.js +182 -18
  5. package/dist/src/acp.js +296 -15
  6. package/dist/src/agent-profiles.js +45 -0
  7. package/dist/src/agentpod-authority.js +169 -11
  8. package/dist/src/agentpod-default-config.js +12 -0
  9. package/dist/src/agentpod-work-decision-protocol.js +40 -6
  10. package/dist/src/agentpod-work-decisions.js +108 -4
  11. package/dist/src/agentpod-work.js +1214 -127
  12. package/dist/src/approval-rules.js +287 -0
  13. package/dist/src/attach.js +3 -1
  14. package/dist/src/autonomy.js +80 -9
  15. package/dist/src/board-reads.js +106 -0
  16. package/dist/src/children.js +83 -0
  17. package/dist/src/cli-help.js +61 -6
  18. package/dist/src/cli.js +140 -4
  19. package/dist/src/command-danger.js +797 -0
  20. package/dist/src/command-network.js +200 -0
  21. package/dist/src/command-sandbox.js +327 -0
  22. package/dist/src/custom-commands.js +159 -0
  23. package/dist/src/danger-classifier.js +286 -0
  24. package/dist/src/execution-evidence.js +33 -1
  25. package/dist/src/git-helpers.js +160 -0
  26. package/dist/src/hooks.js +351 -0
  27. package/dist/src/host-bind.js +111 -0
  28. package/dist/src/host-breaker.js +88 -0
  29. package/dist/src/host-start.js +41 -1
  30. package/dist/src/host.js +284 -6
  31. package/dist/src/house-extension.js +292 -61
  32. package/dist/src/local-coding.js +726 -35
  33. package/dist/src/local-edit-helper.js +25 -0
  34. package/dist/src/local-edits.js +168 -6
  35. package/dist/src/local-undo.js +25 -16
  36. package/dist/src/managed.js +1 -1
  37. package/dist/src/mentions.js +277 -0
  38. package/dist/src/operator-mcp-cli.js +107 -0
  39. package/dist/src/operator-mcp.js +342 -0
  40. package/dist/src/persona.js +31 -0
  41. package/dist/src/pi-sessions.js +5 -1
  42. package/dist/src/pi.js +22 -1
  43. package/dist/src/project-instructions.js +195 -0
  44. package/dist/src/protocol.js +9 -3
  45. package/dist/src/rate-limit-retries.js +62 -0
  46. package/dist/src/recurring-work.js +1 -0
  47. package/dist/src/repository-snapshots.js +5 -2
  48. package/dist/src/run-grants.js +62 -0
  49. package/dist/src/service.js +482 -10
  50. package/dist/src/session-pick.js +77 -0
  51. package/dist/src/shell-passthrough.js +39 -0
  52. package/dist/src/station-checkpoints.js +139 -0
  53. package/dist/src/station-config.js +8 -1
  54. package/dist/src/station-files.js +20 -1
  55. package/dist/src/station-task-verification.js +6 -3
  56. package/dist/src/station-work.js +315 -22
  57. package/dist/src/store.js +70 -4
  58. package/dist/src/supervisor.js +408 -25
  59. package/dist/src/task-budget.js +39 -3
  60. package/dist/src/task-context.js +8 -2
  61. package/dist/src/task-critic.js +115 -0
  62. package/dist/src/task-driver.js +46 -2
  63. package/dist/src/task-policy.js +44 -12
  64. package/dist/src/task-verification.js +42 -9
  65. package/dist/src/task-work.js +47 -0
  66. package/dist/src/terminal-status.js +88 -0
  67. package/dist/src/tool-identity.js +69 -0
  68. package/dist/src/tui-activity.js +99 -0
  69. package/dist/src/tui-classifier.js +25 -0
  70. package/dist/src/tui-client.js +186 -5
  71. package/dist/src/tui-commands.js +23 -5
  72. package/dist/src/tui-composer.js +113 -0
  73. package/dist/src/tui-keymap.js +116 -0
  74. package/dist/src/tui-mentions.js +38 -0
  75. package/dist/src/tui-modals.js +157 -34
  76. package/dist/src/tui-notify.js +62 -0
  77. package/dist/src/tui-review.js +55 -0
  78. package/dist/src/tui-turn-keys.js +52 -0
  79. package/dist/src/tui-view.js +243 -69
  80. package/dist/src/tui.js +875 -118
  81. package/dist/src/turn-checkpoints.js +221 -0
  82. package/dist/src/turn-rewind.js +168 -0
  83. package/dist/src/usage.js +1 -1
  84. package/dist/src/web-fetch.js +114 -0
  85. package/dist/src/web-guard.js +163 -0
  86. package/dist/src/web-search.js +302 -0
  87. package/npm-shrinkwrap.json +2 -2
  88. package/package.json +1 -1
  89. package/src/acp-desk.ts +155 -0
  90. package/src/acp-outcome.ts +174 -20
  91. package/src/acp.ts +386 -30
  92. package/src/agent-profiles.ts +48 -0
  93. package/src/agentpod-authority.ts +234 -15
  94. package/src/agentpod-default-config.ts +16 -0
  95. package/src/agentpod-work-decision-protocol.ts +69 -8
  96. package/src/agentpod-work-decisions.ts +123 -5
  97. package/src/agentpod-work.ts +1455 -152
  98. package/src/approval-rules.ts +360 -0
  99. package/src/attach.ts +4 -1
  100. package/src/autonomy.ts +111 -10
  101. package/src/board-reads.ts +148 -0
  102. package/src/children.ts +112 -0
  103. package/src/cli-help.ts +60 -6
  104. package/src/cli.ts +174 -4
  105. package/src/command-danger.ts +886 -0
  106. package/src/command-network.ts +215 -0
  107. package/src/command-sandbox.ts +393 -0
  108. package/src/custom-commands.ts +175 -0
  109. package/src/danger-classifier.ts +321 -0
  110. package/src/execution-evidence.ts +31 -1
  111. package/src/git-helpers.ts +177 -0
  112. package/src/hooks.ts +425 -0
  113. package/src/host-bind.ts +156 -0
  114. package/src/host-breaker.ts +89 -0
  115. package/src/host-start.ts +51 -1
  116. package/src/host.ts +365 -6
  117. package/src/house-extension.ts +313 -60
  118. package/src/local-coding.ts +894 -43
  119. package/src/local-edit-helper.ts +26 -0
  120. package/src/local-edits.ts +191 -8
  121. package/src/local-undo.ts +29 -14
  122. package/src/managed.ts +2 -2
  123. package/src/mentions.ts +304 -0
  124. package/src/operator-mcp-cli.ts +108 -0
  125. package/src/operator-mcp.ts +420 -0
  126. package/src/persona.ts +37 -0
  127. package/src/pi-sessions.ts +9 -1
  128. package/src/pi.ts +23 -1
  129. package/src/project-instructions.ts +229 -0
  130. package/src/protocol.ts +25 -3
  131. package/src/rate-limit-retries.ts +69 -0
  132. package/src/recurring-work.ts +1 -0
  133. package/src/repository-snapshots.ts +5 -2
  134. package/src/run-grants.ts +107 -0
  135. package/src/service.ts +709 -10
  136. package/src/session-pick.ts +102 -0
  137. package/src/shell-passthrough.ts +38 -0
  138. package/src/station-checkpoints.ts +183 -0
  139. package/src/station-config.ts +10 -1
  140. package/src/station-files.ts +20 -1
  141. package/src/station-task-verification.ts +6 -3
  142. package/src/station-work.ts +369 -20
  143. package/src/store.ts +70 -3
  144. package/src/supervisor.ts +463 -26
  145. package/src/task-budget.ts +48 -3
  146. package/src/task-context.ts +9 -2
  147. package/src/task-critic.ts +171 -0
  148. package/src/task-driver.ts +49 -2
  149. package/src/task-policy.ts +65 -13
  150. package/src/task-verification.ts +53 -9
  151. package/src/task-work.ts +75 -0
  152. package/src/terminal-status.ts +103 -0
  153. package/src/tool-identity.ts +110 -0
  154. package/src/tui-activity.ts +133 -0
  155. package/src/tui-classifier.ts +38 -0
  156. package/src/tui-client.ts +239 -8
  157. package/src/tui-commands.ts +49 -10
  158. package/src/tui-composer.ts +124 -0
  159. package/src/tui-keymap.ts +152 -0
  160. package/src/tui-mentions.ts +43 -0
  161. package/src/tui-modals.ts +173 -43
  162. package/src/tui-notify.ts +77 -0
  163. package/src/tui-review.ts +76 -0
  164. package/src/tui-turn-keys.ts +77 -0
  165. package/src/tui-view.ts +341 -88
  166. package/src/tui.ts +1047 -137
  167. package/src/turn-checkpoints.ts +253 -0
  168. package/src/turn-rewind.ts +258 -0
  169. package/src/usage.ts +1 -1
  170. package/src/web-fetch.ts +140 -0
  171. package/src/web-guard.ts +195 -0
  172. package/src/web-search.ts +402 -0
@@ -312,6 +312,123 @@ export function acpFailure(
312
312
  ...(kind === 'unknown' ? {} : { retryable: false }),
313
313
  };
314
314
  }
315
+ /** ACP tool kind for an approval, so a client's modes can tell edits apart:
316
+ * AgentPod's accept-edits approves only kind "edit"; full-auto approves all; ask none. */
317
+ export function permissionKind(operationKind: string): 'edit' | 'execute' | 'other' {
318
+ if (operationKind === 'workspace') return 'edit';
319
+ if (operationKind === 'harness') return 'execute';
320
+ return 'other';
321
+ }
322
+ /** An operator MCP catalog read is a read; a server's `fetch` tool is a fetch; others stay other. */
323
+ function operatorMcpKind(action: string, tool: unknown): string {
324
+ if (action === 'catalog') return 'read';
325
+ return action === 'call' && tool === 'fetch' ? 'fetch' : 'other';
326
+ }
327
+
328
+ function toolArgs(input: unknown): Record<string, unknown> {
329
+ try {
330
+ const value = typeof input === 'string' ? JSON.parse(input) : input;
331
+ if (value && typeof value === 'object' && !Array.isArray(value))
332
+ return value as Record<string, unknown>;
333
+ } catch {}
334
+ return {};
335
+ }
336
+
337
+ const label = (v: unknown) => (typeof v === 'string' && /^[\w.-]{1,128}$/.test(v) ? v : undefined);
338
+
339
+ /** One line of what a tool acted on, for a card title: no control characters, at most `max`. */
340
+ function shown(v: unknown, max: number): string | undefined {
341
+ if (typeof v !== 'string') return undefined;
342
+ const t = v
343
+ .replace(/[\u0000-\u001f\u007f]+/g, ' ')
344
+ .replace(/\s+/g, ' ')
345
+ .trim();
346
+ if (!t) return undefined;
347
+ return t.length > max ? t.slice(0, max - 1) + '…' : t;
348
+ }
349
+ /** A URL as host and path only: never its login, query or fragment (they may carry a secret). */
350
+ function shownUrl(v: unknown): string | undefined {
351
+ if (typeof v !== 'string') return undefined;
352
+ try {
353
+ const u = new URL(v);
354
+ if (u.protocol !== 'http:' && u.protocol !== 'https:') return undefined;
355
+ return u.host + (u.pathname === '/' ? '' : u.pathname);
356
+ } catch {
357
+ return undefined;
358
+ }
359
+ }
360
+ const titled = (verb: string, what: string | undefined) => {
361
+ if (!what) return verb;
362
+ const t = `${verb}: ${what}`;
363
+ return t.length > 80 ? t.slice(0, 79) + '…' : t;
364
+ };
365
+
366
+ /**
367
+ * The ACP title for a tool call, the way a board agent's card reads: the tool and what it acted on
368
+ * ("fetch: host/path", "write: runs/r/answer.md"). Every operator MCP call goes through one wrapper
369
+ * tool, so it is titled by the MCP tool itself. Only plain identifiers, URL host and path, paths,
370
+ * queries and commands are echoed, clipped to one line of 80 characters.
371
+ */
372
+ /** A clipped input no longer parses: read the leading plain string fields it still carries. */
373
+ function clippedArgs(input: unknown): Record<string, unknown> {
374
+ if (typeof input !== 'string') return {};
375
+ const out: Record<string, unknown> = {};
376
+ for (const key of [
377
+ 'action',
378
+ 'path',
379
+ 'query',
380
+ 'pattern',
381
+ 'command',
382
+ 'server',
383
+ 'tool',
384
+ 'item_id',
385
+ ]) {
386
+ const m = new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.){0,512})"`).exec(input);
387
+ if (m) {
388
+ try {
389
+ out[key] = JSON.parse(`"${m[1]}"`);
390
+ } catch {}
391
+ }
392
+ }
393
+ // A clipped patch is a long one, so a write: a delete (content null) is short and never clipped.
394
+ return out;
395
+ }
396
+
397
+ export function toolTitle(name: string, input: unknown): string {
398
+ const parsed = toolArgs(input);
399
+ const args = Object.keys(parsed).length ? parsed : clippedArgs(input);
400
+ const action = typeof args.action === 'string' ? args.action : '';
401
+ if (name === 'salaam_operator_mcp') {
402
+ const server = label(args.server);
403
+ const tool = label(args.tool);
404
+ if (action === 'call' && server && tool) {
405
+ const a = toolArgs(args.arguments);
406
+ return titled(tool, shownUrl(a.url) ?? shown(a.query ?? a.path ?? a.pattern, 80));
407
+ }
408
+ if (action === 'catalog') return titled('MCP catalog', server);
409
+ if (action === 'call') return titled('MCP call', server ?? tool);
410
+ return action ? titled('MCP request', label(action)) : 'MCP request (no input)';
411
+ }
412
+ if (name === 'salaam_board') {
413
+ if (action === 'card') return 'read this card';
414
+ if (action === 'attempts') return 'read this card’s runs';
415
+ if (action === 'library_search') return titled('library_search', shown(args.query, 80));
416
+ if (action === 'library_get') return titled('library_get', shown(args.item_id, 32));
417
+ return name;
418
+ }
419
+ if (['salaam_station', 'salaam_files', 'salaam_repository'].includes(name)) {
420
+ if (action === 'patch')
421
+ return titled(args.content === null ? 'delete' : 'write', shown(args.path, 80));
422
+ if (action === 'inspect' && args.path === undefined) return 'inspect workspace';
423
+ if (action === 'check') return titled('check', shown(args.command, 80));
424
+ if (['read', 'list', 'inspect', 'diff'].includes(action))
425
+ return titled(action, shown(args.path, 80));
426
+ if (action === 'search') return titled('search', shown(args.query ?? args.pattern, 80));
427
+ if (action) return action;
428
+ }
429
+ return name;
430
+ }
431
+
315
432
  export function toolMetadata(
316
433
  name: string,
317
434
  input: unknown,
@@ -324,27 +441,35 @@ export function toolMetadata(
324
441
  } catch {}
325
442
  const action = String(args.action ?? '');
326
443
  const kind =
327
- name === 'salaam_command' || (name === 'salaam_station' && action === 'check')
328
- ? ['start', 'cancel', 'check'].includes(action)
329
- ? 'execute'
330
- : ['status', 'logs'].includes(action)
331
- ? 'read'
332
- : 'other'
333
- : ['salaam_files', 'salaam_station'].includes(name) && action === 'patch'
334
- ? args.content === null
335
- ? 'delete'
336
- : 'edit'
337
- : ['salaam_files', 'salaam_repository', 'salaam_station'].includes(name) &&
338
- ['search', 'list'].includes(action)
339
- ? 'search'
340
- : ['salaam_files', 'salaam_repository', 'salaam_station'].includes(name) &&
341
- ['read', 'inspect', ...(name === 'salaam_repository' ? ['diff'] : [])].includes(
342
- action,
343
- )
344
- ? 'read'
345
- : ['salaam_history', 'inspect_child', 'consume_child_result'].includes(name)
444
+ name === 'salaam_board'
445
+ ? 'read'
446
+ : name === 'salaam_local' && ['edit', 'command'].includes(action)
447
+ ? action === 'edit'
448
+ ? 'edit'
449
+ : 'execute'
450
+ : name === 'salaam_command' || (name === 'salaam_station' && action === 'check')
451
+ ? ['start', 'cancel', 'check'].includes(action)
452
+ ? 'execute'
453
+ : ['status', 'logs'].includes(action)
346
454
  ? 'read'
347
- : 'other';
455
+ : 'other'
456
+ : ['salaam_files', 'salaam_station'].includes(name) && action === 'patch'
457
+ ? args.content === null
458
+ ? 'delete'
459
+ : 'edit'
460
+ : ['salaam_files', 'salaam_repository', 'salaam_station'].includes(name) &&
461
+ ['search', 'list'].includes(action)
462
+ ? 'search'
463
+ : ['salaam_files', 'salaam_repository', 'salaam_station'].includes(name) &&
464
+ ['read', 'inspect', ...(name === 'salaam_repository' ? ['diff'] : [])].includes(
465
+ action,
466
+ )
467
+ ? 'read'
468
+ : ['salaam_history', 'inspect_child', 'consume_child_result'].includes(name)
469
+ ? 'read'
470
+ : name === 'salaam_operator_mcp'
471
+ ? operatorMcpKind(action, args.tool)
472
+ : 'other';
348
473
  const locations: { path: string; line?: number }[] = [];
349
474
  if (
350
475
  ['salaam_files', 'salaam_station'].includes(name) &&
@@ -361,3 +486,32 @@ export function toolMetadata(
361
486
  }
362
487
  return { kind, locations };
363
488
  }
489
+
490
+ /** A durable task's steps as the ACP `plan` session update: the whole list, replaced on every
491
+ * change (https://agentclientprotocol.com/protocol/agent-plan). ACP has only pending,
492
+ * in_progress and completed, so a step that stopped says so in its text instead. */
493
+ export function planUpdate(
494
+ task: { steps: { title: string; state: string }[] } | null | undefined,
495
+ ): {
496
+ sessionUpdate: 'plan';
497
+ entries: { content: string; priority: 'medium'; status: string }[];
498
+ } | null {
499
+ if (!task?.steps.length) return null;
500
+ return {
501
+ sessionUpdate: 'plan',
502
+ entries: task.steps.map((step) => {
503
+ const status =
504
+ step.state === 'completed'
505
+ ? 'completed'
506
+ : step.state === 'running' || step.state === 'awaiting_review'
507
+ ? 'in_progress'
508
+ : 'pending';
509
+ const stopped = ['cancelled', 'failed', 'interrupted'].includes(step.state);
510
+ return {
511
+ content: stopped ? `${step.title} (${step.state})` : step.title,
512
+ priority: 'medium' as const,
513
+ status,
514
+ };
515
+ }),
516
+ };
517
+ }