@provos/ironcurtain 0.4.1 → 0.5.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.
Files changed (140) hide show
  1. package/README.md +219 -248
  2. package/dist/cli.js +10 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/config-command.js +144 -6
  5. package/dist/config/config-command.js.map +1 -1
  6. package/dist/config/constitution-user-base.md +2 -0
  7. package/dist/config/first-start.js +77 -12
  8. package/dist/config/first-start.js.map +1 -1
  9. package/dist/config/generated/compiled-policy.json +199 -72
  10. package/dist/config/generated/tool-annotations.json +1018 -36
  11. package/dist/config/mcp-servers.json +10 -0
  12. package/dist/config/paths.d.ts +31 -0
  13. package/dist/config/paths.js +52 -2
  14. package/dist/config/paths.js.map +1 -1
  15. package/dist/config/types.d.ts +8 -0
  16. package/dist/config/user-config.d.ts +21 -3
  17. package/dist/config/user-config.js +34 -3
  18. package/dist/config/user-config.js.map +1 -1
  19. package/dist/docker/adapters/claude-code.js +64 -8
  20. package/dist/docker/adapters/claude-code.js.map +1 -1
  21. package/dist/docker/agent-adapter.d.ts +18 -1
  22. package/dist/docker/docker-agent-session.d.ts +15 -8
  23. package/dist/docker/docker-agent-session.js +61 -88
  24. package/dist/docker/docker-agent-session.js.map +1 -1
  25. package/dist/docker/docker-infrastructure.d.ts +51 -0
  26. package/dist/docker/docker-infrastructure.js +261 -0
  27. package/dist/docker/docker-infrastructure.js.map +1 -0
  28. package/dist/docker/docker-manager.js +3 -0
  29. package/dist/docker/docker-manager.js.map +1 -1
  30. package/dist/docker/mitm-proxy.js +4 -0
  31. package/dist/docker/mitm-proxy.js.map +1 -1
  32. package/dist/docker/oauth-credentials.d.ts +81 -0
  33. package/dist/docker/oauth-credentials.js +178 -0
  34. package/dist/docker/oauth-credentials.js.map +1 -0
  35. package/dist/docker/orientation.js +1 -1
  36. package/dist/docker/orientation.js.map +1 -1
  37. package/dist/docker/provider-config.d.ts +3 -0
  38. package/dist/docker/provider-config.js +28 -0
  39. package/dist/docker/provider-config.js.map +1 -1
  40. package/dist/docker/pty-session.d.ts +27 -0
  41. package/dist/docker/pty-session.js +420 -0
  42. package/dist/docker/pty-session.js.map +1 -0
  43. package/dist/docker/pty-types.d.ts +24 -0
  44. package/dist/docker/pty-types.js +12 -0
  45. package/dist/docker/pty-types.js.map +1 -0
  46. package/dist/docker/types.d.ts +6 -0
  47. package/dist/escalation/escalation-watcher.d.ts +44 -0
  48. package/dist/escalation/escalation-watcher.js +92 -0
  49. package/dist/escalation/escalation-watcher.js.map +1 -0
  50. package/dist/escalation/listener-command.d.ts +10 -0
  51. package/dist/escalation/listener-command.js +407 -0
  52. package/dist/escalation/listener-command.js.map +1 -0
  53. package/dist/escalation/listener-state.d.ts +49 -0
  54. package/dist/escalation/listener-state.js +104 -0
  55. package/dist/escalation/listener-state.js.map +1 -0
  56. package/dist/escalation/session-registry.d.ts +17 -0
  57. package/dist/escalation/session-registry.js +98 -0
  58. package/dist/escalation/session-registry.js.map +1 -0
  59. package/dist/index.js +15 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/logger.js +2 -3
  62. package/dist/logger.js.map +1 -1
  63. package/dist/pipeline/annotate.js +35 -15
  64. package/dist/pipeline/annotate.js.map +1 -1
  65. package/dist/pipeline/constitution-customizer.d.ts +2 -1
  66. package/dist/pipeline/constitution-customizer.js +36 -3
  67. package/dist/pipeline/constitution-customizer.js.map +1 -1
  68. package/dist/pipeline/github-identity.d.ts +38 -0
  69. package/dist/pipeline/github-identity.js +64 -0
  70. package/dist/pipeline/github-identity.js.map +1 -0
  71. package/dist/pipeline/handwritten-scenarios.js +34 -0
  72. package/dist/pipeline/handwritten-scenarios.js.map +1 -1
  73. package/dist/sandbox/index.js +12 -3
  74. package/dist/sandbox/index.js.map +1 -1
  75. package/dist/servers/fetch-server.js +2 -2
  76. package/dist/session/base-transport.d.ts +19 -0
  77. package/dist/session/base-transport.js +36 -0
  78. package/dist/session/base-transport.js.map +1 -0
  79. package/dist/session/cli-transport.d.ts +3 -3
  80. package/dist/session/cli-transport.js +7 -4
  81. package/dist/session/cli-transport.js.map +1 -1
  82. package/dist/session/index.js +24 -78
  83. package/dist/session/index.js.map +1 -1
  84. package/dist/session/interaction-log.d.ts +13 -0
  85. package/dist/session/interaction-log.js +28 -0
  86. package/dist/session/interaction-log.js.map +1 -0
  87. package/dist/session/preflight.d.ts +3 -0
  88. package/dist/session/preflight.js +39 -18
  89. package/dist/session/preflight.js.map +1 -1
  90. package/dist/session/types.d.ts +2 -0
  91. package/dist/signal/bot-command.js +5 -0
  92. package/dist/signal/bot-command.js.map +1 -1
  93. package/dist/signal/format.d.ts +21 -1
  94. package/dist/signal/format.js +41 -3
  95. package/dist/signal/format.js.map +1 -1
  96. package/dist/signal/signal-bot-daemon.d.ts +53 -18
  97. package/dist/signal/signal-bot-daemon.js +344 -106
  98. package/dist/signal/signal-bot-daemon.js.map +1 -1
  99. package/dist/signal/signal-config.d.ts +4 -0
  100. package/dist/signal/signal-config.js +2 -0
  101. package/dist/signal/signal-config.js.map +1 -1
  102. package/dist/signal/signal-transport.d.ts +13 -7
  103. package/dist/signal/signal-transport.js +36 -12
  104. package/dist/signal/signal-transport.js.map +1 -1
  105. package/dist/trusted-process/audit-log.d.ts +6 -1
  106. package/dist/trusted-process/audit-log.js +22 -2
  107. package/dist/trusted-process/audit-log.js.map +1 -1
  108. package/dist/trusted-process/audit-redactor.d.ts +25 -0
  109. package/dist/trusted-process/audit-redactor.js +121 -0
  110. package/dist/trusted-process/audit-redactor.js.map +1 -0
  111. package/dist/trusted-process/escalation.d.ts +1 -1
  112. package/dist/trusted-process/escalation.js +6 -1
  113. package/dist/trusted-process/escalation.js.map +1 -1
  114. package/dist/trusted-process/index.d.ts +2 -1
  115. package/dist/trusted-process/index.js +24 -6
  116. package/dist/trusted-process/index.js.map +1 -1
  117. package/dist/trusted-process/mcp-client-manager.js +14 -2
  118. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  119. package/dist/trusted-process/mcp-error-utils.d.ts +11 -0
  120. package/dist/trusted-process/mcp-error-utils.js +50 -0
  121. package/dist/trusted-process/mcp-error-utils.js.map +1 -0
  122. package/dist/trusted-process/mcp-proxy-server.d.ts +4 -0
  123. package/dist/trusted-process/mcp-proxy-server.js +14 -4
  124. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  125. package/dist/trusted-process/sandbox-integration.d.ts +5 -0
  126. package/dist/trusted-process/sandbox-integration.js +1 -1
  127. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  128. package/dist/trusted-process/server-context.d.ts +21 -0
  129. package/dist/trusted-process/server-context.js +42 -0
  130. package/dist/trusted-process/server-context.js.map +1 -0
  131. package/dist/types/argument-roles.d.ts +5 -2
  132. package/dist/types/argument-roles.js +25 -2
  133. package/dist/types/argument-roles.js.map +1 -1
  134. package/docker/Dockerfile.claude-code +8 -3
  135. package/docker/entrypoint-claude-code.sh +64 -4
  136. package/package.json +16 -8
  137. package/src/config/constitution-user-base.md +2 -0
  138. package/src/config/generated/compiled-policy.json +199 -72
  139. package/src/config/generated/tool-annotations.json +1018 -36
  140. package/src/config/mcp-servers.json +10 -0
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { execFile as execFileCb } from 'node:child_process';
9
9
  import { promisify } from 'node:util';
