oh-my-knowledge 0.52.3 → 0.53.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 (88) hide show
  1. package/README.md +13 -1
  2. package/README.zh.md +13 -1
  3. package/dist/assets/agent-skills/omk/SKILL.md +4 -0
  4. package/dist/assets/agent-skills/omk/references/commands.md +1 -1
  5. package/dist/authoring/evolver.js +1 -1
  6. package/dist/authoring/generator.js +1 -1
  7. package/dist/cli/commands/eval/index.js +7 -6
  8. package/dist/cli/commands/install.js +5 -1
  9. package/dist/cli/lib/generation-failure-hint.js +6 -8
  10. package/dist/cli/lib/runtime-defaults.d.ts +2 -0
  11. package/dist/cli/lib/runtime-defaults.js +7 -4
  12. package/dist/dsh-plugin/cordis.patch.yml +3 -0
  13. package/dist/dsh-plugin/host-executor.d.ts +93 -0
  14. package/dist/dsh-plugin/host-executor.js +232 -0
  15. package/dist/dsh-plugin/index.d.ts +28 -0
  16. package/dist/dsh-plugin/index.js +156 -0
  17. package/dist/dsh-plugin/protocol.d.ts +21 -0
  18. package/dist/dsh-plugin/protocol.js +229 -0
  19. package/dist/eval-core/comparability.js +3 -0
  20. package/dist/eval-core/evaluation-execution.js +5 -13
  21. package/dist/eval-core/evaluation-reporting.d.ts +7 -4
  22. package/dist/eval-core/evaluation-reporting.js +39 -18
  23. package/dist/eval-core/judge-independence.d.ts +1 -1
  24. package/dist/eval-core/judge-independence.js +1 -1
  25. package/dist/eval-core/report-document.js +6 -0
  26. package/dist/eval-core/resume-compatibility.d.ts +1 -0
  27. package/dist/eval-core/resume-compatibility.js +5 -4
  28. package/dist/eval-workflows/batch-evaluation-workflow.js +1 -1
  29. package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +4 -2
  30. package/dist/eval-workflows/evaluation-pipeline.d.ts +3 -1
  31. package/dist/eval-workflows/evaluation-pipeline.js +7 -3
  32. package/dist/eval-workflows/run-evaluation.d.ts +4 -2
  33. package/dist/eval-workflows/run-evaluation.js +7 -3
  34. package/dist/executors/{anthropic-api.d.ts → anthropic/api.d.ts} +1 -1
  35. package/dist/executors/{anthropic-api.js → anthropic/api.js} +4 -2
  36. package/dist/executors/{claude-cli.d.ts → anthropic/claude/cli.d.ts} +1 -1
  37. package/dist/executors/{claude-cli.js → anthropic/claude/cli.js} +5 -3
  38. package/dist/executors/anthropic/claude/protocol.d.ts +87 -0
  39. package/dist/executors/{claude-protocol.js → anthropic/claude/protocol.js} +6 -6
  40. package/dist/executors/{claude-sdk.d.ts → anthropic/claude/sdk.d.ts} +2 -2
  41. package/dist/executors/{claude-sdk.js → anthropic/claude/sdk.js} +8 -5
  42. package/dist/executors/anthropic/claude/trace.d.ts +9 -0
  43. package/dist/executors/{claude-sdk-trace.js → anthropic/claude/trace.js} +5 -5
  44. package/dist/executors/{capabilities.d.ts → core/capabilities.d.ts} +3 -5
  45. package/dist/executors/{capabilities.js → core/capabilities.js} +4 -11
  46. package/dist/executors/core/http.d.ts +6 -0
  47. package/dist/executors/core/http.js +19 -0
  48. package/dist/executors/core/limits.d.ts +2 -0
  49. package/dist/executors/core/limits.js +2 -0
  50. package/dist/executors/core/optional-dependencies.d.ts +7 -0
  51. package/dist/executors/core/optional-dependencies.js +35 -0
  52. package/dist/executors/core/registry.d.ts +145 -0
  53. package/dist/executors/core/registry.js +127 -0
  54. package/dist/executors/core/runtime-fingerprint.d.ts +13 -0
  55. package/dist/executors/{runtime-fingerprint.js → core/runtime-fingerprint.js} +119 -59
  56. package/dist/executors/core/runtime.d.ts +12 -0
  57. package/dist/executors/core/runtime.js +61 -0
  58. package/dist/executors/core/subprocess.d.ts +44 -0
  59. package/dist/executors/{shared.js → core/subprocess.js} +20 -156
  60. package/dist/executors/index.d.ts +4 -4
  61. package/dist/executors/index.js +26 -15
  62. package/dist/executors/{openai-api.d.ts → openai/api.d.ts} +1 -1
  63. package/dist/executors/{openai-api.js → openai/api.js} +4 -2
  64. package/dist/executors/{codex-cli.d.ts → openai/codex/cli.d.ts} +3 -3
  65. package/dist/executors/{codex-cli.js → openai/codex/cli.js} +5 -3
  66. package/dist/executors/openai/codex/protocol.d.ts +72 -0
  67. package/dist/executors/{codex-protocol.js → openai/codex/protocol.js} +33 -2
  68. package/dist/executors/{codex-sdk.d.ts → openai/codex/sdk.d.ts} +18 -4
  69. package/dist/executors/{codex-sdk.js → openai/codex/sdk.js} +7 -4
  70. package/dist/executors/{codex-cli-trace.d.ts → openai/codex/trace.d.ts} +2 -2
  71. package/dist/executors/{codex-cli-trace.js → openai/codex/trace.js} +4 -4
  72. package/dist/executors/{script.d.ts → script/index.d.ts} +1 -1
  73. package/dist/executors/{script.js → script/index.js} +6 -4
  74. package/dist/grading/judge.d.ts +1 -1
  75. package/dist/grading/judge.js +1 -1
  76. package/dist/renderer/html-renderer.js +30 -11
  77. package/dist/types/executor.d.ts +13 -2
  78. package/dist/types/judge.d.ts +2 -2
  79. package/package.json +21 -5
  80. package/dist/executors/claude-protocol.d.ts +0 -28
  81. package/dist/executors/claude-sdk-trace.d.ts +0 -9
  82. package/dist/executors/codex-protocol.d.ts +0 -24
  83. package/dist/executors/gemini.d.ts +0 -2
  84. package/dist/executors/gemini.js +0 -156
  85. package/dist/executors/runtime-fingerprint.d.ts +0 -6
  86. package/dist/executors/shared.d.ts +0 -226
  87. /package/dist/executors/{script-command.d.ts → script/command.d.ts} +0 -0
  88. /package/dist/executors/{script-command.js → script/command.js} +0 -0
