openclaw-codex-app-server 0.0.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.
- package/AGENTS.md +22 -0
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/index.ts +69 -0
- package/openclaw.plugin.json +105 -0
- package/package.json +28 -0
- package/src/client.test.ts +332 -0
- package/src/client.ts +2914 -0
- package/src/config.ts +103 -0
- package/src/controller.test.ts +1177 -0
- package/src/controller.ts +3232 -0
- package/src/format.test.ts +502 -0
- package/src/format.ts +869 -0
- package/src/openclaw-plugin-sdk.d.ts +237 -0
- package/src/pending-input.test.ts +298 -0
- package/src/pending-input.ts +785 -0
- package/src/state.test.ts +228 -0
- package/src/state.ts +354 -0
- package/src/thread-picker.test.ts +47 -0
- package/src/thread-picker.ts +98 -0
- package/src/thread-selection.test.ts +89 -0
- package/src/thread-selection.ts +106 -0
- package/src/types.ts +372 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { ThreadSummary } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const THREAD_PICKER_PAGE_SIZE = 8;
|
|
5
|
+
|
|
6
|
+
export type ProjectSummary = {
|
|
7
|
+
name: string;
|
|
8
|
+
threadCount: number;
|
|
9
|
+
latestUpdatedAt?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function getProjectName(projectKey?: string): string | undefined {
|
|
13
|
+
const trimmed = projectKey?.trim();
|
|
14
|
+
if (!trimmed) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const normalized = trimmed.replace(/[\\/]+$/, "");
|
|
18
|
+
const base = path.basename(normalized);
|
|
19
|
+
return base || undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function filterThreadsByProjectName(
|
|
23
|
+
threads: ThreadSummary[],
|
|
24
|
+
projectName?: string,
|
|
25
|
+
): ThreadSummary[] {
|
|
26
|
+
const normalized = projectName?.trim().toLowerCase();
|
|
27
|
+
if (!normalized) {
|
|
28
|
+
return [...threads];
|
|
29
|
+
}
|
|
30
|
+
return threads.filter((thread) => getProjectName(thread.projectKey)?.toLowerCase() === normalized);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function listProjects(
|
|
34
|
+
threads: ThreadSummary[],
|
|
35
|
+
query = "",
|
|
36
|
+
): ProjectSummary[] {
|
|
37
|
+
const filteredQuery = query.trim().toLowerCase();
|
|
38
|
+
const grouped = new Map<string, ProjectSummary>();
|
|
39
|
+
|
|
40
|
+
for (const thread of threads) {
|
|
41
|
+
const projectName = getProjectName(thread.projectKey);
|
|
42
|
+
if (!projectName) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (filteredQuery && !projectName.toLowerCase().includes(filteredQuery)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const existing = grouped.get(projectName);
|
|
49
|
+
const updatedAt = thread.updatedAt ?? thread.createdAt;
|
|
50
|
+
if (!existing) {
|
|
51
|
+
grouped.set(projectName, {
|
|
52
|
+
name: projectName,
|
|
53
|
+
threadCount: 1,
|
|
54
|
+
latestUpdatedAt: updatedAt,
|
|
55
|
+
});
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
existing.threadCount += 1;
|
|
59
|
+
existing.latestUpdatedAt = Math.max(existing.latestUpdatedAt ?? 0, updatedAt ?? 0) || undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [...grouped.values()].sort((left, right) => {
|
|
63
|
+
const updatedDelta = (right.latestUpdatedAt ?? 0) - (left.latestUpdatedAt ?? 0);
|
|
64
|
+
if (updatedDelta !== 0) {
|
|
65
|
+
return updatedDelta;
|
|
66
|
+
}
|
|
67
|
+
return left.name.localeCompare(right.name);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function paginateItems<T>(
|
|
72
|
+
items: T[],
|
|
73
|
+
page: number,
|
|
74
|
+
pageSize = THREAD_PICKER_PAGE_SIZE,
|
|
75
|
+
): {
|
|
76
|
+
items: T[];
|
|
77
|
+
page: number;
|
|
78
|
+
pageSize: number;
|
|
79
|
+
totalItems: number;
|
|
80
|
+
totalPages: number;
|
|
81
|
+
startIndex: number;
|
|
82
|
+
endIndex: number;
|
|
83
|
+
} {
|
|
84
|
+
const totalItems = items.length;
|
|
85
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
86
|
+
const safePage = Math.min(Math.max(0, page), totalPages - 1);
|
|
87
|
+
const startIndex = safePage * pageSize;
|
|
88
|
+
const pageItems = items.slice(startIndex, startIndex + pageSize);
|
|
89
|
+
return {
|
|
90
|
+
items: pageItems,
|
|
91
|
+
page: safePage,
|
|
92
|
+
pageSize,
|
|
93
|
+
totalItems,
|
|
94
|
+
totalPages,
|
|
95
|
+
startIndex,
|
|
96
|
+
endIndex: startIndex + pageItems.length,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
parseThreadSelectionArgs,
|
|
6
|
+
selectThreadFromMatches,
|
|
7
|
+
} from "./thread-selection.js";
|
|
8
|
+
import type { ThreadSummary } from "./types.js";
|
|
9
|
+
|
|
10
|
+
const THREADS: ThreadSummary[] = [
|
|
11
|
+
{
|
|
12
|
+
threadId: "thread-openclaw",
|
|
13
|
+
title: "OpenClaw work",
|
|
14
|
+
projectKey: "/workspace/openclaw",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
threadId: "thread-home",
|
|
18
|
+
title: "Home dotfiles",
|
|
19
|
+
projectKey: "/workspace/home",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
describe("thread selection args", () => {
|
|
24
|
+
it("parses --all without inventing a query", () => {
|
|
25
|
+
expect(parseThreadSelectionArgs("--all")).toEqual({
|
|
26
|
+
includeAll: true,
|
|
27
|
+
listProjects: false,
|
|
28
|
+
syncTopic: false,
|
|
29
|
+
cwd: undefined,
|
|
30
|
+
query: "",
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("parses em dash all from Telegram-style input", () => {
|
|
35
|
+
expect(parseThreadSelectionArgs("—all")).toEqual({
|
|
36
|
+
includeAll: true,
|
|
37
|
+
listProjects: false,
|
|
38
|
+
syncTopic: false,
|
|
39
|
+
cwd: undefined,
|
|
40
|
+
query: "",
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("parses --all with a target query", () => {
|
|
45
|
+
expect(parseThreadSelectionArgs("--all thread-home")).toEqual({
|
|
46
|
+
includeAll: true,
|
|
47
|
+
listProjects: false,
|
|
48
|
+
syncTopic: false,
|
|
49
|
+
cwd: undefined,
|
|
50
|
+
query: "thread-home",
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("parses --projects and expands a home-relative cwd", () => {
|
|
55
|
+
expect(parseThreadSelectionArgs("--projects --cwd ~/github/openclaw")).toEqual({
|
|
56
|
+
includeAll: false,
|
|
57
|
+
listProjects: true,
|
|
58
|
+
syncTopic: false,
|
|
59
|
+
cwd: path.join(os.homedir(), "github/openclaw"),
|
|
60
|
+
query: "",
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("parses --sync separately from the query text", () => {
|
|
65
|
+
expect(parseThreadSelectionArgs("—all —sync approvals")).toEqual({
|
|
66
|
+
includeAll: true,
|
|
67
|
+
listProjects: false,
|
|
68
|
+
syncTopic: true,
|
|
69
|
+
cwd: undefined,
|
|
70
|
+
query: "approvals",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("thread selection", () => {
|
|
76
|
+
it("picks an exact thread id match", () => {
|
|
77
|
+
expect(selectThreadFromMatches(THREADS, "thread-home")).toEqual({
|
|
78
|
+
kind: "unique",
|
|
79
|
+
thread: THREADS[1],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("does not auto-pick the first fuzzy match when multiple threads exist", () => {
|
|
84
|
+
expect(selectThreadFromMatches(THREADS, "thread")).toEqual({
|
|
85
|
+
kind: "ambiguous",
|
|
86
|
+
threads: THREADS,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { ThreadSummary } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export type ParsedThreadSelectionArgs = {
|
|
6
|
+
includeAll: boolean;
|
|
7
|
+
listProjects: boolean;
|
|
8
|
+
syncTopic: boolean;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
query: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ThreadSelectionResult =
|
|
14
|
+
| { kind: "none" }
|
|
15
|
+
| { kind: "unique"; thread: ThreadSummary }
|
|
16
|
+
| { kind: "ambiguous"; threads: ThreadSummary[] };
|
|
17
|
+
|
|
18
|
+
function normalizeOptionDashes(text: string): string {
|
|
19
|
+
return text
|
|
20
|
+
.replace(/(^|\s)[\u2010-\u2015\u2212](?=\S)/g, "$1--")
|
|
21
|
+
.replace(/[\u2010-\u2015\u2212]/g, "-");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseThreadSelectionArgs(args: string): ParsedThreadSelectionArgs {
|
|
25
|
+
const tokens = normalizeOptionDashes(args)
|
|
26
|
+
.split(/\s+/)
|
|
27
|
+
.map((token) => token.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
let includeAll = false;
|
|
30
|
+
let listProjects = false;
|
|
31
|
+
let syncTopic = false;
|
|
32
|
+
let cwd: string | undefined;
|
|
33
|
+
const queryTokens: string[] = [];
|
|
34
|
+
|
|
35
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
36
|
+
const token = tokens[index];
|
|
37
|
+
if (token === "--all" || token === "-a") {
|
|
38
|
+
includeAll = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (token === "--projects" || token === "--project" || token === "-p") {
|
|
42
|
+
listProjects = true;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (token === "--sync") {
|
|
46
|
+
syncTopic = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (token === "--cwd") {
|
|
50
|
+
const next = tokens[index + 1]?.trim();
|
|
51
|
+
if (next) {
|
|
52
|
+
cwd = expandHomeDir(next);
|
|
53
|
+
index += 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
queryTokens.push(token);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
includeAll,
|
|
62
|
+
listProjects,
|
|
63
|
+
syncTopic,
|
|
64
|
+
cwd,
|
|
65
|
+
query: queryTokens.join(" ").trim(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function expandHomeDir(value: string): string {
|
|
70
|
+
if (value === "~") {
|
|
71
|
+
return os.homedir();
|
|
72
|
+
}
|
|
73
|
+
if (value.startsWith("~/")) {
|
|
74
|
+
return path.join(os.homedir(), value.slice(2));
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function selectThreadFromMatches(
|
|
80
|
+
threads: ThreadSummary[],
|
|
81
|
+
query: string,
|
|
82
|
+
): ThreadSelectionResult {
|
|
83
|
+
const trimmedQuery = query.trim();
|
|
84
|
+
if (!trimmedQuery) {
|
|
85
|
+
return { kind: "none" };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (threads.length === 0) {
|
|
89
|
+
return { kind: "none" };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const loweredQuery = trimmedQuery.toLowerCase();
|
|
93
|
+
const exactMatch =
|
|
94
|
+
threads.find((thread) => thread.threadId === trimmedQuery) ??
|
|
95
|
+
threads.find((thread) => thread.title?.trim().toLowerCase() === loweredQuery);
|
|
96
|
+
|
|
97
|
+
if (exactMatch) {
|
|
98
|
+
return { kind: "unique", thread: exactMatch };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (threads.length === 1) {
|
|
102
|
+
return { kind: "unique", thread: threads[0] };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { kind: "ambiguous", threads };
|
|
106
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import type { ConversationRef, PluginInteractiveButtons } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
export const PLUGIN_ID = "openclaw-codex-app-server";
|
|
4
|
+
export const INTERACTIVE_NAMESPACE = "codexapp";
|
|
5
|
+
export const STORE_VERSION = 1;
|
|
6
|
+
export const CALLBACK_TOKEN_BYTES = 9;
|
|
7
|
+
export const CALLBACK_TTL_MS = 30 * 60_000;
|
|
8
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
9
|
+
export const DEFAULT_INPUT_TIMEOUT_MS = 15 * 60_000;
|
|
10
|
+
|
|
11
|
+
export type CodexTransport = "stdio" | "websocket";
|
|
12
|
+
|
|
13
|
+
export type PluginSettings = {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
transport: CodexTransport;
|
|
16
|
+
command: string;
|
|
17
|
+
args: string[];
|
|
18
|
+
url?: string;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
requestTimeoutMs: number;
|
|
21
|
+
inputTimeoutMs: number;
|
|
22
|
+
defaultWorkspaceDir?: string;
|
|
23
|
+
defaultModel?: string;
|
|
24
|
+
defaultServiceTier?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type CodexPlanStep = {
|
|
28
|
+
step: string;
|
|
29
|
+
status: "pending" | "inProgress" | "completed";
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type CodexPlanArtifact = {
|
|
33
|
+
explanation?: string;
|
|
34
|
+
steps: CodexPlanStep[];
|
|
35
|
+
markdown: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type PendingApprovalDecision =
|
|
39
|
+
| "accept"
|
|
40
|
+
| "acceptForSession"
|
|
41
|
+
| "decline"
|
|
42
|
+
| "cancel";
|
|
43
|
+
|
|
44
|
+
export type PendingInputAction =
|
|
45
|
+
| {
|
|
46
|
+
kind: "approval";
|
|
47
|
+
label: string;
|
|
48
|
+
decision: PendingApprovalDecision;
|
|
49
|
+
responseDecision: string;
|
|
50
|
+
proposedExecpolicyAmendment?: Record<string, unknown>;
|
|
51
|
+
sessionPrefix?: string;
|
|
52
|
+
}
|
|
53
|
+
| {
|
|
54
|
+
kind: "option";
|
|
55
|
+
label: string;
|
|
56
|
+
value: string;
|
|
57
|
+
}
|
|
58
|
+
| {
|
|
59
|
+
kind: "steer";
|
|
60
|
+
label: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type PendingQuestionnaireOption = {
|
|
64
|
+
key: string;
|
|
65
|
+
label: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
recommended?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type PendingQuestionnaireAnswer =
|
|
71
|
+
| {
|
|
72
|
+
kind: "option";
|
|
73
|
+
optionKey: string;
|
|
74
|
+
optionLabel: string;
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
kind: "text";
|
|
78
|
+
text: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type PendingQuestionnaireQuestion = {
|
|
82
|
+
index: number;
|
|
83
|
+
id: string;
|
|
84
|
+
header?: string;
|
|
85
|
+
prompt: string;
|
|
86
|
+
options: PendingQuestionnaireOption[];
|
|
87
|
+
guidance: string[];
|
|
88
|
+
allowFreeform?: boolean;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type PendingQuestionnaireState = {
|
|
92
|
+
questions: PendingQuestionnaireQuestion[];
|
|
93
|
+
currentIndex: number;
|
|
94
|
+
answers: Array<PendingQuestionnaireAnswer | null>;
|
|
95
|
+
awaitingFreeform?: boolean;
|
|
96
|
+
responseMode?: "structured" | "compact";
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type PendingInputState = {
|
|
100
|
+
requestId: string;
|
|
101
|
+
options: string[];
|
|
102
|
+
actions?: PendingInputAction[];
|
|
103
|
+
expiresAt: number;
|
|
104
|
+
promptText?: string;
|
|
105
|
+
method?: string;
|
|
106
|
+
questionnaire?: PendingQuestionnaireState;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type ThreadSummary = {
|
|
110
|
+
threadId: string;
|
|
111
|
+
title?: string;
|
|
112
|
+
summary?: string;
|
|
113
|
+
projectKey?: string;
|
|
114
|
+
createdAt?: number;
|
|
115
|
+
updatedAt?: number;
|
|
116
|
+
gitBranch?: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type ModelSummary = {
|
|
120
|
+
id: string;
|
|
121
|
+
label?: string;
|
|
122
|
+
description?: string;
|
|
123
|
+
current?: boolean;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export type SkillSummary = {
|
|
127
|
+
cwd?: string;
|
|
128
|
+
name: string;
|
|
129
|
+
description?: string;
|
|
130
|
+
enabled?: boolean;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type ExperimentalFeatureSummary = {
|
|
134
|
+
name: string;
|
|
135
|
+
stage?: string;
|
|
136
|
+
displayName?: string;
|
|
137
|
+
description?: string;
|
|
138
|
+
enabled?: boolean;
|
|
139
|
+
defaultEnabled?: boolean;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export type McpServerSummary = {
|
|
143
|
+
name: string;
|
|
144
|
+
authStatus?: string;
|
|
145
|
+
toolCount: number;
|
|
146
|
+
resourceCount: number;
|
|
147
|
+
resourceTemplateCount: number;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type RateLimitSummary = {
|
|
151
|
+
name: string;
|
|
152
|
+
limitId?: string;
|
|
153
|
+
remaining?: number;
|
|
154
|
+
limit?: number;
|
|
155
|
+
used?: number;
|
|
156
|
+
usedPercent?: number;
|
|
157
|
+
resetAt?: number;
|
|
158
|
+
windowSeconds?: number;
|
|
159
|
+
windowMinutes?: number;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type ThreadState = {
|
|
163
|
+
threadId: string;
|
|
164
|
+
threadName?: string;
|
|
165
|
+
model?: string;
|
|
166
|
+
modelProvider?: string;
|
|
167
|
+
serviceTier?: string;
|
|
168
|
+
cwd?: string;
|
|
169
|
+
approvalPolicy?: string;
|
|
170
|
+
sandbox?: string;
|
|
171
|
+
reasoningEffort?: string;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export type ThreadReplay = {
|
|
175
|
+
lastUserMessage?: string;
|
|
176
|
+
lastAssistantMessage?: string;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export type AccountSummary = {
|
|
180
|
+
type?: "apiKey" | "chatgpt";
|
|
181
|
+
email?: string;
|
|
182
|
+
planType?: string;
|
|
183
|
+
requiresOpenaiAuth?: boolean;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export type ContextUsageSnapshot = {
|
|
187
|
+
totalTokens?: number;
|
|
188
|
+
inputTokens?: number;
|
|
189
|
+
cachedInputTokens?: number;
|
|
190
|
+
outputTokens?: number;
|
|
191
|
+
reasoningOutputTokens?: number;
|
|
192
|
+
contextWindow?: number;
|
|
193
|
+
remainingTokens?: number;
|
|
194
|
+
remainingPercent?: number;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export type CompactProgress =
|
|
198
|
+
| {
|
|
199
|
+
phase: "started" | "completed";
|
|
200
|
+
itemId?: string;
|
|
201
|
+
usage?: ContextUsageSnapshot;
|
|
202
|
+
}
|
|
203
|
+
| {
|
|
204
|
+
phase: "usage";
|
|
205
|
+
usage: ContextUsageSnapshot;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type CompactResult = {
|
|
209
|
+
itemId?: string;
|
|
210
|
+
usage?: ContextUsageSnapshot;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export type ReviewTarget =
|
|
214
|
+
| { type: "uncommittedChanges" }
|
|
215
|
+
| { type: "custom"; instructions: string };
|
|
216
|
+
|
|
217
|
+
export type CollaborationMode = {
|
|
218
|
+
mode: string;
|
|
219
|
+
settings?: {
|
|
220
|
+
model?: string;
|
|
221
|
+
reasoningEffort?: string;
|
|
222
|
+
developerInstructions?: string | null;
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export type ReviewResult = {
|
|
227
|
+
reviewText: string;
|
|
228
|
+
reviewThreadId?: string;
|
|
229
|
+
turnId?: string;
|
|
230
|
+
aborted?: boolean;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export type TurnResult = {
|
|
234
|
+
threadId: string;
|
|
235
|
+
text?: string;
|
|
236
|
+
planArtifact?: CodexPlanArtifact;
|
|
237
|
+
aborted?: boolean;
|
|
238
|
+
usage?: ContextUsageSnapshot;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export type StoredBinding = {
|
|
242
|
+
conversation: ConversationRef;
|
|
243
|
+
sessionKey: string;
|
|
244
|
+
threadId: string;
|
|
245
|
+
workspaceDir: string;
|
|
246
|
+
threadTitle?: string;
|
|
247
|
+
contextUsage?: ContextUsageSnapshot;
|
|
248
|
+
updatedAt: number;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export type StoredPendingBind = {
|
|
252
|
+
conversation: ConversationRef;
|
|
253
|
+
threadId: string;
|
|
254
|
+
workspaceDir: string;
|
|
255
|
+
threadTitle?: string;
|
|
256
|
+
updatedAt: number;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export type StoredPendingRequest = {
|
|
260
|
+
requestId: string;
|
|
261
|
+
conversation: ConversationRef;
|
|
262
|
+
threadId: string;
|
|
263
|
+
workspaceDir: string;
|
|
264
|
+
state: PendingInputState;
|
|
265
|
+
updatedAt: number;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export type CallbackAction =
|
|
269
|
+
| {
|
|
270
|
+
token: string;
|
|
271
|
+
kind: "resume-thread";
|
|
272
|
+
conversation: ConversationRef;
|
|
273
|
+
threadId: string;
|
|
274
|
+
workspaceDir: string;
|
|
275
|
+
syncTopic?: boolean;
|
|
276
|
+
createdAt: number;
|
|
277
|
+
expiresAt: number;
|
|
278
|
+
}
|
|
279
|
+
| {
|
|
280
|
+
token: string;
|
|
281
|
+
kind: "pending-input";
|
|
282
|
+
conversation: ConversationRef;
|
|
283
|
+
requestId: string;
|
|
284
|
+
actionIndex: number;
|
|
285
|
+
createdAt: number;
|
|
286
|
+
expiresAt: number;
|
|
287
|
+
}
|
|
288
|
+
| {
|
|
289
|
+
token: string;
|
|
290
|
+
kind: "pending-questionnaire";
|
|
291
|
+
conversation: ConversationRef;
|
|
292
|
+
requestId: string;
|
|
293
|
+
questionIndex: number;
|
|
294
|
+
action: "select" | "prev" | "next" | "freeform";
|
|
295
|
+
optionIndex?: number;
|
|
296
|
+
createdAt: number;
|
|
297
|
+
expiresAt: number;
|
|
298
|
+
}
|
|
299
|
+
| {
|
|
300
|
+
token: string;
|
|
301
|
+
kind: "picker-view";
|
|
302
|
+
conversation: ConversationRef;
|
|
303
|
+
view:
|
|
304
|
+
| {
|
|
305
|
+
mode: "threads";
|
|
306
|
+
includeAll: boolean;
|
|
307
|
+
page: number;
|
|
308
|
+
syncTopic?: boolean;
|
|
309
|
+
query?: string;
|
|
310
|
+
workspaceDir?: string;
|
|
311
|
+
projectName?: string;
|
|
312
|
+
}
|
|
313
|
+
| {
|
|
314
|
+
mode: "projects";
|
|
315
|
+
includeAll: boolean;
|
|
316
|
+
page: number;
|
|
317
|
+
syncTopic?: boolean;
|
|
318
|
+
query?: string;
|
|
319
|
+
workspaceDir?: string;
|
|
320
|
+
};
|
|
321
|
+
createdAt: number;
|
|
322
|
+
expiresAt: number;
|
|
323
|
+
}
|
|
324
|
+
| {
|
|
325
|
+
token: string;
|
|
326
|
+
kind: "run-prompt";
|
|
327
|
+
conversation: ConversationRef;
|
|
328
|
+
prompt: string;
|
|
329
|
+
workspaceDir?: string;
|
|
330
|
+
collaborationMode?: CollaborationMode;
|
|
331
|
+
createdAt: number;
|
|
332
|
+
expiresAt: number;
|
|
333
|
+
}
|
|
334
|
+
| {
|
|
335
|
+
token: string;
|
|
336
|
+
kind: "set-model";
|
|
337
|
+
conversation: ConversationRef;
|
|
338
|
+
model: string;
|
|
339
|
+
createdAt: number;
|
|
340
|
+
expiresAt: number;
|
|
341
|
+
}
|
|
342
|
+
| {
|
|
343
|
+
token: string;
|
|
344
|
+
kind: "reply-text";
|
|
345
|
+
conversation: ConversationRef;
|
|
346
|
+
text: string;
|
|
347
|
+
createdAt: number;
|
|
348
|
+
expiresAt: number;
|
|
349
|
+
}
|
|
350
|
+
| {
|
|
351
|
+
token: string;
|
|
352
|
+
kind: "rename-thread";
|
|
353
|
+
conversation: ConversationRef;
|
|
354
|
+
style: "thread-project" | "thread";
|
|
355
|
+
syncTopic: boolean;
|
|
356
|
+
createdAt: number;
|
|
357
|
+
expiresAt: number;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export type StoreSnapshot = {
|
|
361
|
+
version: number;
|
|
362
|
+
bindings: StoredBinding[];
|
|
363
|
+
pendingBinds: StoredPendingBind[];
|
|
364
|
+
pendingRequests: StoredPendingRequest[];
|
|
365
|
+
callbacks: CallbackAction[];
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export type ConversationTarget = ConversationRef & {
|
|
369
|
+
threadId?: number;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export type CommandButtons = PluginInteractiveButtons;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": [
|
|
5
|
+
"ES2023"
|
|
6
|
+
],
|
|
7
|
+
"module": "NodeNext",
|
|
8
|
+
"moduleResolution": "NodeNext",
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"types": [
|
|
15
|
+
"node",
|
|
16
|
+
"vitest/globals"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"index.ts",
|
|
21
|
+
"src/**/*.ts",
|
|
22
|
+
"src/**/*.d.ts"
|
|
23
|
+
]
|
|
24
|
+
}
|