10
+ import { detectAuthMethod, loadOAuthCredentials } from '../docker/oauth-credentials.js';
10
11
  const execFile = promisify(execFileCb);
11
12
  const DOCKER_TIMEOUT_MS = 5_000;
12
13
  const DEFAULT_DOCKER_AGENT = 'claude-code';
@@ -34,11 +35,30 @@ export async function checkDockerAvailable() {
34
35
  }
35
36
  }
36
37
  /**
37
- * Checks whether the API key required by the given agent is configured.
38
- * Currently all Docker agents (claude-code) require ANTHROPIC_API_KEY.
38
+ * Sources that skip macOS Keychain to keep preflight fast and non-interactive.
39
+ * Full Keychain detection happens in prepareDockerInfrastructure().
40
+ *
41
+ * Limitation: macOS users whose OAuth credentials exist only in the Keychain
42
+ * (no ~/.claude/.credentials.json and no API key) will fall back to builtin
43
+ * in auto-detect mode, or get a PreflightError with explicit --agent.
44
+ * In practice this is rare: Claude Code always writes the credentials file.
45
+ */
46
+ const preflightSources = {
47
+ loadFromFile: loadOAuthCredentials,
48
+ loadFromKeychain: () => null,
49
+ };
50
+ /**
51
+ * Checks whether credentials (OAuth or API key) are available for the given agent.
52
+ * Returns the auth kind if available, or null if no credentials found.
53
+ *
54
+ * Skips macOS Keychain to avoid interactive prompts during preflight.
55
+ * Full detection including Keychain happens in prepareDockerInfrastructure().
39
56
  */
