@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
package/src/cli.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import { loadDangerClassifierOptions } from './danger-classifier.js';
3
+ import { loadWebSearchConfig } from './web-search.js';
4
+ import { SANDBOX_LEVELS, type SandboxLevel } from './command-sandbox.js';
2
5
  import { acp } from './acp.js';
3
6
  import { homedir } from 'node:os';
4
7
  import { join, resolve, dirname } from 'node:path';
@@ -14,9 +17,13 @@ import {
14
17
  realpathSync,
15
18
  writeFileSync,
16
19
  fsyncSync,
20
+ existsSync,
17
21
  } from 'node:fs';
18
22
  import { ROOT, VERSION } from './config.js';
19
23
  import { cliHelp, hasHelpFlag } from './cli-help.js';
24
+ import { mcpCli, MCP_HELP } from './operator-mcp-cli.js';
25
+ import { operatorMcpPath, readOperatorMcpFile } from './operator-mcp.js';
26
+ import { defaultAgentPodConfig } from './agentpod-default-config.js';
20
27
  import { SalaamService } from './service.js';
21
28
  import { call } from './transport.js';
22
29
  import { attach, acquireControl, Outbox } from './attach.js';
@@ -62,6 +69,19 @@ async function main(): Promise<void> {
62
69
  console.log('salaam ' + VERSION);
63
70
  return;
64
71
  }
