crewly 1.20.206 → 1.20.208

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 (151) hide show
  1. package/config/hooks/credential-guard/guard.sh +243 -0
  2. package/dist/backend/backend/src/constants.d.ts +63 -2
  3. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  4. package/dist/backend/backend/src/constants.js +67 -1
  5. package/dist/backend/backend/src/constants.js.map +1 -1
  6. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
  7. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +17 -1
  8. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
  9. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts +19 -5
  10. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
  11. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +71 -5
  12. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts.map +1 -1
  14. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js +4 -1
  15. package/dist/backend/backend/src/controllers/cloud/cloud.routes.js.map +1 -1
  16. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
  17. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +4 -0
  18. package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/session/session.controller.d.ts.map +1 -1
  20. package/dist/backend/backend/src/controllers/session/session.controller.js +2 -0
  21. package/dist/backend/backend/src/controllers/session/session.controller.js.map +1 -1
  22. package/dist/backend/backend/src/controllers/workspace/workspace.routes.d.ts.map +1 -1
  23. package/dist/backend/backend/src/controllers/workspace/workspace.routes.js +10 -2
  24. package/dist/backend/backend/src/controllers/workspace/workspace.routes.js.map +1 -1
  25. package/dist/backend/backend/src/index.d.ts +8 -0
  26. package/dist/backend/backend/src/index.d.ts.map +1 -1
  27. package/dist/backend/backend/src/index.js +54 -2
  28. package/dist/backend/backend/src/index.js.map +1 -1
  29. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  30. package/dist/backend/backend/src/services/agent/agent-registration.service.js +16 -2
  31. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  32. package/dist/backend/backend/src/services/agent/codex-daemon.utils.d.ts +16 -0
  33. package/dist/backend/backend/src/services/agent/codex-daemon.utils.d.ts.map +1 -1
  34. package/dist/backend/backend/src/services/agent/codex-daemon.utils.js +44 -0
  35. package/dist/backend/backend/src/services/agent/codex-daemon.utils.js.map +1 -1
  36. package/dist/backend/backend/src/services/agent/control-plane-guard.service.d.ts +14 -2
  37. package/dist/backend/backend/src/services/agent/control-plane-guard.service.d.ts.map +1 -1
  38. package/dist/backend/backend/src/services/agent/control-plane-guard.service.js +18 -3
  39. package/dist/backend/backend/src/services/agent/control-plane-guard.service.js.map +1 -1
  40. package/dist/backend/backend/src/services/agent/credential-guard.service.d.ts +153 -0
  41. package/dist/backend/backend/src/services/agent/credential-guard.service.d.ts.map +1 -0
  42. package/dist/backend/backend/src/services/agent/credential-guard.service.js +314 -0
  43. package/dist/backend/backend/src/services/agent/credential-guard.service.js.map +1 -0
  44. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +5 -0
  46. package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts +39 -0
  48. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js +132 -3
  50. package/dist/backend/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  51. package/dist/backend/backend/src/services/core/credential-files.d.ts +26 -0
  52. package/dist/backend/backend/src/services/core/credential-files.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/core/credential-files.js +60 -0
  54. package/dist/backend/backend/src/services/core/credential-files.js.map +1 -0
  55. package/dist/backend/backend/src/services/google/docs.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/google/docs.service.js +14 -1
  57. package/dist/backend/backend/src/services/google/docs.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/google/drive.service.d.ts.map +1 -1
  59. package/dist/backend/backend/src/services/google/drive.service.js +6 -4
  60. package/dist/backend/backend/src/services/google/drive.service.js.map +1 -1
  61. package/dist/backend/backend/src/services/google/google-api.client.d.ts +8 -3
  62. package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
  63. package/dist/backend/backend/src/services/google/google-api.client.js +5 -2
  64. package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
  65. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.d.ts +10 -0
  66. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.js +16 -0
  68. package/dist/backend/backend/src/services/messaging/input-blocked-retry.service.js.map +1 -1
  69. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +10 -0
  70. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  71. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js +21 -0
  72. package/dist/backend/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  73. package/dist/backend/backend/src/services/monitoring/credential-guard-alerts.d.ts +68 -0
  74. package/dist/backend/backend/src/services/monitoring/credential-guard-alerts.d.ts.map +1 -0
  75. package/dist/backend/backend/src/services/monitoring/credential-guard-alerts.js +95 -0
  76. package/dist/backend/backend/src/services/monitoring/credential-guard-alerts.js.map +1 -0
  77. package/dist/backend/backend/src/services/session/input-ledger.d.ts +115 -0
  78. package/dist/backend/backend/src/services/session/input-ledger.d.ts.map +1 -0
  79. package/dist/backend/backend/src/services/session/input-ledger.js +144 -0
  80. package/dist/backend/backend/src/services/session/input-ledger.js.map +1 -0
  81. package/dist/backend/backend/src/services/session/session-command-helper.d.ts +26 -0
  82. package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/session/session-command-helper.js +156 -37
  84. package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
  85. package/dist/backend/backend/src/services/session/tui-input-guard.d.ts +40 -0
  86. package/dist/backend/backend/src/services/session/tui-input-guard.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/session/tui-input-guard.js +95 -0
  88. package/dist/backend/backend/src/services/session/tui-input-guard.js.map +1 -1
  89. package/dist/backend/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
  90. package/dist/backend/backend/src/websocket/terminal.gateway.js +4 -1
  91. package/dist/backend/backend/src/websocket/terminal.gateway.js.map +1 -1
  92. package/dist/backend/build-info.json +2 -2
  93. package/dist/cli/backend/src/constants.d.ts +63 -2
  94. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  95. package/dist/cli/backend/src/constants.js +67 -1
  96. package/dist/cli/backend/src/constants.js.map +1 -1
  97. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  98. package/dist/cli/backend/src/services/agent/agent-registration.service.js +16 -2
  99. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  100. package/dist/cli/backend/src/services/agent/codex-daemon.utils.d.ts +16 -0
  101. package/dist/cli/backend/src/services/agent/codex-daemon.utils.d.ts.map +1 -1
  102. package/dist/cli/backend/src/services/agent/codex-daemon.utils.js +44 -0
  103. package/dist/cli/backend/src/services/agent/codex-daemon.utils.js.map +1 -1
  104. package/dist/cli/backend/src/services/agent/control-plane-guard.service.d.ts +14 -2
  105. package/dist/cli/backend/src/services/agent/control-plane-guard.service.d.ts.map +1 -1
  106. package/dist/cli/backend/src/services/agent/control-plane-guard.service.js +18 -3
  107. package/dist/cli/backend/src/services/agent/control-plane-guard.service.js.map +1 -1
  108. package/dist/cli/backend/src/services/agent/credential-guard.service.d.ts +153 -0
  109. package/dist/cli/backend/src/services/agent/credential-guard.service.d.ts.map +1 -0
  110. package/dist/cli/backend/src/services/agent/credential-guard.service.js +314 -0
  111. package/dist/cli/backend/src/services/agent/credential-guard.service.js.map +1 -0
  112. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
  113. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +5 -0
  114. package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
  115. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts +39 -0
  116. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.d.ts.map +1 -1
  117. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js +132 -3
  118. package/dist/cli/backend/src/services/agent/runtime-agent.service.abstract.js.map +1 -1
  119. package/dist/cli/backend/src/services/core/credential-files.d.ts +26 -0
  120. package/dist/cli/backend/src/services/core/credential-files.d.ts.map +1 -0
  121. package/dist/cli/backend/src/services/core/credential-files.js +60 -0
  122. package/dist/cli/backend/src/services/core/credential-files.js.map +1 -0
  123. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.d.ts +10 -0
  124. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.d.ts.map +1 -1
  125. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.js +16 -0
  126. package/dist/cli/backend/src/services/messaging/input-blocked-retry.service.js.map +1 -1
  127. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts +10 -0
  128. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.d.ts.map +1 -1
  129. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js +21 -0
  130. package/dist/cli/backend/src/services/messaging/sub-agent-message-queue.service.js.map +1 -1
  131. package/dist/cli/backend/src/services/session/input-ledger.d.ts +115 -0
  132. package/dist/cli/backend/src/services/session/input-ledger.d.ts.map +1 -0
  133. package/dist/cli/backend/src/services/session/input-ledger.js +144 -0
  134. package/dist/cli/backend/src/services/session/input-ledger.js.map +1 -0
  135. package/dist/cli/backend/src/services/session/session-command-helper.d.ts +26 -0
  136. package/dist/cli/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  137. package/dist/cli/backend/src/services/session/session-command-helper.js +156 -37
  138. package/dist/cli/backend/src/services/session/session-command-helper.js.map +1 -1
  139. package/dist/cli/backend/src/services/session/tui-input-guard.d.ts +40 -0
  140. package/dist/cli/backend/src/services/session/tui-input-guard.d.ts.map +1 -1
  141. package/dist/cli/backend/src/services/session/tui-input-guard.js +95 -0
  142. package/dist/cli/backend/src/services/session/tui-input-guard.js.map +1 -1
  143. package/dist/cli/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
  144. package/dist/cli/backend/src/websocket/terminal.gateway.js +4 -1
  145. package/dist/cli/backend/src/websocket/terminal.gateway.js.map +1 -1
  146. package/package.json +1 -1
  147. package/packages/crewly-agent/src/runtime/credential-guard.test.ts +48 -0
  148. package/packages/crewly-agent/src/runtime/credential-guard.ts +56 -0
  149. package/packages/crewly-agent/src/runtime/tool-registry.ts +15 -0
  150. package/packages/crewly-agent/src/runtime/web-search.tool.test.ts +18 -0
  151. package/packages/crewly-agent/src/runtime/web-search.tool.ts +36 -14
