deepline 0.1.12 → 0.1.19

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 (80) hide show
  1. package/README.md +14 -6
  2. package/dist/cli/index.js +1298 -711
  3. package/dist/cli/index.mjs +1294 -707
  4. package/dist/index.d.mts +199 -23
  5. package/dist/index.d.ts +199 -23
  6. package/dist/index.js +219 -13
  7. package/dist/index.mjs +219 -13
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +68 -12
  9. package/dist/repo/apps/play-runner-workers/src/entry.ts +241 -51
  10. package/dist/repo/sdk/src/client.ts +237 -0
  11. package/dist/repo/sdk/src/config.ts +125 -8
  12. package/dist/repo/sdk/src/http.ts +10 -2
  13. package/dist/repo/sdk/src/play.ts +19 -36
  14. package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
  15. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  16. package/dist/repo/sdk/src/types.ts +25 -0
  17. package/dist/repo/sdk/src/version.ts +2 -2
  18. package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
  19. package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
  20. package/dist/repo/shared_libs/plays/dataset.ts +28 -0
  21. package/package.json +5 -4
  22. package/dist/cli/index.js.map +0 -1
  23. package/dist/cli/index.mjs.map +0 -1
  24. package/dist/index.js.map +0 -1
  25. package/dist/index.mjs.map +0 -1
  26. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  27. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  28. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  29. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  30. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  31. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  32. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  33. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  34. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  35. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  36. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
  37. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  38. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
  39. package/dist/repo/sdk/src/cli/index.ts +0 -148
  40. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  41. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  42. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  43. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  44. package/dist/repo/sdk/src/compat.ts +0 -77
  45. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  46. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  47. package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
  48. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  49. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
  50. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  51. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  52. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  53. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  54. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  55. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  56. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  57. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  58. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  59. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  60. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  61. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  62. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  63. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  64. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  65. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  66. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  67. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  68. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  69. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  70. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  71. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  72. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  73. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  74. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  75. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  76. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  77. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  78. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
  79. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  80. package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
@@ -56,6 +56,8 @@ import type {
56
56
  StartPlayRunRequest,
57
57
  DeletePlayResult,
58
58
  ToolDefinition,
59
+ ToolSearchOptions,
60
+ ToolSearchResult,
59
61
  ToolMetadata,
60
62
  CustomerDbQueryResult,
61
63
  } from './types.js';
@@ -63,6 +65,11 @@ import type { PlayStagedFileRef } from './plays/local-file-discovery.js';
63
65
  import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-manifest.js';
64
66
 
65
67
  const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
68
+ const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
69
+
70
+ type ExecuteToolRawOptions = {
71
+ includeToolMetadata?: boolean;
72
+ };
66
73
 
67
74
  export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
68
75
  status: string;
@@ -75,6 +82,45 @@ export type ToolExecution<TData = unknown, TMeta = Record<string, unknown>> = {
75
82
  [key: string]: unknown;
76
83
  };
77
84
 
85
+ export type RunsListOptions = {
86
+ play: string;
87
+ status?: string;
88
+ };
89
+
90
+ export type RunsTailOptions = {
91
+ cursor?: string | number;
92
+ afterLogIndex?: number;
93
+ waitMs?: number;
94
+ terminalOnly?: boolean;
95
+ compact?: boolean;
96
+ };
97
+
98
+ export type RunsLogsOptions = {
99
+ limit?: number;
100
+ };
101
+
102
+ export type RunsLogsResult = {
103
+ runId: string;
104
+ totalCount: number;
105
+ returnedCount: number;
106
+ firstSequence: number | null;
107
+ lastSequence: number | null;
108
+ truncated: boolean;
109
+ hasMore: boolean;
110
+ entries: string[];
111
+ };
112
+
113
+ export type RunsNamespace = {
114
+ get: (runId: string) => Promise<PlayStatus>;
115
+ list: (options: RunsListOptions) => Promise<PlayRunListItem[]>;
116
+ tail: (runId: string, options?: RunsTailOptions) => Promise<PlayStatus>;
117
+ logs: (runId: string, options?: RunsLogsOptions) => Promise<RunsLogsResult>;
118
+ stop: (
119
+ runId: string,
120
+ options?: { reason?: string },
121
+ ) => Promise<StopPlayRunResult>;
122
+ };
123
+
78
124
  function isRecord(value: unknown): value is Record<string, unknown> {
79
125
  return Boolean(value && typeof value === 'object' && !Array.isArray(value));
80
126
  }