40
- function hasApiKeyForAgent(_agentId, config) {
41
- return config.userConfig.anthropicApiKey !== '';
57
+ function detectCredentials(_agentId, config, sources) {
58
+ const auth = detectAuthMethod(config, sources ?? preflightSources);
59
+ if (auth.kind === 'none')
60
+ return null;
61
+ return auth.kind === 'oauth' ? 'oauth' : 'apikey';
42
62
  }
43
63
  /**
44
64
  * Resolves the session mode based on explicit --agent flag or auto-detection.
@@ -47,14 +67,14 @@ function hasApiKeyForAgent(_agentId, config) {
47
67
  * - Auto-detect: prefers Docker when available; silently falls back to builtin. Never throws.
48
68
  */
49
69
  export async function resolveSessionMode(options) {
50
- const { config, requestedAgent } = options;
70
+ const { config, requestedAgent, credentialSources } = options;
51
71
  const isDockerAvailable = options.isDockerAvailable ?? checkDockerAvailable;
52
72
  if (requestedAgent !== undefined) {
53
- return resolveExplicit(requestedAgent, config, isDockerAvailable);
73
+ return resolveExplicit(requestedAgent, config, isDockerAvailable, credentialSources);
54
74
  }
55
- return resolveAutoDetect(config, isDockerAvailable);
75
+ return resolveAutoDetect(config, isDockerAvailable, credentialSources);
56
76
  }
57
- async function resolveExplicit(agent, config, isDockerAvailable) {
77
+ async function resolveExplicit(agent, config, isDockerAvailable, credentialSources) {
58
78
  if (agent === 'builtin') {
59
79
  return {
60
80
  mode: { kind: 'builtin' },
@@ -65,16 +85,16 @@ async function resolveExplicit(agent, config, isDockerAvailable) {
65
85
  if (!dockerOk) {
66
86
  throw new PreflightError(`--agent ${agent} requires Docker, but the Docker daemon is not available. ` + 'Is Docker installed and running?');
67
87
  }
68
- if (!hasApiKeyForAgent(agent, config)) {
69
- throw new PreflightError(`--agent ${agent} requires ANTHROPIC_API_KEY to be set. ` +
70
- 'Set it in your environment or in ~/.ironcurtain/config.json.');
88
+ const credKind = detectCredentials(agent, config, credentialSources);
89
+ if (credKind === null) {
90
+ throw new PreflightError(`--agent ${agent} requires authentication. ` + 'Log in with `claude login` (OAuth) or set ANTHROPIC_API_KEY.');
71
91
  }
72
92
  return {
73
- mode: { kind: 'docker', agent },
74
- reason: 'Explicit --agent selection',
93
+ mode: { kind: 'docker', agent, authKind: credKind },
94
+ reason: `Explicit --agent selection (${credKind === 'oauth' ? 'OAuth' : 'API key'})`,
75
95
  };
76
96
  }
77
- async function resolveAutoDetect(config, isDockerAvailable) {
97
+ async function resolveAutoDetect(config, isDockerAvailable, credentialSources) {
78
98
  const dockerOk = await isDockerAvailable();
79
99
  if (!dockerOk) {
80
100
  return {
@@ -82,15 +102,16 @@ async function resolveAutoDetect(config, isDockerAvailable) {
82
102
  reason: 'Docker not available',
83
103
  };
84
104
  }
85
- if (!hasApiKeyForAgent(DEFAULT_DOCKER_AGENT, config)) {
105
+ const credKind = detectCredentials(DEFAULT_DOCKER_AGENT, config, credentialSources);
106
+ if (credKind === null) {
86
107
  return {
87
108
  mode: { kind: 'builtin' },
88
- reason: 'ANTHROPIC_API_KEY not set',
109
+ reason: 'No credentials (OAuth or API key)',
89
110
  };
90
111
  }
91
112
  return {
92
- mode: { kind: 'docker', agent: DEFAULT_DOCKER_AGENT },
93
- reason: 'Docker available, ANTHROPIC_API_KEY set',
113
+ mode: { kind: 'docker', agent: DEFAULT_DOCKER_AGENT, authKind: credKind },
114
+ reason: `Docker available, ${credKind === 'oauth' ? 'OAuth' : 'ANTHROPIC_API_KEY'} detected`,
94
115
  };
95
116
  }
96
117
  //# sourceMappingURL=preflight.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/session/preflight.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/session/preflight.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,7 @@ export type SessionMode = {
34
34
  } | {
35
35
  readonly kind: 'docker';
36
36
  readonly agent: AgentId;
37
+ readonly authKind?: 'oauth' | 'apikey';
37
38
  };
38
39
  /**
39
40
  * A single turn in the conversation. Captures what the user said,
@@ -139,6 +140,7 @@ export interface EscalationRequest {
139
140
  readonly serverName: string;
140
141
  readonly arguments: Record<string, unknown>;
141
142
  readonly reason: string;
143
+ readonly context?: Readonly<Record<string, string>>;
142
144
  }
143
145
  /**
144
146
  * Options for creating a session. Extends the base config with
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { loadConfig } from '../config/index.js';
10
10
  import { loadUserConfig } from '../config/user-config.js';
11
+ import { getDaemonLogPath } from '../config/paths.js';
11
12
  import { createDockerManager } from '../docker/docker-manager.js';
12
13
  import { resolveSessionMode } from '../session/preflight.js';
13
14
  import { SignalBotDaemon } from './signal-bot-daemon.js';
@@ -21,6 +22,10 @@ export async function runBot(options = {}) {
21
22
  process.stderr.write('Signal is not configured. Run: ironcurtain setup-signal\n');
22
23
  process.exit(1);
23
24
  }
25
+ // Set up daemon-wide logger before creating sessions.
26
+ // Individual session creations will call logger.setup() too,
27
+ // but it's now idempotent — subsequent calls are no-ops.
28
+ logger.setup({ logFilePath: getDaemonLogPath('signal-bot') });
24
29
  const config = loadConfig();
25
30
  // Resolve session mode (same logic as `ironcurtain start`)
26
31
  const preflight = await resolveSessionMode({
@@ -1 +1 @@
1
- {"version":3,"file":"bot-command.js","sourceRoot":"","sources":["../../src/signal/bot-command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAQvC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,UAAsB,EAAE;IACnD,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAErD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,2DAA2D;IAC3D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC;QACzC,MAAM;QACN,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,OAAO,CAAC,KAAiB,CAAC,CAAC,CAAC,SAAS;KACvE,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAEtF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,MAAM,EAAE,YAAY;QACpB,gBAAgB;QAChB,IAAI;KACL,CAAC,CAAC;IAEH,gDAAgD;IAChD,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B;QAC/D,YAAY,GAAG,IAAI,CAAC;QACpB,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAE7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC;IAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAEhD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"bot-command.js","sourceRoot":"","sources":["../../src/signal/bot-command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAQvC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,UAAsB,EAAE;IACnD,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAErD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,sDAAsD;IACtD,6DAA6D;IAC7D,yDAAyD;IACzD,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAE9D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,2DAA2D;IAC3D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC;QACzC,MAAM;QACN,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,OAAO,CAAC,KAAiB,CAAC,CAAC,CAAC,SAAS;KACvE,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAEtF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,MAAM,EAAE,YAAY;QACpB,gBAAgB;QAChB,IAAI;KACL,CAAC,CAAC;IAEH,gDAAgD;IAChD,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B;QAC/D,YAAY,GAAG,IAAI,CAAC;QACpB,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAE7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC;IAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAEhD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC"}
@@ -12,8 +12,11 @@ export declare const SIGNAL_MAX_MESSAGE_LENGTH = 2000;
12
12
  * Formats an escalation request as a styled text banner.
13
13
  * Includes tool name, arguments, reason, and instructions
14
14
  * for the user to reply "approve" or "deny".
15
+ *
16
+ * When sessionLabel is provided, includes `[#N]` in the header
17
+ * so the user knows which session is escalating.
15
18
  */
16
- export declare function formatEscalationBanner(request: EscalationRequest): string;
19
+ export declare function formatEscalationBanner(request: EscalationRequest, sessionLabel?: number): string;
17
20
  /**
18
21
  * Formats budget status for the /budget command.
19
22
  * Shows current turn consumption and session totals.
@@ -29,3 +32,20 @@ export declare function formatBudgetSummary(status: BudgetStatus): string;
29
32
  * then single newlines, then at the hard limit.
30
33
  */
31
34
  export declare function splitMessage(text: string, maxLength: number): string[];
35
+ /**
36
+ * Information about a managed session for display purposes.
37
+ */
38
+ export interface SessionListEntry {
39
+ readonly label: number;
40
+ readonly turnCount: number;
41
+ readonly budgetPercent: number;
42
+ }
43
+ /**
44
+ * Formats the /sessions command output.
45
+ * Marks the current session with `>`.
46
+ */
47
+ export declare function formatSessionList(sessions: SessionListEntry[], currentLabel: number | null): string;
48
+ /**
49
+ * Prefixes a message with `[#N]` when multiple sessions are active.
50
+ */
51
+ export declare function prefixWithLabel(text: string, label: number, sessionCount: number): string;
@@ -11,15 +11,31 @@ export const SIGNAL_MAX_MESSAGE_LENGTH = 2000;
11
11
  * Formats an escalation request as a styled text banner.
12
12
  * Includes tool name, arguments, reason, and instructions
13
13
  * for the user to reply "approve" or "deny".
14
+ *
15
+ * When sessionLabel is provided, includes `[#N]` in the header
16
+ * so the user knows which session is escalating.
14
17
  */
15
- export function formatEscalationBanner(request) {
18
+ export function formatEscalationBanner(request, sessionLabel) {
16
19
  const separator = '\u2501'.repeat(30); // box drawing heavy horizontal
17
- const header = '**ESCALATION: Human approval required**';
20
+ const labelTag = sessionLabel != null ? ` [#${sessionLabel}]` : '';
21
+ const header = `**ESCALATION${labelTag}: Human approval required**`;
18
22
  const toolLine = `Tool: \`${request.serverName}/${request.toolName}\``;
19
23
  const argsLine = `Arguments: \`${JSON.stringify(request.arguments)}\``;
24
+ const contextLines = request.context ? Object.entries(request.context).map(([k, v]) => `${k}: \`${v}\``) : [];
20
25
  const reasonLine = `Reason: *${request.reason}*`;
21
26
  const instructions = '**Reply "approve" or "deny"**';
22
- return [separator, header, separator, toolLine, argsLine, reasonLine, separator, instructions, separator].join('\n');
27
+ return [
28
+ separator,
29
+ header,
30
+ separator,
31
+ toolLine,
32
+ argsLine,
33
+ ...contextLines,
34
+ reasonLine,
35
+ separator,
36
+ instructions,
37
+ separator,
38
+ ].join('\n');
23
39
  }
24
40
  /**
25
41
  * Formats budget status for the /budget command.
@@ -90,6 +106,28 @@ export function splitMessage(text, maxLength) {
90
106
  }
91
107
  return chunks;
92
108
  }
109
+ /**
110
+ * Formats the /sessions command output.
111
+ * Marks the current session with `>`.
112
+ */
113
+ export function formatSessionList(sessions, currentLabel) {
114
+ if (sessions.length === 0)
115
+ return 'No active sessions.';
116
+ const lines = ['**Active sessions:**'];
117
+ for (const s of sessions) {
118
+ const marker = s.label === currentLabel ? '>' : ' ';
119
+ lines.push(`${marker} #${s.label} turns: ${s.turnCount} budget: ${s.budgetPercent}%`);
120
+ }
121
+ return lines.join('\n');
122
+ }
123
+ /**
124
+ * Prefixes a message with `[#N]` when multiple sessions are active.
125
+ */
126
+ export function prefixWithLabel(text, label, sessionCount) {
127
+ if (sessionCount <= 1)
128
+ return text;
129
+ return `[#${label}] ${text}`;
130
+ }
93
131
  // --- Private helpers ---
94
132
  function formatBudgetLine(label, current, limit, format) {
95
133
  const currentStr = format(current);
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/signal/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,kDAAkD;AAClD,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA0B;IAC/D,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,+BAA+B;IACtE,MAAM,MAAM,GAAG,yCAAyC,CAAC;IACzD,MAAM,QAAQ,GAAG,WAAW,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC;IACvE,MAAM,QAAQ,GAAG,gBAAgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC;IACvE,MAAM,UAAU,GAAG,YAAY,OAAO,CAAC,MAAM,GAAG,CAAC;IACjD,MAAM,YAAY,GAAG,+BAA+B,CAAC;IAErD,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAoB;IACtD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IACtC,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;IAErD,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IAC/G,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACjF,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClH,KAAK,CAAC,IAAI,CACR,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAC9G,CAAC;IAEF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,aAAa,UAAU,CAAC,WAAW,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,YAAY,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/C,KAAK,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACtE,KAAK,CAAC,IAAI,CAAC,iBAAiB,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEtE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAoB;IACtD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IAC9B,MAAM,UAAU,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,WAAW,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9G,OAAO,CACL,YAAY,UAAU,EAAE;QACxB,GAAG,UAAU,CAAC,SAAS,QAAQ;QAC/B,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG;QAC5C,OAAO,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAChD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAiB;IAC1D,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,OAAO,SAAS,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QACpC,iDAAiD;QACjD,IAAI,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACxD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,0BAA0B;YAC1B,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,2BAA2B;YAC3B,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;IACxD,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,0BAA0B;AAE1B,SAAS,gBAAgB,CAAC,KAAa,EAAE,OAAe,EAAE,KAAoB,EAAE,MAA6B;IAC3G,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,KAAK,KAAK,UAAU,aAAa,CAAC;IAClE,MAAM,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,KAAK,KAAK,KAAK,UAAU,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;AAClE,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/signal/format.ts"],"names":[],"mappings":"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"}
@@ -5,9 +5,9 @@
5
5
  * - WebSocket connection to signal-cli-rest-api with reconnect + exponential backoff
6
6
  * - Message routing: authorization, identity verification, escalation replies,
7
7
  * control commands, and session forwarding
8
- * - Session lifecycle: creates transport+session on demand, cleans up on end
8
+ * - Multi-session lifecycle: creates transport+session on demand, cleans up on end
9
9
  * - Identity verification with TTL cache (5 min), fail-closed
10
- * - Escalation state with race prevention
10
+ * - Escalation state with race prevention (per-session)
11
11
  * - Message sending via POST /v2/send with text_mode: "styled"
12
12
  */
13
13
  import { type ResolvedSignalConfig } from './signal-config.js';
@@ -57,23 +57,29 @@ export declare class SignalBotDaemon {
57
57
  private reconnectAttempts;
58
58
  private static readonly MAX_RECONNECT_DELAY_MS;
59
59
  private static readonly BASE_RECONNECT_DELAY_MS;
60
- private activeSession;
61
- private activeTransport;
62
- private messageInFlight;
63
- private pendingEscalationId;
64
- private escalationResolving;
60
+ private sessions;
61
+ private currentLabel;
62
+ private nextLabel;
63
+ private readonly maxConcurrentSessions;
65
64
  private identityLocked;
66
65
  private lastIdentityCheckMs;
67
66
  private recentTimestamps;
67
+ private sessionOpInProgress;
68
68
  private exitResolve;
69
69
  constructor(options: SignalBotDaemonOptions);
70
+ /**
71
+ * Schedules a session-mutating operation (/new, /quit) so that
72
+ * concurrent routeToSession() calls wait for it to complete.
73
+ * Operations are chained to prevent interleaving.
74
+ */
75
+ private scheduleSessionOp;
70
76
  /**
71
77
  * Starts the daemon. Returns a promise that resolves when
72
78
  * shutdown() is called (e.g., SIGTERM/SIGINT).
73
79
  */
74
80
  start(): Promise<void>;
75
81
  /**
76
- * Initiates graceful shutdown. Ends the active session,
82
+ * Initiates graceful shutdown. Ends all active sessions,
77
83
  * closes the WebSocket, and unblocks start().
78
84
  */
79
85
  shutdown(): Promise<void>;
@@ -87,36 +93,54 @@ export declare class SignalBotDaemon {
87
93
  private scheduleReconnect;
88
94
  private handleIncomingMessage;
89
95
  /**
90
- * Routes a user message to the active session. Creates a new
96
+ * Routes a user message to the current session. Creates a new
91
97
  * session if none exists. Handles BudgetExhaustedError by ending
92
98
  * the exhausted session and notifying the user.
99
+ *
100
+ * Supports one-shot `#N` prefix routing: `#2 do something` routes
101
+ * "do something" to session #2 without changing currentLabel.
93
102
  */
94
103
  private routeToSession;
104
+ /**
105
+ * Forwards a message to a specific managed session. Handles
106
+ * in-flight guards, BudgetExhaustedError, and response formatting.
107
+ */
108
+ private forwardToSession;
95
109
  /**
96
110
  * Creates a new session with a fresh SignalSessionTransport.
97
111
  * Follows the same pattern as index.ts: create transport,
98
112
  * wire callbacks, create session, start transport.
113
+ * Returns the label of the new session.
99
114
  */
100
115
  private startNewSession;
101
116
  /**
102
- * Ends the current session and cleans up. Awaits session.close()
103
- * to ensure resources are released (sandbox, MCP connections, etc.).
104
- * Guarded against re-entrance.
117
+ * Ends a specific session by label and cleans up.
118
+ * If it was the current session, auto-switches to the most recent remaining.
119
+ */
120
+ endSession(label: number): Promise<void>;
121
+ /**
122
+ * Convenience method: ends the current session (if any).
105
123
  */
106
124
  endCurrentSession(): Promise<void>;
125
+ /**
126
+ * Auto-switches currentLabel to the highest remaining label (most recent),
127
+ * or null if no sessions remain.
128
+ */
129
+ private autoSwitchCurrent;
107
130
  /**
108
131
  * Checks if the message is an escalation reply.
109
132
  * Accepts: approve, deny, /approve, /deny (case-insensitive).
133
+ * Optionally with a session label: "approve #2", "/approve 2".
110
134
  *
111
- * Race condition prevention: escalationResolving flag prevents
112
- * concurrent replies. pendingEscalationId is cleared in .finally()
113
- * after async resolution completes, not before.
135
+ * Routes to the session with a pending escalation. If multiple
136
+ * sessions have pending escalations and no label is specified,
137
+ * asks for disambiguation.
114
138
  */
115
139
  private handleEscalationReply;
116
140
  /** Called by SignalSessionTransport when the session surfaces an escalation. */
117
- setPendingEscalation(escalationId: string): void;
141
+ setPendingEscalation(label: number, escalationId: string): void;
118
142
  /** Called by SignalSessionTransport when an escalation expires. */
119
- clearPendingEscalation(): void;
143
+ clearPendingEscalation(label: number): void;
120
144
  private handleControlCommand;
121
145
  /**
122
146
  * Checks whether the sender's Signal identity key has changed.
@@ -137,6 +161,17 @@ export declare class SignalBotDaemon {
137
161
  private postMessage;
138
162
  }
139
163
  export declare function parseSignalEnvelope(data: string): SignalEnvelope | null;
140
- /** Normalize a phone number by stripping spaces. */
141
164
  export declare function normalizePhoneNumber(number: string): string;
142
165
  export declare function isAuthorizedSender(envelope: SignalEnvelope, recipientNumber: string): boolean;
166
+ export interface HashPrefixResult {
167
+ /** The session label from the #N prefix, or null if no prefix was found. */
168
+ targetLabel: number | null;
169
+ /** The message text with the #N prefix stripped (or the original text if no prefix). */
170
+ messageText: string;
171
+ }
172
+ /**
173
+ * Parses an optional `#N` prefix from a message for one-shot session routing.
174
+ * Pattern: `#N ` at the start, where N is one or more digits, followed by a space.
175
+ * E.g., `#2 do something` -> { targetLabel: 2, messageText: 'do something' }
176
+ */
177
+ export declare function parseHashPrefix(text: string): HashPrefixResult;