dsh-lcx-codex 0.4.2 → 0.4.3-pre.2

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 (47) hide show
  1. package/README.md +116 -202
  2. package/cordis.patch.yml +3 -20
  3. package/lib/client.js +273 -146
  4. package/lib/compact-v2.js +218 -199
  5. package/lib/dsh-compat.js +227 -100
  6. package/lib/dsh-responses.js +445 -277
  7. package/lib/index.js +851 -757
  8. package/lib/json-store.js +50 -31
  9. package/lib/native-checkpoint.js +520 -194
  10. package/lib/responses-request.js +106 -121
  11. package/lib/responses-stream.js +972 -443
  12. package/lib/route.js +229 -355
  13. package/lib/service-mutex.js +73 -64
  14. package/lib/token-budget.js +176 -108
  15. package/lib/transport.js +277 -67
  16. package/lib/types/client/index.d.ts +6 -0
  17. package/lib/types/compact-v2.d.ts +104 -0
  18. package/lib/types/dsh-compat.d.ts +78 -0
  19. package/lib/types/dsh-responses.d.ts +82 -0
  20. package/lib/types/index.d.ts +83 -0
  21. package/lib/types/json-store.d.ts +10 -0
  22. package/lib/types/native-checkpoint.d.ts +213 -0
  23. package/lib/types/responses-request.d.ts +58 -0
  24. package/lib/types/responses-stream.d.ts +51 -0
  25. package/lib/types/route.d.ts +132 -0
  26. package/lib/types/service-mutex.d.ts +14 -0
  27. package/lib/types/token-budget.d.ts +50 -0
  28. package/lib/types/transport.d.ts +20 -0
  29. package/lib/types/web-run-output.d.ts +29 -0
  30. package/lib/types/web-search-alpha.d.ts +286 -0
  31. package/lib/types/web-search-capability.d.ts +26 -0
  32. package/lib/types/web-search-hosted.d.ts +246 -0
  33. package/lib/types/web-search-ref-store.d.ts +22 -0
  34. package/lib/web-run-output.js +167 -18
  35. package/lib/web-search-alpha.js +865 -163
  36. package/lib/web-search-capability.js +55 -65
  37. package/lib/web-search-hosted.js +210 -32
  38. package/lib/web-search-ref-store.js +63 -59
  39. package/package.json +79 -27
  40. package/ARCHITECTURE.md +0 -117
  41. package/CHANGELOG.md +0 -224
  42. package/README_EN.md +0 -277
  43. package/assets/dsh-lcx-codex-banner.jpg +0 -0
  44. package/lib/legacy-v3.js +0 -20
  45. package/lib/responses-replay.js +0 -68
  46. package/scripts/probe-alpha.mjs +0 -43
  47. package/scripts/validate-dsh-schema.mjs +0 -31
@@ -1,145 +1,130 @@
1
1
  // @ts-check
2
-
3
- import { clampOpenAIPromptCacheKey } from '@earendil-works/pi-ai/api/openai-prompt-cache'
4
- import { responsesTools } from './dsh-responses.js'
5
-
6
- const OPENAI_RESPONSES_MIN_OUTPUT_TOKENS = 16
7
-
8
- /** @typedef {'none' | 'short' | 'long'} CacheRetention */
9
- /** @typedef {Record<string, unknown>} UnknownRecord */
10
- /**
11
- * @typedef {object} ResponsesCompat
12
- * @property {boolean} [supportsLongCacheRetention]
13
- * @property {boolean} [supportsExplicitPromptCacheMode]
14
- */
15
- /**
16
- * @typedef {object} PiResponsesModel
17
- * @property {string} id
18
- * @property {string} provider
19
- * @property {boolean} [reasoning]
20
- * @property {Record<string, string | null>} [thinkingLevelMap]
21
- * @property {ResponsesCompat} [compat]
22
- */
23
- /**
24
- * @typedef {object} GenerationControls
25
- * @property {unknown} [reasoningEffort]
26
- * @property {unknown} [temperature]
27
- * @property {unknown} [maxTokens]
28
- */
29
- /**
30
- * @typedef {object} BuildResponsesBodyOptions
31
- * @property {PiResponsesModel | string} model
32
- * @property {unknown[]} input
33
- * @property {string} [instructions]
34
- * @property {unknown} [tools]
35
- * @property {string} [sessionId]
36
- * @property {string} [promptCacheKey]
37
- * @property {string} [promptCacheRetention]
38
- * @property {CacheRetention} [cacheRetention]
39
- * @property {unknown} [reasoningEffort]
40
- * @property {unknown} [temperature]
41
- * @property {unknown} [maxTokens]
42
- * @property {UnknownRecord} [samplingParams]
43
- */
44
-
2
+ import { clampOpenAIPromptCacheKey } from "@earendil-works/pi-ai/api/openai-prompt-cache";
3
+ import { responsesTools } from "./dsh-responses.js";
4
+ const OPENAI_RESPONSES_MIN_OUTPUT_TOKENS = 16;
45
5
  /** @param {unknown} value */