@@ -140,6 +186,7 @@ function mapLegacyTemporalStatus(status: string): PlayStatus['status'] {
140
186
  export class DeeplineClient {
141
187
  private readonly http: HttpClient;
142
188
  private readonly config: ResolvedConfig;
189
+ readonly runs: RunsNamespace;
143
190
 
144
191
  /**
145
192
  * @param options - Optional overrides for API key, base URL, timeout, and retries.
@@ -148,6 +195,13 @@ export class DeeplineClient {
148
195
  constructor(options?: DeeplineClientOptions) {
149
196
  this.config = resolveConfig(options);
150
197
  this.http = new HttpClient(this.config);
198
+ this.runs = {
199
+ get: (runId) => this.getRunStatus(runId),
200
+ list: (options) => this.listRuns(options),
201
+ tail: (runId, options) => this.tailRun(runId, options),
202
+ logs: (runId, options) => this.getRunLogs(runId, options),
203
+ stop: (runId, options) => this.stopRun(runId, options),
204
+ };
151
205
  }
152
206
 
153
207
  /** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
@@ -273,6 +327,34 @@ export class DeeplineClient {
273
327
  return res.tools;
274
328
  }
275
329
 
330
+ /**
331
+ * Search available tools using Deepline's ranked backend search.
332
+ *
333
+ * This is the same discovery surface used by the legacy CLI: it ranks across
334
+ * tool metadata, categories, agent guidance, and input schema fields.
335
+ */
336
+ async searchTools(
337
+ options: ToolSearchOptions = {},
338
+ ): Promise<ToolSearchResult> {
339
+ const params = new URLSearchParams();
340
+ const query = options.query?.trim() ?? '';
341
+ params.set('q', query);
342
+ params.set(
343
+ 'include_search_debug',
344
+ options.includeSearchDebug ? 'true' : 'false',
345
+ );
346
+ params.set('search_mode', options.searchMode ?? 'v2');
347
+ if (options.categories?.trim()) {
348
+ params.set('categories', options.categories.trim());
349
+ }
350
+ if (options.searchTerms?.trim()) {
351
+ params.set('search_terms', options.searchTerms.trim());
352
+ }
353
+ return this.http.get<ToolSearchResult>(
354
+ `/api/v2/integrations/list?${params.toString()}`,
355
+ );
356
+ }
357
+
276
358
  /**
277
359
  * Get detailed metadata for a single tool.
278
360
  *
@@ -312,13 +394,26 @@ export class DeeplineClient {
312
394
  async executeTool<TData = unknown, TMeta = Record<string, unknown>>(
313
395
  toolId: string,
314
396
  input: Record<string, unknown>,
397
+ options?: ExecuteToolRawOptions,
315
398
  ): Promise<ToolExecution<TData, TMeta>> {
399
+ const headers = options?.includeToolMetadata
400
+ ? { [INCLUDE_TOOL_METADATA_HEADER]: 'true' }
401
+ : undefined;
316
402
  return this.http.post<ToolExecution<TData, TMeta>>(
317
403
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
318
404
  { payload: input },
405
+ headers,
319
406
  );
320
407
  }
321
408
 
409
+ async executeToolRaw<TData = unknown, TMeta = Record<string, unknown>>(
410
+ toolId: string,
411
+ input: Record<string, unknown>,
412
+ options?: ExecuteToolRawOptions,
413
+ ): Promise<ToolExecution<TData, TMeta>> {
414
+ return this.executeTool<TData, TMeta>(toolId, input, options);
415
+ }
416
+
322
417
  async queryCustomerDb(input: {
323
418
  sql: string;
324
419
  maxRows?: number;
@@ -374,6 +469,7 @@ export class DeeplineClient {
374
469
  ? { artifactStorageKey: request.artifactStorageKey }
375
470
  : {}),
376
471
  ...(request.sourceCode ? { sourceCode: request.sourceCode } : {}),
472
+ ...(request.sourceFiles ? { sourceFiles: request.sourceFiles } : {}),
377
473
  ...('staticPipeline' in request
378
474
  ? { staticPipeline: request.staticPipeline }
379
475
  : {}),
@@ -418,6 +514,7 @@ export class DeeplineClient {
418
514
  ? { artifactStorageKey: request.artifactStorageKey }
419
515
  : {}),
420
516
  ...(request.sourceCode ? { sourceCode: request.sourceCode } : {}),
517
+ ...(request.sourceFiles ? { sourceFiles: request.sourceFiles } : {}),
421
518
  ...('staticPipeline' in request
422
519
  ? { staticPipeline: request.staticPipeline }
423
520
  : {}),
@@ -466,6 +563,7 @@ export class DeeplineClient {
466
563
  async registerPlayArtifact(input: {
467
564
  name: string;
468
565
  sourceCode: string;
566
+ sourceFiles?: Record<string, string>;
469
567
  artifact: Record<string, unknown>;
470
568
  compilerManifest?: PlayCompilerManifest;
471
569
  publish?: boolean;
@@ -490,6 +588,7 @@ export class DeeplineClient {
490
588
  (await this.compilePlayManifest({
491
589
  name: input.name,
492
590
  sourceCode: input.sourceCode,
591
+ sourceFiles: input.sourceFiles,
493
592
  artifact: input.artifact,
494
593
  }));
495
594
  return this.http.post('/api/v2/plays/artifacts', {
@@ -502,6 +601,7 @@ export class DeeplineClient {
502
601
  artifacts: Array<{
503
602
  name: string;
504
603
  sourceCode: string;
604
+ sourceFiles?: Record<string, string>;
505
605
  artifact: Record<string, unknown>;
506
606
  compilerManifest?: PlayCompilerManifest;
507
607
  publish?: boolean;
@@ -533,6 +633,7 @@ export class DeeplineClient {
533
633
  (await this.compilePlayManifest({
534
634
  name: artifact.name,
535
635
  sourceCode: artifact.sourceCode,
636
+ sourceFiles: artifact.sourceFiles,
536
637
  artifact: artifact.artifact,
537
638
  })),
538
639
  })),
@@ -545,6 +646,7 @@ export class DeeplineClient {
545
646
  async compilePlayManifest(input: {
546
647
  name: string;
547
648
  sourceCode: string;
649
+ sourceFiles?: Record<string, string>;
548
650
  artifact: Record<string, unknown>;
549
651
  importedPlayDependencies?: PlayCompilerManifest[];
550
652
  }): Promise<PlayCompilerManifest> {
@@ -564,6 +666,7 @@ export class DeeplineClient {
564
666
  async checkPlayArtifact(input: {
565
667
  name?: string;
566
668
  sourceCode: string;
669
+ sourceFiles?: Record<string, string>;
567
670
  artifact: Record<string, unknown>;
568
671
  }): Promise<PlayCheckResult> {
569
672
  return this.http.post('/api/v2/plays/check', input);
@@ -572,6 +675,7 @@ export class DeeplineClient {
572
675
  async startPlayRunFromBundle(input: {
573
676
  name: string;
574
677
  sourceCode: string;
678
+ sourceFiles?: Record<string, string>;
575
679
  artifact: Record<string, unknown>;
576
680
  compilerManifest?: PlayCompilerManifest;
577
681
  input?: Record<string, unknown>;
@@ -584,11 +688,13 @@ export class DeeplineClient {
584
688
  (await this.compilePlayManifest({
585
689
  name: input.name,
586
690
  sourceCode: input.sourceCode,
691
+ sourceFiles: input.sourceFiles,
587
692
  artifact: input.artifact,
588
693
  }));
589
694
  const registeredArtifact = await this.registerPlayArtifact({
590
695
  name: input.name,
591
696
  sourceCode: input.sourceCode,
697
+ sourceFiles: input.sourceFiles,
592
698
  artifact: input.artifact,
593
699
  compilerManifest,
594
700
  publish: false,
@@ -642,6 +748,7 @@ export class DeeplineClient {
642
748
  name?: string,
643
749
  options?: {
644
750
  sourceCode?: string;
751
+ sourceFiles?: Record<string, string>;
645
752
  artifact?: Record<string, unknown>;
646
753
  compilerManifest?: PlayCompilerManifest;
647
754
  input?: Record<string, unknown>;
@@ -669,12 +776,14 @@ export class DeeplineClient {
669
776
  (await this.compilePlayManifest({
670
777
  name,
671
778
  sourceCode,
779
+ sourceFiles: options?.sourceFiles,
672
780
  artifact,
673
781
  }));
674
782
 
675
783
  const registeredArtifact = await this.registerPlayArtifact({
676
784
  name,
677
785
  sourceCode,
786
+ sourceFiles: options?.sourceFiles,
678
787
  artifact,
679
788
  compilerManifest,
680
789
  publish: false,
@@ -916,6 +1025,134 @@ export class DeeplineClient {
916
1025
  return response.runs ?? [];
917
1026
  }
918
1027
 
1028
+ /**
1029
+ * Get a run by id using the public runs resource model.
1030
+ *
1031
+ * This is the SDK equivalent of:
1032
+ *
1033
+ * ```bash
1034
+ * deepline runs get <run-id> --json
1035
+ * ```
1036
+ */
1037
+ async getRunStatus(runId: string): Promise<PlayStatus> {
1038
+ const response = await this.http.get<Record<string, unknown>>(
1039
+ `/api/v2/runs/${encodeURIComponent(runId)}`,
1040
+ );
1041
+ return normalizePlayStatus(response);
1042
+ }
1043
+
1044
+ /**
1045
+ * List play runs using the public runs resource model.
1046
+ *
1047
+ * This is the SDK equivalent of:
1048
+ *
1049
+ * ```bash
1050
+ * deepline runs list --play <play-name> --status failed --json
1051
+ * ```
1052
+ */
1053
+ async listRuns(options: RunsListOptions): Promise<PlayRunListItem[]> {
1054
+ const playName = options.play.trim();
1055
+ if (!playName) {
1056
+ throw new Error('runs.list requires options.play.');
1057
+ }
1058
+ const params = new URLSearchParams({ play: playName });
1059
+ const status = options.status?.trim();
1060
+ if (status) {
1061
+ params.set('status', status);
1062
+ }
1063
+ const response = await this.http.get<{ runs: PlayRunListItem[] }>(
1064
+ `/api/v2/runs?${params.toString()}`,
1065
+ );
1066
+ return response.runs ?? [];
1067
+ }
1068
+
1069
+ /**
1070
+ * Fetch the lightweight tail status for a run using the public runs resource model.
1071
+ *
1072
+ * This is the SDK equivalent of:
1073
+ *
1074
+ * ```bash
1075
+ * deepline runs tail <run-id> --json
1076
+ * ```
1077
+ */
1078
+ async tailRun(runId: string, options?: RunsTailOptions): Promise<PlayStatus> {
1079
+ const afterLogIndex =
1080
+ typeof options?.afterLogIndex === 'number'
1081
+ ? options.afterLogIndex
1082
+ : typeof options?.cursor === 'number'
1083
+ ? options.cursor
1084
+ : typeof options?.cursor === 'string' && options.cursor.trim()
1085
+ ? Number(options.cursor)
1086
+ : undefined;
1087
+ const params = new URLSearchParams();
1088
+ if (Number.isFinite(afterLogIndex)) {
1089
+ params.set('afterLogIndex', String(Number(afterLogIndex)));
1090
+ }
1091
+ if (typeof options?.waitMs === 'number') {
1092
+ params.set('waitMs', String(options.waitMs));
1093
+ }
1094
+ if (options?.terminalOnly) {
1095
+ params.set('terminalOnly', 'true');
1096
+ }
1097
+ const suffix = params.toString() ? `?${params.toString()}` : '';
1098
+ const response = await this.http.get<Record<string, unknown>>(
1099
+ `/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`,
1100
+ );
1101
+ return normalizePlayStatus(response);
1102
+ }
1103
+
1104
+ /**
1105
+ * Fetch persisted logs for a run using the public runs resource model.
1106
+ *
1107
+ * This is the SDK equivalent of:
1108
+ *
1109
+ * ```bash
1110
+ * deepline runs logs <run-id> --limit 200 --json
1111
+ * ```
1112
+ */
1113
+ async getRunLogs(
1114
+ runId: string,
1115
+ options?: RunsLogsOptions,
1116
+ ): Promise<RunsLogsResult> {
1117
+ const status = await this.getRunStatus(runId);
1118
+ const logs = status.progress?.logs ?? [];
1119
+ const limit =
1120
+ typeof options?.limit === 'number' && Number.isFinite(options.limit)
1121
+ ? Math.max(0, Math.trunc(options.limit))
1122
+ : 200;
1123
+ const entries = logs.slice(Math.max(0, logs.length - limit));
1124
+ return {
1125
+ runId: status.runId,
1126
+ totalCount: logs.length,
1127
+ returnedCount: entries.length,
1128
+ firstSequence:
1129
+ logs.length === 0 ? null : logs.length - entries.length + 1,
1130
+ lastSequence: logs.length === 0 ? null : logs.length,
1131
+ truncated: logs.length > entries.length,
1132
+ hasMore: logs.length > entries.length,
1133
+ entries,
1134
+ };
1135
+ }
1136
+
1137
+ /**
1138
+ * Stop a run by id using the public runs resource model.
1139
+ *
1140
+ * This is the SDK equivalent of:
1141
+ *
1142
+ * ```bash
1143
+ * deepline runs stop <run-id> --reason "stale lock" --json
1144
+ * ```
1145
+ */
1146
+ async stopRun(
1147
+ runId: string,
1148
+ options?: { reason?: string },
1149
+ ): Promise<StopPlayRunResult> {
1150
+ return this.http.post<StopPlayRunResult>(
1151
+ `/api/v2/runs/${encodeURIComponent(runId)}/stop`,
1152
+ options?.reason ? { reason: options.reason } : {},
1153
+ );
1154
+ }
1155
+
919
1156
  async listPlays(): Promise<PlayListItem[]> {
920
1157
  const response = await this.http.get<{ plays: PlayListItem[] }>(
921
1158
  '/api/v2/plays',
@@ -10,9 +10,12 @@
10
10
  * 1. `options.baseUrl` (explicit constructor argument)
11
11
  * 2. `DEEPLINE_ORIGIN_URL` environment variable
12
12
  * 3. `DEEPLINE_API_BASE_URL` environment variable
13
- * 4. Nearest checkout-local `.env.worktree`
14
- * 5. `DEEPLINE_ORIGIN_URL` from the production host auth file
15
- * 6. Production fallback: `https://code.deepline.com`
13
+ * 4. Nearest checkout-local `.env.deepline`
14
+ * 5. Nearest checkout-local profile file (`.env.deepline.prod`, etc.)
15
+ * 6. Nearest checkout-local app env file (`.env.prod`, `.env.staging`, `.env.local`, `.env`)
16
+ * 7. Nearest checkout-local `.env.worktree`
17
+ * 8. `DEEPLINE_ORIGIN_URL` from the production host auth file
18
+ * 9. Production fallback: `https://code.deepline.com`
16
19
  *
17
20
  * ### API Key
18
21
  * 1. `options.apiKey` (explicit constructor argument)
@@ -48,6 +51,20 @@ const DEFAULT_TIMEOUT = 60_000;
48
51
  /** Default retry count for transient failures. */
49
52
  const DEFAULT_MAX_RETRIES = 3;
50
53
 
54
+ const ACTIVE_DEEPLINE_ENV_FILE = '.env.deepline';
55
+
56
+ function isProdBaseUrl(baseUrl: string): boolean {
57
+ return baseUrl.trim().replace(/\/$/, '') === PROD_URL;
58
+ }
59
+
60
+ function profileNameForBaseUrl(baseUrl: string): 'prod' | 'dev' {
61
+ return isProdBaseUrl(baseUrl) ? 'prod' : 'dev';
62
+ }
63
+
64
+ function projectEnvStartDir(): string {
65
+ return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
66
+ }
67
+
51
68
  /**
52
69
  * Convert a base URL to a filesystem-safe slug for per-host config storage.
53
70
  *
@@ -116,17 +133,70 @@ function parseEnvFile(filePath: string): Record<string, string> {
116
133
  return env;
117
134
  }
118
135
 
119
- function findNearestWorktreeEnv(startDir: string = process.cwd()): Record<string, string> {
136
+ function findNearestEnvFile(
137
+ names: string[],
138
+ startDir: string = process.cwd(),
139
+ ): string | null {
120
140
  let current = resolve(startDir);
121
141
  while (true) {
122
- const values = parseEnvFile(join(current, '.env.worktree'));
123
- if (Object.keys(values).length > 0) return values;
142
+ for (const name of names) {
143
+ const filePath = join(current, name);
144
+ if (existsSync(filePath)) return filePath;
145
+ }
124
146
  const parent = dirname(current);
125
- if (parent === current) return {};
147
+ if (parent === current) return null;
126
148
  current = parent;
127
149
  }
128
150
  }
129
151
 
152
+ function findNearestEnv(names: string[], startDir: string = process.cwd()): Record<string, string> {
153
+ const filePath = findNearestEnvFile(names, startDir);
154
+ return filePath ? parseEnvFile(filePath) : {};
155
+ }
156
+
157
+ function findNearestWorktreeEnv(startDir: string = process.cwd()): Record<string, string> {
158
+ return findNearestEnv(['.env.worktree'], startDir);
159
+ }
160
+
161
+ function resolveProfileEnvFileNames(): string[] {
162
+ const explicitProfile =
163
+ process.env.DEEPLINE_ENV_PROFILE?.trim() ||
164
+ process.env.DEEPLINE_PROFILE?.trim() ||
165
+ '';
166
+ const names: string[] = [];
167
+ if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
168
+ const nodeEnv = process.env.NODE_ENV?.trim();
169
+ if (nodeEnv === 'production') names.push('.env.deepline.prod');
170
+ else if (nodeEnv === 'staging') names.push('.env.deepline.staging');
171
+ names.push(ACTIVE_DEEPLINE_ENV_FILE);
172
+ return names;
173
+ }
174
+
175
+ function resolveProjectAppEnvFileNames(): string[] {
176
+ const nodeEnv = process.env.NODE_ENV?.trim();
177
+ const names: string[] = [];
178
+ if (nodeEnv === 'production') names.push('.env.prod');
179
+ if (nodeEnv === 'staging') names.push('.env.staging');
180
+ names.push('.env.local', '.env');
181
+ return names;
182
+ }
183
+
184
+ function resolveBaseUrlFromEnvValues(env: Record<string, string>): string {
185
+ return (
186
+ env.DEEPLINE_ORIGIN_URL?.trim() ||
187
+ env.DEEPLINE_API_BASE_URL?.trim() ||
188
+ ''
189
+ );
190
+ }
191
+
192
+ function loadProjectDeeplineEnv(): Record<string, string> {
193
+ return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
194
+ }
195
+
196
+ function loadProjectAppEnv(): Record<string, string> {
197
+ return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
198
+ }
199
+
130
200
  function normalizeWorktreeBaseUrl(baseUrl: string, worktreeEnv = findNearestWorktreeEnv()): string {
131
201
  const trimmed = baseUrl.trim().replace(/\/$/, '');
132
202
  if (!trimmed) return trimmed;
@@ -219,6 +289,12 @@ function autoDetectBaseUrl(): string {
219
289
  const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
220
290
  if (envBase) return normalizeWorktreeBaseUrl(envBase);
221
291
 
292
+ const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
293
+ if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
294
+
295
+ const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
296
+ if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
297
+
222
298
  const worktreeBaseUrl = resolveWorktreeBaseUrl();
223
299
  if (worktreeBaseUrl) return worktreeBaseUrl;
224
300
 
@@ -260,11 +336,15 @@ export function resolveConfig(options?: DeeplineClientOptions): ResolvedConfig {
260
336
  const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
261
337
 
262
338
  const cliEnv = loadCliEnv(baseUrl);
339
+ const projectDeeplineEnv = loadProjectDeeplineEnv();
340
+ const projectAppEnv = loadProjectAppEnv();
263
341
 
264
342
  // Resolve API key: option > env var > SDK CLI env
265
343
  const apiKey =
266
344
  options?.apiKey?.trim() ||
267
345
  process.env.DEEPLINE_API_KEY?.trim() ||
346
+ projectDeeplineEnv.DEEPLINE_API_KEY ||
347
+ projectAppEnv.DEEPLINE_API_KEY ||
268
348
  cliEnv.DEEPLINE_API_KEY ||
269
349
  '';
270
350
 
@@ -282,4 +362,41 @@ export function resolveConfig(options?: DeeplineClientOptions): ResolvedConfig {
282
362
  };
283
363
  }
284
364
 
285
- export { baseUrlSlug, loadCliEnv, loadGlobalCliEnv, parseEnvFile, autoDetectBaseUrl, PROD_URL };
365
+ function mergeEnvFile(filePath: string, values: Record<string, string>): void {
366
+ const existing = existsSync(filePath) ? parseEnvFile(filePath) : {};
367
+ const merged = { ...existing, ...values };
368
+ const dir = dirname(filePath);
369
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
370
+ const lines = Object.entries(merged)
371
+ .filter(([, value]) => value !== '')
372
+ .map(([key, value]) => `${key}=${value}`);
373
+ writeFileSync(filePath, `${lines.join('\n')}\n`, 'utf-8');
374
+ }
375
+
376
+ export function saveProjectDeeplineEnvValues(
377
+ baseUrl: string,
378
+ values: Record<string, string>,
379
+ startDir: string = projectEnvStartDir(),
380
+ ): string[] {
381
+ const root = resolve(startDir);
382
+ const profile = profileNameForBaseUrl(baseUrl);
383
+ const files = [
384
+ join(root, ACTIVE_DEEPLINE_ENV_FILE),
385
+ join(root, `.env.deepline.${profile}`),
386
+ ];
387
+ if (profile === 'dev') files.push(join(root, '.env'));
388
+
389
+ for (const filePath of files) {
390
+ mergeEnvFile(filePath, values);
391
+ }
392
+ return files;
393
+ }
394
+
395
+ export {
396
+ baseUrlSlug,
397
+ loadCliEnv,
398
+ loadGlobalCliEnv,
399
+ parseEnvFile,
400
+ autoDetectBaseUrl,
401
+ PROD_URL,
402
+ };
@@ -289,8 +289,16 @@ export class HttpClient {
289
289
  * @param path - API path
290
290
  * @param body - Request body (will be JSON-serialized)
291
291
  */
292
- async post<T = unknown>(path: string, body: unknown): Promise<T> {
293
- return this.request<T>(path, { method: 'POST', body });
292
+ async post<T = unknown>(
293
+ path: string,
294
+ body: unknown,
295
+ headers?: Record<string, string>,
296
+ ): Promise<T> {
297
+ return this.request<T>(path, {
298
+ method: 'POST',
299
+ body,
300
+ headers,
301
+ });
294
302
  }
295
303
 
296
304
  /**
@@ -70,6 +70,7 @@ import type {
70
70
  PlayDataset,
71
71
  PlayDatasetInput,
72
72
  } from '../../shared_libs/plays/dataset.js';
73
+ import type { ToolExecuteResult } from '../../shared_libs/play-runtime/tool-result-types.js';
73
74
  import type {
74
75
  DeeplineClientOptions,
75
76
  PlayDetail,
@@ -140,36 +141,14 @@ export type LoosePlayObject = {
140
141
  [key: string]: LoosePlayObject;
141
142
  };
142
143
 
143
- export type ToolExtractedValue<T = unknown> = {
144
- path: string;
145
- get(): T | null;
146
- };
147
-
148
- export type ToolResultEnvelope<
149
- TData = unknown,
150
- TMeta = Record<string, unknown>,
151
- > = {
152
- data: TData;
153
- meta?: TMeta;
154
- };
155
-
156
- export type ToolExecuteResult<
157
- TData = unknown,
158
- TMeta = Record<string, unknown>,
159
- > = {
160
- status: string;
161
- result: ToolResultEnvelope<TData, TMeta>;
162
- extracted: Record<string, ToolExtractedValue>;
163
- lists: Record<
164
- string,
165
- {
166
- path: string;
167
- count: number | null;
168
- keys: Record<string, string>;
169
- get(): Record<string, unknown>[];
170
- }
171
- >;
172
- };
144
+ export type {
145
+ ToolExecuteResult,
146
+ ToolExecuteResultAccessors,
147
+ ToolExecuteResultBase,
148
+ ToolResultEnvelope,
149
+ ToolResultListAccessor,
150
+ ToolResultTargetAccessor as ToolExtractedValue,
151
+ } from '../../shared_libs/play-runtime/tool-result-types.js';
173
152
 
174
153
  export type ToolExecutionRequest = {
175
154
  id: string;
@@ -242,7 +221,6 @@ export type MapStepBuilder<
242
221
  run(options?: {
243
222
  description?: string;
244
223
  staleAfterSeconds?: number;
245
- concurrency?: number;
246
224
  key?:
247
225
  | (keyof InputRow & string)
248
226
  | readonly (keyof InputRow & string)[]
@@ -270,10 +248,11 @@ export type FileInput<TMetadata = unknown> = string & {
270
248
  * stage local paths passed to that flag, and use `TRow` for row-contract
271
249
  * discovery.
272
250
  */
273
- export type CsvInput<TRow extends object = Record<string, unknown>> = FileInput<{
274
- readonly kind: 'csv';
275
- readonly row: TRow;
276
- }>;
251
+ export type CsvInput<TRow extends object = Record<string, unknown>> =
252
+ FileInput<{
253
+ readonly kind: 'csv';
254
+ readonly row: TRow;
255
+ }>;
277
256
 
278
257
  export type ColumnMap<TRow extends object> = Partial<
279
258
  Record<Extract<keyof TRow, string>, string | readonly string[]>
@@ -943,7 +922,11 @@ export class DeeplineContext {
943
922
  toolId: string,
944
923
  input: Record<string, unknown>,
945
924
  ): Promise<ToolExecuteResult> =>
946
- this.client.executeTool(toolId, input) as Promise<ToolExecuteResult>,
925
+ this.client.executeTool(
926
+ toolId,
927
+ input,
928
+ { includeToolMetadata: true },
929
+ ) as unknown as Promise<ToolExecuteResult>,
947
930
  };
948
931
  }
949
932