deepline 0.1.129 → 0.1.131

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.
@@ -2,6 +2,7 @@ import { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
+ import { sdkCliStateDirPath } from './config.js';
5
6
 
6
7
  export type SdkCompatibilityStatus =
7
8
  | 'current'
@@ -34,10 +35,21 @@ export type SdkCompatibilityResponse = {
34
35
  action: 'force_python';
35
36
  reason: 'rollback_forced';
36
37
  };
38
+ skills?: SdkCompatibilitySkillsUpdate;
39
+ };
40
+
41
+ export type SdkCompatibilitySkillsUpdate = {
42
+ needs_update: boolean;
43
+ local: {
44
+ version: string | null;
45
+ };
46
+ remote: {
47
+ version: string;
48
+ };
37
49
  };
38
50
 
39
51
  const CHECK_TIMEOUT_MS = 2_000;
40
- const COMPAT_CACHE_TTL_MS = 5 * 60 * 1000;
52
+ export const SDK_COMPATIBILITY_CACHE_TTL_MS = 5 * 60 * 1000;
41
53
 
42
54
  type CompatCacheFile = {
43
55
  entries?: Record<
@@ -55,13 +67,34 @@ function shouldSkipCompatibilityCheck(): boolean {
55
67
  return value === '1' || value === 'true' || value === 'yes';
56
68
  }
57
69
 
58
- function compatibilityCachePath(): string {
59
- return join(homedir(), '.cache', 'deepline', 'sdk-compat-cache.json');
70
+ export function sdkCompatibilityCachePath(
71
+ baseUrl: string,
72
+ homeDir = homedir(),
73
+ ): string {
74
+ return join(sdkCliStateDirPath(baseUrl, homeDir), 'compat-cache.json');
75
+ }
76
+
77
+ function legacySdkCompatibilityCachePath(homeDir = homedir()): string {
78
+ return join(homeDir, '.cache', 'deepline', 'sdk-compat-cache.json');
60
79
  }
61
80
 
62
81
  function compatibilityCacheKey(
63
82
  baseUrl: string,
64
83
  command: string | null | undefined,
84
+ skillsVersion: string | null | undefined,
85
+ ): string {
86
+ return JSON.stringify({
87
+ baseUrl: baseUrl.replace(/\/$/, ''),
88
+ version: SDK_VERSION,
89
+ apiContract: SDK_API_CONTRACT,
90
+ command: command?.trim() || null,
91
+ skillsVersion: skillsVersion ?? null,
92
+ });
93
+ }
94
+
95
+ function legacyCompatibilityCacheKey(
96
+ baseUrl: string,
97
+ command: string | null | undefined,
65
98
  ): string {
66
99
  return JSON.stringify({
67
100
  baseUrl: baseUrl.replace(/\/$/, ''),
@@ -74,15 +107,27 @@ function compatibilityCacheKey(
74
107
  function readCachedCompatibility(
75
108
  baseUrl: string,
76
109
  command: string | null | undefined,
110
+ skillsVersion: string | null | undefined,
77
111
  ): SdkCompatibilityResponse | null {
78
112
  try {
79
- const path = compatibilityCachePath();
80
- if (!existsSync(path)) {
113
+ const path = sdkCompatibilityCachePath(baseUrl);
114
+ const legacyPath = legacySdkCompatibilityCachePath();
115
+ const useLegacyCache = !existsSync(path) && existsSync(legacyPath);
116
+ const cachePath = useLegacyCache ? legacyPath : path;
117
+ if (!existsSync(cachePath)) {
81
118
  return null;
82
119
  }
83
- const parsed = JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile;
84
- const entry = parsed.entries?.[compatibilityCacheKey(baseUrl, command)];
85
- if (!entry || Date.now() - entry.savedAt > COMPAT_CACHE_TTL_MS) {
120
+ const parsed = JSON.parse(
121
+ readFileSync(cachePath, 'utf8'),
122
+ ) as CompatCacheFile;
123
+ const entry =
124
+ parsed.entries?.[
125
+ compatibilityCacheKey(baseUrl, command, skillsVersion)
126
+ ] ??
127
+ (useLegacyCache
128
+ ? parsed.entries?.[legacyCompatibilityCacheKey(baseUrl, command)]
129
+ : undefined);
130
+ if (!entry || Date.now() - entry.savedAt > SDK_COMPATIBILITY_CACHE_TTL_MS) {
86
131
  return null;
87
132
  }
88
133
  return entry.response;
@@ -94,15 +139,16 @@ function readCachedCompatibility(
94
139
  function writeCachedCompatibility(
95
140
  baseUrl: string,
96
141
  command: string | null | undefined,
142
+ skillsVersion: string | null | undefined,
97
143
  response: SdkCompatibilityResponse,
98
144
  ): void {
99
145
  try {
100
- const path = compatibilityCachePath();
146
+ const path = sdkCompatibilityCachePath(baseUrl);
101
147
  const existing = existsSync(path)
102
148
  ? (JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile)
103
149
  : {};
104
150
  const entries = existing.entries ?? {};
105
- entries[compatibilityCacheKey(baseUrl, command)] = {
151
+ entries[compatibilityCacheKey(baseUrl, command, skillsVersion)] = {
106
152
  savedAt: Date.now(),
107
153
  response,
108
154
  };
@@ -116,7 +162,7 @@ function writeCachedCompatibility(
116
162
 
117
163
  export async function checkSdkCompatibility(
118
164
  baseUrl: string,
119
- options: { command?: string | null } = {},
165
+ options: { command?: string | null; skillsVersion?: string | null } = {},
120
166
  ): Promise<{
121
167
  response: SdkCompatibilityResponse | null;
122
168
  error: Error | null;
@@ -132,6 +178,9 @@ export async function checkSdkCompatibility(
132
178
  if (options.command?.trim()) {
133
179
  url.searchParams.set('command', options.command.trim());
134
180
  }
181
+ if (options.skillsVersion !== undefined) {
182
+ url.searchParams.set('skills_version', options.skillsVersion ?? '');
183
+ }
135
184
  const response = await fetch(url, {
136
185
  method: 'GET',
137
186
  headers: {
@@ -145,12 +194,24 @@ export async function checkSdkCompatibility(
145
194
  .json()
146
195
  .catch(() => null)) as SdkCompatibilityResponse | null;
147
196
  if (data) {
148
- writeCachedCompatibility(baseUrl, options.command, data);
197
+ writeCachedCompatibility(
198
+ baseUrl,
199
+ options.command,
200
+ options.skillsVersion,
201
+ data,
202
+ );
149
203
  }
150
204
  return { response: data, error: null };
151
205
  } catch (error) {
152
- const cached = readCachedCompatibility(baseUrl, options.command);
153
- if (cached?.ok === false || cached?.status === 'unsupported') {
206
+ // Live compat policy must win when the server is reachable: rollback and
207
+ // support-floor changes are blocking controls. The cache is only a
208
+ // resilience fallback for transient network failures.
209
+ const cached = readCachedCompatibility(
210
+ baseUrl,
211
+ options.command,
212
+ options.skillsVersion,
213
+ );
214
+ if (cached) {
154
215
  return { response: cached, error: null };
155
216
  }
156
217
  return {
@@ -151,6 +151,19 @@ function sdkCliConfigDir(baseUrl: string): string {
151
151
  return join(home, '.local', 'deepline', baseUrlSlug(baseUrl || PROD_URL));
152
152
  }
153
153
 
154
+ export function sdkCliStateDirPath(
155
+ baseUrl: string,
156
+ homeDir: string = process.env.HOME?.trim() || homedir(),
157
+ ): string {
158
+ return join(
159
+ homeDir,
160
+ '.local',
161
+ 'deepline',
162
+ baseUrlSlug(baseUrl || PROD_URL),
163
+ 'sdk-cli',
164
+ );
165
+ }
166
+
154
167
  function sdkCliEnvFilePath(baseUrl: string): string {
155
168
  return join(sdkCliConfigDir(baseUrl), '.env');
156
169
  }
@@ -25,7 +25,7 @@ import type { ResolvedConfig } from './types.js';
25
25
  import { AuthError, DeeplineError, RateLimitError } from './errors.js';
26
26
  import { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
27
27
  import type { LiveEventEnvelope } from './types.js';
28
- import { baseUrlSlug } from './config.js';
28
+ import { baseUrlSlug, sdkCliStateDirPath } from './config.js';
29
29
  import { detectAgentRuntime, isCoworkLikeSandbox } from './agent-runtime.js';
30
30
  import {
31
31
  COORDINATOR_INTERNAL_TOKEN_HEADER,
@@ -102,6 +102,10 @@ export class HttpClient {
102
102
  if (explicit) return explicit;
103
103
  try {
104
104
  const versionPath = join(
105
+ sdkCliStateDirPath(this.config.baseUrl),
106
+ 'skills-version',
107
+ );
108
+ const legacyVersionPath = join(
105
109
  process.env.HOME?.trim() || homedir(),
106
110
  '.local',
107
111
  'deepline',
@@ -109,8 +113,11 @@ export class HttpClient {
109
113
  'sdk-skills',
110
114
  '.version',
111
115
  );
112
- if (!existsSync(versionPath)) return null;
113
- return this.cleanDiagnosticHeader(readFileSync(versionPath, 'utf-8'));
116
+ const resolvedPath = existsSync(versionPath)
117
+ ? versionPath
118
+ : legacyVersionPath;
119
+ if (!existsSync(resolvedPath)) return null;
120
+ return this.cleanDiagnosticHeader(readFileSync(resolvedPath, 'utf-8'));
114
121
  } catch {
115
122
  return null;
116
123
  }
@@ -101,10 +101,10 @@ export const SDK_RELEASE = {
101
101
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
102
102
  // the SDK enrich generator's one-second stale policy.
103
103
  // 0.1.110 ships authored V2 prebuilts and required top-level play descriptions.
104
- version: '0.1.129',
104
+ version: '0.1.131',
105
105
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
106
106
  supportPolicy: {
107
- latest: '0.1.129',
107
+ latest: '0.1.131',
108
108
  minimumSupported: '0.1.53',
109
109
  deprecatedBelow: '0.1.53',
110
110
  commandMinimumSupported: [