@covro/covro 0.0.0-stage → 0.1.0-dev.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 +173 -2
- package/README.zh-CN.md +174 -0
- package/bin/covro.mjs +8 -0
- package/dist/cli/control.js +66 -0
- package/dist/cli/control.js.map +7 -0
- package/dist/cli/daemon.js +118 -0
- package/dist/cli/daemon.js.map +7 -0
- package/dist/cli/main.js +152 -0
- package/dist/cli/main.js.map +7 -0
- package/dist/cli/paths.js +60 -0
- package/dist/cli/paths.js.map +7 -0
- package/dist/cli/service.js +87 -0
- package/dist/cli/service.js.map +7 -0
- package/dist/codex/adapter.js +1195 -0
- package/dist/codex/adapter.js.map +7 -0
- package/dist/codex/auto-review.js +102 -0
- package/dist/codex/auto-review.js.map +7 -0
- package/dist/codex/compatibility.js +7 -0
- package/dist/codex/compatibility.js.map +7 -0
- package/dist/codex/mapping.js +210 -0
- package/dist/codex/mapping.js.map +7 -0
- package/dist/codex/permissions.js +51 -0
- package/dist/codex/permissions.js.map +7 -0
- package/dist/codex/thread-title-policy.js +52 -0
- package/dist/codex/thread-title-policy.js.map +7 -0
- package/dist/codex/thread-titles.js +259 -0
- package/dist/codex/thread-titles.js.map +7 -0
- package/dist/codex/transport.js +236 -0
- package/dist/codex/transport.js.map +7 -0
- package/dist/codex/turn-token-usage.js +46 -0
- package/dist/codex/turn-token-usage.js.map +7 -0
- package/dist/codex/types.js +1 -0
- package/dist/codex/types.js.map +7 -0
- package/dist/drizzle/0000_past_wildside.sql +45 -0
- package/dist/drizzle/0001_shocking_ender_wiggin.sql +2 -0
- package/dist/drizzle/0002_allow_parallel_threads.sql +1 -0
- package/dist/drizzle/0003_project_registrations.sql +11 -0
- package/dist/drizzle/0004_turn_usage.sql +6 -0
- package/dist/drizzle/meta/0000_snapshot.json +309 -0
- package/dist/drizzle/meta/0001_snapshot.json +323 -0
- package/dist/drizzle/meta/0002_snapshot.json +315 -0
- package/dist/drizzle/meta/0003_snapshot.json +381 -0
- package/dist/drizzle/meta/0004_snapshot.json +420 -0
- package/dist/drizzle/meta/_journal.json +41 -0
- package/dist/server/auth.js +47 -0
- package/dist/server/auth.js.map +7 -0
- package/dist/server/compaction.js +40 -0
- package/dist/server/compaction.js.map +7 -0
- package/dist/server/config-store.js +147 -0
- package/dist/server/config-store.js.map +7 -0
- package/dist/server/config.js +124 -0
- package/dist/server/config.js.map +7 -0
- package/dist/server/db/index.js +21 -0
- package/dist/server/db/index.js.map +7 -0
- package/dist/server/db/migrate.js +26 -0
- package/dist/server/db/migrate.js.map +7 -0
- package/dist/server/db/operations.js +201 -0
- package/dist/server/db/operations.js.map +7 -0
- package/dist/server/db/schema.js +99 -0
- package/dist/server/db/schema.js.map +7 -0
- package/dist/server/db/turn-usage.js +31 -0
- package/dist/server/db/turn-usage.js.map +7 -0
- package/dist/server/events.js +123 -0
- package/dist/server/events.js.map +7 -0
- package/dist/server/http.js +305 -0
- package/dist/server/http.js.map +7 -0
- package/dist/server/images.js +49 -0
- package/dist/server/images.js.map +7 -0
- package/dist/server/instance-lock.js +25 -0
- package/dist/server/instance-lock.js.map +7 -0
- package/dist/server/local-files.js +100 -0
- package/dist/server/local-files.js.map +7 -0
- package/dist/server/main.js +125 -0
- package/dist/server/main.js.map +7 -0
- package/dist/server/markdown.js +28 -0
- package/dist/server/markdown.js.map +7 -0
- package/dist/server/projection.js +348 -0
- package/dist/server/projection.js.map +7 -0
- package/dist/server/projects.js +230 -0
- package/dist/server/projects.js.map +7 -0
- package/dist/server/service.js +1221 -0
- package/dist/server/service.js.map +7 -0
- package/dist/server/snapshot.js +98 -0
- package/dist/server/snapshot.js.map +7 -0
- package/dist/server/thread-export.js +97 -0
- package/dist/server/thread-export.js.map +7 -0
- package/dist/server/websocket.js +126 -0
- package/dist/server/websocket.js.map +7 -0
- package/dist/shared/async-questions.js +39 -0
- package/dist/shared/async-questions.js.map +7 -0
- package/dist/shared/codex-compatibility.js +56 -0
- package/dist/shared/codex-compatibility.js.map +7 -0
- package/dist/shared/elicitation.js +314 -0
- package/dist/shared/elicitation.js.map +7 -0
- package/dist/shared/errors.js +24 -0
- package/dist/shared/errors.js.map +7 -0
- package/dist/shared/gateway-config.js +1 -0
- package/dist/shared/gateway-config.js.map +7 -0
- package/dist/shared/images.js +21 -0
- package/dist/shared/images.js.map +7 -0
- package/dist/shared/package.js +5 -0
- package/dist/shared/package.js.map +7 -0
- package/dist/shared/permissions.js +63 -0
- package/dist/shared/permissions.js.map +7 -0
- package/dist/shared/thread-name.js +14 -0
- package/dist/shared/thread-name.js.map +7 -0
- package/dist/shared/thread-summary.js +24 -0
- package/dist/shared/thread-summary.js.map +7 -0
- package/dist/shared/types.js +1 -0
- package/dist/shared/types.js.map +7 -0
- package/dist/shared/updates.js +7 -0
- package/dist/shared/updates.js.map +7 -0
- package/dist/shared/validation.js +116 -0
- package/dist/shared/validation.js.map +7 -0
- package/dist/shared/versions.js +36 -0
- package/dist/shared/versions.js.map +7 -0
- package/dist/update-worker.cjs +21968 -0
- package/dist/updates/files.js +54 -0
- package/dist/updates/files.js.map +7 -0
- package/dist/updates/manager.js +315 -0
- package/dist/updates/manager.js.map +7 -0
- package/dist/updates/registry.js +71 -0
- package/dist/updates/registry.js.map +7 -0
- package/dist/updates/transaction.js +96 -0
- package/dist/updates/transaction.js.map +7 -0
- package/dist/updates/worker.js +345 -0
- package/dist/updates/worker.js.map +7 -0
- package/dist/web/assets/index-BW9afH_Y.js +83 -0
- package/dist/web/assets/index-L3S1ZiO0.css +1 -0
- package/dist/web/covro.svg +6 -0
- package/dist/web/index.html +15 -0
- package/package.json +88 -4
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { permissionModes } from "../shared/permissions.js";
|
|
2
|
+
function permissionSettings(sandbox, approvalPolicy, reviewer, profile, cwd) {
|
|
3
|
+
const result = {
|
|
4
|
+
sandbox: sandbox ? {
|
|
5
|
+
dangerFullAccess: "danger-full-access",
|
|
6
|
+
readOnly: "read-only",
|
|
7
|
+
workspaceWrite: "workspace-write",
|
|
8
|
+
externalSandbox: "external-sandbox"
|
|
9
|
+
}[sandbox.type] : null,
|
|
10
|
+
approvalPolicy: typeof approvalPolicy === "string" ? approvalPolicy : approvalPolicy ? "granular" : null,
|
|
11
|
+
approvalReviewer: reviewer ?? null,
|
|
12
|
+
networkAccess: !sandbox ? null : sandbox.type === "dangerFullAccess" ? true : sandbox.type === "externalSandbox" ? sandbox.networkAccess === "enabled" : sandbox.networkAccess,
|
|
13
|
+
permissionProfile: profile ?? null,
|
|
14
|
+
writableRoots: sandbox?.type === "workspaceWrite" ? sandbox.writableRoots : null,
|
|
15
|
+
excludeTmpdirEnvVar: sandbox?.type === "workspaceWrite" ? sandbox.excludeTmpdirEnvVar : null,
|
|
16
|
+
excludeSlashTmp: sandbox?.type === "workspaceWrite" ? sandbox.excludeSlashTmp : null,
|
|
17
|
+
permissionMode: null
|
|
18
|
+
};
|
|
19
|
+
const builtins = [":workspace", ":read-only", ":danger-full-access"];
|
|
20
|
+
if (profile && (profile.extends !== null || !builtins.includes(profile.id)) || sandbox?.type === "externalSandbox" || result.approvalPolicy === "granular") {
|
|
21
|
+
result.permissionMode = "custom";
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
if (!sandbox || !approvalPolicy || !reviewer || profile === void 0) return result;
|
|
25
|
+
const standardBoundary = sandbox.type === "workspaceWrite" ? !sandbox.networkAccess && !sandbox.excludeTmpdirEnvVar && !sandbox.excludeSlashTmp && sandbox.writableRoots.every((root) => root === cwd) : sandbox.type === "readOnly" ? !sandbox.networkAccess : sandbox.type === "dangerFullAccess";
|
|
26
|
+
result.permissionMode = Object.keys(permissionModes).find((mode) => {
|
|
27
|
+
const preset = permissionModes[mode];
|
|
28
|
+
return standardBoundary && result.sandbox === preset.sandbox && approvalPolicy === preset.approvalPolicy && reviewer === preset.approvalReviewer && (!profile || profile.id === preset.profile);
|
|
29
|
+
}) ?? "custom";
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function permissionCapabilities(requirements, autoReview, unavailableReason) {
|
|
33
|
+
return {
|
|
34
|
+
...unavailableReason ? { unavailableReason } : {},
|
|
35
|
+
autoReview: autoReview && requirements?.featureRequirements?.guardian_approval !== false,
|
|
36
|
+
allowedSandboxModes: requirements?.allowedSandboxModes ?? null,
|
|
37
|
+
allowedApprovalPolicies: requirements?.allowedApprovalPolicies?.filter(
|
|
38
|
+
(policy) => typeof policy === "string"
|
|
39
|
+
) ?? null,
|
|
40
|
+
allowedReviewers: requirements?.allowedApprovalsReviewers ?? null,
|
|
41
|
+
allowedProfiles: requirements?.allowedPermissionProfiles ? Object.keys(requirements.allowedPermissionProfiles).filter(
|
|
42
|
+
(id) => requirements.allowedPermissionProfiles?.[id] === true
|
|
43
|
+
) : null,
|
|
44
|
+
autoReviewRequiredModels: requirements?.autoReview?.requiredOnModels ?? []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
permissionCapabilities,
|
|
49
|
+
permissionSettings
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/codex/permissions.ts"],
|
|
4
|
+
"sourcesContent": ["import { permissionModes } from '../shared/permissions.js';\nimport type { PermissionCapabilities, PermissionMode, RuntimeSettings } from '../shared/types.js';\nimport type { ActivePermissionProfile } from './generated/v2/ActivePermissionProfile.js';\nimport type { AskForApproval } from './generated/v2/AskForApproval.js';\nimport type { ConfigRequirements } from './generated/v2/ConfigRequirements.js';\nimport type { SandboxPolicy } from './generated/v2/SandboxPolicy.js';\n\n/** 0.163.0 experimental wire fields omitted by generate-ts without --experimental.\n * initialize.experimentalApi is already enabled. Keep these extensions at the adapter boundary.\n */\nexport type PermissionSelection = { permissions?: string };\nexport type ProfileProvenance = { activePermissionProfile?: ActivePermissionProfile | null };\nexport type PermissionRequirements = ConfigRequirements & {\n allowedApprovalsReviewers?: string[] | null;\n};\n\n/** Only classify after seeing the actual boundary, reviewer and profile provenance. */\nexport function permissionSettings(\n sandbox?: SandboxPolicy,\n approvalPolicy?: AskForApproval | null,\n reviewer?: string | null,\n profile?: ActivePermissionProfile | null,\n cwd?: string,\n): Omit<RuntimeSettings, 'model' | 'reasoningEffort' | 'permissionCapabilities'> {\n const result: ReturnType<typeof permissionSettings> = {\n sandbox: sandbox\n ? (\n {\n dangerFullAccess: 'danger-full-access',\n readOnly: 'read-only',\n workspaceWrite: 'workspace-write',\n externalSandbox: 'external-sandbox',\n } as const\n )[sandbox.type]\n : null,\n approvalPolicy:\n typeof approvalPolicy === 'string' ? approvalPolicy : approvalPolicy ? 'granular' : null,\n approvalReviewer: reviewer ?? null,\n networkAccess: !sandbox\n ? null\n : sandbox.type === 'dangerFullAccess'\n ? true\n : sandbox.type === 'externalSandbox'\n ? sandbox.networkAccess === 'enabled'\n : sandbox.networkAccess,\n permissionProfile: profile ?? null,\n writableRoots: sandbox?.type === 'workspaceWrite' ? sandbox.writableRoots : null,\n excludeTmpdirEnvVar: sandbox?.type === 'workspaceWrite' ? sandbox.excludeTmpdirEnvVar : null,\n excludeSlashTmp: sandbox?.type === 'workspaceWrite' ? sandbox.excludeSlashTmp : null,\n permissionMode: null,\n };\n const builtins: string[] = [':workspace', ':read-only', ':danger-full-access'];\n if (\n (profile && (profile.extends !== null || !builtins.includes(profile.id))) ||\n sandbox?.type === 'externalSandbox' ||\n result.approvalPolicy === 'granular'\n ) {\n result.permissionMode = 'custom';\n return result;\n }\n if (!sandbox || !approvalPolicy || !reviewer || profile === undefined) return result;\n const standardBoundary =\n sandbox.type === 'workspaceWrite'\n ? !sandbox.networkAccess &&\n !sandbox.excludeTmpdirEnvVar &&\n !sandbox.excludeSlashTmp &&\n sandbox.writableRoots.every((root) => root === cwd)\n : sandbox.type === 'readOnly'\n ? !sandbox.networkAccess\n : sandbox.type === 'dangerFullAccess';\n result.permissionMode =\n (Object.keys(permissionModes) as PermissionMode[]).find((mode) => {\n const preset = permissionModes[mode];\n return (\n standardBoundary &&\n result.sandbox === preset.sandbox &&\n approvalPolicy === preset.approvalPolicy &&\n reviewer === preset.approvalReviewer &&\n (!profile || profile.id === preset.profile)\n );\n }) ?? 'custom';\n return result;\n}\n\nexport function permissionCapabilities(\n requirements: PermissionRequirements | null,\n autoReview: boolean,\n unavailableReason?: string,\n): PermissionCapabilities {\n return {\n ...(unavailableReason ? { unavailableReason } : {}),\n autoReview: autoReview && requirements?.featureRequirements?.guardian_approval !== false,\n allowedSandboxModes: requirements?.allowedSandboxModes ?? null,\n allowedApprovalPolicies:\n requirements?.allowedApprovalPolicies?.filter(\n (policy): policy is string & AskForApproval => typeof policy === 'string',\n ) ?? null,\n allowedReviewers: requirements?.allowedApprovalsReviewers ?? null,\n allowedProfiles: requirements?.allowedPermissionProfiles\n ? Object.keys(requirements.allowedPermissionProfiles).filter(\n (id) => requirements.allowedPermissionProfiles?.[id] === true,\n )\n : null,\n autoReviewRequiredModels: requirements?.autoReview?.requiredOnModels ?? [],\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAiBzB,SAAS,mBACd,SACA,gBACA,UACA,SACA,KAC+E;AAC/E,QAAM,SAAgD;AAAA,IACpD,SAAS,UAEH;AAAA,MACE,kBAAkB;AAAA,MAClB,UAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB,EACA,QAAQ,IAAI,IACd;AAAA,IACJ,gBACE,OAAO,mBAAmB,WAAW,iBAAiB,iBAAiB,aAAa;AAAA,IACtF,kBAAkB,YAAY;AAAA,IAC9B,eAAe,CAAC,UACZ,OACA,QAAQ,SAAS,qBACf,OACA,QAAQ,SAAS,oBACf,QAAQ,kBAAkB,YAC1B,QAAQ;AAAA,IAChB,mBAAmB,WAAW;AAAA,IAC9B,eAAe,SAAS,SAAS,mBAAmB,QAAQ,gBAAgB;AAAA,IAC5E,qBAAqB,SAAS,SAAS,mBAAmB,QAAQ,sBAAsB;AAAA,IACxF,iBAAiB,SAAS,SAAS,mBAAmB,QAAQ,kBAAkB;AAAA,IAChF,gBAAgB;AAAA,EAClB;AACA,QAAM,WAAqB,CAAC,cAAc,cAAc,qBAAqB;AAC7E,MACG,YAAY,QAAQ,YAAY,QAAQ,CAAC,SAAS,SAAS,QAAQ,EAAE,MACtE,SAAS,SAAS,qBAClB,OAAO,mBAAmB,YAC1B;AACA,WAAO,iBAAiB;AACxB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,YAAY,YAAY,OAAW,QAAO;AAC9E,QAAM,mBACJ,QAAQ,SAAS,mBACb,CAAC,QAAQ,iBACT,CAAC,QAAQ,uBACT,CAAC,QAAQ,mBACT,QAAQ,cAAc,MAAM,CAAC,SAAS,SAAS,GAAG,IAClD,QAAQ,SAAS,aACf,CAAC,QAAQ,gBACT,QAAQ,SAAS;AACzB,SAAO,iBACJ,OAAO,KAAK,eAAe,EAAuB,KAAK,CAAC,SAAS;AAChE,UAAM,SAAS,gBAAgB,IAAI;AACnC,WACE,oBACA,OAAO,YAAY,OAAO,WAC1B,mBAAmB,OAAO,kBAC1B,aAAa,OAAO,qBACnB,CAAC,WAAW,QAAQ,OAAO,OAAO;AAAA,EAEvC,CAAC,KAAK;AACR,SAAO;AACT;AAEO,SAAS,uBACd,cACA,YACA,mBACwB;AACxB,SAAO;AAAA,IACL,GAAI,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;AAAA,IACjD,YAAY,cAAc,cAAc,qBAAqB,sBAAsB;AAAA,IACnF,qBAAqB,cAAc,uBAAuB;AAAA,IAC1D,yBACE,cAAc,yBAAyB;AAAA,MACrC,CAAC,WAA8C,OAAO,WAAW;AAAA,IACnE,KAAK;AAAA,IACP,kBAAkB,cAAc,6BAA6B;AAAA,IAC7D,iBAAiB,cAAc,4BAC3B,OAAO,KAAK,aAAa,yBAAyB,EAAE;AAAA,MAClD,CAAC,OAAO,aAAa,4BAA4B,EAAE,MAAM;AAAA,IAC3D,IACA;AAAA,IACJ,0BAA0B,cAAc,YAAY,oBAAoB,CAAC;AAAA,EAC3E;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isRecord } from "./transport.js";
|
|
2
|
+
const TITLE_MODEL = "gpt-5.6-luna";
|
|
3
|
+
const TITLE_MAX_CHARS = 36;
|
|
4
|
+
const PROMPT_MAX_BYTES = 960;
|
|
5
|
+
const TITLE_RESPONSE_MAX_BYTES = 8 * 1024;
|
|
6
|
+
const titleOutputSchema = {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: { title: { type: "string", minLength: 1, maxLength: TITLE_MAX_CHARS } },
|
|
9
|
+
required: ["title"],
|
|
10
|
+
additionalProperties: false
|
|
11
|
+
};
|
|
12
|
+
const instructions = "Generate a concise, single-line task title of at most 36 characters and under five words where possible. Start with an imperative verb. Capitalize only the first word unless the user's language, proper nouns, acronyms, or code terms require otherwise. Preserve ticket references exactly. Write in the user's language. Do not use quotes, markdown, or trailing punctuation. Do not answer the request.";
|
|
13
|
+
function titlePrompt(message) {
|
|
14
|
+
const prefix = `${instructions}
|
|
15
|
+
|
|
16
|
+
User prompt:
|
|
17
|
+
`;
|
|
18
|
+
let remaining = PROMPT_MAX_BYTES - Buffer.byteLength(prefix);
|
|
19
|
+
let text = "";
|
|
20
|
+
for (const character of message.trim()) {
|
|
21
|
+
remaining -= Buffer.byteLength(character);
|
|
22
|
+
if (remaining < 0) break;
|
|
23
|
+
text += character;
|
|
24
|
+
}
|
|
25
|
+
return prefix + text;
|
|
26
|
+
}
|
|
27
|
+
function parseTitle(response) {
|
|
28
|
+
if (Buffer.byteLength(response) > TITLE_RESPONSE_MAX_BYTES) return;
|
|
29
|
+
let value;
|
|
30
|
+
try {
|
|
31
|
+
value = JSON.parse(response);
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!isRecord(value) || Object.keys(value).length !== 1 || typeof value.title !== "string")
|
|
36
|
+
return;
|
|
37
|
+
const title = value.title.trim().replace(/^["'`“”‘’]+|["'`“”‘’]+$/gu, "").replace(/\s+/gu, " ").trim().replace(/[.?!]+$/u, "").trimEnd();
|
|
38
|
+
return title ? [...title].slice(0, TITLE_MAX_CHARS).join("") : void 0;
|
|
39
|
+
}
|
|
40
|
+
function selectTitleModel(currentModel, provider, hasChatgptAccount, models) {
|
|
41
|
+
return provider === "openai" && hasChatgptAccount && models.includes(TITLE_MODEL) ? TITLE_MODEL : currentModel;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
TITLE_MAX_CHARS,
|
|
45
|
+
TITLE_MODEL,
|
|
46
|
+
TITLE_RESPONSE_MAX_BYTES,
|
|
47
|
+
parseTitle,
|
|
48
|
+
selectTitleModel,
|
|
49
|
+
titleOutputSchema,
|
|
50
|
+
titlePrompt
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=thread-title-policy.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/codex/thread-title-policy.ts"],
|
|
4
|
+
"sourcesContent": ["import { isRecord } from './transport.js';\n\n// Keep in sync with codex-rs/tui/src/app/thread_title.rs (rust-v0.163.0).\nexport const TITLE_MODEL = 'gpt-5.6-luna';\nexport const TITLE_MAX_CHARS = 36;\nconst PROMPT_MAX_BYTES = 960;\nexport const TITLE_RESPONSE_MAX_BYTES = 8 * 1024;\nexport const titleOutputSchema = {\n type: 'object',\n properties: { title: { type: 'string', minLength: 1, maxLength: TITLE_MAX_CHARS } },\n required: ['title'],\n additionalProperties: false,\n};\nconst instructions =\n 'Generate a concise, single-line task title of at most 36 characters and under five words where possible. ' +\n 'Start with an imperative verb. Capitalize only the first word unless the ' +\n \"user's language, proper nouns, acronyms, or code terms require otherwise. \" +\n \"Preserve ticket references exactly. Write in the user's language. \" +\n 'Do not use quotes, markdown, or trailing punctuation. Do not answer the request.';\n\nexport function titlePrompt(message: string): string {\n const prefix = `${instructions}\\n\\nUser prompt:\\n`;\n let remaining = PROMPT_MAX_BYTES - Buffer.byteLength(prefix);\n let text = '';\n for (const character of message.trim()) {\n remaining -= Buffer.byteLength(character);\n if (remaining < 0) break;\n text += character;\n }\n return prefix + text;\n}\n\nexport function parseTitle(response: string): string | undefined {\n if (Buffer.byteLength(response) > TITLE_RESPONSE_MAX_BYTES) return;\n let value: unknown;\n try {\n value = JSON.parse(response);\n } catch {\n return;\n }\n if (!isRecord(value) || Object.keys(value).length !== 1 || typeof value.title !== 'string')\n return;\n const title = value.title\n .trim()\n .replace(/^[\"'`\u201C\u201D\u2018\u2019]+|[\"'`\u201C\u201D\u2018\u2019]+$/gu, '')\n .replace(/\\s+/gu, ' ')\n .trim()\n .replace(/[.?!]+$/u, '')\n .trimEnd();\n return title ? [...title].slice(0, TITLE_MAX_CHARS).join('') : undefined;\n}\n\nexport function selectTitleModel(\n currentModel: string,\n provider: string,\n hasChatgptAccount: boolean,\n models: readonly string[],\n): string {\n return provider === 'openai' && hasChatgptAccount && models.includes(TITLE_MODEL)\n ? TITLE_MODEL\n : currentModel;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AAGlB,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAC/B,MAAM,mBAAmB;AAClB,MAAM,2BAA2B,IAAI;AACrC,MAAM,oBAAoB;AAAA,EAC/B,MAAM;AAAA,EACN,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,WAAW,GAAG,WAAW,gBAAgB,EAAE;AAAA,EAClF,UAAU,CAAC,OAAO;AAAA,EAClB,sBAAsB;AACxB;AACA,MAAM,eACJ;AAMK,SAAS,YAAY,SAAyB;AACnD,QAAM,SAAS,GAAG,YAAY;AAAA;AAAA;AAAA;AAC9B,MAAI,YAAY,mBAAmB,OAAO,WAAW,MAAM;AAC3D,MAAI,OAAO;AACX,aAAW,aAAa,QAAQ,KAAK,GAAG;AACtC,iBAAa,OAAO,WAAW,SAAS;AACxC,QAAI,YAAY,EAAG;AACnB,YAAQ;AAAA,EACV;AACA,SAAO,SAAS;AAClB;AAEO,SAAS,WAAW,UAAsC;AAC/D,MAAI,OAAO,WAAW,QAAQ,IAAI,yBAA0B;AAC5D,MAAI;AACJ,MAAI;AACF,YAAQ,KAAK,MAAM,QAAQ;AAAA,EAC7B,QAAQ;AACN;AAAA,EACF;AACA,MAAI,CAAC,SAAS,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,WAAW,KAAK,OAAO,MAAM,UAAU;AAChF;AACF,QAAM,QAAQ,MAAM,MACjB,KAAK,EACL,QAAQ,6BAA6B,EAAE,EACvC,QAAQ,SAAS,GAAG,EACpB,KAAK,EACL,QAAQ,YAAY,EAAE,EACtB,QAAQ;AACX,SAAO,QAAQ,CAAC,GAAG,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,KAAK,EAAE,IAAI;AACjE;AAEO,SAAS,iBACd,cACA,UACA,mBACA,QACQ;AACR,SAAO,aAAa,YAAY,qBAAqB,OAAO,SAAS,WAAW,IAC5E,cACA;AACN;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseTitle,
|
|
3
|
+
selectTitleModel,
|
|
4
|
+
titlePrompt,
|
|
5
|
+
titleOutputSchema,
|
|
6
|
+
TITLE_MODEL,
|
|
7
|
+
TITLE_RESPONSE_MAX_BYTES
|
|
8
|
+
} from "./thread-title-policy.js";
|
|
9
|
+
import { CodexError, isRecord } from "./transport.js";
|
|
10
|
+
class ThreadTitles {
|
|
11
|
+
constructor(rpc, timeoutMs = 3e4) {
|
|
12
|
+
this.rpc = rpc;
|
|
13
|
+
this.timeoutMs = timeoutMs;
|
|
14
|
+
rpc.once("failure", () => {
|
|
15
|
+
this.closed = true;
|
|
16
|
+
this.candidates.clear();
|
|
17
|
+
for (const job of this.jobs.values()) this.cancel(job);
|
|
18
|
+
this.internal.clear();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
rpc;
|
|
22
|
+
timeoutMs;
|
|
23
|
+
candidates = /* @__PURE__ */ new Map();
|
|
24
|
+
jobs = /* @__PURE__ */ new Map();
|
|
25
|
+
internal = /* @__PURE__ */ new Map();
|
|
26
|
+
manualNames = /* @__PURE__ */ new Set();
|
|
27
|
+
nameWrites = /* @__PURE__ */ new Map();
|
|
28
|
+
closed = false;
|
|
29
|
+
hasChatgptAccount = false;
|
|
30
|
+
models = [];
|
|
31
|
+
configure(hasChatgptAccount, models) {
|
|
32
|
+
this.hasChatgptAccount = hasChatgptAccount;
|
|
33
|
+
this.models = models;
|
|
34
|
+
}
|
|
35
|
+
register(result) {
|
|
36
|
+
if (!this.closed && !this.manualNames.has(result.thread.id) && !result.thread.name && !result.thread.ephemeral)
|
|
37
|
+
this.candidates.set(result.thread.id, { ...result, thread: { ...result.thread, turns: [] } });
|
|
38
|
+
else this.candidates.delete(result.thread.id);
|
|
39
|
+
}
|
|
40
|
+
async prepareManualName(id) {
|
|
41
|
+
this.manualNames.add(id);
|
|
42
|
+
this.candidates.delete(id);
|
|
43
|
+
const job = this.jobs.get(id);
|
|
44
|
+
if (job) this.cancel(job);
|
|
45
|
+
await this.nameWrites.get(id);
|
|
46
|
+
}
|
|
47
|
+
start(id, text) {
|
|
48
|
+
const candidate = this.candidates.get(id);
|
|
49
|
+
if (!candidate || this.closed || !text.trim() || this.jobs.has(id) || this.nameWrites.has(id))
|
|
50
|
+
return;
|
|
51
|
+
let finish;
|
|
52
|
+
const completed = new Promise((resolve) => {
|
|
53
|
+
finish = resolve;
|
|
54
|
+
});
|
|
55
|
+
const job = { cancelled: false, terminal: false, finish };
|
|
56
|
+
this.jobs.set(id, job);
|
|
57
|
+
this.armTimeout(job);
|
|
58
|
+
void this.generate(id, text, candidate, job, completed).catch(() => {
|
|
59
|
+
}).finally(() => {
|
|
60
|
+
clearTimeout(job.timer);
|
|
61
|
+
this.jobs.delete(id);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** Called before ordinary event mapping, including notifications preceding RPC receipts. */
|
|
65
|
+
notification(message) {
|
|
66
|
+
const event = message;
|
|
67
|
+
if (event.method === "thread/started" && event.params.thread.ephemeral) {
|
|
68
|
+
if (!this.internal.has(event.params.thread.id))
|
|
69
|
+
this.internal.set(event.params.thread.id, void 0);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const params = message.params;
|
|
73
|
+
if (!isRecord(params) || typeof params.threadId !== "string") return false;
|
|
74
|
+
const id = params.threadId;
|
|
75
|
+
if (this.internal.has(id)) {
|
|
76
|
+
const job = this.internal.get(id);
|
|
77
|
+
if (job && event.method === "item/completed" && event.params.item.type === "agentMessage") {
|
|
78
|
+
if (Buffer.byteLength(event.params.item.text) > TITLE_RESPONSE_MAX_BYTES) this.cancel(job);
|
|
79
|
+
else job.output = event.params.item.text;
|
|
80
|
+
}
|
|
81
|
+
if (job && (event.method === "turn/started" || event.method === "turn/completed")) {
|
|
82
|
+
job.turnId = event.params.turn.id;
|
|
83
|
+
if (event.method === "turn/completed") {
|
|
84
|
+
job.terminal = true;
|
|
85
|
+
job.finish(event.params.turn);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (job && event.method === "error" && !event.params.willRetry) this.cancel(job);
|
|
89
|
+
if (event.method === "thread/closed") this.internal.delete(id);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (event.method === "thread/name/updated") {
|
|
93
|
+
const source = this.candidates.get(id);
|
|
94
|
+
if (source) source.thread.name = event.params.threadName ?? null;
|
|
95
|
+
if (event.params.threadName) this.candidates.delete(id);
|
|
96
|
+
const job = this.jobs.get(id);
|
|
97
|
+
if (job) this.cancel(job);
|
|
98
|
+
}
|
|
99
|
+
if (event.method === "thread/closed") this.candidates.delete(id);
|
|
100
|
+
if (event.method === "item/completed" && event.params.item.type === "userMessage") {
|
|
101
|
+
const text = event.params.item.content.flatMap((input) => {
|
|
102
|
+
if (input.type !== "text") return [];
|
|
103
|
+
return input.text.split("## My request for Codex:").at(-1).trim();
|
|
104
|
+
}).join(" ");
|
|
105
|
+
this.start(id, text);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
isInternal(threadId) {
|
|
110
|
+
return typeof threadId === "string" && this.internal.has(threadId);
|
|
111
|
+
}
|
|
112
|
+
cancel(job) {
|
|
113
|
+
job.cancelled = true;
|
|
114
|
+
job.finish(null);
|
|
115
|
+
}
|
|
116
|
+
armTimeout(job) {
|
|
117
|
+
clearTimeout(job.timer);
|
|
118
|
+
job.timer = setTimeout(() => this.cancel(job), this.timeoutMs);
|
|
119
|
+
job.timer.unref();
|
|
120
|
+
}
|
|
121
|
+
call(method, params) {
|
|
122
|
+
return this.rpc.request(method, params);
|
|
123
|
+
}
|
|
124
|
+
async generate(id, text, source, job, completed) {
|
|
125
|
+
try {
|
|
126
|
+
const before = await this.call("thread/read", { threadId: id, includeTurns: false });
|
|
127
|
+
if (job.cancelled || before.thread.id !== id) return;
|
|
128
|
+
if (before.thread.name) {
|
|
129
|
+
this.candidates.delete(id);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const model = selectTitleModel(
|
|
133
|
+
before.thread.model ?? source.model,
|
|
134
|
+
before.thread.modelProvider,
|
|
135
|
+
this.hasChatgptAccount,
|
|
136
|
+
this.models
|
|
137
|
+
);
|
|
138
|
+
const profile = source.activePermissionProfile?.id;
|
|
139
|
+
const permissions = profile && !profile.startsWith(":") ? profile : void 0;
|
|
140
|
+
const { config } = await this.call("config/read", {
|
|
141
|
+
cwd: before.thread.cwd,
|
|
142
|
+
includeLayers: false
|
|
143
|
+
});
|
|
144
|
+
if (job.cancelled) return;
|
|
145
|
+
const overrides = {
|
|
146
|
+
web_search: "disabled",
|
|
147
|
+
"cloud.skills.enabled": false,
|
|
148
|
+
"skills.include_instructions": false,
|
|
149
|
+
"tools.experimental_request_user_input.enabled": false,
|
|
150
|
+
"tools.update_plan.enabled": false,
|
|
151
|
+
mcp_servers: Object.fromEntries(
|
|
152
|
+
Object.keys(isRecord(config.mcp_servers) ? config.mcp_servers : {}).map((name) => [
|
|
153
|
+
name,
|
|
154
|
+
{ enabled: false }
|
|
155
|
+
])
|
|
156
|
+
)
|
|
157
|
+
};
|
|
158
|
+
for (const feature of [
|
|
159
|
+
"shell_tool",
|
|
160
|
+
"shell_snapshot",
|
|
161
|
+
"apps",
|
|
162
|
+
"plugins",
|
|
163
|
+
"hooks",
|
|
164
|
+
"multi_agent",
|
|
165
|
+
"goals",
|
|
166
|
+
"memories",
|
|
167
|
+
"browser_use",
|
|
168
|
+
"browser_use_external",
|
|
169
|
+
"computer_use",
|
|
170
|
+
"image_generation",
|
|
171
|
+
"code_mode",
|
|
172
|
+
"code_mode_only",
|
|
173
|
+
"context_management",
|
|
174
|
+
"current_time_reminder",
|
|
175
|
+
"deferred_executor",
|
|
176
|
+
"enable_fanout",
|
|
177
|
+
"multi_agent_v2",
|
|
178
|
+
"request_permissions_tool",
|
|
179
|
+
"standalone_web_search",
|
|
180
|
+
"token_budget",
|
|
181
|
+
"tool_suggest",
|
|
182
|
+
"unified_exec",
|
|
183
|
+
"view_image"
|
|
184
|
+
])
|
|
185
|
+
overrides[`features.${feature}`] = false;
|
|
186
|
+
if (!permissions) overrides.default_permissions = ":read-only";
|
|
187
|
+
const params = {
|
|
188
|
+
cwd: before.thread.cwd,
|
|
189
|
+
model,
|
|
190
|
+
modelProvider: before.thread.modelProvider,
|
|
191
|
+
ephemeral: true,
|
|
192
|
+
threadSource: "thread_title",
|
|
193
|
+
...permissions ? { permissions } : { sandbox: "read-only" },
|
|
194
|
+
runtimeWorkspaceRoots: [],
|
|
195
|
+
environments: [],
|
|
196
|
+
dynamicTools: [],
|
|
197
|
+
selectedCapabilityRoots: [],
|
|
198
|
+
config: overrides
|
|
199
|
+
};
|
|
200
|
+
const generated = await this.call("thread/start", params);
|
|
201
|
+
job.threadId = generated.thread.id;
|
|
202
|
+
this.internal.set(job.threadId, job);
|
|
203
|
+
if (job.cancelled) return;
|
|
204
|
+
if (permissions ? generated.activePermissionProfile?.id !== permissions : generated.sandbox.type !== "readOnly")
|
|
205
|
+
return;
|
|
206
|
+
this.armTimeout(job);
|
|
207
|
+
const { turn } = await this.call("turn/start", {
|
|
208
|
+
threadId: job.threadId,
|
|
209
|
+
input: [
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: titlePrompt(text),
|
|
213
|
+
text_elements: []
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
...model === TITLE_MODEL ? { effort: "low" } : {},
|
|
217
|
+
outputSchema: titleOutputSchema
|
|
218
|
+
});
|
|
219
|
+
job.turnId = turn.id;
|
|
220
|
+
if (turn.status !== "inProgress") {
|
|
221
|
+
job.terminal = true;
|
|
222
|
+
job.finish(turn);
|
|
223
|
+
}
|
|
224
|
+
const result = await completed;
|
|
225
|
+
clearTimeout(job.timer);
|
|
226
|
+
if (job.cancelled || result?.status !== "completed") return;
|
|
227
|
+
const output = result.items.filter((item) => item.type === "agentMessage").at(-1)?.text ?? job.output;
|
|
228
|
+
const title = output ? parseTitle(output) : void 0;
|
|
229
|
+
if (!title) return;
|
|
230
|
+
const latest = await this.call("thread/read", { threadId: id, includeTurns: false });
|
|
231
|
+
if (job.cancelled || latest.thread.id !== id || latest.thread.name) return;
|
|
232
|
+
const write = this.call("thread/name/set", { threadId: id, name: title });
|
|
233
|
+
this.nameWrites.set(id, write);
|
|
234
|
+
void write.then(
|
|
235
|
+
() => this.nameWrites.delete(id),
|
|
236
|
+
(error) => {
|
|
237
|
+
if (error instanceof CodexError && !error.uncertain) this.nameWrites.delete(id);
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
await write;
|
|
241
|
+
this.candidates.delete(id);
|
|
242
|
+
} finally {
|
|
243
|
+
if (job.threadId && !this.closed) {
|
|
244
|
+
if (job.turnId && !job.terminal)
|
|
245
|
+
await this.call("turn/interrupt", { threadId: job.threadId, turnId: job.turnId }).catch(
|
|
246
|
+
() => {
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
await this.call("thread/unsubscribe", { threadId: job.threadId }).catch(() => {
|
|
250
|
+
});
|
|
251
|
+
if (this.internal.has(job.threadId)) this.internal.set(job.threadId, void 0);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export {
|
|
257
|
+
ThreadTitles
|
|
258
|
+
};
|
|
259
|
+
//# sourceMappingURL=thread-titles.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/codex/thread-titles.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ClientRequest } from './generated/ClientRequest.js';\nimport type { ServerNotification } from './generated/ServerNotification.js';\nimport type { ConfigReadResponse } from './generated/v2/ConfigReadResponse.js';\nimport type { ThreadReadResponse } from './generated/v2/ThreadReadResponse.js';\nimport type { ThreadStartResponse } from './generated/v2/ThreadStartResponse.js';\nimport type { TurnStartResponse } from './generated/v2/TurnStartResponse.js';\nimport type { Turn } from './generated/v2/Turn.js';\nimport type { JsonValue } from './generated/serde_json/JsonValue.js';\nimport type { ThreadStartParams } from './generated/v2/ThreadStartParams.js';\nimport {\n parseTitle,\n selectTitleModel,\n titlePrompt,\n titleOutputSchema,\n TITLE_MODEL,\n TITLE_RESPONSE_MAX_BYTES,\n} from './thread-title-policy.js';\nimport { CodexError, isRecord, type RpcNotification, type RpcTransport } from './transport.js';\n\n// These experimental fields are omitted by the stable protocol generator.\n// Their shapes match the verified CLI 0.163.0 app-server protocol.\ntype TitleSource = ThreadStartResponse & {\n activePermissionProfile?: { id: string } | null;\n};\ntype TemporaryThreadParams = ThreadStartParams & {\n permissions?: string;\n runtimeWorkspaceRoots: [];\n environments: [];\n dynamicTools: [];\n selectedCapabilityRoots: [];\n};\ntype Job = {\n cancelled: boolean;\n threadId?: string;\n turnId?: string;\n terminal: boolean;\n output?: string;\n timer?: NodeJS.Timeout;\n finish: (turn: Turn | null) => void;\n};\ntype Responses = {\n 'config/read': ConfigReadResponse;\n 'thread/read': ThreadReadResponse;\n 'thread/start': TitleSource;\n 'turn/start': TurnStartResponse;\n 'thread/name/set': unknown;\n 'thread/unsubscribe': unknown;\n 'turn/interrupt': unknown;\n};\n\n/** Optional naming work, scoped to one transport lifetime and never exposed as a user task. */\nexport class ThreadTitles {\n private readonly candidates = new Map<string, TitleSource>();\n private readonly jobs = new Map<string, Job>();\n private readonly internal = new Map<string, Job | undefined>();\n private readonly manualNames = new Set<string>();\n private readonly nameWrites = new Map<string, Promise<unknown>>();\n private closed = false;\n private hasChatgptAccount = false;\n private models: string[] = [];\n\n constructor(\n private readonly rpc: RpcTransport,\n private readonly timeoutMs = 30_000,\n ) {\n rpc.once('failure', () => {\n this.closed = true;\n this.candidates.clear();\n for (const job of this.jobs.values()) this.cancel(job);\n this.internal.clear();\n });\n }\n\n configure(hasChatgptAccount: boolean, models: string[]): void {\n this.hasChatgptAccount = hasChatgptAccount;\n this.models = models;\n }\n\n register(result: TitleSource): void {\n if (\n !this.closed &&\n !this.manualNames.has(result.thread.id) &&\n !result.thread.name &&\n !result.thread.ephemeral\n )\n this.candidates.set(result.thread.id, { ...result, thread: { ...result.thread, turns: [] } });\n else this.candidates.delete(result.thread.id);\n }\n\n async prepareManualName(id: string): Promise<void> {\n this.manualNames.add(id);\n this.candidates.delete(id);\n const job = this.jobs.get(id);\n if (job) this.cancel(job);\n // A timeout does not cancel the upstream write. Retain that failure until a new\n // transport lifetime, rather than let a late generated title overwrite a manual name.\n await this.nameWrites.get(id);\n }\n\n private start(id: string, text: string): void {\n const candidate = this.candidates.get(id);\n // Retry only on a later user-message event, never while another attempt is pending.\n if (!candidate || this.closed || !text.trim() || this.jobs.has(id) || this.nameWrites.has(id))\n return;\n let finish!: Job['finish'];\n const completed = new Promise<Turn | null>((resolve) => {\n finish = resolve;\n });\n const job: Job = { cancelled: false, terminal: false, finish };\n this.jobs.set(id, job);\n this.armTimeout(job);\n void this.generate(id, text, candidate, job, completed)\n .catch(() => {\n /* Keep the upstream preview; naming must never fail the user's task. */\n })\n .finally(() => {\n clearTimeout(job.timer);\n this.jobs.delete(id);\n });\n }\n\n /** Called before ordinary event mapping, including notifications preceding RPC receipts. */\n notification(message: RpcNotification): boolean {\n const event = message as ServerNotification;\n if (event.method === 'thread/started' && event.params.thread.ephemeral) {\n // The notification can arrive before OR after thread/start's response.\n if (!this.internal.has(event.params.thread.id))\n this.internal.set(event.params.thread.id, undefined);\n return true;\n }\n const params = message.params;\n if (!isRecord(params) || typeof params.threadId !== 'string') return false;\n const id = params.threadId;\n if (this.internal.has(id)) {\n const job = this.internal.get(id);\n if (job && event.method === 'item/completed' && event.params.item.type === 'agentMessage') {\n if (Buffer.byteLength(event.params.item.text) > TITLE_RESPONSE_MAX_BYTES) this.cancel(job);\n else job.output = event.params.item.text;\n }\n if (job && (event.method === 'turn/started' || event.method === 'turn/completed')) {\n job.turnId = event.params.turn.id;\n if (event.method === 'turn/completed') {\n job.terminal = true;\n job.finish(event.params.turn);\n }\n }\n if (job && event.method === 'error' && !event.params.willRetry) this.cancel(job);\n if (event.method === 'thread/closed') this.internal.delete(id);\n return true;\n }\n if (event.method === 'thread/name/updated') {\n const source = this.candidates.get(id);\n if (source) source.thread.name = event.params.threadName ?? null;\n if (event.params.threadName) this.candidates.delete(id);\n const job = this.jobs.get(id);\n if (job) this.cancel(job);\n }\n if (event.method === 'thread/closed') this.candidates.delete(id);\n if (event.method === 'item/completed' && event.params.item.type === 'userMessage') {\n const text = event.params.item.content\n .flatMap((input) => {\n if (input.type !== 'text') return [];\n // CLI removes its IDE context wrapper before constructing the title prompt.\n return input.text.split('## My request for Codex:').at(-1)!.trim();\n })\n .join(' ');\n this.start(id, text);\n }\n return false;\n }\n\n isInternal(threadId: unknown): boolean {\n return typeof threadId === 'string' && this.internal.has(threadId);\n }\n\n private cancel(job: Job): void {\n job.cancelled = true;\n job.finish(null);\n }\n\n private armTimeout(job: Job): void {\n clearTimeout(job.timer);\n job.timer = setTimeout(() => this.cancel(job), this.timeoutMs);\n job.timer.unref();\n }\n\n private call<M extends keyof Responses>(\n method: M,\n params: Extract<ClientRequest, { method: M }>['params'],\n ): Promise<Responses[M]> {\n return this.rpc.request<Responses[M]>(method, params);\n }\n\n private async generate(\n id: string,\n text: string,\n source: TitleSource,\n job: Job,\n completed: Promise<Turn | null>,\n ): Promise<void> {\n try {\n const before = await this.call('thread/read', { threadId: id, includeTurns: false });\n if (job.cancelled || before.thread.id !== id) return;\n if (before.thread.name) {\n this.candidates.delete(id);\n return;\n }\n const model = selectTitleModel(\n before.thread.model ?? source.model,\n before.thread.modelProvider,\n this.hasChatgptAccount,\n this.models,\n );\n const profile = source.activePermissionProfile?.id;\n const permissions = profile && !profile.startsWith(':') ? profile : undefined;\n // Config reads may redact values to empty strings. Override only enabled flags,\n // never round-trip the returned server definitions (timeouts/credentials/etc.).\n // The filesystem sandbox does not constrain MCP tools.\n const { config } = await this.call('config/read', {\n cwd: before.thread.cwd,\n includeLayers: false,\n });\n if (job.cancelled) return;\n const overrides: Record<string, JsonValue> = {\n web_search: 'disabled',\n 'cloud.skills.enabled': false,\n 'skills.include_instructions': false,\n 'tools.experimental_request_user_input.enabled': false,\n 'tools.update_plan.enabled': false,\n mcp_servers: Object.fromEntries(\n Object.keys(isRecord(config.mcp_servers) ? config.mcp_servers : {}).map((name) => [\n name,\n { enabled: false },\n ]),\n ) as JsonValue,\n };\n for (const feature of [\n 'shell_tool',\n 'shell_snapshot',\n 'apps',\n 'plugins',\n 'hooks',\n 'multi_agent',\n 'goals',\n 'memories',\n 'browser_use',\n 'browser_use_external',\n 'computer_use',\n 'image_generation',\n 'code_mode',\n 'code_mode_only',\n 'context_management',\n 'current_time_reminder',\n 'deferred_executor',\n 'enable_fanout',\n 'multi_agent_v2',\n 'request_permissions_tool',\n 'standalone_web_search',\n 'token_budget',\n 'tool_suggest',\n 'unified_exec',\n 'view_image',\n ])\n overrides[`features.${feature}`] = false;\n if (!permissions) overrides.default_permissions = ':read-only';\n const params: TemporaryThreadParams = {\n cwd: before.thread.cwd,\n model,\n modelProvider: before.thread.modelProvider,\n ephemeral: true,\n threadSource: 'thread_title',\n ...(permissions ? { permissions } : { sandbox: 'read-only' }),\n runtimeWorkspaceRoots: [],\n environments: [],\n dynamicTools: [],\n selectedCapabilityRoots: [],\n config: overrides,\n };\n const generated = await this.call('thread/start', params);\n job.threadId = generated.thread.id;\n this.internal.set(job.threadId, job);\n if (job.cancelled) return;\n if (\n permissions\n ? generated.activePermissionProfile?.id !== permissions\n : generated.sandbox.type !== 'readOnly'\n )\n return;\n this.armTimeout(job);\n const { turn } = await this.call('turn/start', {\n threadId: job.threadId,\n input: [\n {\n type: 'text',\n text: titlePrompt(text),\n text_elements: [],\n },\n ],\n ...(model === TITLE_MODEL ? { effort: 'low' } : {}),\n outputSchema: titleOutputSchema,\n });\n job.turnId = turn.id;\n if (turn.status !== 'inProgress') {\n job.terminal = true;\n job.finish(turn);\n }\n const result = await completed;\n clearTimeout(job.timer);\n if (job.cancelled || result?.status !== 'completed') return;\n const output =\n result.items.filter((item) => item.type === 'agentMessage').at(-1)?.text ?? job.output;\n const title = output ? parseTitle(output) : undefined;\n if (!title) return;\n // An explicit name always wins, including one set while inference was running.\n const latest = await this.call('thread/read', { threadId: id, includeTurns: false });\n if (job.cancelled || latest.thread.id !== id || latest.thread.name) return;\n const write = this.call('thread/name/set', { threadId: id, name: title });\n this.nameWrites.set(id, write);\n void write.then(\n () => this.nameWrites.delete(id),\n (error: unknown) => {\n if (error instanceof CodexError && !error.uncertain) this.nameWrites.delete(id);\n },\n );\n await write;\n this.candidates.delete(id);\n } finally {\n if (job.threadId && !this.closed) {\n if (job.turnId && !job.terminal)\n await this.call('turn/interrupt', { threadId: job.threadId, turnId: job.turnId }).catch(\n () => {},\n );\n await this.call('thread/unsubscribe', { threadId: job.threadId }).catch(() => {});\n // Retain the ID until thread/closed so late notifications cannot leak into the UI.\n if (this.internal.has(job.threadId)) this.internal.set(job.threadId, undefined);\n }\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|