@longtable/provider-codex 0.1.9
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/README.md +50 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +39 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +105 -0
- package/dist/wrapper-cli.d.ts +2 -0
- package/dist/wrapper-cli.js +138 -0
- package/dist/wrapper.d.ts +20 -0
- package/dist/wrapper.js +141 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @longtable/provider-codex
|
|
2
|
+
|
|
3
|
+
Codex-specific adapter logic for LongTable.
|
|
4
|
+
|
|
5
|
+
This package implements numbered checkpoint interaction and thin runtime-guidance injection rather than depending on a provider-native question widget.
|
|
6
|
+
|
|
7
|
+
## Role
|
|
8
|
+
|
|
9
|
+
This package provides the Codex adapter used by `@longtable/cli`.
|
|
10
|
+
|
|
11
|
+
It is publishable as a library package, but the primary researcher-facing command surface is `longtable`, not a standalone wrapper executable.
|
|
12
|
+
|
|
13
|
+
### Local build and test
|
|
14
|
+
|
|
15
|
+
From the workspace root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
npm run typecheck
|
|
20
|
+
npm run build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then test the adapter directly:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
node packages/longtable-provider-codex/dist/wrapper-cli.js \
|
|
27
|
+
--print \
|
|
28
|
+
--mode explore \
|
|
29
|
+
--prompt "I want exploration, not a conclusion yet."
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node packages/longtable-provider-codex/dist/wrapper-cli.js \
|
|
34
|
+
--exec \
|
|
35
|
+
--json \
|
|
36
|
+
--mode review \
|
|
37
|
+
--prompt "Review this claim critically."
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If the wrapped behavior changes, update this README in the same commit.
|
|
41
|
+
|
|
42
|
+
The wrapper reads the managed setup artifact when available and prepends a short runtime-guidance block so Codex receives:
|
|
43
|
+
|
|
44
|
+
- mode
|
|
45
|
+
- minimum question requirements
|
|
46
|
+
- closure disposition
|
|
47
|
+
- narrative-trace preservation rules
|
|
48
|
+
- human commitment stakes when relevant
|
|
49
|
+
|
|
50
|
+
For the recommended setup path, pair this package with `@longtable/setup`.
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SetupPersistedOutput } from "@longtable/setup";
|
|
2
|
+
import { createCodexRuntimeBridge } from "./index.js";
|
|
3
|
+
export interface CodexConfigFragment {
|
|
4
|
+
setupPath: string;
|
|
5
|
+
provider: "codex";
|
|
6
|
+
checkpointProtocol: "numbered";
|
|
7
|
+
defaultInteractionMode: SetupPersistedOutput["defaultInteractionMode"];
|
|
8
|
+
profileSummary: {
|
|
9
|
+
field: string;
|
|
10
|
+
careerStage: string;
|
|
11
|
+
experienceLevel: string;
|
|
12
|
+
currentProjectType: string;
|
|
13
|
+
};
|
|
14
|
+
runtimeGuidance: ReturnType<typeof createCodexRuntimeBridge>["runtimeDefaults"];
|
|
15
|
+
}
|
|
16
|
+
export declare function createCodexConfigFragment(setup: SetupPersistedOutput, setupPath: string): CodexConfigFragment;
|
|
17
|
+
export declare function renderCodexConfigToml(setup: SetupPersistedOutput, setupPath: string): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createCodexRuntimeBridge } from "./index.js";
|
|
2
|
+
export function createCodexConfigFragment(setup, setupPath) {
|
|
3
|
+
const bridge = createCodexRuntimeBridge(setup);
|
|
4
|
+
return {
|
|
5
|
+
setupPath,
|
|
6
|
+
provider: "codex",
|
|
7
|
+
checkpointProtocol: bridge.checkpointProtocol,
|
|
8
|
+
defaultInteractionMode: bridge.defaultInteractionMode,
|
|
9
|
+
profileSummary: {
|
|
10
|
+
field: bridge.profile.field,
|
|
11
|
+
careerStage: bridge.profile.careerStage,
|
|
12
|
+
experienceLevel: bridge.profile.experienceLevel,
|
|
13
|
+
currentProjectType: bridge.profile.currentProjectType
|
|
14
|
+
},
|
|
15
|
+
runtimeGuidance: bridge.runtimeDefaults
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function renderCodexConfigToml(setup, setupPath) {
|
|
19
|
+
const fragment = createCodexConfigFragment(setup, setupPath);
|
|
20
|
+
return [
|
|
21
|
+
"[longtable]",
|
|
22
|
+
`provider = "${fragment.provider}"`,
|
|
23
|
+
`setup_path = "${fragment.setupPath}"`,
|
|
24
|
+
`checkpoint_protocol = "${fragment.checkpointProtocol}"`,
|
|
25
|
+
`default_interaction_mode = "${fragment.defaultInteractionMode}"`,
|
|
26
|
+
"",
|
|
27
|
+
"[longtable.profile]",
|
|
28
|
+
`field = "${fragment.profileSummary.field}"`,
|
|
29
|
+
`career_stage = "${fragment.profileSummary.careerStage}"`,
|
|
30
|
+
`experience_level = "${fragment.profileSummary.experienceLevel}"`,
|
|
31
|
+
`current_project_type = "${fragment.profileSummary.currentProjectType}"`,
|
|
32
|
+
"",
|
|
33
|
+
"[longtable.runtime_guidance]",
|
|
34
|
+
`ask_at_least_two_questions_in_explore = ${fragment.runtimeGuidance.askAtLeastTwoQuestionsInExplore}`,
|
|
35
|
+
`preserve_narrative_trace_in_draft = ${fragment.runtimeGuidance.preserveNarrativeTraceInDraft}`,
|
|
36
|
+
`require_why_may_be_wrong_in_review = ${fragment.runtimeGuidance.requireWhyMayBeWrongInReview}`,
|
|
37
|
+
`question_bias_compensation = "${fragment.runtimeGuidance.questionBiasCompensation}"`
|
|
38
|
+
].join("\n");
|
|
39
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CheckpointSignal, ResearcherProfile, ResolvedCheckpointPolicy, RuntimeGuidance } from "@longtable/checkpoints";
|
|
2
|
+
import type { NumberedCheckpointSpec, ParsedCheckpointSelection, SetupPersistedOutput } from "@longtable/setup";
|
|
3
|
+
export interface CodexCheckpointContext {
|
|
4
|
+
profile: ResearcherProfile;
|
|
5
|
+
signal: CheckpointSignal;
|
|
6
|
+
spec: NumberedCheckpointSpec;
|
|
7
|
+
}
|
|
8
|
+
export interface CodexCheckpointResult {
|
|
9
|
+
policy: ResolvedCheckpointPolicy;
|
|
10
|
+
guidance: RuntimeGuidance;
|
|
11
|
+
prompt: string;
|
|
12
|
+
selection?: ParsedCheckpointSelection;
|
|
13
|
+
accepted: boolean;
|
|
14
|
+
reprompt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CodexRuntimeDefaults {
|
|
17
|
+
askAtLeastTwoQuestionsInExplore: boolean;
|
|
18
|
+
preserveNarrativeTraceInDraft: boolean;
|
|
19
|
+
requireWhyMayBeWrongInReview: boolean;
|
|
20
|
+
questionBiasCompensation: "strong";
|
|
21
|
+
}
|
|
22
|
+
export interface CodexRuntimeBridge {
|
|
23
|
+
provider: "codex";
|
|
24
|
+
checkpointProtocol: "numbered";
|
|
25
|
+
supportsStructuredQuestions: false;
|
|
26
|
+
defaultInteractionMode: SetupPersistedOutput["defaultInteractionMode"];
|
|
27
|
+
profile: ResearcherProfile;
|
|
28
|
+
runtimeDefaults: CodexRuntimeDefaults;
|
|
29
|
+
}
|
|
30
|
+
export declare function normalizeResearcherProfile(profile: SetupPersistedOutput["profileSeed"]): ResearcherProfile;
|
|
31
|
+
export declare function renderRuntimeGuidance(guidance: RuntimeGuidance): string;
|
|
32
|
+
export declare function renderCheckpointPrompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
|
|
33
|
+
export declare function parseCheckpointResponse(spec: NumberedCheckpointSpec, input: string): ParsedCheckpointSelection | null;
|
|
34
|
+
export declare function buildReprompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
|
|
35
|
+
export declare function runBlockingCheckpoint(context: CodexCheckpointContext, input?: string): CodexCheckpointResult;
|
|
36
|
+
export declare function createCodexRuntimeBridge(setup: SetupPersistedOutput): CodexRuntimeBridge;
|
|
37
|
+
export * from "./config.js";
|
|
38
|
+
export * from "./wrapper.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolveCheckpointPolicy } from "@longtable/checkpoints";
|
|
2
|
+
import { resolveRuntimeGuidance } from "@longtable/checkpoints";
|
|
3
|
+
import { buildNumberedCheckpointPrompt, parseNumberedCheckpointResponse } from "@longtable/setup";
|
|
4
|
+
export function normalizeResearcherProfile(profile) {
|
|
5
|
+
return {
|
|
6
|
+
field: profile.field,
|
|
7
|
+
careerStage: profile.careerStage,
|
|
8
|
+
experienceLevel: profile.experienceLevel,
|
|
9
|
+
preferredCheckpointIntensity: profile.preferredCheckpointIntensity,
|
|
10
|
+
currentProjectType: profile.currentProjectType ?? "unspecified research task",
|
|
11
|
+
...(profile.humanAuthorshipSignal ? { humanAuthorshipSignal: profile.humanAuthorshipSignal } : {}),
|
|
12
|
+
...(profile.aiAutonomyPreference ? { aiAutonomyPreference: profile.aiAutonomyPreference } : {})
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function renderRuntimeGuidance(guidance) {
|
|
16
|
+
const lines = [
|
|
17
|
+
"LongTable runtime guidance",
|
|
18
|
+
`- mode: ${guidance.mode}`,
|
|
19
|
+
`- closure: ${guidance.closureDisposition}`,
|
|
20
|
+
`- minimum questions before closure: ${guidance.minimumQuestions}`,
|
|
21
|
+
`- question types: ${guidance.questionTypes.length > 0 ? guidance.questionTypes.join(", ") : "none"}`,
|
|
22
|
+
`- questions first: ${guidance.preferQuestionsFirst ? "yes" : "no"}`,
|
|
23
|
+
`- preserve narrative trace: ${guidance.preserveNarrativeTrace ? "yes" : "no"}`
|
|
24
|
+
];
|
|
25
|
+
if (guidance.includeWhyMayBeWrong) {
|
|
26
|
+
lines.push("- include: why this may be wrong");
|
|
27
|
+
}
|
|
28
|
+
if (guidance.includeOpenTensions) {
|
|
29
|
+
lines.push("- include: open tensions");
|
|
30
|
+
}
|
|
31
|
+
if (guidance.surfaceHumanCommitment) {
|
|
32
|
+
lines.push("- include: why this requires explicit human commitment");
|
|
33
|
+
}
|
|
34
|
+
if (guidance.mandatoryQuestions.length > 0) {
|
|
35
|
+
lines.push("- ask first:");
|
|
36
|
+
for (const question of guidance.mandatoryQuestions) {
|
|
37
|
+
lines.push(` - ${question}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
export function renderCheckpointPrompt(spec, guidance) {
|
|
43
|
+
const sections = [];
|
|
44
|
+
if (guidance) {
|
|
45
|
+
sections.push(renderRuntimeGuidance(guidance));
|
|
46
|
+
}
|
|
47
|
+
sections.push(buildNumberedCheckpointPrompt(spec));
|
|
48
|
+
return sections.join("\n\n");
|
|
49
|
+
}
|
|
50
|
+
export function parseCheckpointResponse(spec, input) {
|
|
51
|
+
return parseNumberedCheckpointResponse(spec, input);
|
|
52
|
+
}
|
|
53
|
+
export function buildReprompt(spec, guidance) {
|
|
54
|
+
return [
|
|
55
|
+
"Your response could not be parsed.",
|
|
56
|
+
renderCheckpointPrompt(spec, guidance)
|
|
57
|
+
].join("\n\n");
|
|
58
|
+
}
|
|
59
|
+
export function runBlockingCheckpoint(context, input) {
|
|
60
|
+
const policy = resolveCheckpointPolicy(context.profile, context.signal);
|
|
61
|
+
const guidance = resolveRuntimeGuidance(context.profile, context.signal, policy);
|
|
62
|
+
const prompt = renderCheckpointPrompt(context.spec, guidance);
|
|
63
|
+
if (!policy.blocking) {
|
|
64
|
+
return {
|
|
65
|
+
policy,
|
|
66
|
+
guidance,
|
|
67
|
+
prompt,
|
|
68
|
+
accepted: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const selection = input ? parseCheckpointResponse(context.spec, input) : null;
|
|
72
|
+
if (!selection) {
|
|
73
|
+
return {
|
|
74
|
+
policy,
|
|
75
|
+
guidance,
|
|
76
|
+
prompt,
|
|
77
|
+
accepted: false,
|
|
78
|
+
reprompt: buildReprompt(context.spec, guidance)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
policy,
|
|
83
|
+
guidance,
|
|
84
|
+
prompt,
|
|
85
|
+
selection,
|
|
86
|
+
accepted: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function createCodexRuntimeBridge(setup) {
|
|
90
|
+
return {
|
|
91
|
+
provider: "codex",
|
|
92
|
+
checkpointProtocol: "numbered",
|
|
93
|
+
supportsStructuredQuestions: false,
|
|
94
|
+
defaultInteractionMode: setup.defaultInteractionMode,
|
|
95
|
+
profile: normalizeResearcherProfile(setup.profileSeed),
|
|
96
|
+
runtimeDefaults: {
|
|
97
|
+
askAtLeastTwoQuestionsInExplore: true,
|
|
98
|
+
preserveNarrativeTraceInDraft: true,
|
|
99
|
+
requireWhyMayBeWrongInReview: true,
|
|
100
|
+
questionBiasCompensation: "strong"
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export * from "./config.js";
|
|
105
|
+
export * from "./wrapper.js";
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { cwd, exit } from "node:process";
|
|
4
|
+
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "./wrapper.js";
|
|
5
|
+
const VALID_MODES = new Set([
|
|
6
|
+
"explore",
|
|
7
|
+
"review",
|
|
8
|
+
"critique",
|
|
9
|
+
"draft",
|
|
10
|
+
"commit",
|
|
11
|
+
"submit"
|
|
12
|
+
]);
|
|
13
|
+
const VALID_STAGES = new Set([
|
|
14
|
+
"problem_framing",
|
|
15
|
+
"theory_selection",
|
|
16
|
+
"method_design",
|
|
17
|
+
"measurement_design",
|
|
18
|
+
"analysis_planning",
|
|
19
|
+
"writing",
|
|
20
|
+
"submission"
|
|
21
|
+
]);
|
|
22
|
+
function usage() {
|
|
23
|
+
return [
|
|
24
|
+
"Usage:",
|
|
25
|
+
" longtable-codex-wrapper --print --prompt \"...\" [--mode explore] [--stage problem_framing]",
|
|
26
|
+
" longtable-codex-wrapper --exec --prompt \"...\" [--mode review] [--json]",
|
|
27
|
+
"",
|
|
28
|
+
"Options:",
|
|
29
|
+
" --prompt <text> Prompt to wrap before sending to Codex",
|
|
30
|
+
" --mode <mode> explore|review|critique|draft|commit|submit",
|
|
31
|
+
" --stage <stage> problem_framing|theory_selection|method_design|measurement_design|analysis_planning|writing|submission",
|
|
32
|
+
" --setup <path> Override managed setup path",
|
|
33
|
+
" --cwd <path> Working directory to pass to codex exec",
|
|
34
|
+
" --print Print the wrapped prompt only",
|
|
35
|
+
" --exec Run codex exec with the wrapped prompt",
|
|
36
|
+
" --json When used with --exec, request JSONL events from Codex",
|
|
37
|
+
" --help Show this message"
|
|
38
|
+
].join("\n");
|
|
39
|
+
}
|
|
40
|
+
function parseArgs(argv) {
|
|
41
|
+
const parsed = {
|
|
42
|
+
workingDirectory: cwd(),
|
|
43
|
+
json: false,
|
|
44
|
+
print: false,
|
|
45
|
+
exec: false
|
|
46
|
+
};
|
|
47
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
48
|
+
const token = argv[index];
|
|
49
|
+
switch (token) {
|
|
50
|
+
case "--prompt":
|
|
51
|
+
parsed.prompt = argv[index + 1];
|
|
52
|
+
index += 1;
|
|
53
|
+
break;
|
|
54
|
+
case "--mode":
|
|
55
|
+
if (!VALID_MODES.has(argv[index + 1])) {
|
|
56
|
+
throw new Error(`Invalid mode: ${argv[index + 1]}`);
|
|
57
|
+
}
|
|
58
|
+
parsed.mode = argv[index + 1];
|
|
59
|
+
index += 1;
|
|
60
|
+
break;
|
|
61
|
+
case "--stage":
|
|
62
|
+
if (!VALID_STAGES.has(argv[index + 1])) {
|
|
63
|
+
throw new Error(`Invalid stage: ${argv[index + 1]}`);
|
|
64
|
+
}
|
|
65
|
+
parsed.stage = argv[index + 1];
|
|
66
|
+
index += 1;
|
|
67
|
+
break;
|
|
68
|
+
case "--setup":
|
|
69
|
+
parsed.setupPath = argv[index + 1];
|
|
70
|
+
index += 1;
|
|
71
|
+
break;
|
|
72
|
+
case "--cwd":
|
|
73
|
+
parsed.workingDirectory = argv[index + 1];
|
|
74
|
+
index += 1;
|
|
75
|
+
break;
|
|
76
|
+
case "--json":
|
|
77
|
+
parsed.json = true;
|
|
78
|
+
break;
|
|
79
|
+
case "--print":
|
|
80
|
+
parsed.print = true;
|
|
81
|
+
break;
|
|
82
|
+
case "--exec":
|
|
83
|
+
parsed.exec = true;
|
|
84
|
+
break;
|
|
85
|
+
case "--help":
|
|
86
|
+
console.log(usage());
|
|
87
|
+
exit(0);
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown argument: ${token}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!parsed.print && !parsed.exec) {
|
|
93
|
+
parsed.print = true;
|
|
94
|
+
}
|
|
95
|
+
return parsed;
|
|
96
|
+
}
|
|
97
|
+
function resolvePrompt(prompt) {
|
|
98
|
+
if (prompt && prompt.trim()) {
|
|
99
|
+
return prompt.trim();
|
|
100
|
+
}
|
|
101
|
+
if (!process.stdin.isTTY) {
|
|
102
|
+
return readFileSync(0, "utf8").trim();
|
|
103
|
+
}
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
async function main() {
|
|
107
|
+
const args = parseArgs(process.argv.slice(2));
|
|
108
|
+
const prompt = resolvePrompt(args.prompt);
|
|
109
|
+
if (!prompt) {
|
|
110
|
+
throw new Error("A prompt is required. Pass --prompt or pipe text on stdin.");
|
|
111
|
+
}
|
|
112
|
+
if (args.print) {
|
|
113
|
+
const wrapped = await buildCodexThinWrappedPrompt({
|
|
114
|
+
prompt,
|
|
115
|
+
mode: args.mode,
|
|
116
|
+
researchStage: args.stage,
|
|
117
|
+
setupPath: args.setupPath,
|
|
118
|
+
workingDirectory: args.workingDirectory
|
|
119
|
+
});
|
|
120
|
+
console.log(wrapped.wrappedPrompt);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const exitCode = await runCodexThinWrapper({
|
|
124
|
+
prompt,
|
|
125
|
+
mode: args.mode,
|
|
126
|
+
researchStage: args.stage,
|
|
127
|
+
setupPath: args.setupPath,
|
|
128
|
+
workingDirectory: args.workingDirectory,
|
|
129
|
+
json: args.json
|
|
130
|
+
});
|
|
131
|
+
exit(exitCode);
|
|
132
|
+
}
|
|
133
|
+
main().catch((error) => {
|
|
134
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
135
|
+
console.error("");
|
|
136
|
+
console.error(usage());
|
|
137
|
+
exit(1);
|
|
138
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { InteractionMode, ResearchStage, RuntimeGuidance } from "@longtable/core";
|
|
2
|
+
export interface CodexThinWrapperOptions {
|
|
3
|
+
prompt: string;
|
|
4
|
+
mode?: InteractionMode;
|
|
5
|
+
researchStage?: ResearchStage;
|
|
6
|
+
workingDirectory?: string;
|
|
7
|
+
setupPath?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CodexWrappedPrompt {
|
|
10
|
+
mode: InteractionMode;
|
|
11
|
+
researchStage: ResearchStage;
|
|
12
|
+
guidance: RuntimeGuidance;
|
|
13
|
+
wrappedPrompt: string;
|
|
14
|
+
setupLoaded: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildCodexThinWrappedPrompt(options: CodexThinWrapperOptions): Promise<CodexWrappedPrompt>;
|
|
17
|
+
export interface CodexThinWrapperExecOptions extends CodexThinWrapperOptions {
|
|
18
|
+
json?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function runCodexThinWrapper(options: CodexThinWrapperExecOptions): Promise<number>;
|
package/dist/wrapper.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { cwd } from "node:process";
|
|
4
|
+
import { resolveCheckpointPolicy, resolveRuntimeGuidance } from "@longtable/checkpoints";
|
|
5
|
+
import { loadSetupOutput, resolveDefaultSetupPath } from "@longtable/setup";
|
|
6
|
+
import { normalizeResearcherProfile, renderRuntimeGuidance } from "./index.js";
|
|
7
|
+
function defaultProfile() {
|
|
8
|
+
return {
|
|
9
|
+
field: "unknown",
|
|
10
|
+
careerStage: "unknown",
|
|
11
|
+
experienceLevel: "intermediate",
|
|
12
|
+
preferredCheckpointIntensity: "balanced",
|
|
13
|
+
currentProjectType: "unspecified research task"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function defaultMode(setup) {
|
|
17
|
+
return setup?.defaultInteractionMode ?? "explore";
|
|
18
|
+
}
|
|
19
|
+
function defaultStage(mode) {
|
|
20
|
+
switch (mode) {
|
|
21
|
+
case "draft":
|
|
22
|
+
return "writing";
|
|
23
|
+
case "commit":
|
|
24
|
+
return "theory_selection";
|
|
25
|
+
case "submit":
|
|
26
|
+
return "submission";
|
|
27
|
+
case "review":
|
|
28
|
+
case "critique":
|
|
29
|
+
return "analysis_planning";
|
|
30
|
+
case "explore":
|
|
31
|
+
default:
|
|
32
|
+
return "problem_framing";
|
|
33
|
+
}
|
|
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
|
+
async function loadManagedSetup(customPath) {
|
|
50
|
+
const target = resolveDefaultSetupPath(customPath).path;
|
|
51
|
+
if (!existsSync(target)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return await loadSetupOutput(customPath);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function readPromptFromStdin() {
|
|
62
|
+
if (process.stdin.isTTY) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
return readFileSync(0, "utf8").trim();
|
|
66
|
+
}
|
|
67
|
+
export async function buildCodexThinWrappedPrompt(options) {
|
|
68
|
+
const setup = await loadManagedSetup(options.setupPath);
|
|
69
|
+
const profile = setup ? normalizeResearcherProfile(setup.profileSeed) : defaultProfile();
|
|
70
|
+
const mode = options.mode ?? defaultMode(setup ?? undefined);
|
|
71
|
+
const researchStage = options.researchStage ?? defaultStage(mode);
|
|
72
|
+
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,
|
|
85
|
+
unresolvedTensions: setup?.initialState.openTensions ?? [],
|
|
86
|
+
studyContract: setup?.initialState.studyContract
|
|
87
|
+
};
|
|
88
|
+
const policy = resolveCheckpointPolicy(profile, signal);
|
|
89
|
+
const guidance = resolveRuntimeGuidance(profile, signal, policy);
|
|
90
|
+
const sections = [renderRuntimeGuidance(guidance)];
|
|
91
|
+
const instructionLines = [
|
|
92
|
+
"LongTable ordering rules",
|
|
93
|
+
guidance.minimumQuestions > 0 || guidance.mustAskBeforeClosure
|
|
94
|
+
? "- surface the mandatory questions before any recommendation or closure"
|
|
95
|
+
: "- keep the response aligned to the stated mode",
|
|
96
|
+
guidance.includeWhyMayBeWrong ? "- include why this may be wrong before synthesis" : undefined,
|
|
97
|
+
guidance.includeOpenTensions ? "- keep unresolved tensions visible" : undefined,
|
|
98
|
+
guidance.preserveNarrativeTrace ? "- preserve narrative trace and avoid generic fluency" : undefined,
|
|
99
|
+
guidance.surfaceHumanCommitment ? "- make the human commitment stakes explicit before closing" : undefined,
|
|
100
|
+
profile.humanAuthorshipSignal
|
|
101
|
+
? `- preserve this human authorship signal: ${profile.humanAuthorshipSignal}`
|
|
102
|
+
: undefined
|
|
103
|
+
].filter(Boolean);
|
|
104
|
+
sections.push(instructionLines.join("\n"));
|
|
105
|
+
sections.push(["User prompt", prompt].join("\n"));
|
|
106
|
+
return {
|
|
107
|
+
mode,
|
|
108
|
+
researchStage,
|
|
109
|
+
guidance,
|
|
110
|
+
wrappedPrompt: sections.join("\n\n"),
|
|
111
|
+
setupLoaded: Boolean(setup)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export async function runCodexThinWrapper(options) {
|
|
115
|
+
const prompt = options.prompt.trim() || readPromptFromStdin();
|
|
116
|
+
if (!prompt) {
|
|
117
|
+
throw new Error("A prompt is required. Pass --prompt or pipe text on stdin.");
|
|
118
|
+
}
|
|
119
|
+
const wrapped = await buildCodexThinWrappedPrompt({
|
|
120
|
+
...options,
|
|
121
|
+
prompt
|
|
122
|
+
});
|
|
123
|
+
const args = [
|
|
124
|
+
"exec",
|
|
125
|
+
"--color",
|
|
126
|
+
"never",
|
|
127
|
+
"-s",
|
|
128
|
+
"read-only",
|
|
129
|
+
"-C",
|
|
130
|
+
options.workingDirectory ?? cwd(),
|
|
131
|
+
"--skip-git-repo-check"
|
|
132
|
+
];
|
|
133
|
+
if (options.json) {
|
|
134
|
+
args.push("--json");
|
|
135
|
+
}
|
|
136
|
+
args.push(wrapped.wrappedPrompt);
|
|
137
|
+
const result = spawnSync("codex", args, {
|
|
138
|
+
stdio: "inherit"
|
|
139
|
+
});
|
|
140
|
+
return result.status ?? 1;
|
|
141
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longtable/provider-codex",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Codex adapter surface for LongTable",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@longtable/checkpoints": "0.1.9",
|
|
19
|
+
"@longtable/core": "0.1.9",
|
|
20
|
+
"@longtable/setup": "0.1.9"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.10.1",
|
|
24
|
+
"typescript": "^5.6.0"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"longtable",
|
|
28
|
+
"research",
|
|
29
|
+
"codex",
|
|
30
|
+
"wrapper",
|
|
31
|
+
"checkpoints"
|
|
32
|
+
],
|
|
33
|
+
"author": "Hosung You",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/HosungYou/LongTable.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/HosungYou/LongTable#readme",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|