@agent-proxy/zcode 0.0.0-stage → 0.11.1
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.
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/catalog.d.ts +30 -0
- package/dist/catalog.js +150 -0
- package/dist/client-signing.d.ts +46 -0
- package/dist/client-signing.js +225 -0
- package/dist/config.d.ts +78 -0
- package/dist/config.js +944 -0
- package/dist/credential-cipher.d.ts +2 -0
- package/dist/credential-cipher.js +43 -0
- package/dist/endpoint-routing.d.ts +33 -0
- package/dist/endpoint-routing.js +147 -0
- package/dist/fingerprint-data.json +12 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +648 -0
- package/dist/individual-credentials.d.ts +19 -0
- package/dist/individual-credentials.js +39 -0
- package/dist/plans.d.ts +19 -0
- package/dist/plans.js +74 -0
- package/dist/request-context.d.ts +39 -0
- package/dist/request-context.js +213 -0
- package/dist/request.d.ts +5 -0
- package/dist/request.js +414 -0
- package/dist/resources/models.js +3854 -0
- package/dist/response.d.ts +27 -0
- package/dist/response.js +725 -0
- package/dist/team-credentials.d.ts +12 -0
- package/dist/team-credentials.js +123 -0
- package/dist/vision-template.json +1 -0
- package/dist/vision.d.ts +62 -0
- package/dist/vision.js +204 -0
- package/dist/wire.d.ts +23 -0
- package/dist/wire.js +25 -0
- package/package.json +42 -4
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { decryptZcodeCredential } from "./credential-cipher.js";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
/** 读取当前账号身份对应的个人 Coding Plan Key。 */
|
|
6
|
+
export function readZcodeIndividualCredential(credentials, family, env = process.env) {
|
|
7
|
+
const rawProfile = credentials[`oauth:${family}:user_info`];
|
|
8
|
+
if (rawProfile === undefined || rawProfile === null)
|
|
9
|
+
return { state: "absent" };
|
|
10
|
+
if (typeof rawProfile !== "string" || !rawProfile.trim())
|
|
11
|
+
return { state: "unavailable" };
|
|
12
|
+
let profile;
|
|
13
|
+
try {
|
|
14
|
+
profile = JSON.parse(decryptZcodeCredential(rawProfile, env));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { state: "unavailable" };
|
|
18
|
+
}
|
|
19
|
+
// ZCode 3.14 的 user_info 使用 `user_id`;部分旧布局/接口写 `id`,
|
|
20
|
+
// 再兜底 email 只用于识别账号变化,不参与 Key 名拼接之外的任何输出。
|
|
21
|
+
const accountIdentity = isRecord(profile)
|
|
22
|
+
? [profile.id, profile.user_id, profile.email]
|
|
23
|
+
.find((value) => typeof value === "string" && Boolean(value.trim()))
|
|
24
|
+
?.trim() ?? ""
|
|
25
|
+
: "";
|
|
26
|
+
if (!accountIdentity)
|
|
27
|
+
return { state: "unavailable" };
|
|
28
|
+
const provider = `account:${family}-individual-coding-plan`;
|
|
29
|
+
const key = credentials[`account-provider:coding-plan:${provider}:account:${encodeURIComponent(accountIdentity)}:api-key`];
|
|
30
|
+
if (typeof key !== "string" || !key.trim())
|
|
31
|
+
return { state: "unavailable", identity: accountIdentity };
|
|
32
|
+
try {
|
|
33
|
+
const apiKey = decryptZcodeCredential(key, env).trim();
|
|
34
|
+
return apiKey ? { state: "available", identity: accountIdentity, apiKey } : { state: "unavailable", identity: accountIdentity };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { state: "unavailable", identity: accountIdentity };
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/plans.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** ZCode billing/balance 返回的可用模型能力。 */
|
|
2
|
+
export interface ZcodeBillingPlanModels {
|
|
3
|
+
planId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
status: string;
|
|
6
|
+
modelIds: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/** ZCode 客户端用于把 billing 计划归入 Start Plan 的字符串规则。 */
|
|
9
|
+
export declare function isZcodeStartPlanIdentity(planId: unknown, name: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* 从 billing/balance 的原始 JSON 中提取当前有效 Start Plan 实例的模型能力。
|
|
12
|
+
*
|
|
13
|
+
* 这里只消费公开的套餐元数据,不保留 balances、token 或任何凭据;未知字段和
|
|
14
|
+
* 非模型 capability 会被忽略。个人/团队 Coding Plan 不从此接口推断,调用方仍需把
|
|
15
|
+
* planId 映射到已解析的本地 Start provider。
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseZcodeBillingPlans(value: unknown): ZcodeBillingPlanModels[];
|
|
18
|
+
/** 汇总多个有效套餐的模型能力,按模型 ID 大小写不敏感去重。 */
|
|
19
|
+
export declare function mergeZcodeBillingModelIds(plans: readonly ZcodeBillingPlanModels[]): string[];
|
package/dist/plans.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** ZCode 客户端用于把 billing 计划归入 Start Plan 的字符串规则。 */
|
|
2
|
+
export function isZcodeStartPlanIdentity(planId, name) {
|
|
3
|
+
const id = typeof planId === "string" ? planId.trim().toLowerCase() : "";
|
|
4
|
+
const label = typeof name === "string" ? name.trim().toLowerCase() : "";
|
|
5
|
+
// billing 端的 start-plan 实例通常由 plan_id 标识;name 仅作为兜底。
|
|
6
|
+
// 两个字段都为空时沿用客户端的默认归类,但仍由调用方单独校验 active 状态。
|
|
7
|
+
return !id && !label || id.includes("start-plan") || label.includes("start plan");
|
|
8
|
+
}
|
|
9
|
+
function record(value) {
|
|
10
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function modelIds(value) {
|
|
13
|
+
if (!Array.isArray(value))
|
|
14
|
+
return [];
|
|
15
|
+
const ids = value.flatMap((capability) => {
|
|
16
|
+
if (typeof capability !== "string" || !capability.startsWith("model:"))
|
|
17
|
+
return [];
|
|
18
|
+
const id = capability.slice("model:".length).trim();
|
|
19
|
+
return id && /^[\x20-\x7e]+$/.test(id) ? [id] : [];
|
|
20
|
+
});
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
return ids.filter((id) => {
|
|
23
|
+
const key = id.toLowerCase();
|
|
24
|
+
if (seen.has(key))
|
|
25
|
+
return false;
|
|
26
|
+
seen.add(key);
|
|
27
|
+
return true;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 从 billing/balance 的原始 JSON 中提取当前有效 Start Plan 实例的模型能力。
|
|
32
|
+
*
|
|
33
|
+
* 这里只消费公开的套餐元数据,不保留 balances、token 或任何凭据;未知字段和
|
|
34
|
+
* 非模型 capability 会被忽略。个人/团队 Coding Plan 不从此接口推断,调用方仍需把
|
|
35
|
+
* planId 映射到已解析的本地 Start provider。
|
|
36
|
+
*/
|
|
37
|
+
export function parseZcodeBillingPlans(value) {
|
|
38
|
+
const root = record(value) && record(value.data) ? value.data : value;
|
|
39
|
+
const plans = record(root) && Array.isArray(root.plans) ? root.plans : [];
|
|
40
|
+
return plans.flatMap((plan) => {
|
|
41
|
+
if (!record(plan))
|
|
42
|
+
return [];
|
|
43
|
+
const status = typeof plan.status === "string" ? plan.status.trim() : "";
|
|
44
|
+
const planId = typeof plan.plan_id === "string" ? plan.plan_id.trim() : "";
|
|
45
|
+
const name = typeof plan.name === "string" ? plan.name.trim() : "";
|
|
46
|
+
if (status !== "active" || !isZcodeStartPlanIdentity(plan.plan_id, plan.name)
|
|
47
|
+
|| !planId || !name || !Array.isArray(plan.entitlements))
|
|
48
|
+
return [];
|
|
49
|
+
const models = plan.entitlements.flatMap((entitlement) => record(entitlement) ? modelIds(entitlement.capabilities) : []);
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const unique = models.filter((id) => {
|
|
52
|
+
const key = id.toLowerCase();
|
|
53
|
+
if (seen.has(key))
|
|
54
|
+
return false;
|
|
55
|
+
seen.add(key);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
return unique.length ? [{ planId, name, status, modelIds: unique }] : [];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** 汇总多个有效套餐的模型能力,按模型 ID 大小写不敏感去重。 */
|
|
62
|
+
export function mergeZcodeBillingModelIds(plans) {
|
|
63
|
+
const result = [];
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
for (const plan of plans)
|
|
66
|
+
for (const id of plan.modelIds) {
|
|
67
|
+
const key = id.toLowerCase();
|
|
68
|
+
if (!seen.has(key)) {
|
|
69
|
+
seen.add(key);
|
|
70
|
+
result.push(id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ZcodeProviderSnapshot } from "./config.ts";
|
|
2
|
+
/** ZCode 客户端内置 agent 系统提示词(真实会话流量提取),供目录条目的 base_instructions 使用。 */
|
|
3
|
+
export declare const ZCODE_AGENT_SYSTEM_PROMPT: string;
|
|
4
|
+
export type ZcodePlan = "coding-plan" | "start-plan" | "api-key";
|
|
5
|
+
export interface ZcodeIdentity {
|
|
6
|
+
appVersion: string;
|
|
7
|
+
language: string;
|
|
8
|
+
timezone: string;
|
|
9
|
+
platform: string;
|
|
10
|
+
arch: string;
|
|
11
|
+
osVersion: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ZcodeRequestContext {
|
|
14
|
+
requestId: string;
|
|
15
|
+
traceId: string;
|
|
16
|
+
queryId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
sessionType: "main" | "subagent" | "other";
|
|
19
|
+
}
|
|
20
|
+
export declare function readZcodeIdentity(): ZcodeIdentity;
|
|
21
|
+
export declare function zcodePlan(snapshot: ZcodeProviderSnapshot): ZcodePlan;
|
|
22
|
+
/** 构建完全由网关控制的来源头;不接受入站请求覆盖。 */
|
|
23
|
+
export declare function buildZcodeSourceHeaders(identity: ZcodeIdentity): Headers;
|
|
24
|
+
/** 模型请求所需的协议、归因和套餐鉴权头。 */
|
|
25
|
+
export declare function buildZcodeModelHeaders(identity: ZcodeIdentity, context: ZcodeRequestContext, plan: ZcodePlan, apiKey: string): Headers;
|
|
26
|
+
/**
|
|
27
|
+
* 只处理已转换的 Anthropic body:移除非 system 的旧标记,并在最后一个可
|
|
28
|
+
* 缓存内容块写入临时标记。thinking/redacted_thinking 永远不作为标记目标。
|
|
29
|
+
*/
|
|
30
|
+
export declare function decorateZcodeBody(body: Record<string, unknown>, context: ZcodeRequestContext): Record<string, unknown>;
|
|
31
|
+
export declare function createZcodeContexts(options?: {
|
|
32
|
+
now?: () => number;
|
|
33
|
+
uuid?: () => string;
|
|
34
|
+
maxEntries?: number;
|
|
35
|
+
ttlMs?: number;
|
|
36
|
+
}): {
|
|
37
|
+
resolve(request: Request, snapshot: ZcodeProviderSnapshot): ZcodeRequestContext;
|
|
38
|
+
close(): void;
|
|
39
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import fingerprintData from "./fingerprint-data.json";
|
|
5
|
+
const FINGERPRINT = fingerprintData;
|
|
6
|
+
/** ZCode 客户端内置 agent 系统提示词(真实会话流量提取),供目录条目的 base_instructions 使用。 */
|
|
7
|
+
export const ZCODE_AGENT_SYSTEM_PROMPT = FINGERPRINT.agentSystemPrompt;
|
|
8
|
+
const PRINTABLE_ASCII = /^[\x20-\x7e]+$/;
|
|
9
|
+
const DEFAULT_TTL_MS = 900_000;
|
|
10
|
+
const DEFAULT_MAX_ENTRIES = 1024;
|
|
11
|
+
function printable(value, fallback = "unknown") {
|
|
12
|
+
if (typeof value !== "string")
|
|
13
|
+
return fallback;
|
|
14
|
+
const normalized = value.trim();
|
|
15
|
+
return normalized && PRINTABLE_ASCII.test(normalized) ? normalized : fallback;
|
|
16
|
+
}
|
|
17
|
+
function readIdentity() {
|
|
18
|
+
let appVersion = "unknown";
|
|
19
|
+
if (process.platform === "darwin") {
|
|
20
|
+
try {
|
|
21
|
+
appVersion = printable(execFileSync("/usr/bin/plutil", ["-extract", "CFBundleShortVersionString", "raw", "/Applications/ZCode.app/Contents/Info.plist"], {
|
|
22
|
+
encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1000,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
catch { /* 未安装或不可读取时保持无凭据的 unknown。 */ }
|
|
26
|
+
}
|
|
27
|
+
let language = "unknown";
|
|
28
|
+
let timezone = "unknown";
|
|
29
|
+
try {
|
|
30
|
+
language = printable(Intl.DateTimeFormat().resolvedOptions().locale);
|
|
31
|
+
}
|
|
32
|
+
catch { /* 保持 unknown。 */ }
|
|
33
|
+
try {
|
|
34
|
+
timezone = printable(Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
35
|
+
}
|
|
36
|
+
catch { /* 保持 unknown。 */ }
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
appVersion,
|
|
39
|
+
language,
|
|
40
|
+
timezone,
|
|
41
|
+
platform: printable(process.platform),
|
|
42
|
+
arch: printable(os.arch()),
|
|
43
|
+
osVersion: printable(os.release()),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// 首次启用时读取公开版本;关闭 ZCode 的命令不执行额外系统查询。
|
|
47
|
+
let identityCache;
|
|
48
|
+
export function readZcodeIdentity() {
|
|
49
|
+
return { ...(identityCache ??= readIdentity()) };
|
|
50
|
+
}
|
|
51
|
+
export function zcodePlan(snapshot) {
|
|
52
|
+
if (snapshot.plan === "start-plan")
|
|
53
|
+
return "start-plan";
|
|
54
|
+
if (snapshot.plan === "api-key")
|
|
55
|
+
return "api-key";
|
|
56
|
+
return "coding-plan";
|
|
57
|
+
}
|
|
58
|
+
function osCategory(platform) {
|
|
59
|
+
if (platform === "darwin")
|
|
60
|
+
return "macos";
|
|
61
|
+
if (platform === "win32")
|
|
62
|
+
return "windows";
|
|
63
|
+
return platform === "unknown" ? "unknown" : "linux";
|
|
64
|
+
}
|
|
65
|
+
/** 构建完全由网关控制的来源头;不接受入站请求覆盖。 */
|
|
66
|
+
export function buildZcodeSourceHeaders(identity) {
|
|
67
|
+
const appVersion = printable(identity.appVersion);
|
|
68
|
+
const platform = printable(identity.platform);
|
|
69
|
+
const headers = new Headers({
|
|
70
|
+
"HTTP-Referer": FINGERPRINT.referer,
|
|
71
|
+
"User-Agent": FINGERPRINT.userAgentTemplate.replace("{version}", appVersion),
|
|
72
|
+
"X-Title": FINGERPRINT.title,
|
|
73
|
+
"X-Release-Channel": FINGERPRINT.releaseChannel,
|
|
74
|
+
"X-Client-Language": printable(identity.language),
|
|
75
|
+
"X-Client-Timezone": printable(identity.timezone),
|
|
76
|
+
"X-Platform": `${platform}-${printable(identity.arch)}`,
|
|
77
|
+
"X-Os-Category": osCategory(platform),
|
|
78
|
+
"X-Os-Version": printable(identity.osVersion),
|
|
79
|
+
});
|
|
80
|
+
if (appVersion !== "unknown")
|
|
81
|
+
headers.set("X-ZCode-App-Version", appVersion);
|
|
82
|
+
headers.set("X-ZCode-Agent", FINGERPRINT.agent);
|
|
83
|
+
return headers;
|
|
84
|
+
}
|
|
85
|
+
/** 模型请求所需的协议、归因和套餐鉴权头。 */
|
|
86
|
+
export function buildZcodeModelHeaders(identity, context, plan, apiKey) {
|
|
87
|
+
const headers = buildZcodeSourceHeaders(identity);
|
|
88
|
+
headers.set("content-type", "application/json");
|
|
89
|
+
headers.set("accept", "text/event-stream");
|
|
90
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
91
|
+
headers.set("x-request-id", context.requestId);
|
|
92
|
+
headers.set("x-zcode-session-type", context.sessionType);
|
|
93
|
+
headers.set("x-zcode-trace-id", context.traceId);
|
|
94
|
+
headers.set("x-query-id", context.queryId);
|
|
95
|
+
headers.set("x-session-id", context.sessionId);
|
|
96
|
+
headers.set("authorization", `Bearer ${apiKey}`);
|
|
97
|
+
if (plan !== "start-plan")
|
|
98
|
+
headers.set("x-api-key", apiKey);
|
|
99
|
+
return headers;
|
|
100
|
+
}
|
|
101
|
+
function headerSessionKey(request) {
|
|
102
|
+
for (const name of ["thread-id", "session-id"]) {
|
|
103
|
+
const value = request.headers.get(name);
|
|
104
|
+
if (value) {
|
|
105
|
+
const normalized = value.trim();
|
|
106
|
+
if (normalized.length <= 128 && PRINTABLE_ASCII.test(normalized))
|
|
107
|
+
return `${name}:${normalized}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function clone(value) {
|
|
112
|
+
if (Array.isArray(value))
|
|
113
|
+
return value.map(clone);
|
|
114
|
+
if (value !== null && typeof value === "object")
|
|
115
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone(item)]));
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function record(value) {
|
|
119
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 只处理已转换的 Anthropic body:移除非 system 的旧标记,并在最后一个可
|
|
123
|
+
* 缓存内容块写入临时标记。thinking/redacted_thinking 永远不作为标记目标。
|
|
124
|
+
*/
|
|
125
|
+
export function decorateZcodeBody(body, context) {
|
|
126
|
+
const result = clone(body);
|
|
127
|
+
const messages = result.messages;
|
|
128
|
+
if (Array.isArray(messages)) {
|
|
129
|
+
for (const message of messages) {
|
|
130
|
+
if (!record(message) || message.role === "system" || !Array.isArray(message.content))
|
|
131
|
+
continue;
|
|
132
|
+
for (const block of message.content)
|
|
133
|
+
if (record(block))
|
|
134
|
+
delete block.cache_control;
|
|
135
|
+
}
|
|
136
|
+
outer: for (let index = messages.length - 1; index >= 0; index--) {
|
|
137
|
+
const message = messages[index];
|
|
138
|
+
if (!record(message) || message.role === "system" || !Array.isArray(message.content))
|
|
139
|
+
continue;
|
|
140
|
+
for (let blockIndex = message.content.length - 1; blockIndex >= 0; blockIndex--) {
|
|
141
|
+
const block = message.content[blockIndex];
|
|
142
|
+
if (!record(block) || block.type === "thinking" || block.type === "redacted_thinking")
|
|
143
|
+
continue;
|
|
144
|
+
block.cache_control = { type: "ephemeral" };
|
|
145
|
+
break outer;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
result.metadata = {
|
|
150
|
+
...(record(result.metadata) ? result.metadata : {}),
|
|
151
|
+
user_id: JSON.stringify({ account_uuid: "", session_id: context.sessionId }),
|
|
152
|
+
};
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
export function createZcodeContexts(options = {}) {
|
|
156
|
+
const now = options.now ?? Date.now;
|
|
157
|
+
const uuid = options.uuid ?? randomUUID;
|
|
158
|
+
const maxEntries = Math.max(1, Math.trunc(options.maxEntries ?? DEFAULT_MAX_ENTRIES));
|
|
159
|
+
const ttlMs = Math.max(0, options.ttlMs ?? DEFAULT_TTL_MS);
|
|
160
|
+
const scopes = new WeakMap();
|
|
161
|
+
const sessions = new Map();
|
|
162
|
+
let closed = false;
|
|
163
|
+
function scope(snapshot) {
|
|
164
|
+
const cached = scopes.get(snapshot);
|
|
165
|
+
if (cached)
|
|
166
|
+
return cached;
|
|
167
|
+
// 摘要只作为内存 Map 的不可逆作用域,不进入返回值、请求头或日志。
|
|
168
|
+
const value = createHash("sha256").update(`${snapshot.family}\n${snapshot.providerID}\n${snapshot.apiKey}`).digest("hex");
|
|
169
|
+
scopes.set(snapshot, value);
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function purge(time) {
|
|
173
|
+
for (const [key, value] of sessions)
|
|
174
|
+
if (value.expiresAt <= time)
|
|
175
|
+
sessions.delete(key);
|
|
176
|
+
while (sessions.size > maxEntries)
|
|
177
|
+
sessions.delete(sessions.keys().next().value);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
resolve(request, snapshot) {
|
|
181
|
+
if (closed)
|
|
182
|
+
throw new Error("ZCode 请求上下文已关闭");
|
|
183
|
+
const time = now();
|
|
184
|
+
purge(time);
|
|
185
|
+
const incoming = headerSessionKey(request);
|
|
186
|
+
let sessionId;
|
|
187
|
+
if (incoming) {
|
|
188
|
+
const key = `${scope(snapshot)}\n${incoming}`;
|
|
189
|
+
const existing = sessions.get(key);
|
|
190
|
+
if (existing && existing.expiresAt > time) {
|
|
191
|
+
sessionId = existing.sessionId;
|
|
192
|
+
existing.expiresAt = time + ttlMs;
|
|
193
|
+
sessions.delete(key);
|
|
194
|
+
sessions.set(key, existing);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
sessionId = uuid();
|
|
198
|
+
sessions.set(key, { sessionId, expiresAt: time + ttlMs });
|
|
199
|
+
purge(time);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else
|
|
203
|
+
sessionId = uuid();
|
|
204
|
+
const parent = request.headers.get("x-codex-parent-thread-id")?.trim();
|
|
205
|
+
const subagent = Boolean(parent && parent.length <= 128 && PRINTABLE_ASCII.test(parent));
|
|
206
|
+
return {
|
|
207
|
+
requestId: uuid(), traceId: uuid(), queryId: uuid(), sessionId,
|
|
208
|
+
sessionType: subagent ? "subagent" : incoming ? "main" : "other",
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
close() { closed = true; sessions.clear(); },
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ZcodeTranslatedRequest } from "./wire.ts";
|
|
2
|
+
export declare class ZcodeRequestError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function translateZcodeRequest(body: Record<string, unknown>, upstreamModel: string): ZcodeTranslatedRequest;
|