72
+ if (args[0] === '-c' || args[0] === '--continue') {
73
+ const { continueArgs } = await import('./session-pick.js');
74
+ args.splice(0, args.length, ...continueArgs(args)!);
75
+ }
76
+ if (args[0] === 'mcp') {
77
+ // Parsed before global help: a declared command's own flags follow "--".
78
+ const rest = args.slice(1);
79
+ const dash = rest.indexOf('--');
80
+ const flags = dash < 0 ? rest : rest.slice(0, dash);
81
+ if (!rest.length || flags.includes('--help') || flags.includes('-h')) console.log(MCP_HELP);
82
+ else console.log(mcpCli(rest));
83
+ return;
84
+ }
65
85
  if (
66
86
  (!args.length && !(process.stdin.isTTY && process.stdout.isTTY)) ||
67
87
  args[0] === 'help' ||
@@ -494,7 +514,21 @@ async function main(): Promise<void> {
494
514
  return;
495
515
  }
496
516
  if (group === 'host') {
497
- const { planHost, readHost, hostUnit, runHost } = await import('./host.js');
517
+ const {
518
+ planHost,
519
+ readHost,
520
+ hostUnit,
521
+ runHost,
522
+ hostReceipt,
523
+ hostToolsUser,
524
+ hostApprovalTtlMs,
525
+ hostAllowsUnsandboxed,
526
+ hostAllowsFullAccessSessions,
527
+ hostAllowsYoloSessions,
528
+ hostDangerClassifier,
529
+ hostCommandNetwork,
530
+ hostReadPaths,
531
+ } = await import('./host.js');
498
532
  if (verb?.startsWith('config-')) {
499
533
  const file = option('--manifest');
500
534
  if (!file) throw Error('Supply --manifest FILE');
@@ -526,6 +560,44 @@ async function main(): Promise<void> {
526
560
  );
527
561
  return;
528
562
  }
563
+ if (verb === 'bind') {
564
+ const { bindHost } = await import('./host-bind.js');
565
+ const required = (name: string) => {
566
+ const v = option(name);
567
+ if (!v) throw new Error('Supply ' + name);
568
+ return v;
569
+ };
570
+ console.log(
571
+ JSON.stringify(
572
+ await bindHost({
573
+ organization_id: required('--organization'),
574
+ node_id: required('--node'),
575
+ station_key: required('--station-key'),
576
+ workspace: required('--workspace'),
577
+ tools_user: required('--tools-user'),
578
+ files: required('--files'),
579
+ runtime_root: required('--runtime-root'),
580
+ runtime_image_digest: required('--runtime-image-digest'),
581
+ operator_group: option('--operator-group'),
582
+ dir: option('--dir'),
583
+ state: explicitState ? state : undefined,
584
+ provider: option('--provider'),
585
+ model: option('--model'),
586
+ mcp_config: option('--mcp-config'),
587
+ persona: option('--persona'),
588
+ managed_mcp_config: option('--managed-mcp-config'),
589
+ }),
590
+ ),
591
+ );
592
+ return;
593
+ }
594
+ if (verb === 'unit-start') {
595
+ const { startHostUnit } = await import('./host-start.js');
596
+ const unit = option('--unit');
597
+ if (!unit || !explicitState) throw new Error('Supply --unit UNIT and --state DIR');
598
+ console.log(JSON.stringify(await startHostUnit(unit, state)));
599
+ return;
600
+ }
529
601
  const file = option('--manifest');
530
602
  if (!file) throw new Error('Supply --manifest FILE');
531
603
  if (verb === 'inspect') {
@@ -585,9 +657,32 @@ async function main(): Promise<void> {
585
657
  : {}),
586
658
  ...(m.schema === 5 || m.schema === 6 ? { stationConfig: m.station_execution } : {}),
587
659
  ...(m.schema === 6 ? { agentpodWorkProgression: true } : {}),
660
+ ...(m.schema === 6 && m.operator_access
661
+ ? { operatorAccess: m.operator_access }
662
+ : {}),
663
+ ...(m.schema === 6 && m.operator_mcp
664
+ ? { operatorMcpConfig: m.operator_mcp.config }
665
+ : {}),
666
+ ...(m.schema === 6 && m.persona ? { personaConfig: m.persona } : {}),
588
667
  }),
589
668
  hostManifestDigest: digest(m),
669
+ hostReceipt: hostReceipt(m),
670
+ toolsUser: hostToolsUser(m),
590
671
  providerRoute: m.provider,
672
+ approvalTtlMs: hostApprovalTtlMs(m),
673
+ ...(hostAllowsUnsandboxed(m) ? { unsandboxedCommands: 'allow' as const } : {}),
674
+ // A host session may choose full access only when the reviewed manifest says so.
675
+ allowFullAccessSessions: hostAllowsFullAccessSessions(m),
676
+ allowYoloSessions: hostAllowsYoloSessions(m),
677
+ // The command reviewer only as the reviewed manifest names it (forbidden when absent).
678
+ ...(hostDangerClassifier(m).config
679
+ ? { dangerClassifierConfig: hostDangerClassifier(m).config }
680
+ : {}),
681
+ ...(hostDangerClassifier(m).pin
682
+ ? { dangerClassifierPin: hostDangerClassifier(m).pin }
683
+ : {}),
684
+ ...(hostCommandNetwork(m) ? { commandNetwork: hostCommandNetwork(m)! } : {}),
685
+ ...(hostReadPaths(m).length ? { localSandboxReadPaths: hostReadPaths(m) } : {}),
591
686
  });
592
687
  if (process.connected) process.once('disconnect', () => void service.close());
593
688
  for (const signal of ['SIGTERM', 'SIGINT'] as const)
@@ -614,6 +709,20 @@ async function main(): Promise<void> {
614
709
  });
615
710
  return;
616
711
  }
