@telora/daemon 0.18.40 → 0.18.55
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/build-info.json +12 -3
- package/dist/completion/advanceability.d.ts +66 -0
- package/dist/completion/advanceability.d.ts.map +1 -0
- package/dist/completion/advanceability.js +112 -0
- package/dist/completion/advanceability.js.map +1 -0
- package/dist/completion/event.d.ts +28 -52
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +79 -99
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/progress-snapshot.d.ts +15 -4
- package/dist/completion/progress-snapshot.d.ts.map +1 -1
- package/dist/completion/progress-snapshot.js +20 -4
- package/dist/completion/progress-snapshot.js.map +1 -1
- package/dist/completion/team-completion.d.ts.map +1 -1
- package/dist/completion/team-completion.js +32 -2
- package/dist/completion/team-completion.js.map +1 -1
- package/dist/crash-recovery.d.ts +48 -0
- package/dist/crash-recovery.d.ts.map +1 -1
- package/dist/crash-recovery.js +166 -30
- package/dist/crash-recovery.js.map +1 -1
- package/dist/delivery-advance-block.d.ts +17 -1
- package/dist/delivery-advance-block.d.ts.map +1 -1
- package/dist/delivery-advance-block.js +31 -8
- package/dist/delivery-advance-block.js.map +1 -1
- package/dist/escalation-provenance.d.ts +5 -1
- package/dist/escalation-provenance.d.ts.map +1 -1
- package/dist/escalation-provenance.js +46 -4
- package/dist/escalation-provenance.js.map +1 -1
- package/dist/focus-executor.d.ts +7 -2
- package/dist/focus-executor.d.ts.map +1 -1
- package/dist/focus-executor.js +21 -9
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-lifecycle.d.ts.map +1 -1
- package/dist/focus-lifecycle.js +20 -0
- package/dist/focus-lifecycle.js.map +1 -1
- package/dist/git-activity.d.ts +15 -0
- package/dist/git-activity.d.ts.map +1 -1
- package/dist/git-activity.js +24 -0
- package/dist/git-activity.js.map +1 -1
- package/dist/git-genesis.d.ts +100 -0
- package/dist/git-genesis.d.ts.map +1 -0
- package/dist/git-genesis.js +231 -0
- package/dist/git-genesis.js.map +1 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +28 -5
- package/dist/git.js.map +1 -1
- package/dist/loop-engine-types.d.ts +40 -0
- package/dist/loop-engine-types.d.ts.map +1 -0
- package/dist/loop-engine-types.js +11 -0
- package/dist/loop-engine-types.js.map +1 -0
- package/dist/loop-engine.d.ts +52 -0
- package/dist/loop-engine.d.ts.map +1 -1
- package/dist/loop-engine.js +91 -10
- package/dist/loop-engine.js.map +1 -1
- package/dist/loop-llm-flag.d.ts +32 -0
- package/dist/loop-llm-flag.d.ts.map +1 -0
- package/dist/loop-llm-flag.js +35 -0
- package/dist/loop-llm-flag.js.map +1 -0
- package/dist/loop-operational-block.d.ts +44 -0
- package/dist/loop-operational-block.d.ts.map +1 -0
- package/dist/loop-operational-block.js +47 -0
- package/dist/loop-operational-block.js.map +1 -0
- package/dist/merge-back-loop.d.ts.map +1 -1
- package/dist/merge-back-loop.js +1 -0
- package/dist/merge-back-loop.js.map +1 -1
- package/dist/nexus-intake.d.ts +26 -3
- package/dist/nexus-intake.d.ts.map +1 -1
- package/dist/nexus-intake.js +62 -10
- package/dist/nexus-intake.js.map +1 -1
- package/dist/output-monitor.js +5 -8
- package/dist/output-monitor.js.map +1 -1
- package/dist/prd-controller.d.ts +0 -2
- package/dist/prd-controller.d.ts.map +1 -1
- package/dist/prd-controller.js +14 -35
- package/dist/prd-controller.js.map +1 -1
- package/dist/prd-divergence.d.ts +19 -8
- package/dist/prd-divergence.d.ts.map +1 -1
- package/dist/prd-divergence.js +7 -26
- package/dist/prd-divergence.js.map +1 -1
- package/dist/prd-viability.d.ts +14 -15
- package/dist/prd-viability.d.ts.map +1 -1
- package/dist/prd-viability.js +21 -34
- package/dist/prd-viability.js.map +1 -1
- package/dist/queries/prd.d.ts +24 -2
- package/dist/queries/prd.d.ts.map +1 -1
- package/dist/queries/prd.js +17 -0
- package/dist/queries/prd.js.map +1 -1
- package/dist/queries/schemas.d.ts +28 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +26 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/sessions.d.ts +14 -0
- package/dist/queries/sessions.d.ts.map +1 -1
- package/dist/queries/sessions.js +19 -0
- package/dist/queries/sessions.js.map +1 -1
- package/dist/queries/starter-repo.d.ts +27 -0
- package/dist/queries/starter-repo.d.ts.map +1 -0
- package/dist/queries/starter-repo.js +32 -0
- package/dist/queries/starter-repo.js.map +1 -0
- package/dist/session-lineage.d.ts +25 -0
- package/dist/session-lineage.d.ts.map +1 -1
- package/dist/session-lineage.js +23 -0
- package/dist/session-lineage.js.map +1 -1
- package/dist/session-stamp.d.ts +63 -0
- package/dist/session-stamp.d.ts.map +1 -0
- package/dist/session-stamp.js +65 -0
- package/dist/session-stamp.js.map +1 -0
- package/dist/spawner/helpers.d.ts.map +1 -1
- package/dist/spawner/helpers.js +5 -8
- package/dist/spawner/helpers.js.map +1 -1
- package/dist/supabase.d.ts +2 -2
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js +1 -1
- package/dist/supabase.js.map +1 -1
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.d.ts.map +1 -1
- package/dist/templates/claude-md.js +6 -3
- package/dist/templates/claude-md.js.map +1 -1
- package/dist/types/focus.d.ts +24 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/dist/types/session.d.ts +24 -0
- package/dist/types/session.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/crash-recovery-cleanup.d.ts +0 -21
- package/dist/crash-recovery-cleanup.d.ts.map +0 -1
- package/dist/crash-recovery-cleanup.js +0 -205
- package/dist/crash-recovery-cleanup.js.map +0 -1
- package/dist/crash-recovery-scan.d.ts +0 -19
- package/dist/crash-recovery-scan.d.ts.map +0 -1
- package/dist/crash-recovery-scan.js +0 -145
- package/dist/crash-recovery-scan.js.map +0 -1
- package/dist/crash-recovery-types.d.ts +0 -49
- package/dist/crash-recovery-types.d.ts.map +0 -1
- package/dist/crash-recovery-types.js +0 -13
- package/dist/crash-recovery-types.js.map +0 -1
- package/dist/prd-clearance-engine.d.ts +0 -121
- package/dist/prd-clearance-engine.d.ts.map +0 -1
- package/dist/prd-clearance-engine.js +0 -164
- package/dist/prd-clearance-engine.js.map +0 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starter-repo genesis: seed an EMPTY product repo from a registered, polished
|
|
3
|
+
* starter the active PRD selected, instead of scaffolding a frontend from zero
|
|
4
|
+
* (focus 80ecc233).
|
|
5
|
+
*
|
|
6
|
+
* When a product's git repo has no commits AND its active PRD carries a
|
|
7
|
+
* `starterRepoId`, the daemon clones the starter's pinned ref, squashes its
|
|
8
|
+
* working tree into a fresh INITIAL commit (starter history is NOT inherited),
|
|
9
|
+
* and records provenance (git URL + resolved sha) both as a `.telora`
|
|
10
|
+
* metadata file and in the genesis commit trailers. The PRD then runs normally
|
|
11
|
+
* against that base -- the agent starts ABOVE the UX floor.
|
|
12
|
+
*
|
|
13
|
+
* It is a strict NO-OP (returns seeded:false with a reason) when the repo
|
|
14
|
+
* already has history (greenfield guard), no active PRD selects a starter, or
|
|
15
|
+
* the selected starter cannot be resolved/cloned -- in every such case the
|
|
16
|
+
* caller falls back to the empty-initial-commit path.
|
|
17
|
+
*
|
|
18
|
+
* Private starters clone via the daemon process's inherited git credentials
|
|
19
|
+
* (ssh-agent / credential helper) -- no special handling here; `git clone`
|
|
20
|
+
* runs in the daemon's environment.
|
|
21
|
+
*
|
|
22
|
+
* All filesystem + git effects are injectable (GenesisDeps) so the path is
|
|
23
|
+
* unit-tested against a local fixture starter repo without a live backend.
|
|
24
|
+
*/
|
|
25
|
+
import { mkdtempSync, rmSync, cpSync, writeFileSync, mkdirSync, existsSync, } from 'node:fs';
|
|
26
|
+
import { tmpdir } from 'node:os';
|
|
27
|
+
import { join, sep } from 'node:path';
|
|
28
|
+
import { runGitSync } from './git-types.js';
|
|
29
|
+
import { repoHasCommits } from './git-branch.js';
|
|
30
|
+
import { loadActivePrds, hasPendingStarterSelection } from './queries/prd.js';
|
|
31
|
+
import { loadStarterRepo, } from './queries/starter-repo.js';
|
|
32
|
+
const defaultDeps = {
|
|
33
|
+
hasCommits: repoHasCommits,
|
|
34
|
+
loadActivePrds,
|
|
35
|
+
loadStarterRepo,
|
|
36
|
+
hasPendingStarterSelection,
|
|
37
|
+
runGit: runGitSync,
|
|
38
|
+
log: (msg) => console.log(msg),
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The placeholder initial-commit message written when an empty repo has no
|
|
42
|
+
* starter to seed from. Shared so callers can recognise the placeholder.
|
|
43
|
+
*/
|
|
44
|
+
export const PLACEHOLDER_COMMIT_MESSAGE = 'chore: initialize repository for Telora';
|
|
45
|
+
// Cloning a polished app (full clone so an arbitrary sha is reachable) can take
|
|
46
|
+
// a while; give it a generous ceiling distinct from the 60s default.
|
|
47
|
+
const CLONE_TIMEOUT_MS = 300_000;
|
|
48
|
+
/**
|
|
49
|
+
* Seed an empty product repo's genesis commit from the starter its active PRD
|
|
50
|
+
* selected. Returns a typed result; the caller decides the fallback.
|
|
51
|
+
*/
|
|
52
|
+
export async function seedGenesisFromStarter(config, deps = {}) {
|
|
53
|
+
const d = { ...defaultDeps, ...deps };
|
|
54
|
+
// Greenfield guard: only an empty repo is seeded. A repo with history is left
|
|
55
|
+
// untouched -- a starter is the INITIAL commit, never grafted onto a history.
|
|
56
|
+
if (d.hasCommits(config.repoPath)) {
|
|
57
|
+
return { seeded: false, reason: 'repo-has-history' };
|
|
58
|
+
}
|
|
59
|
+
// Resolve the starter the active PRD selected (first active PRD that carries
|
|
60
|
+
// a selection wins).
|
|
61
|
+
let starterRepoId = null;
|
|
62
|
+
try {
|
|
63
|
+
const prds = await d.loadActivePrds(config.productId);
|
|
64
|
+
starterRepoId = prds.find((p) => p.starterRepoId)?.starterRepoId ?? null;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
d.log(`[genesis] could not load active PRDs for product ${config.productId}: ${err.message} -- skipping starter genesis`);
|
|
68
|
+
return { seeded: false, reason: 'no-starter-selected' };
|
|
69
|
+
}
|
|
70
|
+
if (!starterRepoId) {
|
|
71
|
+
d.log('[genesis] no active PRD selects a starter -- using empty genesis');
|
|
72
|
+
return { seeded: false, reason: 'no-starter-selected' };
|
|
73
|
+
}
|
|
74
|
+
// Resolve the starter's clone coordinates.
|
|
75
|
+
let coords = null;
|
|
76
|
+
try {
|
|
77
|
+
coords = await d.loadStarterRepo(starterRepoId);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
d.log(`[genesis] could not load starter ${starterRepoId}: ${err.message}`);
|
|
81
|
+
}
|
|
82
|
+
if (!coords) {
|
|
83
|
+
d.log(`[genesis] starter ${starterRepoId} not found/unclonable -- using empty genesis`);
|
|
84
|
+
return { seeded: false, reason: 'starter-not-found', starterRepoId };
|
|
85
|
+
}
|
|
86
|
+
const tmpRoot = mkdtempSync(join(tmpdir(), 'telora-starter-'));
|
|
87
|
+
const cloneDir = join(tmpRoot, 'starter');
|
|
88
|
+
try {
|
|
89
|
+
// Full clone (so a pinned sha is reachable), then check out the exact ref.
|
|
90
|
+
const clone = d.runGit(['clone', coords.gitUrl, cloneDir], tmpRoot, {
|
|
91
|
+
timeoutMs: CLONE_TIMEOUT_MS,
|
|
92
|
+
});
|
|
93
|
+
if (!clone.success) {
|
|
94
|
+
d.log(`[genesis] clone of ${coords.gitUrl} failed: ${clone.error} -- using empty genesis`);
|
|
95
|
+
return { seeded: false, reason: 'clone-failed', starterRepoId, gitUrl: coords.gitUrl, ref: coords.ref };
|
|
96
|
+
}
|
|
97
|
+
const checkout = d.runGit(['checkout', coords.ref], cloneDir, { timeoutMs: CLONE_TIMEOUT_MS });
|
|
98
|
+
if (!checkout.success) {
|
|
99
|
+
d.log(`[genesis] checkout of ref ${coords.ref} failed: ${checkout.error} -- using empty genesis`);
|
|
100
|
+
return { seeded: false, reason: 'clone-failed', starterRepoId, gitUrl: coords.gitUrl, ref: coords.ref };
|
|
101
|
+
}
|
|
102
|
+
const resolved = d.runGit(['rev-parse', 'HEAD'], cloneDir);
|
|
103
|
+
const resolvedSha = resolved.success ? resolved.output.trim() : coords.ref;
|
|
104
|
+
// Copy the starter working tree (MINUS its .git) over the empty product
|
|
105
|
+
// repo. Excluding .git is what squashes the result to a single fresh
|
|
106
|
+
// initial commit -- starter history is not inherited.
|
|
107
|
+
const starterGitDir = join(cloneDir, '.git');
|
|
108
|
+
cpSync(cloneDir, config.repoPath, {
|
|
109
|
+
recursive: true,
|
|
110
|
+
filter: (src) => src !== starterGitDir && !src.startsWith(starterGitDir + sep),
|
|
111
|
+
});
|
|
112
|
+
// Record provenance as repo metadata (force-added past any .telora gitignore)
|
|
113
|
+
// so a product traces back to exactly what it started from.
|
|
114
|
+
const provenance = {
|
|
115
|
+
starterRepoId,
|
|
116
|
+
name: coords.name,
|
|
117
|
+
gitUrl: coords.gitUrl,
|
|
118
|
+
ref: coords.ref,
|
|
119
|
+
resolvedSha,
|
|
120
|
+
seededAt: new Date().toISOString(),
|
|
121
|
+
};
|
|
122
|
+
const teloraDir = join(config.repoPath, '.telora');
|
|
123
|
+
if (!existsSync(teloraDir))
|
|
124
|
+
mkdirSync(teloraDir, { recursive: true });
|
|
125
|
+
writeFileSync(join(teloraDir, 'starter-provenance.json'), `${JSON.stringify(provenance, null, 2)}\n`);
|
|
126
|
+
d.runGit(['add', '-A'], config.repoPath);
|
|
127
|
+
// Force-add the provenance file in case the product's .gitignore drops
|
|
128
|
+
// .telora -- the provenance must land in the genesis commit.
|
|
129
|
+
d.runGit(['add', '-f', '.telora/starter-provenance.json'], config.repoPath);
|
|
130
|
+
const message = `chore: initialize from starter ${coords.name || coords.ref}\n\n` +
|
|
131
|
+
'Seeded the product genesis commit from a registered starter repo ' +
|
|
132
|
+
'(squashed -- starter history not inherited).\n\n' +
|
|
133
|
+
`Starter-Repo-Id: ${starterRepoId}\n` +
|
|
134
|
+
`Starter-Name: ${coords.name}\n` +
|
|
135
|
+
`Starter-Git-Url: ${coords.gitUrl}\n` +
|
|
136
|
+
`Starter-Ref: ${coords.ref}\n` +
|
|
137
|
+
`Starter-Resolved-Sha: ${resolvedSha}\n`;
|
|
138
|
+
let commit = d.runGit(['commit', '-m', message], config.repoPath);
|
|
139
|
+
if (!commit.success) {
|
|
140
|
+
// The daemon host may have no committer identity configured. Retry with an
|
|
141
|
+
// explicit one scoped to this command only.
|
|
142
|
+
commit = d.runGit([
|
|
143
|
+
'-c',
|
|
144
|
+
'user.name=Telora Factory',
|
|
145
|
+
'-c',
|
|
146
|
+
'user.email=factory@telora.local',
|
|
147
|
+
'commit',
|
|
148
|
+
'-m',
|
|
149
|
+
message,
|
|
150
|
+
], config.repoPath);
|
|
151
|
+
}
|
|
152
|
+
if (!commit.success) {
|
|
153
|
+
d.log(`[genesis] genesis commit failed: ${commit.error} -- using empty genesis`);
|
|
154
|
+
return {
|
|
155
|
+
seeded: false,
|
|
156
|
+
reason: 'commit-failed',
|
|
157
|
+
starterRepoId,
|
|
158
|
+
gitUrl: coords.gitUrl,
|
|
159
|
+
ref: coords.ref,
|
|
160
|
+
resolvedSha,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
d.log(`[genesis] seeded product genesis from starter "${coords.name}" ` +
|
|
164
|
+
`(${coords.gitUrl} @ ${coords.ref} -> ${resolvedSha.slice(0, 12)})`);
|
|
165
|
+
return {
|
|
166
|
+
seeded: true,
|
|
167
|
+
reason: 'seeded',
|
|
168
|
+
starterRepoId,
|
|
169
|
+
gitUrl: coords.gitUrl,
|
|
170
|
+
ref: coords.ref,
|
|
171
|
+
resolvedSha,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Ensure an empty product repo has its initial commit, preferring a starter
|
|
180
|
+
* genesis. This is the single decision point both daemon startup (initGit) and
|
|
181
|
+
* the per-poll worktree-provisioning safety net share, so the
|
|
182
|
+
* defer-rather-than-foreclose rule is applied consistently (focus 80ecc233).
|
|
183
|
+
*
|
|
184
|
+
* Order:
|
|
185
|
+
* 1. Repo already has commits -> nothing to do.
|
|
186
|
+
* 2. Try to seed from the active PRD's starter.
|
|
187
|
+
* 3. No active starter, BUT a starter-selecting PRD is still draft -> DEFER:
|
|
188
|
+
* leave the repo empty (no foreclosing placeholder). The worktree-provisioning
|
|
189
|
+
* safety net re-runs this once the PRD activates and seeds the genesis then.
|
|
190
|
+
* 4. Otherwise (no starter anywhere, or the clone genuinely failed) -> write an
|
|
191
|
+
* empty placeholder commit so git operations are never wedged.
|
|
192
|
+
*/
|
|
193
|
+
export async function ensureInitialCommit(config, deps = {}) {
|
|
194
|
+
const d = { ...defaultDeps, ...deps };
|
|
195
|
+
if (d.hasCommits(config.repoPath)) {
|
|
196
|
+
return { committed: true, seeded: false, deferred: false };
|
|
197
|
+
}
|
|
198
|
+
const genesis = await seedGenesisFromStarter(config, d);
|
|
199
|
+
if (genesis.seeded) {
|
|
200
|
+
return { committed: true, seeded: true, deferred: false, genesis };
|
|
201
|
+
}
|
|
202
|
+
// Defer ONLY for the genuinely-pending case. A clone/commit failure on an
|
|
203
|
+
// active selection (reason !== 'no-starter-selected') must still fall through
|
|
204
|
+
// to a placeholder -- never leave the product permanently without a commit.
|
|
205
|
+
if (genesis.reason === 'no-starter-selected') {
|
|
206
|
+
let pending = false;
|
|
207
|
+
try {
|
|
208
|
+
pending = await d.hasPendingStarterSelection(config.productId);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
d.log(`[genesis] could not check pending starter PRDs: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
if (pending) {
|
|
214
|
+
d.log('[genesis] a starter-selecting PRD exists but is not active yet -- ' +
|
|
215
|
+
'deferring the initial commit so genesis can seed once it activates');
|
|
216
|
+
return { committed: false, seeded: false, deferred: true, genesis };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Fall back to an empty placeholder initial commit.
|
|
220
|
+
let commit = d.runGit(['commit', '--allow-empty', '-m', PLACEHOLDER_COMMIT_MESSAGE], config.repoPath);
|
|
221
|
+
if (!commit.success) {
|
|
222
|
+
// Retry with an explicit committer identity scoped to this command only.
|
|
223
|
+
commit = d.runGit([
|
|
224
|
+
'-c', 'user.name=Telora Factory',
|
|
225
|
+
'-c', 'user.email=factory@telora.local',
|
|
226
|
+
'commit', '--allow-empty', '-m', PLACEHOLDER_COMMIT_MESSAGE,
|
|
227
|
+
], config.repoPath);
|
|
228
|
+
}
|
|
229
|
+
return { committed: commit.success, seeded: false, deferred: false, genesis };
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=git-genesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-genesis.js","sourceRoot":"","sources":["../src/git-genesis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,WAAW,EACX,MAAM,EACN,MAAM,EACN,aAAa,EACb,SAAS,EACT,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAkB,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EACL,eAAe,GAEhB,MAAM,2BAA2B,CAAC;AA0CnC,MAAM,WAAW,GAAgB;IAC/B,UAAU,EAAE,cAAc;IAC1B,cAAc;IACd,eAAe;IACf,0BAA0B;IAC1B,MAAM,EAAE,UAAU;IAClB,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,yCAAyC,CAAC;AAEpF,gFAAgF;AAChF,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAEjC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAqB,EACrB,OAA6B,EAAE;IAE/B,MAAM,CAAC,GAAgB,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC;IAEnD,8EAA8E;IAC9E,8EAA8E;IAC9E,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACvD,CAAC;IAED,6EAA6E;IAC7E,qBAAqB;IACrB,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtD,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,aAAa,IAAI,IAAI,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,GAAG,CACH,oDAAoD,MAAM,CAAC,SAAS,KAAM,GAAa,CAAC,OAAO,8BAA8B,CAC9H,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,CAAC,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;QAC1E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAC1D,CAAC;IAED,2CAA2C;IAC3C,IAAI,MAAM,GAAkC,IAAI,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,CAAC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,GAAG,CAAC,oCAAoC,aAAa,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,CAAC,CAAC,GAAG,CAAC,qBAAqB,aAAa,8CAA8C,CAAC,CAAC;QACxF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,2EAA2E;QAC3E,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE;YAClE,SAAS,EAAE,gBAAgB;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,MAAM,YAAY,KAAK,CAAC,KAAK,yBAAyB,CAAC,CAAC;YAC3F,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;QAC1G,CAAC;QACD,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC/F,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,CAAC,CAAC,GAAG,CAAC,6BAA6B,MAAM,CAAC,GAAG,YAAY,QAAQ,CAAC,KAAK,yBAAyB,CAAC,CAAC;YAClG,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;QAC1G,CAAC;QACD,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAE3E,wEAAwE;QACxE,qEAAqE;QACrE,sDAAsD;QACtD,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;YAChC,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,aAAa,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;SAC/E,CAAC,CAAC;QAEH,8EAA8E;QAC9E,4DAA4D;QAC5D,MAAM,UAAU,GAAG;YACjB,aAAa;YACb,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,WAAW;YACX,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACnC,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,aAAa,CACX,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,EAC1C,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAC3C,CAAC;QAEF,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,uEAAuE;QACvE,6DAA6D;QAC7D,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,iCAAiC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE5E,MAAM,OAAO,GACX,kCAAkC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,MAAM;YACjE,mEAAmE;YACnE,kDAAkD;YAClD,oBAAoB,aAAa,IAAI;YACrC,iBAAiB,MAAM,CAAC,IAAI,IAAI;YAChC,oBAAoB,MAAM,CAAC,MAAM,IAAI;YACrC,gBAAgB,MAAM,CAAC,GAAG,IAAI;YAC9B,yBAAyB,WAAW,IAAI,CAAC;QAE3C,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,2EAA2E;YAC3E,4CAA4C;YAC5C,MAAM,GAAG,CAAC,CAAC,MAAM,CACf;gBACE,IAAI;gBACJ,0BAA0B;gBAC1B,IAAI;gBACJ,iCAAiC;gBACjC,QAAQ;gBACR,IAAI;gBACJ,OAAO;aACR,EACD,MAAM,CAAC,QAAQ,CAChB,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,CAAC,CAAC,GAAG,CAAC,oCAAoC,MAAM,CAAC,KAAK,yBAAyB,CAAC,CAAC;YACjF,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,eAAe;gBACvB,aAAa;gBACb,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,WAAW;aACZ,CAAC;QACJ,CAAC;QAED,CAAC,CAAC,GAAG,CACH,kDAAkD,MAAM,CAAC,IAAI,IAAI;YAC/D,IAAI,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,GAAG,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CACtE,CAAC;QACF,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,QAAQ;YAChB,aAAa;YACb,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,WAAW;SACZ,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAkBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAqB,EACrB,OAA6B,EAAE;IAE/B,MAAM,CAAC,GAAgB,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC;IAEnD,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7D,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACrE,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAI,OAAO,CAAC,MAAM,KAAK,qBAAqB,EAAE,CAAC;QAC7C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,CAAC,CAAC,0BAA0B,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,CAAC,CAAC,GAAG,CAAC,mDAAoD,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,GAAG,CACH,oEAAoE;gBAClE,oEAAoE,CACvE,CAAC;YACF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IAED,oDAAoD;IACpD,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,0BAA0B,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,yEAAyE;QACzE,MAAM,GAAG,CAAC,CAAC,MAAM,CACf;YACE,IAAI,EAAE,0BAA0B;YAChC,IAAI,EAAE,iCAAiC;YACvC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,0BAA0B;SAC5D,EACD,MAAM,CAAC,QAAQ,CAChB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAChF,CAAC"}
|
package/dist/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe/C,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGlJ,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,2BAA2B,EAC3B,kCAAkC,EAClC,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,wBAAwB,EACxB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,6BAA6B,EAClC,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFjE"}
|
package/dist/git.js
CHANGED
|
@@ -17,6 +17,7 @@ import { configureMergeLock } from './git-merge-lock.js';
|
|
|
17
17
|
import { ensureIntegrationBranch, repoHasCommits } from './git-branch.js';
|
|
18
18
|
import { syncIntegrationWithMain } from './git-integration.js';
|
|
19
19
|
import { runGitSync } from './git-types.js';
|
|
20
|
+
import { ensureInitialCommit } from './git-genesis.js';
|
|
20
21
|
// ── Re-exports from sub-modules ─────────────────────────────────
|
|
21
22
|
// git-types.ts: shared types and utilities
|
|
22
23
|
export { runGitSync, DEFAULT_GIT_TIMEOUT_MS } from './git-types.js';
|
|
@@ -59,15 +60,37 @@ export async function initGit(config) {
|
|
|
59
60
|
pruneWorktrees(config.repoPath);
|
|
60
61
|
// Empty repos need an initial commit before branches or worktrees can be created.
|
|
61
62
|
// Create one automatically so the full git subsystem initializes immediately.
|
|
63
|
+
// When the product's active PRD selects a starter repo, seed the genesis
|
|
64
|
+
// commit from that polished starter instead of an empty commit (focus
|
|
65
|
+
// 80ecc233) -- the agent then starts ABOVE the UX floor. Falls back to the
|
|
66
|
+
// empty commit when there is no starter / it cannot be cloned.
|
|
62
67
|
if (!repoHasCommits(config.repoPath)) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
const init = await ensureInitialCommit({
|
|
69
|
+
repoPath: config.repoPath,
|
|
70
|
+
productId: config.productId,
|
|
71
|
+
});
|
|
72
|
+
if (init.seeded) {
|
|
73
|
+
const g = init.genesis;
|
|
74
|
+
console.log(`Genesis commit seeded from starter ${g?.starterRepoId} ` +
|
|
75
|
+
`(${g?.gitUrl} @ ${g?.ref} -> ${g?.resolvedSha?.slice(0, 12)})`);
|
|
76
|
+
}
|
|
77
|
+
else if (init.deferred) {
|
|
78
|
+
// A starter-selecting PRD is still draft -- leave the repo empty rather
|
|
79
|
+
// than foreclose genesis with a placeholder. The worktree-provisioning
|
|
80
|
+
// safety net seeds it once the PRD activates.
|
|
81
|
+
console.log('Repository has no commits -- deferring initial commit until a ' +
|
|
82
|
+
'starter-selecting PRD activates (genesis will seed it then)');
|
|
83
|
+
console.log('Git subsystem initialized (limited -- waiting for starter PRD / first commit)');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
else if (!init.committed) {
|
|
87
|
+
console.error('Failed to create initial commit');
|
|
67
88
|
console.log('Git subsystem initialized (limited -- waiting for first commit)');
|
|
68
89
|
return;
|
|
69
90
|
}
|
|
70
|
-
|
|
91
|
+
else {
|
|
92
|
+
console.log(`Repository has no commits (${init.genesis?.reason}) -- created empty initial commit for Telora`);
|
|
93
|
+
}
|
|
71
94
|
}
|
|
72
95
|
// Ensure integration branch exists
|
|
73
96
|
await ensureIntegrationBranch(config);
|
package/dist/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,oDAAoD;AACpD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEvF,8CAA8C;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-response shapes consumed by the LoopEngine (loop-engine.ts).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from loop-engine.ts to keep that file under the daemon structural
|
|
5
|
+
* size lint -- the same reason the operational-block guard and the LLM-flag
|
|
6
|
+
* resolver live in their own modules. These are the minimal projections the
|
|
7
|
+
* engine reads from focus_list / loop_document_list / loop_question_list /
|
|
8
|
+
* reality_metrics_focus.
|
|
9
|
+
*/
|
|
10
|
+
export interface FocusWithLoopData {
|
|
11
|
+
id: string;
|
|
12
|
+
productId: string;
|
|
13
|
+
organizationId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
status: string;
|
|
16
|
+
assignedAgentRoleId: string | null;
|
|
17
|
+
targetDate: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface LoopDocumentItem {
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
content: string;
|
|
23
|
+
sortOrder: number;
|
|
24
|
+
}
|
|
25
|
+
export interface LoopQuestionItem {
|
|
26
|
+
id: string;
|
|
27
|
+
text: string;
|
|
28
|
+
questionType: string;
|
|
29
|
+
sortOrder: number | null;
|
|
30
|
+
isRetired: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface RealityMetricsResponse {
|
|
33
|
+
focusId: string;
|
|
34
|
+
metrics: Record<string, unknown> | null;
|
|
35
|
+
projections: {
|
|
36
|
+
completion: Record<string, unknown> | null;
|
|
37
|
+
cost: Record<string, unknown> | null;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=loop-engine-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-engine-types.d.ts","sourceRoot":"","sources":["../src/loop-engine-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;KACtC,CAAC;CACH"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-response shapes consumed by the LoopEngine (loop-engine.ts).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from loop-engine.ts to keep that file under the daemon structural
|
|
5
|
+
* size lint -- the same reason the operational-block guard and the LLM-flag
|
|
6
|
+
* resolver live in their own modules. These are the minimal projections the
|
|
7
|
+
* engine reads from focus_list / loop_document_list / loop_question_list /
|
|
8
|
+
* reality_metrics_focus.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=loop-engine-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-engine-types.js","sourceRoot":"","sources":["../src/loop-engine-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
package/dist/loop-engine.d.ts
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* 5. shutdown() -- no-op (no processes to kill)
|
|
15
15
|
*/
|
|
16
16
|
import type { ExecutionEngine, EngineHealth, ResourceUsage, BaseConfig, ResourceGovernor } from '@telora/daemon-core';
|
|
17
|
+
import { type LoopIntentData, type RealityData, type VarianceResult } from './loop-variance.js';
|
|
18
|
+
import { isFocusOperationallyBlocked } from './loop-operational-block.js';
|
|
19
|
+
import { isLoopLlmReCurationEnabled } from './loop-llm-flag.js';
|
|
20
|
+
import type { FocusWithLoopData } from './loop-engine-types.js';
|
|
17
21
|
export interface LoopEventTriggerConfig {
|
|
18
22
|
/** Trigger on delivery completion. Default: true. */
|
|
19
23
|
deliveryCompleted: boolean;
|
|
@@ -36,6 +40,41 @@ export interface LoopEngineConfig {
|
|
|
36
40
|
/** Event triggers that cause immediate re-evaluation. Default: all enabled. */
|
|
37
41
|
eventTriggers: LoopEventTriggerConfig;
|
|
38
42
|
}
|
|
43
|
+
export { isFocusOperationallyBlocked };
|
|
44
|
+
export type { OperationalBlockDeps } from './loop-operational-block.js';
|
|
45
|
+
export { isLoopLlmReCurationEnabled };
|
|
46
|
+
/**
|
|
47
|
+
* Injectable collaborators for {@link LoopEngine.processFocus}.
|
|
48
|
+
*
|
|
49
|
+
* Every field defaults to the engine's real implementation, so an omitted (or
|
|
50
|
+
* partial) override is byte-identical to production. The seam exists ONLY to
|
|
51
|
+
* make the operational-block suppression behavior testable WITHOUT module
|
|
52
|
+
* mocking: `loop-engine.ts`'s transitive graph (queries/shared, worktree-safety,
|
|
53
|
+
* git-types) runtime-imports `@telora/daemon-core`, and `mock.module(...)` is
|
|
54
|
+
* incompatible with that under tsx's experimental resolver (documented in
|
|
55
|
+
* focus-engine-characterization.test.ts). Injecting these collaborators lets a
|
|
56
|
+
* test drive processFocus with the REAL modules and assert that the LLM update
|
|
57
|
+
* is suppressed while a focus is operationally blocked, runs once the block
|
|
58
|
+
* clears, and that the variance snapshot is still persisted while blocked.
|
|
59
|
+
*/
|
|
60
|
+
export interface ProcessFocusDeps {
|
|
61
|
+
/** Load intent (loop documents/questions/persona) for the focus. */
|
|
62
|
+
loadIntentData: (focusId: string) => Promise<LoopIntentData>;
|
|
63
|
+
/** Load forecasted-reality (metrics + projections) for the focus. */
|
|
64
|
+
loadRealityData: (focusId: string) => Promise<RealityData>;
|
|
65
|
+
/** Persist the computed variance snapshot (cheap; kept even while blocked). */
|
|
66
|
+
persistVarianceSnapshot: (focus: FocusWithLoopData, variance: VarianceResult) => Promise<void>;
|
|
67
|
+
/** True when the focus is operationally blocked (LLM drift must be suppressed). */
|
|
68
|
+
isFocusOperationallyBlocked: (focusId: string) => Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Opt-in gate for the whole LLM re-curation concern (the block guard + the
|
|
71
|
+
* update). Default reads TELORA_LOOP_LLM via isLoopLlmReCurationEnabled (OFF).
|
|
72
|
+
* Injected so a test can exercise the on-path without mutating process.env.
|
|
73
|
+
*/
|
|
74
|
+
isLoopLlmReCurationEnabled: () => boolean;
|
|
75
|
+
/** Run the LLM drift update -- the call suppressed while a focus is blocked. */
|
|
76
|
+
runLLMUpdate: (focus: FocusWithLoopData, intent: LoopIntentData, reality: RealityData, variance: VarianceResult) => Promise<void>;
|
|
77
|
+
}
|
|
39
78
|
export declare class LoopEngine implements ExecutionEngine {
|
|
40
79
|
readonly name = "loop";
|
|
41
80
|
private config;
|
|
@@ -53,6 +92,19 @@ export declare class LoopEngine implements ExecutionEngine {
|
|
|
53
92
|
private lastVariance;
|
|
54
93
|
/** Focuses that have been triggered by lifecycle events (pending immediate evaluation). */
|
|
55
94
|
private triggeredFocuses;
|
|
95
|
+
/**
|
|
96
|
+
* Focuses currently in an operational-block episode (LLM drift suppressed).
|
|
97
|
+
* Tracked so we log ONCE per episode (on entry) and resume ONCE (on exit),
|
|
98
|
+
* rather than every poll tick.
|
|
99
|
+
*/
|
|
100
|
+
private blockedFocuses;
|
|
101
|
+
/**
|
|
102
|
+
* Focuses for which the "LLM re-curation disabled by default" line has already
|
|
103
|
+
* been logged. The opt-in is a persistent config (not a clearing episode), so
|
|
104
|
+
* the default-OFF skip is logged at most once per focus -- observable without
|
|
105
|
+
* per-tick spam.
|
|
106
|
+
*/
|
|
107
|
+
private llmDisabledLoggedFocuses;
|
|
56
108
|
/** Cleanup function for event bus subscription. */
|
|
57
109
|
private unsubscribeEvents;
|
|
58
110
|
setGovernor(gov: ResourceGovernor): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../src/loop-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../src/loop-engine.ts"],"names":[],"mappings":"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"}
|
package/dist/loop-engine.js
CHANGED
|
@@ -19,6 +19,11 @@ import { computeVariance } from './loop-variance.js';
|
|
|
19
19
|
import { buildLoopUpdatePrompt, parseLoopUpdateResponse } from './loop-prompt-builder.js';
|
|
20
20
|
import { callLoopLLM } from './loop-llm-client.js';
|
|
21
21
|
import { onLoopTrigger } from './loop-event-bus.js';
|
|
22
|
+
import { hasOpenWorktreeDirtyEscalation } from './queries/issues.js';
|
|
23
|
+
import { getFocusWorktree } from './focus-worktree-state.js';
|
|
24
|
+
import { worktreeHasUncommittedChanges } from './worktree-safety.js';
|
|
25
|
+
import { isFocusOperationallyBlocked } from './loop-operational-block.js';
|
|
26
|
+
import { isLoopLlmReCurationEnabled } from './loop-llm-flag.js';
|
|
22
27
|
const DEFAULT_EVENT_TRIGGERS = {
|
|
23
28
|
deliveryCompleted: true,
|
|
24
29
|
focusStatusChanged: true,
|
|
@@ -32,6 +37,15 @@ const DEFAULT_LOOP_CONFIG = {
|
|
|
32
37
|
staleThresholdMs: 86_400_000,
|
|
33
38
|
eventTriggers: { ...DEFAULT_EVENT_TRIGGERS },
|
|
34
39
|
};
|
|
40
|
+
// Operational-block guard (OperationalBlockDeps + isFocusOperationallyBlocked)
|
|
41
|
+
// lives in ./loop-operational-block.ts; imported above for processFocus's default
|
|
42
|
+
// wiring and re-exported here so existing importers (e.g.
|
|
43
|
+
// loop-engine-operational-block.test.ts) are unaffected.
|
|
44
|
+
export { isFocusOperationallyBlocked };
|
|
45
|
+
// LLM re-curation opt-in flag (default OFF). Extracted to ./loop-llm-flag.ts to
|
|
46
|
+
// keep this file under the structural size lint; re-exported so existing
|
|
47
|
+
// importers (and the flag's tests) reach it from loop-engine.
|
|
48
|
+
export { isLoopLlmReCurationEnabled };
|
|
35
49
|
// ---------------------------------------------------------------------------
|
|
36
50
|
// LoopEngine
|
|
37
51
|
// ---------------------------------------------------------------------------
|
|
@@ -52,6 +66,19 @@ export class LoopEngine {
|
|
|
52
66
|
lastVariance = new Map();
|
|
53
67
|
/** Focuses that have been triggered by lifecycle events (pending immediate evaluation). */
|
|
54
68
|
triggeredFocuses = new Set();
|
|
69
|
+
/**
|
|
70
|
+
* Focuses currently in an operational-block episode (LLM drift suppressed).
|
|
71
|
+
* Tracked so we log ONCE per episode (on entry) and resume ONCE (on exit),
|
|
72
|
+
* rather than every poll tick.
|
|
73
|
+
*/
|
|
74
|
+
blockedFocuses = new Set();
|
|
75
|
+
/**
|
|
76
|
+
* Focuses for which the "LLM re-curation disabled by default" line has already
|
|
77
|
+
* been logged. The opt-in is a persistent config (not a clearing episode), so
|
|
78
|
+
* the default-OFF skip is logged at most once per focus -- observable without
|
|
79
|
+
* per-tick spam.
|
|
80
|
+
*/
|
|
81
|
+
llmDisabledLoggedFocuses = new Set();
|
|
55
82
|
/** Cleanup function for event bus subscription. */
|
|
56
83
|
unsubscribeEvents = null;
|
|
57
84
|
// -----------------------------------------------------------------------
|
|
@@ -222,26 +249,80 @@ export class LoopEngine {
|
|
|
222
249
|
* Process a single focus: load intent + reality, compute variance,
|
|
223
250
|
* optionally call LLM for updates, persist variance snapshot.
|
|
224
251
|
*/
|
|
225
|
-
async processFocus(focus) {
|
|
252
|
+
async processFocus(focus, depsOverride) {
|
|
253
|
+
// Resolve injectable collaborators; each defaults to the real
|
|
254
|
+
// implementation so an omitted override is byte-identical to production.
|
|
255
|
+
// The default block-check binds the exact module trio the inline call used.
|
|
256
|
+
const loadIntent = depsOverride?.loadIntentData ?? ((id) => this.loadIntentData(id));
|
|
257
|
+
const loadReality = depsOverride?.loadRealityData ?? ((id) => this.loadRealityData(id));
|
|
258
|
+
const persistVariance = depsOverride?.persistVarianceSnapshot ??
|
|
259
|
+
((f, v) => this.persistVarianceSnapshot(f, v));
|
|
260
|
+
const checkBlocked = depsOverride?.isFocusOperationallyBlocked ??
|
|
261
|
+
((id) => isFocusOperationallyBlocked(id, {
|
|
262
|
+
hasOpenWorktreeDirtyEscalation,
|
|
263
|
+
getFocusWorktree,
|
|
264
|
+
worktreeHasUncommittedChanges,
|
|
265
|
+
}));
|
|
266
|
+
const runUpdate = depsOverride?.runLLMUpdate ??
|
|
267
|
+
((f, i, r, v) => this.runLLMUpdate(f, i, r, v));
|
|
268
|
+
const llmEnabled = depsOverride?.isLoopLlmReCurationEnabled ?? (() => isLoopLlmReCurationEnabled());
|
|
226
269
|
// 1. Load intent data (loop documents + questions + answers)
|
|
227
|
-
const intent = await
|
|
270
|
+
const intent = await loadIntent(focus.id);
|
|
228
271
|
// Skip focuses with no loop data (not yet initialized)
|
|
229
272
|
if (intent.documents.length === 0 && intent.questions.length === 0) {
|
|
230
273
|
return;
|
|
231
274
|
}
|
|
232
275
|
// 2. Load reality data (metrics + projections)
|
|
233
|
-
const reality = await
|
|
276
|
+
const reality = await loadReality(focus.id);
|
|
234
277
|
// 3. Compute variance (pass previous for trend direction)
|
|
235
278
|
const previousVariance = this.lastVariance.get(focus.id) ?? null;
|
|
236
279
|
const variance = computeVariance(intent, reality, focus.targetDate ?? null, previousVariance);
|
|
237
280
|
// 4. Persist variance snapshot
|
|
238
|
-
await
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
281
|
+
await persistVariance(focus, variance);
|
|
282
|
+
// 4b. LLM re-curation opt-in gate (default OFF -- the OUTER guard). The
|
|
283
|
+
// deterministic variance/lock snapshot above is the load-bearing
|
|
284
|
+
// differential signal and has already been computed + persisted. The LLM
|
|
285
|
+
// re-curation pass (the operational-block check AND the update below) is
|
|
286
|
+
// empirically net-negative even on healthy focuses, so it is suppressed
|
|
287
|
+
// entirely unless TELORA_LOOP_LLM opts in. lastVariance is still stamped
|
|
288
|
+
// (step 6) regardless, so trend comparison stays correct if the pass is
|
|
289
|
+
// later re-enabled. The default-off skip is logged at most once per focus.
|
|
290
|
+
if (llmEnabled()) {
|
|
291
|
+
// 4c. Operational-block guard. Intent-vs-reality drift cannot be reconciled
|
|
292
|
+
// by an LLM while reality is frozen by an operational block (an open
|
|
293
|
+
// advance-block / worktree-dirty escalation, or a dirty worktree). Skip the
|
|
294
|
+
// LLM call entirely while blocked -- otherwise the loop fires repeated no-op
|
|
295
|
+
// "drift" sessions on a stuck focus. The variance snapshot above is kept
|
|
296
|
+
// (cheap + useful), and lastVariance is still stamped (step 6) so trend
|
|
297
|
+
// comparison stays correct when the block clears.
|
|
298
|
+
const blocked = await checkBlocked(focus.id);
|
|
299
|
+
if (blocked) {
|
|
300
|
+
if (!this.blockedFocuses.has(focus.id)) {
|
|
301
|
+
this.blockedFocuses.add(focus.id);
|
|
302
|
+
console.log(`[Loop] Focus "${focus.name}" (${focus.id.slice(0, 8)}) is operationally blocked ` +
|
|
303
|
+
`(open advance-block/worktree-dirty escalation or dirty worktree); ` +
|
|
304
|
+
`suppressing LLM drift update until it clears.`);
|
|
305
|
+
}
|
|
306
|
+
this.lastVariance.set(focus.id, variance);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (this.blockedFocuses.delete(focus.id)) {
|
|
310
|
+
console.log(`[Loop] Focus "${focus.name}" (${focus.id.slice(0, 8)}) operational block cleared; ` +
|
|
311
|
+
`resuming LLM drift updates.`);
|
|
312
|
+
}
|
|
313
|
+
// 5. Determine if LLM update is needed
|
|
314
|
+
const shouldUpdate = this.shouldTriggerLLMUpdate(focus.id, variance, reality);
|
|
315
|
+
if (shouldUpdate) {
|
|
316
|
+
console.log(`[Loop] Triggering LLM update for focus "${focus.name}" (${focus.id.slice(0, 8)}) -- lock: ${variance.lockStatus}`);
|
|
317
|
+
await runUpdate(focus, intent, reality, variance);
|
|
318
|
+
this.lastEvaluationAt.set(focus.id, Date.now());
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (!this.llmDisabledLoggedFocuses.has(focus.id)) {
|
|
322
|
+
this.llmDisabledLoggedFocuses.add(focus.id);
|
|
323
|
+
console.log(`[Loop] LLM re-curation disabled by default (TELORA_LOOP_LLM unset) for focus ` +
|
|
324
|
+
`"${focus.name}" (${focus.id.slice(0, 8)}); variance snapshot persisted, ` +
|
|
325
|
+
`skipping LLM drift update.`);
|
|
245
326
|
}
|
|
246
327
|
// 6. Store variance for threshold comparison
|
|
247
328
|
this.lastVariance.set(focus.id, variance);
|