@@ -0,0 +1,145 @@
1
+ export type ExecutorVendor = 'anthropic' | 'openai' | 'unknown';
2
+ export type ExecutorFamily = 'claude' | 'codex' | 'dsh' | 'anthropic-api' | 'openai-api';
3
+ export type SampleMockSupport = 'native-hooks' | 'delegated-script' | 'unsupported';
4
+ export interface ExecutorCapabilities {
5
+ sampleMocks: SampleMockSupport;
6
+ }
7
+ export type ExecutorFingerprintSpec = {
8
+ readonly strategy: 'path-cli';
9
+ readonly command: 'claude' | 'codex';
10
+ } | {
11
+ readonly strategy: 'claude-sdk';
12
+ } | {
13
+ readonly strategy: 'codex-sdk';
14
+ } | {
15
+ readonly strategy: 'api';
16
+ } | {
17
+ readonly strategy: 'dsh-host';
18
+ };
19
+ declare const EXECUTOR_DESCRIPTORS: readonly [{
20
+ readonly name: "claude";
21
+ readonly execution: "builtin";
22
+ readonly family: "claude";
23
+ readonly vendor: "anthropic";
24
+ readonly sampleMocks: "native-hooks";
25
+ readonly runtimeKind: "agent-cli";
26
+ readonly runtimeCapabilities: {
27
+ readonly systemPrompt: "native";
28
+ readonly costUSD: "reported";
29
+ readonly trace: "native";
30
+ readonly skillIsolation: "full-no-partial";
31
+ };
32
+ readonly fingerprint: {
33
+ readonly strategy: "path-cli";
34
+ readonly command: "claude";
35
+ };
36
+ }, {
37
+ readonly name: "claude-sdk";
38
+ readonly execution: "builtin";
39
+ readonly family: "claude";
40
+ readonly vendor: "anthropic";
41
+ readonly sampleMocks: "native-hooks";
42
+ readonly runtimeKind: "agent-sdk";
43
+ readonly runtimeCapabilities: {
44
+ readonly systemPrompt: "native";
45
+ readonly costUSD: "reported";
46
+ readonly trace: "native";
47
+ readonly skillIsolation: "full";
48
+ };
49
+ readonly fingerprint: {
50
+ readonly strategy: "claude-sdk";
51
+ };
52
+ }, {
53
+ readonly name: "codex";
54
+ readonly execution: "builtin";
55
+ readonly family: "codex";
56
+ readonly vendor: "openai";
57
+ readonly sampleMocks: "unsupported";
58
+ readonly runtimeKind: "agent-cli";
59
+ readonly runtimeCapabilities: {
60
+ readonly systemPrompt: "prepended";
61
+ readonly costUSD: "not-reported";
62
+ readonly trace: "best-effort";
63
+ readonly skillIsolation: "cwd-only";
64
+ };
65
+ readonly fingerprint: {
66
+ readonly strategy: "path-cli";
67
+ readonly command: "codex";
68
+ };
69
+ }, {
70
+ readonly name: "codex-sdk";
71
+ readonly execution: "builtin";
72
+ readonly family: "codex";
73
+ readonly vendor: "openai";
74
+ readonly sampleMocks: "unsupported";
75
+ readonly runtimeKind: "agent-sdk";
76
+ readonly runtimeCapabilities: {
77
+ readonly systemPrompt: "prepended";
78
+ readonly costUSD: "not-reported";
79
+ readonly trace: "best-effort";
80
+ readonly skillIsolation: "cwd-only";
81
+ };
82
+ readonly fingerprint: {
83
+ readonly strategy: "codex-sdk";
84
+ };
85
+ }, {
86
+ readonly name: "dsh-host";
87
+ readonly execution: "host-only";
88
+ readonly family: "dsh";
89
+ readonly vendor: "unknown";
90
+ readonly sampleMocks: "unsupported";
91
+ readonly runtimeKind: "agent-sdk";
92
+ readonly runtimeCapabilities: {
93
+ readonly systemPrompt: "native";
94
+ readonly costUSD: "not-reported";
95
+ readonly trace: "native";
96
+ readonly skillIsolation: "full-no-partial";
97
+ };
98
+ readonly fingerprint: {
99
+ readonly strategy: "dsh-host";
100
+ };
101
+ }, {
102
+ readonly name: "anthropic-api";
103
+ readonly execution: "builtin";
104
+ readonly family: "anthropic-api";
105
+ readonly vendor: "anthropic";
106
+ readonly sampleMocks: "unsupported";
107
+ readonly runtimeKind: "api";
108
+ readonly runtimeCapabilities: {
109
+ readonly systemPrompt: "native";
110
+ readonly costUSD: "not-reported";
111
+ readonly trace: "none";
112
+ readonly skillIsolation: "none";
113
+ };
114
+ readonly fingerprint: {
115
+ readonly strategy: "api";
116
+ };
117
+ }, {
118
+ readonly name: "openai-api";
119
+ readonly execution: "builtin";
120
+ readonly family: "openai-api";
121
+ readonly vendor: "openai";
122
+ readonly sampleMocks: "unsupported";
123
+ readonly runtimeKind: "api";
124
+ readonly runtimeCapabilities: {
125
+ readonly systemPrompt: "native";
126
+ readonly costUSD: "not-reported";
127
+ readonly trace: "none";
128
+ readonly skillIsolation: "none";
129
+ };
130
+ readonly fingerprint: {
131
+ readonly strategy: "api";
132
+ };
133
+ }];
134
+ export type RegisteredExecutorDescriptor = (typeof EXECUTOR_DESCRIPTORS)[number];
135
+ export type RegisteredExecutorName = RegisteredExecutorDescriptor['name'];
136
+ export type ExecutableExecutorName = Extract<RegisteredExecutorDescriptor, {
137
+ execution: 'builtin';
138
+ }>['name'];
139
+ export declare function executorDescriptors(): readonly RegisteredExecutorDescriptor[];
140
+ export declare function getExecutorDescriptor(name: string): RegisteredExecutorDescriptor | undefined;
141
+ export declare function isRegisteredExecutorName(name: string): name is RegisteredExecutorName;
142
+ export declare function executorVendor(executor: string): ExecutorVendor;
143
+ export declare function executorFamily(executor: string): ExecutorFamily | 'custom';
144
+ export declare function executorNamesForFamily(family: ExecutorFamily): ReadonlySet<string>;
145
+ export {};
@@ -0,0 +1,127 @@
1
+ const EXECUTOR_DESCRIPTORS = [
2
+ {
3
+ name: 'claude',
4
+ execution: 'builtin',
5
+ family: 'claude',
6
+ vendor: 'anthropic',
7
+ sampleMocks: 'native-hooks',
8
+ runtimeKind: 'agent-cli',
9
+ runtimeCapabilities: {
10
+ systemPrompt: 'native',
11
+ costUSD: 'reported',
12
+ trace: 'native',
13
+ skillIsolation: 'full-no-partial',
14
+ },
15
+ fingerprint: { strategy: 'path-cli', command: 'claude' },
16
+ },
17
+ {
18
+ name: 'claude-sdk',
19
+ execution: 'builtin',
20
+ family: 'claude',
21
+ vendor: 'anthropic',
22
+ sampleMocks: 'native-hooks',
23
+ runtimeKind: 'agent-sdk',
24
+ runtimeCapabilities: {
25
+ systemPrompt: 'native',
26
+ costUSD: 'reported',
27
+ trace: 'native',
28
+ skillIsolation: 'full',
29
+ },
30
+ fingerprint: { strategy: 'claude-sdk' },
31
+ },
32
+ {
33
+ name: 'codex',
34
+ execution: 'builtin',
35
+ family: 'codex',
36
+ vendor: 'openai',
37
+ sampleMocks: 'unsupported',
38
+ runtimeKind: 'agent-cli',
39
+ runtimeCapabilities: {
40
+ systemPrompt: 'prepended',
41
+ costUSD: 'not-reported',
42
+ trace: 'best-effort',
43
+ skillIsolation: 'cwd-only',
44
+ },
45
+ fingerprint: { strategy: 'path-cli', command: 'codex' },
46
+ },
47
+ {
48
+ name: 'codex-sdk',
49
+ execution: 'builtin',
50
+ family: 'codex',
51
+ vendor: 'openai',
52
+ sampleMocks: 'unsupported',
53
+ runtimeKind: 'agent-sdk',
54
+ runtimeCapabilities: {
55
+ systemPrompt: 'prepended',
56
+ costUSD: 'not-reported',
57
+ trace: 'best-effort',
58
+ skillIsolation: 'cwd-only',
59
+ },
60
+ fingerprint: { strategy: 'codex-sdk' },
61
+ },
62
+ {
63
+ name: 'dsh-host',
64
+ execution: 'host-only',
65
+ family: 'dsh',
66
+ vendor: 'unknown',
67
+ sampleMocks: 'unsupported',
68
+ runtimeKind: 'agent-sdk',
69
+ runtimeCapabilities: {
70
+ systemPrompt: 'native',
71
+ costUSD: 'not-reported',
72
+ trace: 'native',
73
+ skillIsolation: 'full-no-partial',
74
+ },
75
+ fingerprint: { strategy: 'dsh-host' },
76
+ },
77
+ {
78
+ name: 'anthropic-api',
79
+ execution: 'builtin',
80
+ family: 'anthropic-api',
81
+ vendor: 'anthropic',
82
+ sampleMocks: 'unsupported',
83
+ runtimeKind: 'api',
84
+ runtimeCapabilities: {
85
+ systemPrompt: 'native',
86
+ costUSD: 'not-reported',
87
+ trace: 'none',
88
+ skillIsolation: 'none',
89
+ },
90
+ fingerprint: { strategy: 'api' },
91
+ },
92
+ {
93
+ name: 'openai-api',
94
+ execution: 'builtin',
95
+ family: 'openai-api',
96
+ vendor: 'openai',
97
+ sampleMocks: 'unsupported',
98
+ runtimeKind: 'api',
99
+ runtimeCapabilities: {
100
+ systemPrompt: 'native',
101
+ costUSD: 'not-reported',
102
+ trace: 'none',
103
+ skillIsolation: 'none',
104
+ },
105
+ fingerprint: { strategy: 'api' },
106
+ },
107
+ ];
108
+ export function executorDescriptors() {
109
+ return EXECUTOR_DESCRIPTORS;
110
+ }
111
+ export function getExecutorDescriptor(name) {
112
+ return EXECUTOR_DESCRIPTORS.find((descriptor) => descriptor.name === name);
113
+ }
114
+ export function isRegisteredExecutorName(name) {
115
+ return getExecutorDescriptor(name) !== undefined;
116
+ }
117
+ export function executorVendor(executor) {
118
+ return getExecutorDescriptor(executor)?.vendor ?? 'unknown';
119
+ }
120
+ export function executorFamily(executor) {
121
+ return getExecutorDescriptor(executor)?.family ?? 'custom';
122
+ }
123
+ export function executorNamesForFamily(family) {
124
+ return new Set(EXECUTOR_DESCRIPTORS
125
+ .filter((descriptor) => descriptor.family === family)
126
+ .map((descriptor) => descriptor.name));
127
+ }
@@ -0,0 +1,13 @@
1
+ import type { ExecutorFn, ExecutorRuntimeFingerprint } from '../../types/index.js';
2
+ export interface DshHostRuntimeIdentity {
3
+ provider?: string;
4
+ agentPreset?: string;
5
+ toolSchemas?: readonly unknown[];
6
+ }
7
+ export interface ExecutorRuntimeFingerprintOptions {
8
+ skillDir?: string | null;
9
+ env?: NodeJS.ProcessEnv;
10
+ }
11
+ export declare function createDshHostRuntimeFingerprint(model: string, identity?: DshHostRuntimeIdentity): ExecutorRuntimeFingerprint;
12
+ export declare function resolveExecutorRuntimeFingerprint(executorName: string, model: string, options?: ExecutorRuntimeFingerprintOptions, executor?: ExecutorFn): ExecutorRuntimeFingerprint;
13
+ export declare function getExecutorRuntimeFingerprint(executorName: string, model: string, options?: ExecutorRuntimeFingerprintOptions): ExecutorRuntimeFingerprint;
@@ -1,11 +1,12 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
- import { existsSync, readFileSync, statSync } from 'node:fs';
3
+ import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { delimiter, dirname, isAbsolute, join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
- import { buildExecEnv } from './shared.js';
8
- import { isScriptFileInterpreter, resolveScriptCommand, } from './script-command.js';
7
+ import { buildExecEnv } from './runtime.js';
8
+ import { isScriptFileInterpreter, resolveScriptCommand, } from '../script/command.js';
9
+ import { getExecutorDescriptor } from './registry.js';
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
11
  const requireFromHere = createRequire(import.meta.url);
11
12
  const RUNTIME_CACHE = new Map();
@@ -56,6 +57,50 @@ function findNearestPackageJson(fromPath) {
56
57
  }
57
58
  return null;
58
59
  }
