@stravica-ai/digital-operator-pa 0.1.6 → 0.1.8
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/engineering-pa-cli/package.json +2 -0
- package/dist/engineering-pa-cli/src/research/assemble.mjs +281 -0
- package/dist/engineering-pa-cli/src/research/briefs/deep-extractor.md +141 -0
- package/dist/engineering-pa-cli/src/research/briefs/deep-verifier.md +95 -0
- package/dist/engineering-pa-cli/src/research/briefs/judge.md +108 -0
- package/dist/engineering-pa-cli/src/research/briefs/verifier.md +17 -0
- package/dist/engineering-pa-cli/src/research/commands/show.mjs +10 -1
- package/dist/engineering-pa-cli/src/research/engine/agents.mjs +163 -0
- package/dist/engineering-pa-cli/src/research/engine/deep.mjs +513 -0
- package/dist/engineering-pa-cli/src/research/engine/deepen.mjs +97 -0
- package/dist/engineering-pa-cli/src/research/engine/finalise.mjs +121 -0
- package/dist/engineering-pa-cli/src/research/engine/io.mjs +60 -0
- package/dist/engineering-pa-cli/src/research/engine/plan.mjs +129 -0
- package/dist/engineering-pa-cli/src/research/engine/rank.mjs +80 -0
- package/dist/engineering-pa-cli/src/research/engine/verify.mjs +52 -0
- package/dist/engineering-pa-cli/src/research/schema/cost.schema.json +19 -0
- package/dist/engineering-pa-cli/src/research/schema/judge.schema.json +35 -0
- package/dist/engineering-pa-cli/src/research/schema/rounds.schema.json +34 -0
- package/dist/engineering-pa-cli/src/research/store/cost.mjs +41 -0
- package/dist/engineering-pa-cli/src/research/store/index.mjs +31 -0
- package/dist/engineering-pa-cli/src/research/store/judge.mjs +53 -0
- package/dist/engineering-pa-cli/src/research/store/rounds.mjs +61 -0
- package/dist/engineering-pa-cli/src/research/workflows/research-deep.js +1364 -0
- package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +561 -327
- package/dist/engineering-pa-cli/src/research/wrappers/deep.mjs +146 -0
- package/dist/engineering-pa-cli/src/research/wrappers/normal.mjs +100 -0
- package/dist/engineering-pa-core/docs/commands/research.md +438 -0
- package/dist/engineering-pa-core/docs/entity-location-map.md +1 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +27 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +1 -1
- package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +1 -1
- package/dist/engineering-pa-workspace/app/project-detail.html +16 -3
- package/dist/engineering-pa-workspace/app/viewer.html +54 -13
- package/package.json +1 -1
|
@@ -28,7 +28,16 @@ export async function showCommand(argv, { store, stdout = process.stdout } = {})
|
|
|
28
28
|
const findings = await store.listFindings(id);
|
|
29
29
|
|
|
30
30
|
if (parsed.values.json) {
|
|
31
|
-
|
|
31
|
+
const assembled = { manifest, plan, sources, gates, findings };
|
|
32
|
+
// Deep-mode-only artifacts (D4): surface them when present, omit cleanly when
|
|
33
|
+
// absent so a normal/light session's --json is byte-identical to before.
|
|
34
|
+
const rounds = await store.readRounds(id);
|
|
35
|
+
const judge = await store.readJudge(id);
|
|
36
|
+
const cost = await store.readCost(id);
|
|
37
|
+
if (rounds !== null) assembled.rounds = rounds;
|
|
38
|
+
if (judge !== null) assembled.judge = judge;
|
|
39
|
+
if (cost !== null) assembled.cost = cost;
|
|
40
|
+
stdout.write(JSON.stringify(assembled, null, 2) + '\n');
|
|
32
41
|
return 0;
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Shared research engine — agent schemas, per-role agent wrappers, and the
|
|
2
|
+
// search+extract pipeline. Model/effort pins are explicit per call (build-spec
|
|
3
|
+
// §4.1 — never harness-inherited); verify pins are overridable so deep can lift
|
|
4
|
+
// its lead-vote tier without editing normal's behaviour.
|
|
5
|
+
import { brief } from './io.mjs';
|
|
6
|
+
import { topoOrder } from './plan.mjs';
|
|
7
|
+
|
|
8
|
+
// ── JSON schemas for structured agent returns ──────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export const SEARCH_SCHEMA = {
|
|
11
|
+
type: 'object', additionalProperties: false,
|
|
12
|
+
required: ['subQuestionId', 'sources', 'toolCallsUsed'],
|
|
13
|
+
properties: {
|
|
14
|
+
subQuestionId: { type: 'string' },
|
|
15
|
+
sources: {
|
|
16
|
+
type: 'array',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'object', additionalProperties: false,
|
|
19
|
+
required: ['sourceId', 'url', 'title', 'kind', 'sourceQuality'],
|
|
20
|
+
properties: {
|
|
21
|
+
sourceId: { type: 'string' },
|
|
22
|
+
url: { type: 'string' },
|
|
23
|
+
title: { type: 'string' },
|
|
24
|
+
kind: { enum: ['web', 'playwright', 'kb', 'codebase', 'prior-session'] },
|
|
25
|
+
sourceQuality: { enum: ['primary', 'secondary', 'low'] },
|
|
26
|
+
snippet: { type: 'string' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
toolCallsUsed: { type: 'integer' },
|
|
31
|
+
gatedDropped: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object', additionalProperties: false,
|
|
35
|
+
required: ['url', 'gateType', 'whatIsNeeded'],
|
|
36
|
+
properties: { url: { type: 'string' }, gateType: { type: 'string' }, whatIsNeeded: { type: 'string' } },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
notes: { type: 'string' },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const EXTRACT_SCHEMA = {
|
|
44
|
+
type: 'object', additionalProperties: false,
|
|
45
|
+
required: ['subQuestionId', 'claims', 'unsourcedClaims'],
|
|
46
|
+
properties: {
|
|
47
|
+
subQuestionId: { type: 'string' },
|
|
48
|
+
claims: {
|
|
49
|
+
type: 'array',
|
|
50
|
+
items: {
|
|
51
|
+
type: 'object', additionalProperties: false,
|
|
52
|
+
required: ['claimId', 'text', 'quote', 'sourceId', 'importance', 'sourceQuality'],
|
|
53
|
+
properties: {
|
|
54
|
+
claimId: { type: 'string' },
|
|
55
|
+
text: { type: 'string' },
|
|
56
|
+
quote: { type: 'string' },
|
|
57
|
+
sourceId: { type: 'string' },
|
|
58
|
+
importance: { enum: ['central', 'supporting', 'incidental'] },
|
|
59
|
+
sourceQuality: { enum: ['primary', 'secondary', 'low'] },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
unsourcedClaims: { type: 'integer' },
|
|
64
|
+
notes: { type: 'string' },
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const VERIFY_SCHEMA = {
|
|
69
|
+
type: 'object', additionalProperties: false,
|
|
70
|
+
required: ['outcome', 'note'],
|
|
71
|
+
properties: {
|
|
72
|
+
outcome: { enum: ['confirmed', 'refuted', 'unverified-infra'] },
|
|
73
|
+
note: { type: 'string' },
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const SYNTH_SCHEMA = {
|
|
78
|
+
type: 'object', additionalProperties: false,
|
|
79
|
+
required: ['subQuestionId', 'written', 'path', 'confidence', 'claimCount', 'centralConfirmed', 'centralRefuted', 'unsourcedCount'],
|
|
80
|
+
properties: {
|
|
81
|
+
subQuestionId: { type: 'string' },
|
|
82
|
+
written: { type: 'boolean' },
|
|
83
|
+
path: { type: 'string' },
|
|
84
|
+
confidence: { enum: ['low', 'medium', 'high'] },
|
|
85
|
+
claimCount: { type: 'integer' },
|
|
86
|
+
centralConfirmed: { type: 'integer' },
|
|
87
|
+
centralRefuted: { type: 'integer' },
|
|
88
|
+
unsourcedCount: { type: 'integer' },
|
|
89
|
+
refutedTransparency: { type: 'string' },
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// ── agent wrappers (model/effort per build-spec §4.1 — explicit, never inherited) ──
|
|
94
|
+
// Mode-varying knobs live on ctx (defaults reproduce normal exactly): `ctx.toolCallCap`
|
|
95
|
+
// (deep lifts the searcher cap 12→20), `ctx.extractorRole`/`ctx.extractSchema` (deep
|
|
96
|
+
// swaps in deep-extractor.md + claimKind tagging), `ctx.modelOverride` (deep's
|
|
97
|
+
// testOverrides.modelPanel forces a cheap panel for mechanical smoke runs). When a
|
|
98
|
+
// knob is unset, `?? default` / `|| pinned` yields normal's byte-identical output.
|
|
99
|
+
|
|
100
|
+
export function searchAgent(ctx, sq, siblings, priorContext) {
|
|
101
|
+
return agent(brief(ctx, 'searcher', {
|
|
102
|
+
sessionId: ctx.sessionId, sessionQuestion: ctx.baseQuestion, intent: ctx.baseIntent, gatePolicy: ctx.basePolicy,
|
|
103
|
+
subQuestionId: sq.id, subQuestionText: sq.text,
|
|
104
|
+
siblingSubQuestions: siblings.filter((s) => s.id !== sq.id).map((s) => `${s.id}: ${s.text}`).join(' | ') || 'none',
|
|
105
|
+
sourceIdPrefix: sq.id.replace(/[^a-zA-Z0-9]/g, ''), toolCallCap: ctx.toolCallCap ?? 12, sessionDir: ctx.sessionDir,
|
|
106
|
+
dependencyContext: priorContext || 'none (independent sub-question)',
|
|
107
|
+
}), { model: ctx.modelOverride || 'sonnet', effort: 'medium', phase: 'Search', label: `search:${sq.id}`, schema: SEARCH_SCHEMA });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function extractAgent(ctx, sq, searchRes, priorContext) {
|
|
111
|
+
const sources = (searchRes && searchRes.sources) || [];
|
|
112
|
+
return agent(brief(ctx, ctx.extractorRole ?? 'extractor', {
|
|
113
|
+
sessionId: ctx.sessionId, sessionQuestion: ctx.baseQuestion,
|
|
114
|
+
subQuestionId: sq.id, subQuestionText: sq.text,
|
|
115
|
+
sources: sources.map((s) => ({ sourceId: s.sourceId, url: s.url, title: s.title, sourceQuality: s.sourceQuality })),
|
|
116
|
+
sessionDir: ctx.sessionDir, dependencyContext: priorContext || 'none',
|
|
117
|
+
}), { model: ctx.modelOverride || 'sonnet', effort: 'medium', phase: 'Extract', label: `extract:${sq.id}`, schema: ctx.extractSchema ?? EXTRACT_SCHEMA });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function verifyAgent(ctx, claim, lens, model = 'sonnet', effort = 'high', role = 'verifier') {
|
|
121
|
+
return agent(brief(ctx, role, {
|
|
122
|
+
sessionId: ctx.sessionId, claimText: claim.text, quote: claim.quote, sourceUrl: claim.url || claim.sourceId,
|
|
123
|
+
subQuestionText: claim.sqText, lens,
|
|
124
|
+
}), { model, effort, phase: 'Verify', label: `verify:${claim.claimId}:${lens}`, schema: VERIFY_SCHEMA });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function synthAgent(ctx, sq, buckets) {
|
|
128
|
+
return agent(brief(ctx, 'synthesizer', {
|
|
129
|
+
sessionId: ctx.sessionId, subQuestionId: sq.id, subQuestionText: sq.text, outputMode: ctx.baseOutputMode,
|
|
130
|
+
findingFilename: `${sq.id}.json`, sessionDir: ctx.sessionDir, schemaDir: ctx.schemaDir, cli: ctx.cli,
|
|
131
|
+
verifiedClaims: buckets.verified, supportingClaims: buckets.supporting, refutedClaims: buckets.refuted,
|
|
132
|
+
}), { model: ctx.modelOverride || 'opus', effort: 'high', phase: 'Synthesize', label: `synth:${sq.id}`, schema: SYNTH_SCHEMA });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// -- Search + Extract --
|
|
136
|
+
// Independent facets flow search→extract with no cross-facet barrier (pipeline).
|
|
137
|
+
// Dependent facets are chained AFTER their dependsOn extract completes (the DAG that
|
|
138
|
+
// pipeline() cannot express — documented fallback, §8). Returns sq.id -> { sq, search, extract }.
|
|
139
|
+
export async function runSearchExtract(ctx, subQuestions) {
|
|
140
|
+
const independent = subQuestions.filter((sq) => sq.kind !== 'dependent' || (sq.dependsOn || []).length === 0);
|
|
141
|
+
const dependents = subQuestions.filter((sq) => sq.kind === 'dependent' && (sq.dependsOn || []).length > 0);
|
|
142
|
+
|
|
143
|
+
const sqResults = new Map(); // sq.id -> { sq, search, extract }
|
|
144
|
+
|
|
145
|
+
const indepPairs = await pipeline(
|
|
146
|
+
independent,
|
|
147
|
+
(sq) => searchAgent(ctx, sq, subQuestions),
|
|
148
|
+
(searchRes, sq) => extractAgent(ctx, sq, searchRes).then((extract) => ({ sq, search: searchRes, extract })),
|
|
149
|
+
);
|
|
150
|
+
for (const r of indepPairs.filter(Boolean)) sqResults.set(r.sq.id, r);
|
|
151
|
+
|
|
152
|
+
for (const sq of topoOrder(dependents)) {
|
|
153
|
+
const priorContext = (sq.dependsOn || [])
|
|
154
|
+
.map((d) => sqResults.get(d))
|
|
155
|
+
.filter(Boolean)
|
|
156
|
+
.map((r) => `${r.sq.id}: ${(r.extract.claims || []).map((c) => c.text).slice(0, 3).join('; ')}`)
|
|
157
|
+
.join(' || ') || 'dependencies produced no claims';
|
|
158
|
+
const searchRes = await searchAgent(ctx, sq, subQuestions, priorContext);
|
|
159
|
+
const extract = await extractAgent(ctx, sq, searchRes, priorContext);
|
|
160
|
+
sqResults.set(sq.id, { sq, search: searchRes, extract });
|
|
161
|
+
}
|
|
162
|
+
return sqResults;
|
|
163
|
+
}
|