46
- function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value) }
47
-
48
- /** @param {PiResponsesModel | string} model */
49
- function modelId(model) { return typeof model === 'string' ? model : model.id }
50
-
51
- /** @param {PiResponsesModel | string} model */
6
+ function isObject(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8
+ }
9
+ function modelId(model) {
10
+ return typeof model === "string" ? model : model.id;
11
+ }
52
12
  function modelRecord(model) {
53
- return typeof model === 'string'
54
- ? /** @type {PiResponsesModel} */ ({ id: model, provider: 'lcx', reasoning: true })
55
- : model
13
+ return typeof model === "string"
14
+ ? /** @type {PiResponsesModel} */ {
15
+ id: model,
16
+ provider: "lcx",
17
+ reasoning: true,
18
+ }
19
+ : model;
56
20
  }
57
-
58
21
  /** @param {unknown} value */
59
22
  function normalizeCacheRetention(value) {
60
- return /** @type {CacheRetention} */ (['none', 'short', 'long'].includes(String(value)) ? value : 'short')
23
+ const normalized = String(value);
24
+ return normalized === "none" || normalized === "short" || normalized === "long"
25
+ ? normalized
26
+ : "short";
61
27
  }
62
-
63
28
  /**
64
29
  * Pi-parity generation controls for OpenAI Responses.
65
30
  * @param {GenerationControls & { model?: PiResponsesModel | string, includeDefaultReasoning?: boolean }} [controls]
66
31
  */