60
+ function readInvokingDshPackage() {
61
+ const invokedEntrypoint = process.argv[1];
62
+ if (!invokedEntrypoint || !isAbsolute(invokedEntrypoint)) {
63
+ return {
64
+ package: { name: '@deepseek-ai/dsh', error: 'DSH host entrypoint not found' },
65
+ };
66
+ }
67
+ let entrypoint = invokedEntrypoint;
68
+ try {
69
+ entrypoint = realpathSync(invokedEntrypoint);
70
+ }
71
+ catch {
72
+ // Keep the invoked path so the audit record still explains the lookup failure.
73
+ }
74
+ let dir = dirname(entrypoint);
75
+ for (let i = 0; i < 10; i++) {
76
+ const candidate = join(dir, 'package.json');
77
+ if (existsSync(candidate)) {
78
+ try {
79
+ const pkg = JSON.parse(readFileSync(candidate, 'utf-8'));
80
+ if (pkg.name === '@deepseek-ai/dsh') {
81
+ return {
82
+ entrypoint,
83
+ package: {
84
+ name: pkg.name,
85
+ ...(pkg.version ? { version: pkg.version } : {}),
86
+ },
87
+ };
88
+ }
89
+ }
90
+ catch {
91
+ // Keep walking: a parent package may still be the DSH CLI package.
92
+ }
93
+ }
94
+ const parent = dirname(dir);
95
+ if (parent === dir)
96
+ break;
97
+ dir = parent;
98
+ }
99
+ return {
100
+ entrypoint,
101
+ package: { name: '@deepseek-ai/dsh', error: 'invoking @deepseek-ai/dsh package.json not found' },
102
+ };
103
+ }
59
104
  function resolvePackageJson(packageName, from) {
60
105
  const req = from ? createRequire(from) : requireFromHere;
61
106
  try {
@@ -165,6 +210,7 @@ function withFingerprint(input) {
165
210
  status: input.sdk.version ? 'ok' : input.sdk.error ? 'error' : 'missing',
166
211
  }
167
212
  : undefined,
213
+ ...(input.auditability ? { auditability: input.auditability } : {}),
168
214
  capabilities: input.capabilities,
169
215
  };
170
216
  return { ...input, fingerprint: hashString(canonicalStringify(stablePayload)) };
@@ -178,6 +224,32 @@ function runtime(executor, model, kind, capabilities, extra = {}) {
178
224
  ...extra,
179
225
  });