@@ -52,6 +52,7 @@ function completionResult(summary: string, evidence: CompletionEvidenceInput | u
52
52
  return { summary, ...(evidence && evidence.length > 0 ? { evidence } : {}) };
53
53
  }
54
54
  import { createWebSearchTool } from './web-search.tool.js';
55
+ import { checkCredentialAccess } from './credential-guard.js';
55
56
  import { createComputerTool } from './computer.tool.js';
56
57
  import { createDesktopTaskTool } from './desktop-task.tool.js';
57
58
  import { checkInteractiveLoginCommand } from './interactive-login-guard.js';
@@ -1455,6 +1456,9 @@ export function createTools(client: CrewlyApiClient, sessionName: string, projec
1455
1456
  }),
1456
1457
  execute: async ({ file_path, offset, limit }) => {
1457
1458
  const fp = expandPath(file_path as string, projectPath);
1459
+ // specs/2026-10-04-agent-credential-isolation.md: never Crewly's own credentials.
1460
+ const credentialRefusal = checkCredentialAccess('Read', { file_path: fp }, projectPath || process.cwd());
1461
+ if (credentialRefusal) return { success: false, error: credentialRefusal };
1458
1462
  try {
1459
1463
  // Check if this is an image file
1460
1464
  const ext = fp.split('.').pop()?.toLowerCase() || '';
@@ -1543,6 +1547,8 @@ export function createTools(client: CrewlyApiClient, sessionName: string, projec
1543
1547
  ignore: z.array(z.string()).optional().describe('Additional directory names to ignore (node_modules, .git, dist are always ignored)'),
1544
1548
  }),
1545
1549
  execute: async ({ pattern, path: searchPath, ignore }) => {
1550
+ const globRefusal = checkCredentialAccess('Glob', { pattern, path: searchPath ?? '' }, projectPath || process.cwd());
1551
+ if (globRefusal) return { success: false, error: globRefusal };
1546
1552
  try {
1547
1553
  const rootDir = expandPath((searchPath as string | undefined) || projectPath || process.cwd());
1548
1554
  const stat = await fsPromises.stat(rootDir);
@@ -1589,6 +1595,8 @@ export function createTools(client: CrewlyApiClient, sessionName: string, projec
1589
1595
  ignore: z.array(z.string()).optional().describe('Additional directory names to ignore'),
1590
1596
  }),
1591
1597
  execute: async ({ pattern: searchPattern, path: searchPath, file_pattern, context_lines, case_insensitive, max_matches, ignore }) => {
1598
+ const grepRefusal = checkCredentialAccess('Grep', { path: searchPath ?? '', glob: file_pattern ?? '' }, projectPath || process.cwd());
1599
+ if (grepRefusal) return { success: false, error: grepRefusal };
1592
1600
  try {
1593
1601
  const patternStr = searchPattern as string;
1594
1602
  const flags = (case_insensitive as boolean) ? 'gi' : 'g';
@@ -1857,6 +1865,13 @@ export function createTools(client: CrewlyApiClient, sessionName: string, projec
1857
1865
  execute: async ({ command, cwd, timeout }) => {
1858
1866
  const cmd = command as string;
1859
1867
 
1868
+ // Crewly's own credentials are not available to agents
1869
+ // (specs/2026-10-04-agent-credential-isolation.md).
1870
+ const credentialRefusal = checkCredentialAccess('Bash', { command: cmd }, (cwd as string | undefined) || projectPath || process.cwd());
1871
+ if (credentialRefusal) {
1872
+ return { success: false, exitCode: 126, stdout: '', stderr: credentialRefusal, error: credentialRefusal };
1873
+ }
1874
+
1860
1875
  // Interactive harness logins die with this one-shot call; the owner's
1861
1876
  // pasted code would go stale (2026-09-26). Point at harness-login.
1862
1877
  const loginRefusal = checkInteractiveLoginCommand(cmd);
@@ -51,6 +51,24 @@ describe('createWebSearchTool', () => {
51
51
  expect(result.error).toMatch(/Crewly Cloud is not connected/);
52
52
  });
53
53
 
54
+ it('by default goes through the local backend with the agent badge, never a Cloud token', async () => {
55
+ const fetchSpy = vi.fn().mockResolvedValue(
56
+ jsonResponse({ success: true, answer: 'via backend', sources: [] }),
57
+ );
58
+ const tool = createWebSearchTool({
59
+ fetchImpl: fetchSpy as unknown as typeof fetch,
60
+ env: { CREWLY_SESSION_NAME: 'ruth', CREWLY_AGENT_BADGE: 'badge-1', CREWLY_API_URL: 'http://127.0.0.1:9999/' },
61
+ });
62
+ const result = (await tool.execute({ query: 'q' })) as { success: boolean; result?: string };
63
+ expect(result.success).toBe(true);
64
+ const [url, init] = fetchSpy.mock.calls[0] as [string, RequestInit];
65
+ expect(url).toBe('http://127.0.0.1:9999/api/cloud/search');
66
+ const headers = init.headers as Record<string, string>;
67
+ expect(headers['X-Agent-Session']).toBe('ruth');
68
+ expect(headers['X-Agent-Badge']).toBe('badge-1');
69
+ expect(headers.Authorization).toBeUndefined();
70
+ });
71
+
54
72
  it('sends the query + bearer token to the configured endpoint', async () => {
55
73
  const fetchSpy = vi.fn().mockResolvedValue(
56
74
  jsonResponse({
@@ -15,9 +15,11 @@
15
15
 
16
16
  import { z } from 'zod';
17
17
  import type { ToolDefinition } from './types.js';
18
- import { loadCloudConfig, CloudNotLoggedInError, type CloudConfig } from './cloud-config.js';
18
+ import { CloudNotLoggedInError, type CloudConfig } from './cloud-config.js';
19
19
 
20
20
  const SEARCH_PATH = '/api/v1/search';
21
+ /** The local backend's proxy for Cloud search (it holds the Cloud token; agents never see it). */
22
+ const BACKEND_SEARCH_PATH = '/api/cloud/search';
21
23
  const DEFAULT_TIMEOUT_MS = 30_000;
22
24
 
23
25
  interface SearchSource {
@@ -35,13 +37,25 @@ interface SearchResponse {
35
37
 
36
38
  /** Injectable IO for tests. */
37
39
  export interface WebSearchDeps {
40
+ /**
41
+ * Direct mode: read the Cloud token and call Cloud. Only for tests and
42
+ * tools outside an agent. Without it the tool goes through the local
43
+ * backend (specs/2026-10-04-agent-credential-isolation.md): the Cloud
44
+ * credentials are sealed and are not available to agents.
45
+ */
38
46
  loadConfig?: () => Promise<CloudConfig>;
47
+ /** Backend base URL for the default mode (default CREWLY_API_URL or http://localhost:8787). */
48
+ apiBaseUrl?: string;
49
+ /** Environment for the agent's session + badge headers (default process.env). */
50
+ env?: NodeJS.ProcessEnv;
39
51
  fetchImpl?: typeof fetch;
40
52
  timeoutMs?: number;
41
53
  }
42
54
 
43
55
  export function createWebSearchTool(deps: WebSearchDeps = {}): ToolDefinition {
44
- const loadConfig = deps.loadConfig ?? (() => loadCloudConfig());
56
+ const loadConfig = deps.loadConfig;
57
+ const env = deps.env ?? process.env;
58
+ const apiBaseUrl = (deps.apiBaseUrl ?? env.CREWLY_API_URL ?? 'http://localhost:8787').replace(/\/$/, '');
45
59
  const fetchImpl = deps.fetchImpl ?? fetch;
46
60
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
47
61
 
@@ -61,26 +75,34 @@ export function createWebSearchTool(deps: WebSearchDeps = {}): ToolDefinition {
61
75
  execute: async (args) => {
62
76
  const { query, max_results } = args as { query: string; max_results?: number };
63
77
 
64
- let config: CloudConfig;
65
- try {
66
- config = await loadConfig();
67
- } catch (err) {
68
- if (err instanceof CloudNotLoggedInError) {
69
- return { success: false, error: err.message };
78
+ let url: string;
79
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
80
+ if (loadConfig) {
81
+ let config: CloudConfig;
82
+ try {
83
+ config = await loadConfig();
84
+ } catch (err) {
85
+ if (err instanceof CloudNotLoggedInError) {
86
+ return { success: false, error: err.message };
87
+ }
88
+ throw err;
70
89
  }
71
- throw err;
90
+ url = `${config.cloudUrl}${SEARCH_PATH}`;
91
+ headers.Authorization = `Bearer ${config.token}`;
92
+ } else {
93
+ // Through the backend, identified by this agent's session + badge.
94
+ url = `${apiBaseUrl}${BACKEND_SEARCH_PATH}`;
95
+ if (env.CREWLY_SESSION_NAME) headers['X-Agent-Session'] = env.CREWLY_SESSION_NAME;
96
+ if (env.CREWLY_AGENT_BADGE) headers['X-Agent-Badge'] = env.CREWLY_AGENT_BADGE;
72
97
  }
73
98
 
74
99
  const controller = new AbortController();
75
100
  const timer = setTimeout(() => controller.abort(), timeoutMs);
76
101
  let resp: Response;
77
102
  try {
78
- resp = await fetchImpl(`${config.cloudUrl}${SEARCH_PATH}`, {
103
+ resp = await fetchImpl(url, {
79
104
  method: 'POST',
80
- headers: {
81
- 'Content-Type': 'application/json',
82
- Authorization: `Bearer ${config.token}`,
83
- },
105
+ headers,
84
106
  body: JSON.stringify(
85
107
  max_results !== undefined ? { query, max_results } : { query },
86
108
  ),