@longtable/cli 0.1.24 → 0.1.25
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 +12 -5
- package/dist/cli.js +47 -25
- package/dist/debate.d.ts +1 -0
- package/dist/debate.js +70 -43
- package/dist/panel.js +3 -0
- package/dist/project-session.d.ts +4 -1
- package/dist/project-session.js +6 -4
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ longtable ask --cwd "<project-path>" --prompt "..."
|
|
|
92
92
|
longtable panel --prompt "..."
|
|
93
93
|
longtable sentinel --prompt "Should I define a new measurement construct?"
|
|
94
94
|
longtable hud --watch
|
|
95
|
+
longtable team --prompt "Review this measurement plan." --role editor,measurement_auditor --json
|
|
95
96
|
longtable team --tmux --prompt "Review this measurement plan."
|
|
96
97
|
longtable team --debate --prompt "Review this measurement plan." --role editor,measurement_auditor --json
|
|
97
98
|
longtable codex install-skills
|
|
@@ -164,7 +165,7 @@ Default panel roles include:
|
|
|
164
165
|
|
|
165
166
|
Use `--role` to constrain the panel when the research problem is already clear.
|
|
166
167
|
|
|
167
|
-
## Sentinel, HUD, And
|
|
168
|
+
## Sentinel, HUD, And Agent Team
|
|
168
169
|
|
|
169
170
|
`longtable sentinel` is an explicit gap/tacit check for prompts that may contain
|
|
170
171
|
measurement, theory, method, evidence, authorship, or tacit-assumption risks.
|
|
@@ -175,16 +176,22 @@ unconfirmed inferred hypothesis.
|
|
|
175
176
|
blocker, pending checkpoints, recent decisions, and invocation counts.
|
|
176
177
|
`longtable hud --tmux` opens that view in a tmux pane.
|
|
177
178
|
|
|
178
|
-
`longtable team
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
`longtable team` creates a file-backed agent-team review under
|
|
180
|
+
`.longtable/team/<id>/`: independent review, cross-review, and
|
|
181
|
+
synthesis/checkpoint. Use it when roles should inspect each other's concerns
|
|
182
|
+
before LongTable proposes a researcher decision.
|
|
183
|
+
|
|
184
|
+
`longtable team --tmux` opens role-specific panes for live discussion. The panes
|
|
185
|
+
can add logs, but the file-backed team artifact remains the source of truth.
|
|
182
186
|
|
|
183
187
|
`longtable team --debate` creates a fixed five-round debate record under
|
|
184
188
|
`.longtable/team/<id>/`: independent review, cross-review, rebuttal,
|
|
185
189
|
convergence, and synthesis/checkpoint. Tmux can show live role panes, but the
|
|
186
190
|
file-backed artifact directory is the source of truth.
|
|
187
191
|
|
|
192
|
+
See `docs/AGENT-TEAM-README.md` in the repository for a user-facing guide to
|
|
193
|
+
panel, team, debate, and tmux surfaces.
|
|
194
|
+
|
|
188
195
|
## Evidence And Search Direction
|
|
189
196
|
|
|
190
197
|
LongTable should not behave like a generic web scraper. Research search should
|
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router
|
|
|
16
16
|
import { PERSONA_DEFINITIONS, listRoleDefinitions } from "./personas.js";
|
|
17
17
|
import { buildPanelFallback, renderPanelSummary } from "./panel.js";
|
|
18
18
|
import { appendInvocationRecordToWorkspace, assertWorkspaceNotBlocked, answerWorkspaceQuestion, createWorkspaceClarificationCard, createWorkspaceQuestion, createOrUpdateProjectWorkspace, inspectProjectWorkspace, loadWorkspaceState, loadProjectContextFromDirectory, renderProjectWorkspaceSummary, syncCurrentWorkspaceView } from "./project-session.js";
|
|
19
|
-
import { buildTeamDebate, renderTeamDebateSummary } from "./debate.js";
|
|
19
|
+
import { buildTeamDebate, buildTeamReview, renderTeamDebateSummary } from "./debate.js";
|
|
20
20
|
const VALID_MODES = new Set([
|
|
21
21
|
"explore",
|
|
22
22
|
"review",
|
|
@@ -42,7 +42,7 @@ const ANSI = {
|
|
|
42
42
|
green: "\u001B[32m"
|
|
43
43
|
};
|
|
44
44
|
const LONGTABLE_MCP_SERVER_NAME = "longtable-state";
|
|
45
|
-
const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.
|
|
45
|
+
const LONGTABLE_MCP_PACKAGE_VERSION = "0.1.25";
|
|
46
46
|
const LONGTABLE_MCP_MARKER_START = "# LongTable state MCP START";
|
|
47
47
|
const LONGTABLE_MCP_MARKER_END = "# LongTable state MCP END";
|
|
48
48
|
function style(text, prefix) {
|
|
@@ -89,7 +89,7 @@ function usage() {
|
|
|
89
89
|
" longtable install [--json] [--path <file>] [--runtime-path <file>]",
|
|
90
90
|
" longtable mcp install [--provider codex|claude|all] [--write] [--checkpoint-ui off|interactive|strong] [--json] [--codex-config <path>] [--claude-settings <path>] [--package <spec>]",
|
|
91
91
|
" longtable sentinel --prompt <text> [--cwd <path>] [--json] [--record]",
|
|
92
|
-
" longtable team --prompt <text> [--role <role[,role]>] [--tmux] [--debate] [--rounds 5] [--cwd <path>] [--json]",
|
|
92
|
+
" longtable team --prompt <text> [--role <role[,role]>] [--tmux] [--debate] [--rounds 3|5] [--cwd <path>] [--json]",
|
|
93
93
|
" longtable ask [--prompt <text>] [--print] [--json] [--setup <path>] [--cwd <path>]",
|
|
94
94
|
" longtable clarify --prompt <task-context> [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json] [--force]",
|
|
95
95
|
" longtable question --prompt <decision-context> [--title <text>] [--text <question>] [--provider codex|claude] [--required|--advisory] [--print] [--cwd <path>] [--json]",
|
|
@@ -2305,9 +2305,13 @@ async function runTeam(args) {
|
|
|
2305
2305
|
if (!prompt) {
|
|
2306
2306
|
throw new Error("A prompt is required.");
|
|
2307
2307
|
}
|
|
2308
|
-
const
|
|
2309
|
-
|
|
2310
|
-
|
|
2308
|
+
const isDebate = args.debate === true;
|
|
2309
|
+
const expectedRounds = isDebate ? 5 : 3;
|
|
2310
|
+
const rounds = typeof args.rounds === "string" ? Number(args.rounds) : expectedRounds;
|
|
2311
|
+
if (!Number.isInteger(rounds) || rounds !== expectedRounds) {
|
|
2312
|
+
throw new Error(isDebate
|
|
2313
|
+
? "LongTable team debate v1 supports `--rounds 5` only."
|
|
2314
|
+
: "LongTable team v1 supports `--rounds 3` cross-review only.");
|
|
2311
2315
|
}
|
|
2312
2316
|
const setup = await loadOptionalSetup(typeof args.setup === "string" ? args.setup : undefined);
|
|
2313
2317
|
const projectContext = await loadProjectContextFromDirectory(workingDirectory);
|
|
@@ -2315,16 +2319,9 @@ async function runTeam(args) {
|
|
|
2315
2319
|
await assertWorkspaceNotBlocked(projectContext);
|
|
2316
2320
|
}
|
|
2317
2321
|
const projectAware = await buildProjectAwarePrompt(prompt, workingDirectory);
|
|
2318
|
-
const fallback = buildPanelFallback({
|
|
2319
|
-
prompt,
|
|
2320
|
-
mode: "review",
|
|
2321
|
-
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
2322
|
-
provider: setup?.providerSelection.provider,
|
|
2323
|
-
visibility: "always_visible"
|
|
2324
|
-
});
|
|
2325
2322
|
const teamId = localId("team");
|
|
2326
2323
|
const teamDir = join(workingDirectory, ".longtable", "team", teamId);
|
|
2327
|
-
if (
|
|
2324
|
+
if (isDebate) {
|
|
2328
2325
|
const debate = buildTeamDebate({
|
|
2329
2326
|
teamId,
|
|
2330
2327
|
teamDir,
|
|
@@ -2395,17 +2392,42 @@ async function runTeam(args) {
|
|
|
2395
2392
|
console.log(`- checkpoint: ${debate.questionRecord.id}`);
|
|
2396
2393
|
return;
|
|
2397
2394
|
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2395
|
+
const team = buildTeamReview({
|
|
2396
|
+
teamId,
|
|
2397
|
+
teamDir,
|
|
2398
|
+
prompt: projectAware.prompt,
|
|
2399
|
+
roleFlag: typeof args.role === "string" ? args.role : undefined,
|
|
2400
|
+
provider: setup?.providerSelection.provider,
|
|
2401
|
+
visibility: "always_visible",
|
|
2402
|
+
roundCount: rounds,
|
|
2403
|
+
tmux: args.tmux === true
|
|
2404
|
+
});
|
|
2405
|
+
await writeTeamDebateArtifacts(team, teamDir, prompt);
|
|
2406
|
+
const canRecordWorkspace = projectAware.projectContextFound && projectContext && existsSync(projectContext.stateFilePath);
|
|
2407
|
+
if (canRecordWorkspace) {
|
|
2408
|
+
await appendInvocationRecordToWorkspace(projectContext, team.invocationRecord, [team.questionRecord]);
|
|
2409
|
+
}
|
|
2401
2410
|
if (args.json === true) {
|
|
2402
|
-
console.log(JSON.stringify({
|
|
2411
|
+
console.log(JSON.stringify({
|
|
2412
|
+
teamId,
|
|
2413
|
+
teamDir,
|
|
2414
|
+
plan: team.plan,
|
|
2415
|
+
run: team.run,
|
|
2416
|
+
questionRecord: team.questionRecord,
|
|
2417
|
+
invocationRecord: team.invocationRecord,
|
|
2418
|
+
execution: {
|
|
2419
|
+
status: "completed",
|
|
2420
|
+
surface: team.run.surface,
|
|
2421
|
+
interactionDepth: team.run.interactionDepth,
|
|
2422
|
+
projectContextFound: projectAware.projectContextFound,
|
|
2423
|
+
invocationLogged: canRecordWorkspace
|
|
2424
|
+
}
|
|
2425
|
+
}, null, 2));
|
|
2403
2426
|
return;
|
|
2404
2427
|
}
|
|
2405
2428
|
if (args.tmux !== true) {
|
|
2406
|
-
console.log(
|
|
2407
|
-
console.log(
|
|
2408
|
-
console.log("Run with `--tmux` to launch role panes for parallel discussion.");
|
|
2429
|
+
console.log(renderTeamDebateSummary(team.run));
|
|
2430
|
+
console.log(`- checkpoint: ${team.questionRecord.id}`);
|
|
2409
2431
|
return;
|
|
2410
2432
|
}
|
|
2411
2433
|
const sessionName = `longtable-${teamId.replaceAll("_", "-")}`;
|
|
@@ -2413,13 +2435,13 @@ async function runTeam(args) {
|
|
|
2413
2435
|
const launcher = process.argv[1] ?? "longtable";
|
|
2414
2436
|
const leaderCommand = [
|
|
2415
2437
|
`echo ${shellEscape(`LongTable team ${teamId}`)}`,
|
|
2416
|
-
`echo ${shellEscape(`
|
|
2417
|
-
|
|
2418
|
-
`echo ${shellEscape(`
|
|
2438
|
+
`echo ${shellEscape(`Artifacts: ${teamDir}`)}`,
|
|
2439
|
+
`echo ${shellEscape("Cross-review artifacts are recorded. Role panes can add live review logs.")}`,
|
|
2440
|
+
`echo ${shellEscape(`Checkpoint: ${team.questionRecord.id}`)}`,
|
|
2419
2441
|
`exec ${shellEscape(shell)}`
|
|
2420
2442
|
].join("; ");
|
|
2421
2443
|
execFileSync("tmux", ["new-session", "-d", "-s", sessionName, "-c", workingDirectory, leaderCommand], { stdio: "inherit" });
|
|
2422
|
-
for (const member of
|
|
2444
|
+
for (const member of team.plan.members) {
|
|
2423
2445
|
const rolePrompt = [
|
|
2424
2446
|
`LongTable team discussion role: ${member.label} (${member.role}).`,
|
|
2425
2447
|
"Give claims, objections, open questions, and evidence needs. Address likely disagreement with other roles.",
|
package/dist/debate.d.ts
CHANGED
|
@@ -17,5 +17,6 @@ export interface TeamDebateBundle {
|
|
|
17
17
|
questionRecord: QuestionRecord;
|
|
18
18
|
}
|
|
19
19
|
export declare function createTeamDebateQuestionRecord(run: TeamDebateRun, provider?: ProviderKind): QuestionRecord;
|
|
20
|
+
export declare function buildTeamReview(options: BuildTeamDebateOptions): TeamDebateBundle;
|
|
20
21
|
export declare function buildTeamDebate(options: BuildTeamDebateOptions): TeamDebateBundle;
|
|
21
22
|
export declare function renderTeamDebateSummary(run: TeamDebateRun): string;
|
package/dist/debate.js
CHANGED
|
@@ -68,15 +68,18 @@ function independentContribution(roundId, plan, role, label, artifactPath) {
|
|
|
68
68
|
checkpointTriggers: tags.map((tag) => `${tag}_commitment`)
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
function crossReviewContribution(roundId, plan, role, label, targetRole, targetLabel, artifactPath) {
|
|
71
|
+
function crossReviewContribution(roundId, plan, role, label, targetRole, targetLabel, targetContribution, artifactPath) {
|
|
72
72
|
return contribution({
|
|
73
73
|
roundId,
|
|
74
74
|
role,
|
|
75
75
|
label,
|
|
76
76
|
targetRole,
|
|
77
|
+
respondsToContributionId: targetContribution.id,
|
|
78
|
+
stance: "conditional",
|
|
77
79
|
artifactPath,
|
|
78
|
-
summary: `${label}
|
|
80
|
+
summary: `${label} cross-reviews ${targetLabel}'s independent contribution (${targetContribution.id}) before synthesis.`,
|
|
79
81
|
claims: [
|
|
82
|
+
`Responds to ${targetLabel}'s claim: ${targetContribution.claims[0]}`,
|
|
80
83
|
`${targetLabel}'s concern is useful only if it does not erase ${label}'s domain-specific risk.`,
|
|
81
84
|
"The debate should expose disagreement as a researcher decision point rather than normalize it away."
|
|
82
85
|
],
|
|
@@ -160,12 +163,13 @@ function convergenceContribution(roundId, plan, role, label, artifactPath) {
|
|
|
160
163
|
checkpointTriggers: ["panel_next_decision"]
|
|
161
164
|
});
|
|
162
165
|
}
|
|
163
|
-
function buildSynthesis(plan, artifactPath) {
|
|
166
|
+
function buildSynthesis(plan, artifactPath, kind) {
|
|
164
167
|
const labels = plan.members.map((member) => member.label);
|
|
165
168
|
const highSensitivity = plan.checkpointSensitivity === "high";
|
|
169
|
+
const runLabel = kind === "debate" ? "debate" : "team review";
|
|
166
170
|
return {
|
|
167
171
|
artifactPath,
|
|
168
|
-
summary: `The
|
|
172
|
+
summary: `The ${runLabel} completed across ${labels.join(", ")}. It should slow closure by turning role disagreement into an explicit researcher decision.`,
|
|
169
173
|
consensus: [
|
|
170
174
|
"The researcher should see role disagreement before LongTable drafts, commits, or submits a conclusion.",
|
|
171
175
|
"Evidence gaps and tacit assumptions should remain visible instead of being smoothed into fluent prose."
|
|
@@ -183,12 +187,13 @@ function buildSynthesis(plan, artifactPath) {
|
|
|
183
187
|
"Choose whether the debate should affect the current artifact, the research design, or only the decision log."
|
|
184
188
|
],
|
|
185
189
|
recommendedCheckpoint: highSensitivity
|
|
186
|
-
?
|
|
187
|
-
:
|
|
190
|
+
? `The ${runLabel} surfaced high-sensitivity disagreement. What should LongTable treat as the next human decision before closure?`
|
|
191
|
+
: `The ${runLabel} surfaced role disagreement. Should LongTable revise, verify evidence, proceed, or keep the tension open?`
|
|
188
192
|
};
|
|
189
193
|
}
|
|
190
194
|
export function createTeamDebateQuestionRecord(run, provider) {
|
|
191
195
|
const createdAt = nowIso();
|
|
196
|
+
const isDebate = run.interactionDepth === "debated";
|
|
192
197
|
return {
|
|
193
198
|
id: createId("question_record"),
|
|
194
199
|
createdAt,
|
|
@@ -197,7 +202,7 @@ export function createTeamDebateQuestionRecord(run, provider) {
|
|
|
197
202
|
prompt: {
|
|
198
203
|
id: createId("question_prompt"),
|
|
199
204
|
checkpointKey: "team_debate_next_decision",
|
|
200
|
-
title: "Team debate follow-up decision",
|
|
205
|
+
title: isDebate ? "Team debate follow-up decision" : "Agent team follow-up decision",
|
|
201
206
|
question: run.synthesis.recommendedCheckpoint,
|
|
202
207
|
type: "single_choice",
|
|
203
208
|
options: [
|
|
@@ -229,10 +234,15 @@ export function createTeamDebateQuestionRecord(run, provider) {
|
|
|
229
234
|
return key ? getPersonaDefinition(key).checkpointSensitivity === "high" : false;
|
|
230
235
|
}),
|
|
231
236
|
source: "runtime_guidance",
|
|
237
|
+
displayReason: isDebate
|
|
238
|
+
? "Role rebuttals and convergence should connect to an explicit researcher decision."
|
|
239
|
+
: "Cross-review created role disagreement that should connect to an explicit researcher decision.",
|
|
232
240
|
rationale: [
|
|
233
|
-
"
|
|
234
|
-
|
|
235
|
-
|
|
241
|
+
"Agent team orchestration is a research harness surface, not a substitute for researcher judgment.",
|
|
242
|
+
isDebate
|
|
243
|
+
? "The fixed debate rounds created disagreement that should connect to an explicit researcher decision."
|
|
244
|
+
: "The cross-review round created disagreement that should connect to an explicit researcher decision.",
|
|
245
|
+
`Agent team run: ${run.id}.`
|
|
236
246
|
],
|
|
237
247
|
preferredSurfaces: provider === "claude"
|
|
238
248
|
? ["native_structured", "numbered"]
|
|
@@ -240,10 +250,13 @@ export function createTeamDebateQuestionRecord(run, provider) {
|
|
|
240
250
|
}
|
|
241
251
|
};
|
|
242
252
|
}
|
|
243
|
-
|
|
244
|
-
const roundCount = options.roundCount ?? 5;
|
|
245
|
-
|
|
246
|
-
|
|
253
|
+
function buildTeamBundle(options, kind) {
|
|
254
|
+
const roundCount = options.roundCount ?? (kind === "debate" ? 5 : 3);
|
|
255
|
+
const expectedRounds = kind === "debate" ? 5 : 3;
|
|
256
|
+
if (roundCount !== expectedRounds) {
|
|
257
|
+
throw new Error(kind === "debate"
|
|
258
|
+
? "LongTable debate v1 supports fixed 5-round debate only."
|
|
259
|
+
: "LongTable team v1 supports fixed 3-round cross-review only.");
|
|
247
260
|
}
|
|
248
261
|
const createdAt = nowIso();
|
|
249
262
|
const plan = buildPanelPlan({
|
|
@@ -255,6 +268,7 @@ export function buildTeamDebate(options) {
|
|
|
255
268
|
});
|
|
256
269
|
const rounds = [];
|
|
257
270
|
const round1Id = createId("team_round");
|
|
271
|
+
const independentContributions = plan.members.map((member) => independentContribution(round1Id, plan, member.role, member.label, join("round-1-independent", `${member.role}.json`)));
|
|
258
272
|
rounds.push({
|
|
259
273
|
id: round1Id,
|
|
260
274
|
index: 1,
|
|
@@ -262,12 +276,12 @@ export function buildTeamDebate(options) {
|
|
|
262
276
|
title: "Independent review",
|
|
263
277
|
status: "completed",
|
|
264
278
|
artifactDir: join(options.teamDir, "round-1-independent"),
|
|
265
|
-
contributions:
|
|
279
|
+
contributions: independentContributions
|
|
266
280
|
});
|
|
267
281
|
const round2Id = createId("team_round");
|
|
268
282
|
const crossContributions = plan.members.flatMap((member) => plan.members
|
|
269
283
|
.filter((target) => target.role !== member.role)
|
|
270
|
-
.map((target) => crossReviewContribution(round2Id, plan, member.role, member.label, target.role, target.label, join("round-2-cross-review", `${member.role}-on-${target.role}.json`))));
|
|
284
|
+
.map((target) => crossReviewContribution(round2Id, plan, member.role, member.label, target.role, target.label, independentContributions.find((contribution) => contribution.role === target.role), join("round-2-cross-review", `${member.role}-on-${target.role}.json`))));
|
|
271
285
|
rounds.push({
|
|
272
286
|
id: round2Id,
|
|
273
287
|
index: 2,
|
|
@@ -277,27 +291,29 @@ export function buildTeamDebate(options) {
|
|
|
277
291
|
artifactDir: join(options.teamDir, "round-2-cross-review"),
|
|
278
292
|
contributions: crossContributions
|
|
279
293
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
if (kind === "debate") {
|
|
295
|
+
const round3Id = createId("team_round");
|
|
296
|
+
rounds.push({
|
|
297
|
+
id: round3Id,
|
|
298
|
+
index: 3,
|
|
299
|
+
kind: "rebuttal",
|
|
300
|
+
title: "Rebuttal and revision",
|
|
301
|
+
status: "completed",
|
|
302
|
+
artifactDir: join(options.teamDir, "round-3-rebuttal"),
|
|
303
|
+
contributions: plan.members.map((member) => rebuttalContribution(round3Id, member.role, member.label, join("round-3-rebuttal", `${member.role}.json`)))
|
|
304
|
+
});
|
|
305
|
+
const round4Id = createId("team_round");
|
|
306
|
+
rounds.push({
|
|
307
|
+
id: round4Id,
|
|
308
|
+
index: 4,
|
|
309
|
+
kind: "convergence",
|
|
310
|
+
title: "Convergence and unresolved gaps",
|
|
311
|
+
status: "completed",
|
|
312
|
+
artifactDir: join(options.teamDir, "round-4-convergence"),
|
|
313
|
+
contributions: plan.members.map((member) => convergenceContribution(round4Id, plan, member.role, member.label, join("round-4-convergence", `${member.role}.json`)))
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
const synthesis = buildSynthesis(plan, "synthesis.json", kind);
|
|
301
317
|
const run = {
|
|
302
318
|
id: createId("team_debate_run"),
|
|
303
319
|
teamId: options.teamId,
|
|
@@ -307,14 +323,15 @@ export function buildTeamDebate(options) {
|
|
|
307
323
|
roles: plan.members,
|
|
308
324
|
status: "completed",
|
|
309
325
|
surface: options.tmux ? "tmux_console" : "file_backed_debate",
|
|
310
|
-
|
|
326
|
+
interactionDepth: kind === "debate" ? "debated" : "cross_reviewed",
|
|
327
|
+
roundPolicy: kind === "debate" ? "fixed" : "team_cross_review",
|
|
311
328
|
roundCount,
|
|
312
329
|
artifactRoot: options.teamDir,
|
|
313
330
|
rounds: [
|
|
314
331
|
...rounds,
|
|
315
332
|
{
|
|
316
333
|
id: createId("team_round"),
|
|
317
|
-
index:
|
|
334
|
+
index: roundCount,
|
|
318
335
|
kind: "synthesis",
|
|
319
336
|
title: "Coordinator synthesis and checkpoint",
|
|
320
337
|
status: "completed",
|
|
@@ -335,11 +352,13 @@ export function buildTeamDebate(options) {
|
|
|
335
352
|
visibility: plan.visibility,
|
|
336
353
|
checkpointSensitivity: plan.checkpointSensitivity,
|
|
337
354
|
rationale: [
|
|
338
|
-
|
|
339
|
-
|
|
355
|
+
kind === "debate"
|
|
356
|
+
? "Autonomous debate requested through LongTable team orchestration."
|
|
357
|
+
: "Agent team cross-review requested through LongTable team orchestration.",
|
|
358
|
+
"File-backed rounds keep disagreement inspectable before researcher closure."
|
|
340
359
|
]
|
|
341
360
|
});
|
|
342
|
-
intent.kind = "team_debate";
|
|
361
|
+
intent.kind = kind === "debate" ? "team_debate" : "team";
|
|
343
362
|
intent.requestedSurface = run.surface;
|
|
344
363
|
const invocationRecord = {
|
|
345
364
|
id: createId("invocation_record"),
|
|
@@ -349,6 +368,7 @@ export function buildTeamDebate(options) {
|
|
|
349
368
|
status: "completed",
|
|
350
369
|
provider: options.provider,
|
|
351
370
|
surface: run.surface,
|
|
371
|
+
interactionDepth: run.interactionDepth,
|
|
352
372
|
panelPlan: plan,
|
|
353
373
|
teamDebateRun: run,
|
|
354
374
|
degradationReason: options.tmux
|
|
@@ -363,12 +383,19 @@ export function buildTeamDebate(options) {
|
|
|
363
383
|
questionRecord
|
|
364
384
|
};
|
|
365
385
|
}
|
|
386
|
+
export function buildTeamReview(options) {
|
|
387
|
+
return buildTeamBundle(options, "team");
|
|
388
|
+
}
|
|
389
|
+
export function buildTeamDebate(options) {
|
|
390
|
+
return buildTeamBundle(options, "debate");
|
|
391
|
+
}
|
|
366
392
|
export function renderTeamDebateSummary(run) {
|
|
367
393
|
return [
|
|
368
394
|
"LongTable Team Debate",
|
|
369
395
|
`- team: ${run.teamId}`,
|
|
370
396
|
`- surface: ${run.surface}`,
|
|
371
|
-
`-
|
|
397
|
+
`- interaction depth: ${run.interactionDepth}`,
|
|
398
|
+
`- rounds: ${run.roundCount} ${run.roundPolicy}`,
|
|
372
399
|
`- roles: ${run.roles.map((role) => `${role.label} (${role.role})`).join(", ")}`,
|
|
373
400
|
`- artifacts: ${run.artifactRoot}`,
|
|
374
401
|
"",
|
package/dist/panel.js
CHANGED
|
@@ -143,6 +143,7 @@ export function createPlannedPanelQuestionRecord(plan, provider) {
|
|
|
143
143
|
"Panel review creates disagreement or risk visibility that should connect to an explicit researcher decision.",
|
|
144
144
|
`Panel checkpoint sensitivity: ${plan.checkpointSensitivity}.`
|
|
145
145
|
],
|
|
146
|
+
displayReason: "Panel review can surface role disagreement that should not be collapsed without researcher approval.",
|
|
146
147
|
preferredSurfaces: provider === "claude"
|
|
147
148
|
? ["native_structured", "numbered"]
|
|
148
149
|
: ["mcp_elicitation", "numbered"]
|
|
@@ -159,6 +160,7 @@ export function createPlannedPanelResult(plan, provider, linkedQuestionRecordIds
|
|
|
159
160
|
provider,
|
|
160
161
|
surface: "sequential_fallback",
|
|
161
162
|
status: "planned",
|
|
163
|
+
interactionDepth: "independent",
|
|
162
164
|
memberResults: plan.members.map((member) => ({
|
|
163
165
|
role: member.role,
|
|
164
166
|
label: member.label,
|
|
@@ -178,6 +180,7 @@ export function createPlannedInvocationRecord(options) {
|
|
|
178
180
|
status: "planned",
|
|
179
181
|
provider: options.provider,
|
|
180
182
|
surface: "sequential_fallback",
|
|
183
|
+
interactionDepth: "independent",
|
|
181
184
|
panelPlan: options.plan,
|
|
182
185
|
panelResult: options.result,
|
|
183
186
|
degradationReason: "Native provider team execution is optional; sequential fallback is the stable LongTable surface."
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DecisionRecord, InvocationRecord, ProviderKind, QuestionSurface, QuestionRecord, ResearchState } from "@longtable/core";
|
|
1
|
+
import type { DecisionRecord, InvocationRecord, ProviderKind, QuestionOption, QuestionSurface, QuestionRecord, ResearchState } from "@longtable/core";
|
|
2
2
|
import type { SetupPersistedOutput } from "@longtable/setup";
|
|
3
3
|
export type ProjectDisagreementPreference = "synthesis_only" | "show_on_conflict" | "always_visible";
|
|
4
4
|
export interface LongTableProjectRecord {
|
|
@@ -130,6 +130,9 @@ export declare function createWorkspaceQuestion(options: {
|
|
|
130
130
|
prompt: string;
|
|
131
131
|
title?: string;
|
|
132
132
|
question?: string;
|
|
133
|
+
checkpointKey?: string;
|
|
134
|
+
questionOptions?: QuestionOption[];
|
|
135
|
+
displayReason?: string;
|
|
133
136
|
provider?: ProviderKind;
|
|
134
137
|
required?: boolean;
|
|
135
138
|
}): Promise<{
|
package/dist/project-session.js
CHANGED
|
@@ -777,6 +777,7 @@ export async function createWorkspaceQuestion(options) {
|
|
|
777
777
|
unresolvedTensions: state.openTensions ?? [],
|
|
778
778
|
studyContract: state.studyContract
|
|
779
779
|
});
|
|
780
|
+
const checkpointKey = options.checkpointKey ?? trigger.signal.checkpointKey;
|
|
780
781
|
const createdAt = nowIso();
|
|
781
782
|
const question = {
|
|
782
783
|
id: createId("question_record"),
|
|
@@ -785,15 +786,16 @@ export async function createWorkspaceQuestion(options) {
|
|
|
785
786
|
status: "pending",
|
|
786
787
|
prompt: {
|
|
787
788
|
id: createId("question_prompt"),
|
|
788
|
-
checkpointKey
|
|
789
|
-
title: options.title ?? questionTitleForCheckpoint(trigger.family,
|
|
790
|
-
question: options.question ?? questionTextForCheckpoint(trigger.family, options.prompt,
|
|
789
|
+
checkpointKey,
|
|
790
|
+
title: options.title ?? questionTitleForCheckpoint(trigger.family, checkpointKey),
|
|
791
|
+
question: options.question ?? questionTextForCheckpoint(trigger.family, options.prompt, checkpointKey),
|
|
791
792
|
type: "single_choice",
|
|
792
|
-
options: optionsForCheckpointTrigger(trigger.family,
|
|
793
|
+
options: options.questionOptions ?? optionsForCheckpointTrigger(trigger.family, checkpointKey),
|
|
793
794
|
allowOther: true,
|
|
794
795
|
otherLabel: "Other decision",
|
|
795
796
|
required: options.required ?? trigger.requiresQuestionBeforeClosure,
|
|
796
797
|
source: "checkpoint",
|
|
798
|
+
displayReason: options.displayReason ?? trigger.rationale[0],
|
|
797
799
|
rationale: [
|
|
798
800
|
...trigger.rationale,
|
|
799
801
|
`Trigger family: ${trigger.family}.`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Researcher-facing LongTable CLI",
|
|
6
6
|
"type": "module",
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@longtable/checkpoints": "0.1.
|
|
32
|
-
"@longtable/core": "0.1.
|
|
33
|
-
"@longtable/memory": "0.1.
|
|
34
|
-
"@longtable/provider-claude": "0.1.
|
|
35
|
-
"@longtable/provider-codex": "0.1.
|
|
36
|
-
"@longtable/setup": "0.1.
|
|
31
|
+
"@longtable/checkpoints": "0.1.25",
|
|
32
|
+
"@longtable/core": "0.1.25",
|
|
33
|
+
"@longtable/memory": "0.1.25",
|
|
34
|
+
"@longtable/provider-claude": "0.1.25",
|
|
35
|
+
"@longtable/provider-codex": "0.1.25",
|
|
36
|
+
"@longtable/setup": "0.1.25"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.10.1",
|