180
226
  }
227
+ function ownPackage() {
228
+ const packageJson = findNearestPackageJson(fileURLToPath(import.meta.url));
229
+ if (!packageJson)
230
+ return { name: 'oh-my-knowledge', error: 'package.json not found' };
231
+ try {
232
+ const pkg = JSON.parse(readFileSync(packageJson, 'utf-8'));
233
+ return {
234
+ name: pkg.name ?? 'oh-my-knowledge',
235
+ ...(pkg.version ? { version: pkg.version } : {}),
236
+ };
237
+ }
238
+ catch (error) {
239
+ return {
240
+ name: 'oh-my-knowledge',
241
+ error: error instanceof Error ? error.message : String(error),
242
+ };
243
+ }
244
+ }
245
+ function dshCompositionHash(identity) {
246
+ return createHash('sha256').update(canonicalStringify({
247
+ adapter: 'omk-dsh-host-v1',
248
+ provider: identity.provider ?? null,
249
+ agentPreset: identity.agentPreset ?? null,
250
+ toolSchemas: identity.toolSchemas ?? null,
251
+ })).digest('hex');
252
+ }
181
253
  function runtimeEnv(options) {
182
254
  return options?.env ?? buildExecEnv(options?.skillDir);
183
255
  }
@@ -245,48 +317,63 @@ function scriptRuntime(executorName, model, env) {
245
317
  },