67
- export function responsesGenerationEnvelope({ model = 'unknown', reasoningEffort, temperature, maxTokens, includeDefaultReasoning = false } = {}) {
68
- const descriptor = modelRecord(model)
69
- /** @type {UnknownRecord} */
70
- const result = {}
71
- if (descriptor.reasoning !== false) {
72
- if (reasoningEffort !== undefined && reasoningEffort !== 'off') {
73
- const requested = String(reasoningEffort)
74
- const wire = descriptor.thinkingLevelMap?.[requested] ?? requested
75
- if (wire !== null) {
76
- result.reasoning = { effort: wire, summary: 'auto' }
77
- result.include = ['reasoning.encrypted_content']
78
- }
79
- } else if (includeDefaultReasoning && descriptor.provider !== 'github-copilot' && descriptor.thinkingLevelMap?.off !== null) {
80
- result.reasoning = { effort: descriptor.thinkingLevelMap?.off ?? 'none' }
32
+ export function responsesGenerationEnvelope({ model = "unknown", reasoningEffort, temperature, maxTokens, includeDefaultReasoning = false, } = {}) {
33
+ const descriptor = modelRecord(model);
34
+ const result = {};
35
+ if (descriptor.reasoning !== false) {
36
+ if (reasoningEffort !== undefined && reasoningEffort !== "off") {
37
+ const requested = String(reasoningEffort);
38
+ const wire = descriptor.thinkingLevelMap?.[requested] ?? requested;
39
+ if (wire !== null) {
40
+ result.reasoning = { effort: wire, summary: "auto" };
41
+ result.include = ["reasoning.encrypted_content"];
42
+ }
43
+ }
44
+ else if (includeDefaultReasoning &&
45
+ descriptor.provider !== "github-copilot" &&
46
+ descriptor.thinkingLevelMap?.off !== null) {
47
+ result.reasoning = { effort: descriptor.thinkingLevelMap?.off ?? "none" };
48
+ }
81
49
  }
82
- }
83
- if (temperature !== undefined) {
84
- if (!Number.isFinite(temperature)) throw Object.assign(new Error('Responses temperature must be finite'), { code: 'LCX_RESPONSES_INVALID_INPUT' })
85
- result.temperature = Number(temperature)
86
- }
87
- if (maxTokens !== undefined) {
88
- if (!Number.isSafeInteger(maxTokens) || Number(maxTokens) <= 0) throw Object.assign(new Error('Responses maxTokens must be a positive safe integer'), { code: 'LCX_RESPONSES_INVALID_INPUT' })
89
- result.max_output_tokens = Math.max(OPENAI_RESPONSES_MIN_OUTPUT_TOKENS, Number(maxTokens))
90
- }
91
- return result
50
+ if (temperature !== undefined) {
51
+ if (!Number.isFinite(temperature))
52
+ throw Object.assign(new Error("Responses temperature must be finite"), {
53
+ code: "LCX_RESPONSES_INVALID_INPUT",
54
+ });
55
+ result.temperature = Number(temperature);
56
+ }
57
+ if (maxTokens !== undefined) {
58
+ if (!Number.isSafeInteger(maxTokens) || Number(maxTokens) <= 0)
59
+ throw Object.assign(new Error("Responses maxTokens must be a positive safe integer"), { code: "LCX_RESPONSES_INVALID_INPUT" });
60
+ if (descriptor.compat?.supportsMaxOutputTokens !== false)
61
+ result.max_output_tokens = Math.max(OPENAI_RESPONSES_MIN_OUTPUT_TOKENS, Number(maxTokens));
62
+ }
63
+ return result;
92
64
  }
93
-
94
65
  /**
95
- * Build the shared LCX-owned request envelope while retaining Pi 0.84 Responses semantics.
66
+ * Build the shared LCX-owned request envelope while retaining Pi 0.85.1 Responses semantics.
96
67
  * Production LCX ordinary/compact/replay construction places the DSH system prompt in canonical input.
97
- * `instructions` remains accepted only for the exported low-level helper's backward-compatible callers.
98
68
  * @param {BuildResponsesBodyOptions} options
99
69
  */
100
- export function buildResponsesBody({ model, input, instructions, tools, sessionId, promptCacheKey, promptCacheRetention, cacheRetention, reasoningEffort, temperature, maxTokens, samplingParams }) {
101
- if (!Array.isArray(input)) throw Object.assign(new Error('Responses input must be an array'), { code: 'LCX_RESPONSES_INVALID_INPUT' })
102
- const descriptor = modelRecord(model)
103
- const retention = normalizeCacheRetention(cacheRetention)
104
- const compat = descriptor.compat ?? {}
105
- const cacheKey = retention === 'none'
106
- ? undefined
107
- : promptCacheKey ?? clampOpenAIPromptCacheKey(sessionId)
108
- const longRetention = promptCacheRetention ?? (retention === 'long' && compat.supportsLongCacheRetention !== false ? '24h' : undefined)
109
- // Pi's flag is route/model proof that prompt_cache_options is accepted; no mode keeps implicit caching.
110
- const currentCache = retention === 'short' && compat.supportsExplicitPromptCacheMode === true
111
- const explicitCache = retention === 'none' && compat.supportsExplicitPromptCacheMode === true
112
- const promptCacheOptions = currentCache ? { ttl: '30m' } : explicitCache ? { mode: 'explicit' } : undefined
113
- const nativeTools = responsesTools(tools)
114
- /** @type {UnknownRecord} */
115
- const body = {
116
- model: modelId(model),
117
- input: structuredClone(input),
118
- stream: true,
119
- store: false,
120
- ...(instructions === undefined ? {} : { instructions }),
121
- ...(nativeTools !== undefined && nativeTools.length > 0 ? { tools: nativeTools } : {}),
122
- ...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
123
- ...(longRetention ? { prompt_cache_retention: longRetention } : {}),
124
- ...(promptCacheOptions ? { prompt_cache_options: promptCacheOptions } : {}),
125
- ...responsesGenerationEnvelope({ model: descriptor, reasoningEffort, temperature, maxTokens, includeDefaultReasoning: true }),
126
- }
127
- if (isObject(samplingParams)) Object.assign(body, structuredClone(samplingParams))
128
- return body
70
+ export function buildResponsesBody({ model, input, tools, sessionId, promptCacheKey, promptCacheRetention, cacheRetention, reasoningEffort, temperature, maxTokens, samplingParams, }) {
71
+ if (!Array.isArray(input))
72
+ throw Object.assign(new Error("Responses input must be an array"), {
73
+ code: "LCX_RESPONSES_INVALID_INPUT",
74
+ });
75
+ const descriptor = modelRecord(model);
76
+ const retention = normalizeCacheRetention(cacheRetention);
77
+ const compat = descriptor.compat ?? {};
78
+ const cacheKey = retention === "none"
79
+ ? undefined
80
+ : (promptCacheKey ?? clampOpenAIPromptCacheKey(sessionId));
81
+ const longRetention = retention === "long" && compat.supportsExplicitPromptCacheMode !== true
82
+ ? promptCacheRetention ?? (compat.supportsLongCacheRetention === true ? "24h" : undefined)
83
+ : undefined;
84
+ // Pi 0.85.1 uses explicit 30m only for long retention; short is implicit.
85
+ const currentCache = retention === "long" && compat.supportsLongCacheRetention === true && compat.supportsExplicitPromptCacheMode === true;
86
+ const explicitCache = retention === "none" && compat.supportsExplicitPromptCacheMode === true;
87
+ const promptCacheOptions = currentCache
88
+ ? { ttl: "30m" }
89
+ : explicitCache
90
+ ? { mode: "explicit" }
91
+ : undefined;
92
+ const nativeTools = responsesTools(Array.isArray(tools) ? tools : undefined);
93
+ const body = {
94
+ model: modelId(model),
95
+ input: structuredClone(input),
96
+ stream: true,
97
+ store: false,
98
+ ...(nativeTools !== undefined && nativeTools.length > 0
99
+ ? { tools: nativeTools }
100
+ : {}),
101
+ ...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
102
+ ...(longRetention ? { prompt_cache_retention: longRetention } : {}),
103
+ ...(promptCacheOptions ? { prompt_cache_options: promptCacheOptions } : {}),
104
+ ...responsesGenerationEnvelope({
105
+ model: descriptor,
106
+ reasoningEffort,
107
+ temperature,
108
+ maxTokens,
109
+ includeDefaultReasoning: true,
110
+ }),
111
+ };
112
+ if (isObject(samplingParams))
113
+ Object.assign(body, structuredClone(samplingParams));
114
+ return body;
129
115
  }
130
-
131
116
  /**
132
117
  * Compact is the standard request plus the one opaque-history transition patch.
133
118
  * @param {BuildResponsesBodyOptions} options
134
119
  */
135
120
  export function buildCompactionResponsesBody(options) {
136
- const body = /** @type {UnknownRecord & { input: unknown[] }} */ (buildResponsesBody(options))
137
- if (body.input.some((item) => /** @type {UnknownRecord | undefined} */ (item)?.type === 'compaction_trigger')) {
138
- throw Object.assign(new Error('native compaction input already contains compaction_trigger'), { code: 'LCX_COMPACT_DUPLICATE_TRIGGER' })
139
- }
140
- body.input = [...body.input, { type: 'compaction_trigger' }]
141
- // Remote Compaction V2 has historically required these explicit controls.
142
- body.tool_choice = 'auto'
143
- body.parallel_tool_calls = true
144
- return body
121
+ const body = buildResponsesBody(options);
122
+ if (body.input.some((item) => isObject(item) && item.type === "compaction_trigger")) {
123
+ throw Object.assign(new Error("native compaction input already contains compaction_trigger"), { code: "LCX_COMPACT_DUPLICATE_TRIGGER" });
124
+ }
125
+ body.input = [...body.input, { type: "compaction_trigger" }];
126
+ // Remote Compaction V2 has historically required these explicit controls.
127
+ body.tool_choice = "auto";
128
+ body.parallel_tool_calls = true;
129
+ return body;
145
130
  }