712
+ if (group === 'code' || group === 'here') {
713
+ const { parseCodeStart } = await import('./session-pick.js');
714
+ const start = parseCodeStart(args.slice(1));
715
+ const { tui } = await import('./tui.js');
716
+ await tui({
717
+ state,
718
+ start,
719
+ cwd: option('--cwd'),
720
+ takeover: args.includes('--takeover'),
721
+ codingRoot: option('--coding-root'),
722
+ codingSocket: option('--coding-socket'),
723
+ });
724
+ return;
725
+ }
617
726
  if (
618
727
  !group ||
619
728
  group === 'tui' ||
@@ -767,7 +876,7 @@ async function main(): Promise<void> {
767
876
  return;
768
877
  }
769
878
  if (group === 'acp') {
770
- await acp(state, { coding: args.includes('--coding') });
879
+ await acp(state, { coding: args.includes('--coding'), local: args.includes('--local') });
771
880
  return;
772
881
  }
773
882
  if (group === 'providers' && verb === 'list') {
@@ -1553,24 +1662,65 @@ async function main(): Promise<void> {
1553
1662
  codingSocket = option('--coding-socket'),
1554
1663
  harnessConfig = option('--harness-config'),
1555
1664
  a2aConfig = option('--a2a-config'),
1556
- agentpodConfig = option('--agentpod-config'),
1557
- a2aListenConfig = option('--a2a-listen-config');
1665
+ agentpodConfig =
1666
+ option('--agentpod-config') ?? defaultAgentPodConfig(process.env, existsSync),
1667
+ a2aListenConfig = option('--a2a-listen-config'),
1668
+ mcpConfig = resolve(option('--mcp-config') ?? operatorMcpPath(process.env)),
1669
+ toolsUser = option('--tools-user') ?? (process.env.SALAAM_TOOLS_USER || undefined),
1670
+ localSandbox = option('--local-sandbox') ?? (process.env.SALAAM_LOCAL_SANDBOX || undefined),
1671
+ unsandboxedCommands =
1672
+ option('--unsandboxed-commands') ?? (process.env.SALAAM_UNSANDBOXED_COMMANDS || undefined),
1673
+ localSandboxRead = (
1674
+ option('--local-sandbox-read') ??
1675
+ (process.env.SALAAM_LOCAL_SANDBOX_READ || '')
1676
+ )
1677
+ .split(',')
1678
+ .map((p) => p.trim())
1679
+ .filter(Boolean),
1680
+ webSearchConfig =
1681
+ option('--web-search-config') ?? (process.env.SALAAM_WEB_SEARCH_CONFIG || undefined),
1682
+ dangerClassifierConfig =
1683
+ option('--danger-classifier-config') ??
1684
+ (process.env.SALAAM_DANGER_CLASSIFIER_CONFIG || undefined);
1685
+ // Fail early on a bad command-reviewer config; its key is read from the named variable, never printed.
1686
+ if (dangerClassifierConfig)
1687
+ loadDangerClassifierOptions(
1688
+ JSON.parse(readFileSync(resolve(dangerClassifierConfig), 'utf8')),
1689
+ );
1690
+ // Fail early on a bad web search config; its keys are read, never printed.
1691
+ if (webSearchConfig) loadWebSearchConfig(resolve(webSearchConfig));
1692
+ if (localSandbox !== undefined && !(SANDBOX_LEVELS as readonly string[]).includes(localSandbox))
1693
+ throw Error('--local-sandbox is read-only, workspace-write or full-access');
1694
+ if (unsandboxedCommands !== undefined && unsandboxedCommands !== 'allow')
1695
+ throw Error('--unsandboxed-commands is allow or absent');
1558
1696
  if (foreground) {
1559
1697
  const service = await SalaamService.open({
1560
1698
  stateDir: state,
1561
1699
  providerRoute: route,
1562
1700
  agentpodConfig: agentpodConfig ? resolve(agentpodConfig) : undefined,
1701
+ operatorMcpConfig: mcpConfig,
1563
1702
  codingRoot: codingRoot ? resolve(codingRoot) : undefined,
1564
1703
  codingSocket: codingSocket ? resolve(codingSocket) : undefined,
1565
1704
  harnessConfig: harnessConfig ? resolve(harnessConfig) : undefined,
1566
1705
  a2aConfig: a2aConfig ? resolve(a2aConfig) : undefined,
1567
1706
  a2aListenConfig: a2aListenConfig ? resolve(a2aListenConfig) : undefined,
1707
+ toolsUser,
1708
+ ...(localSandbox ? { localSandbox: localSandbox as SandboxLevel } : {}),
1709
+ ...(unsandboxedCommands ? { unsandboxedCommands: 'allow' as const } : {}),
1710
+ ...(localSandboxRead.length ? { localSandboxReadPaths: localSandboxRead } : {}),
1711
+ ...(webSearchConfig ? { webSearchConfig: resolve(webSearchConfig) } : {}),
1712
+ ...(dangerClassifierConfig
1713
+ ? { dangerClassifierConfig: resolve(dangerClassifierConfig) }
1714
+ : {}),
1568
1715
  effectConnector: effectSocket
1569
1716
  ? new SocketEffectConnector(resolve(effectSocket), { opaque })
1570
1717
  : undefined,
1571
1718
  });
1572
1719
  const warning = service.supervisor.providerCapabilities().warning;
1573
1720
  if (warning) console.error(warning);
1721
+ // Only a live provider has a credential to share; the offline proof route stays quiet.
1722
+ if (service.toolIdentity.warning && route.provider !== 'salaam-proof')
1723
+ console.error('warning: ' + service.toolIdentity.warning);
1574
1724
  process.stdout.write(JSON.stringify({ socket, service: 'started' }) + '\n');
1575
1725
  for (const signal of ['SIGINT', 'SIGTERM'] as const)
1576
1726
  process.once(signal, () => {
@@ -1589,6 +1739,7 @@ async function main(): Promise<void> {
1589
1739
  coding_socket?: string | null;
1590
1740
  agentpod_config?: string | null;
1591
1741
  agentpod_pins_digest?: string | null;
1742
+ operator_mcp?: { config: string; digest: string } | null;
1592
1743
  }
1593
1744
  | undefined;
1594
1745
  try {
@@ -1613,6 +1764,15 @@ async function main(): Promise<void> {
1613
1764
  throw new Error(
1614
1765
  'Service is already running with another coding configuration; stop it first',
1615
1766
  );
1767
+ if ((health.operator_mcp?.config ?? null) !== mcpConfig)
1768
+ throw new Error('Service is already running with another MCP config; stop it first');
1769
+ if (
1770
+ health.operator_mcp &&
1771
+ health.operator_mcp.digest !== digest(readOperatorMcpFile(mcpConfig))
1772
+ )
1773
+ console.error(
1774
+ 'Declared MCP servers changed since the service started; run salaam service stop, then start, to apply them',
1775
+ );
1616
1776
  console.log('Salaam service already running');
1617
1777
  return;
1618
1778
  }
@@ -1640,6 +1800,16 @@ async function main(): Promise<void> {
1640
1800
  ...(a2aConfig ? ['--a2a-config', resolve(a2aConfig)] : []),
1641
1801
  ...(agentpodConfig ? ['--agentpod-config', resolve(agentpodConfig)] : []),
1642
1802
  ...(a2aListenConfig ? ['--a2a-listen-config', resolve(a2aListenConfig)] : []),
1803
+ '--mcp-config',
1804
+ mcpConfig,
1805
+ ...(toolsUser ? ['--tools-user', toolsUser] : []),
1806
+ ...(localSandbox ? ['--local-sandbox', localSandbox] : []),
1807
+ ...(unsandboxedCommands ? ['--unsandboxed-commands', 'allow'] : []),
1808
+ ...(localSandboxRead.length ? ['--local-sandbox-read', localSandboxRead.join(',')] : []),
1809
+ ...(webSearchConfig ? ['--web-search-config', resolve(webSearchConfig)] : []),
1810
+ ...(dangerClassifierConfig
1811
+ ? ['--danger-classifier-config', resolve(dangerClassifierConfig)]
1812
+ : []),
1643
1813
  ],
1644
1814
  { detached: true, stdio: ['ignore', fd, fd] },
1645
1815
  );