246
318
  });
247
319
  }
320
+ export function createDshHostRuntimeFingerprint(model, identity = {}) {
321
+ const descriptor = getExecutorDescriptor('dsh-host');
322
+ if (!descriptor || descriptor.name !== 'dsh-host') {
323
+ throw new Error('dsh-host executor descriptor is missing');
324
+ }
325
+ const host = readInvokingDshPackage();
326
+ return runtime(descriptor.name, model, descriptor.runtimeKind, descriptor.runtimeCapabilities, {
327
+ binary: {
328
+ name: '@deepseek-ai/dsh',
329
+ source: host.package.version ? 'path' : 'unknown',
330
+ ...(host.package.version ? { version: host.package.version } : {}),
331
+ ...(host.entrypoint ? { path: host.entrypoint } : {}),
332
+ contentHash: dshCompositionHash(identity),
333
+ package: host.package,
334
+ ...(host.package.error ? { error: host.package.error } : {}),
335
+ },
336
+ sdk: ownPackage(),
337
+ auditability: {
338
+ status: 'partial',
339
+ reasons: [
340
+ 'DSH does not expose a canonical digest for every active plugin and policy; the fingerprint covers provider, agent preset, and effective tool schemas',
341
+ ],
342
+ },
343
+ });
344
+ }
345
+ export function resolveExecutorRuntimeFingerprint(executorName, model, options = {}, executor) {
346
+ return executor?.runtimeFingerprint?.(model, options)
347
+ ?? getExecutorRuntimeFingerprint(executorName, model, options);
348
+ }
248
349
  export function getExecutorRuntimeFingerprint(executorName, model, options = {}) {
249
350
  const env = runtimeEnv(options);
250
- if (![
251
- 'claude',
252
- 'claude-sdk',
253
- 'codex',
254
- 'codex-sdk',
255
- 'gemini',
256
- 'anthropic-api',
257
- 'openai-api',
258
- ].includes(executorName)) {
351
+ const descriptor = getExecutorDescriptor(executorName);
352
+ if (!descriptor) {
259
353
  // Custom executors are local code. Re-read their referenced files so a
260
354
  // long-running Studio process cannot reuse stale outputs after the script
261
355
  // changes while the command line remains identical.
262
356
  return scriptRuntime(executorName, model, env);
263
357
  }
358
+ if (descriptor.fingerprint.strategy === 'dsh-host') {
359
+ return createDshHostRuntimeFingerprint(model);
360
+ }
264
361
  const pathHash = hashString(env.PATH || '');
265
362
  const cacheKey = `${executorName}\0${model}\0${pathHash}`;
266
363
  const cached = RUNTIME_CACHE.get(cacheKey);
267
364
  if (cached)
268
365
  return structuredClone(cached);
269
366
  let fp;
270
- switch (executorName) {
271
- case 'claude': {
272
- fp = runtime(executorName, model, 'agent-cli', {
273
- systemPrompt: 'native',
274
- costUSD: 'reported',
275
- trace: 'native',
276
- skillIsolation: 'full-no-partial',
277
- }, { binary: readPathBinary('claude', env) });
367
+ switch (descriptor.fingerprint.strategy) {
368
+ case 'path-cli': {
369
+ fp = runtime(descriptor.name, model, descriptor.runtimeKind, descriptor.runtimeCapabilities, { binary: readPathBinary(descriptor.fingerprint.command, env) });
278
370
  break;
279
371
  }
280
372
  case 'claude-sdk': {
281
373
  const sdkPackageJson = resolvePackageJson('@anthropic-ai/claude-agent-sdk');
282
374
  const sdk = readPackage('@anthropic-ai/claude-agent-sdk', sdkPackageJson ?? undefined);
283
375
  const claudeCodeVersion = readPackageField('@anthropic-ai/claude-agent-sdk', 'claudeCodeVersion', sdkPackageJson ?? undefined);
284
- fp = runtime(executorName, model, 'agent-sdk', {
285
- systemPrompt: 'native',
286
- costUSD: 'reported',
287
- trace: 'native',
288
- skillIsolation: 'full',
289
- }, {
376
+ fp = runtime(descriptor.name, model, descriptor.runtimeKind, descriptor.runtimeCapabilities, {
290
377
  sdk,
291
378
  binary: {
292
379
  name: 'claude-code',
@@ -297,25 +384,11 @@ export function getExecutorRuntimeFingerprint(executorName, model, options = {})
297
384
  });
298
385
  break;
299
386
  }
300
- case 'codex': {
301
- fp = runtime(executorName, model, 'agent-cli', {
302
- systemPrompt: 'prepended',
303
- costUSD: 'not-reported',
304
- trace: 'best-effort',
305
- skillIsolation: 'cwd-only',
306
- }, { binary: readPathBinary('codex', env) });
307
- break;
308
- }
309
387
  case 'codex-sdk': {
310
388
  const sdkPackageJson = resolvePackageJson('@openai/codex-sdk');
311
389
  const sdk = readPackage('@openai/codex-sdk', sdkPackageJson ?? undefined);
312
390
  const bundledCodex = readPackage('@openai/codex', sdkPackageJson ?? undefined);
313
- fp = runtime(executorName, model, 'agent-sdk', {
314
- systemPrompt: 'prepended',
315
- costUSD: 'not-reported',
316
- trace: 'best-effort',
317
- skillIsolation: 'cwd-only',
318
- }, {
391
+ fp = runtime(descriptor.name, model, descriptor.runtimeKind, descriptor.runtimeCapabilities, {
319
392
  sdk,
320
393
  binary: {
321
394
  name: 'codex',
@@ -326,27 +399,14 @@ export function getExecutorRuntimeFingerprint(executorName, model, options = {})
326
399
  });
327
400
  break;
328
401
  }
329
- case 'gemini': {
330
- fp = runtime(executorName, model, 'agent-cli', {
331
- systemPrompt: 'prepended',
332
- costUSD: 'not-reported',
333
- trace: 'none',
334
- skillIsolation: 'none',
335
- }, { binary: readPathBinary('gemini', env) });
402
+ case 'api': {
403
+ fp = runtime(descriptor.name, model, descriptor.runtimeKind, descriptor.runtimeCapabilities, { binary: { name: descriptor.name, source: 'none' } });
336
404
  break;
337
405
  }
338
- case 'anthropic-api':
339
- case 'openai-api': {
340
- fp = runtime(executorName, model, 'api', {
341
- systemPrompt: 'native',
342
- costUSD: 'not-reported',
343
- trace: 'none',
344
- skillIsolation: 'none',
345
- }, { binary: { name: executorName, source: 'none' } });
346
- break;
406
+ default: {
407
+ const unreachable = descriptor.fingerprint;
408
+ throw new Error(`unreachable executor fingerprint strategy: ${JSON.stringify(unreachable)}`);
347
409
  }
348
- default:
349
- throw new Error(`unreachable executor runtime: ${executorName}`);
350
410
  }
351
411
  RUNTIME_CACHE.set(cacheKey, structuredClone(fp));
352
412
  return structuredClone(fp);
@@ -0,0 +1,12 @@
1
+ import type { ExecResult } from '../../types/index.js';
2
+ export interface ExecutorErrorLike {
3
+ message?: string;
4
+ name?: string;
5
+ killed?: boolean;
6
+ stdout?: string;
7
+ }
8
+ export declare function asErrorLike(err: unknown): ExecutorErrorLike;
9
+ export declare function errorMessage(err: unknown, fallback?: string): string;
10
+ export declare function buildExecEnv(skillDir?: string | null): NodeJS.ProcessEnv;
11
+ export declare function timeoutExecResult(timeoutMs: number, durationMs: number): ExecResult;
12
+ export declare function interruptedExecResult(durationMs: number): ExecResult;
@@ -0,0 +1,61 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { delimiter, join } from 'node:path';
3
+ export function asErrorLike(err) {
4
+ return typeof err === 'object' && err !== null ? err : {};
5
+ }
6
+ export function errorMessage(err, fallback = 'unknown error') {
7
+ const details = asErrorLike(err);
8
+ return details.message || fallback;
9
+ }
10
+ export function buildExecEnv(skillDir) {
11
+ const proxyUrl = process.env.CCV_PROXY_URL || undefined;
12
+ const env = proxyUrl
13
+ ? { ...process.env, ANTHROPIC_BASE_URL: proxyUrl }
14
+ : { ...process.env };
15
+ if (skillDir) {
16
+ const nodeBin = join(skillDir, 'node_modules', '.bin');
17
+ if (existsSync(nodeBin)) {
18
+ env.PATH = `${nodeBin}${env.PATH ? delimiter + env.PATH : ''}`;
19
+ }
20
+ }
21
+ return env;
22
+ }
23
+ export function timeoutExecResult(timeoutMs, durationMs) {
24
+ return {
25
+ ok: false,
26
+ error: `execution timed out after ${timeoutMs / 1000}s`,
27
+ durationMs,
28
+ durationApiMs: 0,
29
+ inputTokens: 0,
30
+ outputTokens: 0,
31
+ cacheReadTokens: 0,
32
+ cacheCreationTokens: 0,
33
+ tokenUsageReportedByExecutor: false,
34
+ costUSD: 0,
35
+ costReportedByExecutor: false,
36
+ output: null,
37
+ stopReason: 'timeout',
38
+ numTurns: 0,
39
+ };
40
+ }
41
+ // SIGINT-killed child(用户在 host 按 Ctrl+C,parent 收到 SIGINT 后广播 SIGTERM 给 children)
42
+ // 跟 timeout-killed 区分:stopReason='interrupted',跟 'timeout' / 'error' 都不一样,
43
+ // caller / renderer 可以据此显示不同的语义("用户中断" vs "超时" vs "执行错误")。
44
+ export function interruptedExecResult(durationMs) {
45
+ return {
46
+ ok: false,
47
+ error: 'execution interrupted (SIGINT)',
48
+ durationMs,
49
+ durationApiMs: 0,
50
+ inputTokens: 0,
51
+ outputTokens: 0,
52
+ cacheReadTokens: 0,
53
+ cacheCreationTokens: 0,
54
+ tokenUsageReportedByExecutor: false,
55
+ costUSD: 0,
56
+ costReportedByExecutor: false,
57
+ output: null,
58
+ stopReason: 'interrupted',
59
+ numTurns: 0,
60
+ };
61
+ }
@@ -0,0 +1,44 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ /**
3
+ * Register an in-process runtime (for example an SDK-owned child) with the
4
+ * same SIGINT coordinator used by spawned executors.
5
+ */
6
+ export declare function registerSigintSubscriber(subscriber: () => void): () => void;
7
+ export declare function __resetSigintRegistryForTest(): void;
8
+ export interface SpawnHelperResult {
9
+ stdout: string;
10
+ stderr: string;
11
+ code: number | null;
12
+ signal: NodeJS.Signals | null;
13
+ killedByTimeout: boolean;
14
+ killedBySignal: NodeJS.Signals | null;
15
+ }
16
+ export interface SpawnHelperError extends Error {
17
+ stdout?: string;
18
+ stderr?: string;
19
+ code?: number | null;
20
+ signal?: NodeJS.Signals | null;
21
+ killedByTimeout?: boolean;
22
+ killedBySignal?: NodeJS.Signals | null;
23
+ }
24
+ export interface SpawnHelperOptions {
25
+ cwd?: string;
26
+ env?: NodeJS.ProcessEnv;
27
+ /** kill child after this many ms; reject with killedByTimeout=true */
28
+ timeoutMs?: number;
29
+ /** per-stream stdout/stderr byte limit; reject when either stream exceeds it */
30
+ maxBuffer?: number;
31
+ /** external abort signal; abort() 走跟 SIGINT 同一 grace 路径 */
32
+ abortSignal?: AbortSignal;
33
+ }
34
+ /**
35
+ * spawn child + 注册到全局 SIGINT registry。返回 { child, done } 让 caller 自己
36
+ * 操作 stdin(写入 / 关闭),通过 done 等结果。
37
+ *
38
+ * 适用:claude / codex / script CLI 子进程。HTTP executor(*-api)用 fetch +
39
+ * AbortSignal.timeout,自带 abort,不走这个。claude-sdk in-process 也不走。
40
+ */
41
+ export declare function spawnWithSigintPropagation(command: string, args: string[], options?: SpawnHelperOptions): {
42
+ child: ChildProcess;
43
+ done: Promise<SpawnHelperResult>;
44
+ };