@longtable/provider-codex 0.1.9 → 0.1.11

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/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ProviderCapabilities, QuestionRecord } from "@longtable/core";
1
2
  import type { CheckpointSignal, ResearcherProfile, ResolvedCheckpointPolicy, RuntimeGuidance } from "@longtable/checkpoints";
2
3
  import type { NumberedCheckpointSpec, ParsedCheckpointSelection, SetupPersistedOutput } from "@longtable/setup";
3
4
  export interface CodexCheckpointContext {
@@ -27,12 +28,23 @@ export interface CodexRuntimeBridge {
27
28
  profile: ResearcherProfile;
28
29
  runtimeDefaults: CodexRuntimeDefaults;
29
30
  }
31
+ export interface CodexRenderedQuestionRecord {
32
+ questionId: string;
33
+ checkpointKey: string;
34
+ spec: NumberedCheckpointSpec;
35
+ prompt: string;
36
+ }
37
+ export declare const CODEX_PROVIDER_CAPABILITIES: ProviderCapabilities;
38
+ export declare function getCodexProviderCapabilities(): ProviderCapabilities;
30
39
  export declare function normalizeResearcherProfile(profile: SetupPersistedOutput["profileSeed"]): ResearcherProfile;
31
40
  export declare function renderRuntimeGuidance(guidance: RuntimeGuidance): string;
32
41
  export declare function renderCheckpointPrompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
42
+ export declare function questionRecordToNumberedCheckpointSpec(record: QuestionRecord): NumberedCheckpointSpec;
43
+ export declare function renderQuestionRecordPrompt(record: QuestionRecord): CodexRenderedQuestionRecord;
33
44
  export declare function parseCheckpointResponse(spec: NumberedCheckpointSpec, input: string): ParsedCheckpointSelection | null;
34
45
  export declare function buildReprompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
35
46
  export declare function runBlockingCheckpoint(context: CodexCheckpointContext, input?: string): CodexCheckpointResult;
36
47
  export declare function createCodexRuntimeBridge(setup: SetupPersistedOutput): CodexRuntimeBridge;
37
48
  export * from "./config.js";
49
+ export * from "./skills.js";
38
50
  export * from "./wrapper.js";
package/dist/index.js CHANGED
@@ -1,6 +1,24 @@
1
1
  import { resolveCheckpointPolicy } from "@longtable/checkpoints";
2
2
  import { resolveRuntimeGuidance } from "@longtable/checkpoints";
3
3
  import { buildNumberedCheckpointPrompt, parseNumberedCheckpointResponse } from "@longtable/setup";
4
+ export const CODEX_PROVIDER_CAPABILITIES = {
5
+ provider: "codex",
6
+ nativeStructuredQuestions: false,
7
+ generatedSkills: "stable",
8
+ promptAliases: "unavailable",
9
+ nativeParallelSubagents: "session_dependent",
10
+ sequentialFallback: true,
11
+ mcpTransport: "planned",
12
+ notes: [
13
+ "Codex generated skills are the preferred native surface.",
14
+ "Installed prompt files are not guaranteed to become slash commands in current Codex builds.",
15
+ "Native parallel subagents may exist in an interactive session, but the npm CLI must not require them.",
16
+ "Sequential panel fallback is the stable provider-neutral path."
17
+ ]
18
+ };
19
+ export function getCodexProviderCapabilities() {
20
+ return CODEX_PROVIDER_CAPABILITIES;
21
+ }
4
22
  export function normalizeResearcherProfile(profile) {
5
23
  return {
6
24
  field: profile.field,
@@ -47,6 +65,41 @@ export function renderCheckpointPrompt(spec, guidance) {
47
65
  sections.push(buildNumberedCheckpointPrompt(spec));
48
66
  return sections.join("\n\n");
49
67
  }
68
+ export function questionRecordToNumberedCheckpointSpec(record) {
69
+ const options = [
70
+ ...record.prompt.options.map((option) => ({
71
+ value: option.value,
72
+ label: option.description ? `${option.label} — ${option.description}` : option.label
73
+ })),
74
+ ...(record.prompt.allowOther
75
+ ? [{
76
+ value: "other",
77
+ label: record.prompt.otherLabel ?? "Other"
78
+ }]
79
+ : [])
80
+ ];
81
+ return {
82
+ title: record.prompt.title,
83
+ instructions: [
84
+ record.prompt.question,
85
+ ...record.prompt.rationale.map((entry) => `Why now: ${entry}`),
86
+ `Question id: ${record.id}`,
87
+ `Checkpoint: ${record.prompt.checkpointKey ?? "manual"}`,
88
+ `Required: ${record.prompt.required ? "yes" : "no"}`
89
+ ].join("\n"),
90
+ options,
91
+ allowRationale: true
92
+ };
93
+ }
94
+ export function renderQuestionRecordPrompt(record) {
95
+ const spec = questionRecordToNumberedCheckpointSpec(record);
96
+ return {
97
+ questionId: record.id,
98
+ checkpointKey: record.prompt.checkpointKey ?? "manual",
99
+ spec,
100
+ prompt: buildNumberedCheckpointPrompt(spec)
101
+ };
102
+ }
50
103
  export function parseCheckpointResponse(spec, input) {
51
104
  return parseNumberedCheckpointResponse(spec, input);
52
105
  }
@@ -102,4 +155,5 @@ export function createCodexRuntimeBridge(setup) {
102
155
  };
103
156
  }
104
157
  export * from "./config.js";
158
+ export * from "./skills.js";
105
159
  export * from "./wrapper.js";
@@ -0,0 +1,16 @@
1
+ import type { RoleDefinition } from "@longtable/core";
2
+ export interface CodexSkillSpec {
3
+ name: string;
4
+ description: string;
5
+ body: string[];
6
+ }
7
+ export interface InstalledCodexSkill {
8
+ name: string;
9
+ path: string;
10
+ description: string;
11
+ }
12
+ export declare function resolveCodexSkillsDir(customDir?: string): string;
13
+ export declare function buildCodexSkillSpecs(roles: RoleDefinition[]): CodexSkillSpec[];
14
+ export declare function installCodexSkills(roles: RoleDefinition[], customDir?: string): Promise<InstalledCodexSkill[]>;
15
+ export declare function removeCodexSkills(roles: RoleDefinition[], customDir?: string): Promise<string[]>;
16
+ export declare function listInstalledCodexSkills(roles: RoleDefinition[], customDir?: string): Promise<InstalledCodexSkill[]>;
package/dist/skills.js ADDED
@@ -0,0 +1,214 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ export function resolveCodexSkillsDir(customDir) {
6
+ return customDir ? resolve(customDir) : join(homedir(), ".codex", "skills");
7
+ }
8
+ function skillNameForRole(role) {
9
+ return `longtable-${role.key.replaceAll("_", "-")}`;
10
+ }
11
+ function yamlString(value) {
12
+ return JSON.stringify(value);
13
+ }
14
+ function renderSkillFile(spec) {
15
+ return [
16
+ "---",
17
+ `name: ${spec.name}`,
18
+ `description: ${yamlString(spec.description)}`,
19
+ "---",
20
+ "",
21
+ `# ${spec.name}`,
22
+ "",
23
+ ...spec.body
24
+ ].join("\n");
25
+ }
26
+ function baseSkillSpecs() {
27
+ return [
28
+ {
29
+ name: "longtable",
30
+ description: "Use for LongTable research conversations, lt explore/review/panel requests, researcher checkpoints, and role routing.",
31
+ body: [
32
+ "## Purpose",
33
+ "",
34
+ "Act as the LongTable adapter inside Codex. LongTable is a researcher-centered workspace, not a replacement for the researcher.",
35
+ "",
36
+ "## Natural Invocation",
37
+ "",
38
+ "Use this skill when the user says things like:",
39
+ "",
40
+ "- `longtable: help me narrow this project`",
41
+ "- `lt explore: ...`",
42
+ "- `lt review: ...`",
43
+ "- `lt panel: ...`",
44
+ "- `use the LongTable methods critic on this design`",
45
+ "",
46
+ "## Routing Rules",
47
+ "",
48
+ "- `lt explore` keeps the problem open and asks clarifying or tension questions before recommending closure.",
49
+ "- `lt review` starts with the strongest weakness, objection, or missing evidence.",
50
+ "- `lt panel` creates a visible multi-role review with synthesis, role opinions, conflict summary, and a decision prompt.",
51
+ "- Natural references to methods, measurement, theory, reviewer, editor, ethics, venue, or voice should foreground the matching LongTable role.",
52
+ "- When research responsibility is about to shift, surface a Researcher Checkpoint before closure.",
53
+ "- When changing LongTable product language, README positioning, or checkpoint policy, surface a Meta-Decision Checkpoint first.",
54
+ "",
55
+ "## Panel And Team Behavior",
56
+ "",
57
+ "- If the user asks for a panel, team, disagreement, or multiple perspectives, treat that as permission to coordinate multiple LongTable roles.",
58
+ "- Use provider-native subagents only when the current Codex runtime makes them available and the user's request clearly calls for multi-agent work.",
59
+ "- If native subagents are unavailable, use LongTable's sequential panel fallback and make the fallback explicit in the technical record.",
60
+ "- Do not expose hidden reasoning, tool logs, or private chain-of-thought. Expose a structured deliberation record instead.",
61
+ "",
62
+ "## Project State",
63
+ "",
64
+ "- Treat `.longtable/` state as the source of truth when present.",
65
+ "- Read `CURRENT.md` when available before giving project-specific advice.",
66
+ "- If a Researcher Checkpoint is needed, ask a concise question with meaningful options and wait for the researcher.",
67
+ "- If a checkpoint allows `other`, make `other` visible instead of hiding it in state.",
68
+ "- Treat provider-native question UI as transport; LongTable state records are the source of truth.",
69
+ "- When the `longtable` CLI is available in a workspace, use `longtable question --print --provider codex --prompt \"...\"` to create and render a canonical checkpoint before proceeding.",
70
+ "- If `CURRENT.md` shows a pending required checkpoint, do not proceed until `longtable decide --question <id> --answer <value>` records the researcher's choice.",
71
+ "- Preserve open tensions and authorship instead of forcing closure.",
72
+ "- Label unsupported external claims as inference or estimate."
73
+ ]
74
+ },
75
+ {
76
+ name: "longtable-panel",
77
+ description: "Use when LongTable should run a panel or team-style review with visible role disagreement.",
78
+ body: [
79
+ "## Purpose",
80
+ "",
81
+ "Run a LongTable panel-style review in Codex.",
82
+ "",
83
+ "## When To Use",
84
+ "",
85
+ "- The user says `lt panel`.",
86
+ "- The user asks for disagreement, multiple perspectives, a team review, or pre-commit challenge.",
87
+ "- The work touches several research risks at once, such as theory, methods, measurement, venue fit, ethics, or authorship.",
88
+ "",
89
+ "## Output Contract",
90
+ "",
91
+ "Return:",
92
+ "",
93
+ "1. LongTable synthesis",
94
+ "2. Panel opinions by role",
95
+ "3. Conflict summary",
96
+ "4. Decision prompt for the researcher",
97
+ "5. Technical record: roles consulted, execution surface, fallback/native mode, and source/file references used",
98
+ "",
99
+ "## Rules",
100
+ "",
101
+ "- Do not collapse disagreement too early.",
102
+ "- Use a Researcher Checkpoint before treating a high-stakes research decision as settled.",
103
+ "- If the CLI is available, prefer `longtable question --print --provider codex --prompt \"...\"` for canonical numbered checkpoint rendering.",
104
+ "- Use `longtable panel --print --prompt \"...\"` only as an optional canonical prompt aid, not as the user's primary interface."
105
+ ]
106
+ },
107
+ {
108
+ name: "longtable-explore",
109
+ description: "Use for early LongTable research exploration, problem framing, and question narrowing.",
110
+ body: [
111
+ "## Purpose",
112
+ "",
113
+ "Help the researcher keep the problem open long enough to find a defensible research question.",
114
+ "",
115
+ "## Rules",
116
+ "",
117
+ "- Ask at least two clarifying or tension questions before recommending a direction.",
118
+ "- Surface scope boundaries, candidate constructs, and hidden assumptions.",
119
+ "- Keep unresolved tensions visible.",
120
+ "- Avoid generic search advice unless the user asks for evidence discovery."
121
+ ]
122
+ },
123
+ {
124
+ name: "longtable-review",
125
+ description: "Use for LongTable critical review of a claim, paragraph, study design, manuscript section, or plan.",
126
+ body: [
127
+ "## Purpose",
128
+ "",
129
+ "Review a research object without smoothing over weaknesses.",
130
+ "",
131
+ "## Rules",
132
+ "",
133
+ "- Start with the most important weakness, objection, or missing evidence.",
134
+ "- Separate sourced facts, interpretation, and speculation.",
135
+ "- Preserve the researcher's own language where possible.",
136
+ "- Ask a checkpoint question before treating a high-stakes decision as settled."
137
+ ]
138
+ }
139
+ ];
140
+ }
141
+ function roleSkillSpec(role) {
142
+ const label = role.label;
143
+ return {
144
+ name: skillNameForRole(role),
145
+ description: `Use the LongTable ${label} role: ${role.shortDescription}`,
146
+ body: [
147
+ "## Purpose",
148
+ "",
149
+ `Foreground the LongTable ${label} role.`,
150
+ "",
151
+ "## Natural Triggers",
152
+ "",
153
+ role.synonyms.map((synonym) => `- ${synonym}`).join("\n"),
154
+ "",
155
+ "## Role Focus",
156
+ "",
157
+ role.shortDescription,
158
+ "",
159
+ "## Rules",
160
+ "",
161
+ `- Disclose: \`LongTable consulted: ${label}\`.`,
162
+ "- Keep the role grounded in the user's research object and project state.",
163
+ "- Do not invent a separate role definition; this skill is an adapter generated from the LongTable role registry.",
164
+ "- If evidence is needed, ask whether the researcher wants scholarly search or citation verification.",
165
+ "- If the role's judgment would change the project direction, ask a Researcher Checkpoint before closure."
166
+ ]
167
+ };
168
+ }
169
+ export function buildCodexSkillSpecs(roles) {
170
+ return [...baseSkillSpecs(), ...roles.map(roleSkillSpec)];
171
+ }
172
+ export async function installCodexSkills(roles, customDir) {
173
+ const skillsDir = resolveCodexSkillsDir(customDir);
174
+ await mkdir(skillsDir, { recursive: true });
175
+ const installed = [];
176
+ for (const spec of buildCodexSkillSpecs(roles)) {
177
+ const skillDir = join(skillsDir, spec.name);
178
+ await mkdir(skillDir, { recursive: true });
179
+ const path = join(skillDir, "SKILL.md");
180
+ await writeFile(path, renderSkillFile(spec), "utf8");
181
+ installed.push({
182
+ name: spec.name,
183
+ path,
184
+ description: spec.description
185
+ });
186
+ }
187
+ return installed;
188
+ }
189
+ export async function removeCodexSkills(roles, customDir) {
190
+ const skillsDir = resolveCodexSkillsDir(customDir);
191
+ const removed = [];
192
+ for (const spec of buildCodexSkillSpecs(roles)) {
193
+ const skillDir = join(skillsDir, spec.name);
194
+ if (existsSync(skillDir)) {
195
+ await rm(skillDir, { recursive: true, force: true });
196
+ removed.push(skillDir);
197
+ }
198
+ }
199
+ return removed;
200
+ }
201
+ export async function listInstalledCodexSkills(roles, customDir) {
202
+ const skillsDir = resolveCodexSkillsDir(customDir);
203
+ if (!existsSync(skillsDir)) {
204
+ return [];
205
+ }
206
+ const entries = new Set(await readdir(skillsDir));
207
+ return buildCodexSkillSpecs(roles)
208
+ .filter((spec) => entries.has(spec.name) && existsSync(join(skillsDir, spec.name, "SKILL.md")))
209
+ .map((spec) => ({
210
+ name: spec.name,
211
+ path: join(skillsDir, spec.name, "SKILL.md"),
212
+ description: spec.description
213
+ }));
214
+ }
package/dist/wrapper.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { classifyCheckpointTrigger } from "@longtable/checkpoints";
1
2
  import type { InteractionMode, ResearchStage, RuntimeGuidance } from "@longtable/core";
2
3
  export interface CodexThinWrapperOptions {
3
4
  prompt: string;
@@ -9,6 +10,8 @@ export interface CodexThinWrapperOptions {
9
10
  export interface CodexWrappedPrompt {
10
11
  mode: InteractionMode;
11
12
  researchStage: ResearchStage;
13
+ checkpointKey: string;
14
+ checkpointTrigger: ReturnType<typeof classifyCheckpointTrigger>;
12
15
  guidance: RuntimeGuidance;
13
16
  wrappedPrompt: string;
14
17
  setupLoaded: boolean;
package/dist/wrapper.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { cwd } from "node:process";
4
- import { resolveCheckpointPolicy, resolveRuntimeGuidance } from "@longtable/checkpoints";
4
+ import { classifyCheckpointTrigger, resolveCheckpointPolicy, resolveRuntimeGuidance } from "@longtable/checkpoints";
5
5
  import { loadSetupOutput, resolveDefaultSetupPath } from "@longtable/setup";
6
6
  import { normalizeResearcherProfile, renderRuntimeGuidance } from "./index.js";
7
7
  function defaultProfile() {
@@ -32,20 +32,6 @@ function defaultStage(mode) {
32
32
  return "problem_framing";
33
33
  }
34
34
  }
35
- function defaultCheckpointLevel(mode) {
36
- switch (mode) {
37
- case "submit":
38
- return "adaptive_required";
39
- case "commit":
40
- return "recommended";
41
- case "review":
42
- case "critique":
43
- case "draft":
44
- case "explore":
45
- default:
46
- return "recommended";
47
- }
48
- }
49
35
  async function loadManagedSetup(customPath) {
50
36
  const target = resolveDefaultSetupPath(customPath).path;
51
37
  if (!existsSync(target)) {
@@ -67,27 +53,26 @@ function readPromptFromStdin() {
67
53
  export async function buildCodexThinWrappedPrompt(options) {
68
54
  const setup = await loadManagedSetup(options.setupPath);
69
55
  const profile = setup ? normalizeResearcherProfile(setup.profileSeed) : defaultProfile();
70
- const mode = options.mode ?? defaultMode(setup ?? undefined);
71
- const researchStage = options.researchStage ?? defaultStage(mode);
72
56
  const prompt = options.prompt.trim();
73
- const signal = {
74
- checkpointKey: "runtime_guidance",
75
- baseLevel: defaultCheckpointLevel(mode),
76
- mode,
77
- artifactStakes: mode === "submit"
78
- ? "external_submission"
79
- : mode === "commit"
80
- ? "study_protocol"
81
- : mode === "draft"
82
- ? "internal_draft"
83
- : "private_note",
84
- researchStage,
57
+ const fallbackMode = defaultMode(setup ?? undefined);
58
+ const trigger = classifyCheckpointTrigger(prompt, {
59
+ preferredMode: options.mode,
60
+ fallbackMode,
61
+ researchStage: options.researchStage ?? (options.mode ? defaultStage(options.mode) : undefined),
85
62
  unresolvedTensions: setup?.initialState.openTensions ?? [],
86
63
  studyContract: setup?.initialState.studyContract
87
- };
88
- const policy = resolveCheckpointPolicy(profile, signal);
89
- const guidance = resolveRuntimeGuidance(profile, signal, policy);
90
- const sections = [renderRuntimeGuidance(guidance)];
64
+ });
65
+ const policy = resolveCheckpointPolicy(profile, trigger.signal);
66
+ const guidance = resolveRuntimeGuidance(profile, trigger.signal, policy);
67
+ const triggerLines = [
68
+ "LongTable checkpoint trigger",
69
+ `- checkpoint: ${trigger.signal.checkpointKey}`,
70
+ `- trigger family: ${trigger.family}`,
71
+ `- confidence: ${trigger.confidence}`,
72
+ `- status: ${trigger.requiresQuestionBeforeClosure ? "question required before closure" : "advisory guidance"}`,
73
+ `- rationale: ${trigger.rationale.join(" ")}`
74
+ ];
75
+ const sections = [triggerLines.join("\n"), renderRuntimeGuidance(guidance)];
91
76
  const instructionLines = [
92
77
  "LongTable ordering rules",
93
78
  guidance.minimumQuestions > 0 || guidance.mustAskBeforeClosure
@@ -104,8 +89,10 @@ export async function buildCodexThinWrappedPrompt(options) {
104
89
  sections.push(instructionLines.join("\n"));
105
90
  sections.push(["User prompt", prompt].join("\n"));
106
91
  return {
107
- mode,
108
- researchStage,
92
+ mode: trigger.signal.mode,
93
+ researchStage: trigger.signal.researchStage,
94
+ checkpointKey: trigger.signal.checkpointKey,
95
+ checkpointTrigger: trigger,
109
96
  guidance,
110
97
  wrappedPrompt: sections.join("\n\n"),
111
98
  setupLoaded: Boolean(setup)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longtable/provider-codex",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": false,
5
5
  "description": "Codex adapter surface for LongTable",
6
6
  "type": "module",
@@ -15,9 +15,9 @@
15
15
  "typecheck": "tsc -p tsconfig.json --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "@longtable/checkpoints": "0.1.9",
19
- "@longtable/core": "0.1.9",
20
- "@longtable/setup": "0.1.9"
18
+ "@longtable/checkpoints": "0.1.11",
19
+ "@longtable/core": "0.1.11",
20
+ "@longtable/setup": "0.1.11"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^22.10.1",
@@ -42,5 +42,11 @@
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=18.0.0"
45
+ },
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/index.d.ts",
49
+ "import": "./dist/index.js"
50
+ }
45
51
  }
46
52
  }