@kaelio/ktx 0.8.0 → 0.10.0
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/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.10.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +17 -2
- package/dist/cli-program.d.ts +3 -0
- package/dist/cli-program.js +42 -2
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.js +94 -3
- package/dist/commands/setup-commands.js +3 -4
- package/dist/connection-recovery.d.ts +34 -0
- package/dist/connection-recovery.js +82 -0
- package/dist/connection.js +26 -2
- package/dist/connectors/bigquery/connector.d.ts +2 -5
- package/dist/connectors/bigquery/connector.js +2 -2
- package/dist/connectors/clickhouse/connector.d.ts +2 -5
- package/dist/connectors/clickhouse/connector.js +2 -2
- package/dist/connectors/mysql/connector.d.ts +7 -6
- package/dist/connectors/mysql/connector.js +25 -5
- package/dist/connectors/mysql/dialect.d.ts +1 -1
- package/dist/connectors/mysql/dialect.js +12 -2
- package/dist/connectors/postgres/connector.d.ts +2 -5
- package/dist/connectors/postgres/connector.js +2 -2
- package/dist/connectors/snowflake/connector.d.ts +2 -5
- package/dist/connectors/snowflake/connector.js +2 -2
- package/dist/connectors/sqlite/connector.d.ts +2 -5
- package/dist/connectors/sqlite/connector.js +2 -2
- package/dist/connectors/sqlserver/connector.d.ts +2 -5
- package/dist/connectors/sqlserver/connector.js +2 -2
- package/dist/context/connections/drivers.d.ts +0 -1
- package/dist/context/connections/drivers.js +0 -7
- package/dist/context/connections/query-executor.d.ts +2 -1
- package/dist/context/core/abort.d.ts +9 -0
- package/dist/context/core/abort.js +36 -0
- package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
- package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
- package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
- package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +30 -0
- package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +194 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
- package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
- package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
- package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
- package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
- package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
- package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
- package/dist/context/ingest/context-candidates/curator-pagination.service.d.ts +1 -5
- package/dist/context/ingest/context-candidates/curator-pagination.service.js +1 -3
- package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
- package/dist/context/ingest/final-gate-repair.d.ts +1 -0
- package/dist/context/ingest/final-gate-repair.js +1 -0
- package/dist/context/ingest/ingest-bundle.runner.d.ts +3 -0
- package/dist/context/ingest/ingest-bundle.runner.js +127 -53
- package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +1 -0
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +1 -0
- package/dist/context/ingest/isolated-diff/work-unit-executor.d.ts +1 -0
- package/dist/context/ingest/local-adapters.js +21 -4
- package/dist/context/ingest/local-bundle-runtime.js +13 -5
- package/dist/context/ingest/local-ingest.d.ts +1 -0
- package/dist/context/ingest/local-ingest.js +13 -3
- package/dist/context/ingest/memory-flow/events.js +1 -1
- package/dist/context/ingest/memory-flow/schema.js +8 -3
- package/dist/context/ingest/memory-flow/types.d.ts +7 -3
- package/dist/context/ingest/ports.d.ts +3 -5
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +1 -4
- package/dist/context/ingest/stages/stage-3-work-units.js +5 -1
- package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +1 -4
- package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
- package/dist/context/ingest/types.d.ts +1 -0
- package/dist/context/llm/ai-sdk-runtime.d.ts +3 -0
- package/dist/context/llm/ai-sdk-runtime.js +152 -16
- package/dist/context/llm/claude-code-runtime.d.ts +6 -4
- package/dist/context/llm/claude-code-runtime.js +127 -48
- package/dist/context/llm/codex-exec-events.d.ts +20 -0
- package/dist/context/llm/codex-exec-events.js +155 -0
- package/dist/context/llm/codex-isolation.d.ts +3 -0
- package/dist/context/llm/codex-isolation.js +5 -0
- package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
- package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
- package/dist/context/llm/codex-models.d.ts +2 -0
- package/dist/context/llm/codex-models.js +17 -0
- package/dist/context/llm/codex-runtime-config.d.ts +16 -0
- package/dist/context/llm/codex-runtime-config.js +19 -0
- package/dist/context/llm/codex-runtime.d.ts +37 -0
- package/dist/context/llm/codex-runtime.js +347 -0
- package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
- package/dist/context/llm/codex-sdk-runner.js +63 -0
- package/dist/context/llm/local-config.d.ts +16 -4
- package/dist/context/llm/local-config.js +18 -2
- package/dist/context/llm/rate-limit-governor.d.ts +103 -0
- package/dist/context/llm/rate-limit-governor.js +285 -0
- package/dist/context/llm/runtime-port.d.ts +3 -6
- package/dist/context/mcp/context-tools.js +43 -13
- package/dist/context/project/config.d.ts +14 -0
- package/dist/context/project/config.js +37 -2
- package/dist/context/scan/types.d.ts +15 -2
- package/dist/context/scan/types.js +12 -0
- package/dist/context/sl/description-normalization.js +4 -14
- package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
- package/dist/context/sql-analysis/ports.d.ts +12 -2
- package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
- package/dist/context-build-view.d.ts +13 -0
- package/dist/context-build-view.js +63 -32
- package/dist/demo-metrics.d.ts +0 -2
- package/dist/demo-metrics.js +1 -11
- package/dist/ingest.d.ts +1 -0
- package/dist/ingest.js +32 -3
- package/dist/io/buffered-command-io.d.ts +11 -0
- package/dist/io/buffered-command-io.js +28 -0
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/llm/types.d.ts +1 -1
- package/dist/local-adapters.d.ts +10 -2
- package/dist/local-adapters.js +19 -3
- package/dist/memory-flow-hud.js +8 -16
- package/dist/next-steps.js +1 -2
- package/dist/progress-port-adapter.d.ts +6 -0
- package/dist/progress-port-adapter.js +18 -0
- package/dist/public-ingest.d.ts +20 -1
- package/dist/public-ingest.js +228 -42
- package/dist/reveal-password-prompt.d.ts +24 -0
- package/dist/reveal-password-prompt.js +78 -0
- package/dist/scan.js +21 -3
- package/dist/setup-context.d.ts +2 -0
- package/dist/setup-context.js +133 -27
- package/dist/setup-databases.d.ts +18 -1
- package/dist/setup-databases.js +378 -249
- package/dist/setup-demo-tour.js +1 -0
- package/dist/setup-embeddings.js +1 -1
- package/dist/setup-models.d.ts +11 -15
- package/dist/setup-models.js +140 -276
- package/dist/setup-prompts.js +3 -2
- package/dist/setup-ready-menu.d.ts +16 -2
- package/dist/setup-ready-menu.js +37 -5
- package/dist/setup-sources.js +115 -35
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +23 -11
- package/dist/sl.d.ts +2 -2
- package/dist/sl.js +20 -4
- package/dist/sql.js +18 -2
- package/dist/star-prompt/cache.d.ts +16 -0
- package/dist/star-prompt/cache.js +45 -0
- package/dist/star-prompt/star-count.d.ts +7 -0
- package/dist/star-prompt/star-count.js +66 -0
- package/dist/star-prompt/star-line.d.ts +12 -0
- package/dist/star-prompt/star-line.js +26 -0
- package/dist/status-project.d.ts +11 -0
- package/dist/status-project.js +50 -1
- package/dist/telemetry/command-hook.d.ts +1 -0
- package/dist/telemetry/command-hook.js +3 -1
- package/dist/telemetry/emitter.d.ts +10 -0
- package/dist/telemetry/emitter.js +31 -0
- package/dist/telemetry/events.d.ts +35 -6
- package/dist/telemetry/events.js +25 -2
- package/dist/telemetry/exception.d.ts +18 -0
- package/dist/telemetry/exception.js +162 -0
- package/dist/telemetry/identity.d.ts +0 -1
- package/dist/telemetry/identity.js +6 -6
- package/dist/telemetry/index.d.ts +15 -2
- package/dist/telemetry/index.js +15 -3
- package/dist/telemetry/redaction-secrets.d.ts +11 -0
- package/dist/telemetry/redaction-secrets.js +92 -0
- package/dist/telemetry/scrubber.d.ts +10 -0
- package/dist/telemetry/scrubber.js +20 -0
- package/dist/update-check/cache.d.ts +21 -0
- package/dist/update-check/cache.js +38 -0
- package/dist/update-check/channel.d.ts +15 -0
- package/dist/update-check/channel.js +30 -0
- package/dist/update-check/registry.d.ts +1 -0
- package/dist/update-check/registry.js +45 -0
- package/dist/update-check/update-check.d.ts +43 -0
- package/dist/update-check/update-check.js +116 -0
- package/package.json +12 -4
- package/dist/context/connections/local-query-executor.d.ts +0 -6
- package/dist/context/connections/local-query-executor.js +0 -39
- package/dist/context/connections/postgres-query-executor.d.ts +0 -25
- package/dist/context/connections/postgres-query-executor.js +0 -53
- package/dist/context/connections/sqlite-query-executor.d.ts +0 -4
- package/dist/context/connections/sqlite-query-executor.js +0 -74
|
@@ -115,6 +115,10 @@ export class IngestBundleRunner {
|
|
|
115
115
|
this.logger = deps.logger ?? noopLogger;
|
|
116
116
|
}
|
|
117
117
|
async run(job, ctx) {
|
|
118
|
+
const unsubscribeRateLimitGovernor = this.subscribeRateLimitGovernor({
|
|
119
|
+
trace: this.createTrace(job),
|
|
120
|
+
memoryFlow: ctx?.memoryFlow,
|
|
121
|
+
});
|
|
118
122
|
const key = job.connectionId;
|
|
119
123
|
const previous = this.chainByConnection.get(key);
|
|
120
124
|
if (previous) {
|
|
@@ -139,9 +143,64 @@ export class IngestBundleRunner {
|
|
|
139
143
|
throw error;
|
|
140
144
|
}
|
|
141
145
|
finally {
|
|
146
|
+
unsubscribeRateLimitGovernor();
|
|
142
147
|
await this.maybeEmitIngestProfile(job.jobId);
|
|
143
148
|
}
|
|
144
149
|
}
|
|
150
|
+
formatRateLimitWait(state) {
|
|
151
|
+
const seconds = Math.ceil(state.remainingMs / 1_000);
|
|
152
|
+
const minutes = Math.floor(seconds / 60);
|
|
153
|
+
const remainder = seconds % 60;
|
|
154
|
+
const duration = minutes > 0 ? `${minutes}m${String(remainder).padStart(2, '0')}s` : `${seconds}s`;
|
|
155
|
+
const type = state.rateLimitType ? ` ${state.rateLimitType}` : '';
|
|
156
|
+
return `Rate-limited (${state.provider}${type}); resuming in ${duration}; Ctrl+C to stop`;
|
|
157
|
+
}
|
|
158
|
+
subscribeRateLimitGovernor(input) {
|
|
159
|
+
const governor = this.deps.settings.rateLimitGovernor;
|
|
160
|
+
if (!governor) {
|
|
161
|
+
return () => undefined;
|
|
162
|
+
}
|
|
163
|
+
return governor.subscribe((state) => {
|
|
164
|
+
if (state.kind === 'rate_limit_observed') {
|
|
165
|
+
void input.trace.event('info', 'rate_limit', 'rate_limit_observed', { ...state });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (state.kind === 'concurrency_adjusted') {
|
|
169
|
+
void input.trace.event('info', 'rate_limit', 'concurrency_adjusted', { ...state });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
void input.trace.event('info', 'rate_limit', state.kind, { ...state });
|
|
173
|
+
if (state.kind === 'wait_tick' || state.kind === 'wait_started') {
|
|
174
|
+
input.memoryFlow?.emit({
|
|
175
|
+
type: 'rate_limit_wait',
|
|
176
|
+
provider: state.provider,
|
|
177
|
+
...(state.rateLimitType ? { rateLimitType: state.rateLimitType } : {}),
|
|
178
|
+
resumeAtMs: state.resumeAtMs,
|
|
179
|
+
remainingMs: state.remainingMs,
|
|
180
|
+
});
|
|
181
|
+
input.memoryFlow?.emit({
|
|
182
|
+
type: 'stage_progress',
|
|
183
|
+
stage: 'integration',
|
|
184
|
+
percent: 50,
|
|
185
|
+
message: this.formatRateLimitWait(state),
|
|
186
|
+
transient: true,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async withRateLimitWorkSlot(abortSignal, fn) {
|
|
192
|
+
const governor = this.deps.settings.rateLimitGovernor;
|
|
193
|
+
if (!governor) {
|
|
194
|
+
return fn();
|
|
195
|
+
}
|
|
196
|
+
const release = await governor.acquireWorkSlot(abortSignal);
|
|
197
|
+
try {
|
|
198
|
+
return await fn();
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
release();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
145
204
|
/**
|
|
146
205
|
* When profiling is enabled — via the `KTX_PROFILE_INGEST` env var or the
|
|
147
206
|
* `ingest.profile` config setting — read the job's trace + tool transcripts
|
|
@@ -711,7 +770,6 @@ export class IngestBundleRunner {
|
|
|
711
770
|
type: 'work_unit_started',
|
|
712
771
|
unitKey: input.wu.unitKey,
|
|
713
772
|
skills: input.wuSkillNames,
|
|
714
|
-
stepBudget: input.workUnitSettings.stepBudget,
|
|
715
773
|
});
|
|
716
774
|
return executeWorkUnit({
|
|
717
775
|
sessionWorktreeGit: input.worktree.git,
|
|
@@ -731,14 +789,30 @@ export class IngestBundleRunner {
|
|
|
731
789
|
slIndex: input.slIndex,
|
|
732
790
|
priorProvenance: input.priorProvenance,
|
|
733
791
|
}),
|
|
734
|
-
buildToolSet: (wuInner) =>
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
792
|
+
buildToolSet: (wuInner) => {
|
|
793
|
+
const transcriptPath = join(input.transcriptDir, `${wuInner.unitKey}.jsonl`);
|
|
794
|
+
const record = input.recordTranscriptEntry(transcriptPath);
|
|
795
|
+
return wrapToolsWithLogger(buildWuToolSet({
|
|
796
|
+
sourceKey: input.job.sourceKey,
|
|
797
|
+
stagedDir: input.stagedDir,
|
|
798
|
+
wu: wuInner,
|
|
799
|
+
loadSkillTool,
|
|
800
|
+
emitUnmappedFallbackTool: wuEmitUnmappedFallbackTool,
|
|
801
|
+
toolsetTools: wuToolset.toRuntimeTools(wuToolContext),
|
|
802
|
+
}), transcriptPath, wuInner.unitKey, {
|
|
803
|
+
// Drive the live HUD heartbeat from real tool calls: each invocation
|
|
804
|
+
// ticks the running per-unit count. This is an observed signal, not a
|
|
805
|
+
// re-derived turn count, so it can never overshoot a budget.
|
|
806
|
+
onEntry: (entry) => {
|
|
807
|
+
const summary = record(entry);
|
|
808
|
+
input.memoryFlow?.emit({
|
|
809
|
+
type: 'work_unit_step',
|
|
810
|
+
unitKey: wuInner.unitKey,
|
|
811
|
+
toolCalls: summary.toolCallCount,
|
|
812
|
+
});
|
|
813
|
+
},
|
|
814
|
+
});
|
|
815
|
+
},
|
|
742
816
|
captureSession: session,
|
|
743
817
|
sessionActions,
|
|
744
818
|
modelRole: 'candidateExtraction',
|
|
@@ -747,7 +821,7 @@ export class IngestBundleRunner {
|
|
|
747
821
|
connectionId: input.job.connectionId,
|
|
748
822
|
jobId: input.job.jobId,
|
|
749
823
|
toolFailureCount: (unitKey) => input.transcriptSummaries.get(unitKey)?.fatalErrorCount ?? 0,
|
|
750
|
-
|
|
824
|
+
abortSignal: input.abortSignal,
|
|
751
825
|
}, input.wu);
|
|
752
826
|
}
|
|
753
827
|
async runInner(job, ctx) {
|
|
@@ -797,6 +871,7 @@ export class IngestBundleRunner {
|
|
|
797
871
|
const current = transcriptSummaries.get(entry.wuKey) ?? createMutableToolTranscriptSummary(entry.wuKey, path);
|
|
798
872
|
recordToolTranscriptEntry(current, entry);
|
|
799
873
|
transcriptSummaries.set(entry.wuKey, current);
|
|
874
|
+
return current;
|
|
800
875
|
};
|
|
801
876
|
const overrideReport = await this.loadOverrideReport(job);
|
|
802
877
|
const stage1 = ctx?.startPhase(0.08);
|
|
@@ -1111,7 +1186,7 @@ export class IngestBundleRunner {
|
|
|
1111
1186
|
await stage3?.updateProgress(1.0, '0 of 0 work units complete');
|
|
1112
1187
|
}
|
|
1113
1188
|
try {
|
|
1114
|
-
await Promise.all(workUnits.map((wu, index) => limitWorkUnit(async () => {
|
|
1189
|
+
await Promise.all(workUnits.map((wu, index) => limitWorkUnit(() => this.withRateLimitWorkSlot(ctx?.abortSignal, async () => {
|
|
1115
1190
|
const outcome = await runIsolatedWorkUnit({
|
|
1116
1191
|
unitIndex: index,
|
|
1117
1192
|
ingestionBaseSha,
|
|
@@ -1119,6 +1194,7 @@ export class IngestBundleRunner {
|
|
|
1119
1194
|
patchDir,
|
|
1120
1195
|
trace: runTrace,
|
|
1121
1196
|
workUnit: wu,
|
|
1197
|
+
abortSignal: ctx?.abortSignal,
|
|
1122
1198
|
afterSuccess: (child) => copyTransientIngestEvidence(child.workdir, sessionWorktree.workdir),
|
|
1123
1199
|
run: async (child) => {
|
|
1124
1200
|
const scopedWikiService = this.deps.wikiService.forWorktree(child.workdir);
|
|
@@ -1147,11 +1223,9 @@ export class IngestBundleRunner {
|
|
|
1147
1223
|
stageIndex,
|
|
1148
1224
|
includeContextEvidenceTools: adapter.evidenceIndexing === 'documents' && !!contextReport,
|
|
1149
1225
|
currentTableExists: (tableRef) => this.tableRefExistsInSemanticLayer(scopedSemanticLayerService, slConnectionIds, tableRef),
|
|
1226
|
+
abortSignal: ctx?.abortSignal,
|
|
1150
1227
|
memoryFlow,
|
|
1151
1228
|
wuSkillNames,
|
|
1152
|
-
onStepFinish: ({ stepIndex, stepBudget }) => {
|
|
1153
|
-
memoryFlow?.emit({ type: 'work_unit_step', unitKey: wu.unitKey, stepIndex, stepBudget });
|
|
1154
|
-
},
|
|
1155
1229
|
});
|
|
1156
1230
|
},
|
|
1157
1231
|
});
|
|
@@ -1173,7 +1247,7 @@ export class IngestBundleRunner {
|
|
|
1173
1247
|
});
|
|
1174
1248
|
completedWorkUnits += 1;
|
|
1175
1249
|
await stage3?.updateProgress(completedWorkUnits / workUnits.length, `${completedWorkUnits} of ${workUnits.length} work units complete`);
|
|
1176
|
-
})));
|
|
1250
|
+
}))));
|
|
1177
1251
|
}
|
|
1178
1252
|
catch (error) {
|
|
1179
1253
|
await this.deps.runs.markFailed(runRow.id);
|
|
@@ -1250,6 +1324,7 @@ export class IngestBundleRunner {
|
|
|
1250
1324
|
reason: context.reason,
|
|
1251
1325
|
maxAttempts: 1,
|
|
1252
1326
|
stepBudget: 12,
|
|
1327
|
+
abortSignal: ctx?.abortSignal,
|
|
1253
1328
|
});
|
|
1254
1329
|
emitStageProgress('integration', 82, result.status === 'repaired'
|
|
1255
1330
|
? `Resolved text conflict for ${context.unitKey}`
|
|
@@ -1267,6 +1342,7 @@ export class IngestBundleRunner {
|
|
|
1267
1342
|
repairKind: 'patch_semantic_gate',
|
|
1268
1343
|
maxAttempts: 1,
|
|
1269
1344
|
stepBudget: 16,
|
|
1345
|
+
abortSignal: ctx?.abortSignal,
|
|
1270
1346
|
});
|
|
1271
1347
|
emitStageProgress('integration', 83, result.status === 'repaired'
|
|
1272
1348
|
? `Repaired semantic gate for ${context.unitKey}`
|
|
@@ -1451,6 +1527,37 @@ export class IngestBundleRunner {
|
|
|
1451
1527
|
let curatorReport = null;
|
|
1452
1528
|
let curatorWarnings = [];
|
|
1453
1529
|
let reconcileOutcome;
|
|
1530
|
+
// Reconcile shares the work-unit liveness model: the HUD heartbeat is driven
|
|
1531
|
+
// by real tool calls (a monotonic, observed count), not a re-derived turn
|
|
1532
|
+
// counter. The soft cap only paces the phase progress bar; it is never shown
|
|
1533
|
+
// to the user, so it cannot read as a misleading "X/Y" fraction.
|
|
1534
|
+
const reconcileTranscriptPath = join(transcriptDir, 'reconcile.jsonl');
|
|
1535
|
+
const reconcileProgressSoftCap = 40;
|
|
1536
|
+
const buildReconcileToolSetWithHeartbeat = () => {
|
|
1537
|
+
const record = recordTranscriptEntry(reconcileTranscriptPath);
|
|
1538
|
+
return wrapToolsWithLogger(buildReconcileToolSet({
|
|
1539
|
+
loadSkillTool: rcLoadSkill,
|
|
1540
|
+
stageListTool: rcStageListTool,
|
|
1541
|
+
stageDiffTool: rcStageDiffTool,
|
|
1542
|
+
evictionListTool: rcEvictionListTool,
|
|
1543
|
+
emitConflictResolutionTool: rcEmitConflictResolutionTool,
|
|
1544
|
+
emitEvictionDecisionTool: rcEmitEvictionDecisionTool,
|
|
1545
|
+
emitArtifactResolutionTool: rcEmitArtifactResolutionTool,
|
|
1546
|
+
emitUnmappedFallbackTool: rcEmitUnmappedFallbackTool,
|
|
1547
|
+
readRawSpanTool: rcRawSpanTool,
|
|
1548
|
+
toolsetTools: rcToolset.toRuntimeTools(rcToolContext),
|
|
1549
|
+
}), reconcileTranscriptPath, 'reconcile', {
|
|
1550
|
+
onEntry: (entry) => {
|
|
1551
|
+
const summary = record(entry);
|
|
1552
|
+
if (!stage4) {
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
const label = `Reconciling results · ${summary.toolCallCount} action${summary.toolCallCount === 1 ? '' : 's'}`;
|
|
1556
|
+
emitStageProgress('reconciliation', 85, label, { transient: true });
|
|
1557
|
+
void stage4.updateProgress(Math.min(0.95, summary.toolCallCount / reconcileProgressSoftCap), label);
|
|
1558
|
+
},
|
|
1559
|
+
});
|
|
1560
|
+
};
|
|
1454
1561
|
const reconcileStartedAt = Date.now();
|
|
1455
1562
|
const reconcileMode = contextReport && this.deps.curatorPagination ? 'curator' : 'single';
|
|
1456
1563
|
if (contextReport && this.deps.curatorPagination) {
|
|
@@ -1471,25 +1578,9 @@ export class IngestBundleRunner {
|
|
|
1471
1578
|
canonicalPins: relevantCanonicalPins,
|
|
1472
1579
|
}),
|
|
1473
1580
|
buildUserPrompt: ({ summary, items, runState }) => buildReconcileUserPrompt(stageIndex, eviction, { summary, items }, reconcileNotes, runState),
|
|
1474
|
-
buildToolSet: (_passNumber) =>
|
|
1475
|
-
loadSkillTool: rcLoadSkill,
|
|
1476
|
-
stageListTool: rcStageListTool,
|
|
1477
|
-
stageDiffTool: rcStageDiffTool,
|
|
1478
|
-
evictionListTool: rcEvictionListTool,
|
|
1479
|
-
emitConflictResolutionTool: rcEmitConflictResolutionTool,
|
|
1480
|
-
emitEvictionDecisionTool: rcEmitEvictionDecisionTool,
|
|
1481
|
-
emitArtifactResolutionTool: rcEmitArtifactResolutionTool,
|
|
1482
|
-
emitUnmappedFallbackTool: rcEmitUnmappedFallbackTool,
|
|
1483
|
-
readRawSpanTool: rcRawSpanTool,
|
|
1484
|
-
toolsetTools: rcToolset.toRuntimeTools(rcToolContext),
|
|
1485
|
-
}), join(transcriptDir, 'reconcile.jsonl'), 'reconcile', { onEntry: recordTranscriptEntry(join(transcriptDir, 'reconcile.jsonl')) }),
|
|
1581
|
+
buildToolSet: (_passNumber) => buildReconcileToolSetWithHeartbeat(),
|
|
1486
1582
|
getReconciliationActions: () => reconcileActions,
|
|
1487
|
-
|
|
1488
|
-
? ({ passNumber, stepIndex, stepBudget }) => {
|
|
1489
|
-
emitStageProgress('reconciliation', 85, `Reconciling results: pass ${passNumber} step ${stepIndex}/${stepBudget}`, { transient: true });
|
|
1490
|
-
void stage4.updateProgress(stepIndex / stepBudget, `Reconciling results · pass ${passNumber} step ${stepIndex}`);
|
|
1491
|
-
}
|
|
1492
|
-
: undefined,
|
|
1583
|
+
abortSignal: ctx?.abortSignal,
|
|
1493
1584
|
});
|
|
1494
1585
|
curatorReport = curatorOutcome.report;
|
|
1495
1586
|
curatorWarnings = curatorOutcome.warnings;
|
|
@@ -1512,31 +1603,13 @@ export class IngestBundleRunner {
|
|
|
1512
1603
|
canonicalPins: relevantCanonicalPins,
|
|
1513
1604
|
}),
|
|
1514
1605
|
buildUserPrompt: (idx, ev) => buildReconcileUserPrompt(idx, ev, undefined, reconcileNotes),
|
|
1515
|
-
buildToolSet: () =>
|
|
1516
|
-
loadSkillTool: rcLoadSkill,
|
|
1517
|
-
stageListTool: rcStageListTool,
|
|
1518
|
-
stageDiffTool: rcStageDiffTool,
|
|
1519
|
-
evictionListTool: rcEvictionListTool,
|
|
1520
|
-
emitConflictResolutionTool: rcEmitConflictResolutionTool,
|
|
1521
|
-
emitEvictionDecisionTool: rcEmitEvictionDecisionTool,
|
|
1522
|
-
emitArtifactResolutionTool: rcEmitArtifactResolutionTool,
|
|
1523
|
-
emitUnmappedFallbackTool: rcEmitUnmappedFallbackTool,
|
|
1524
|
-
readRawSpanTool: rcRawSpanTool,
|
|
1525
|
-
toolsetTools: rcToolset.toRuntimeTools(rcToolContext),
|
|
1526
|
-
}), join(transcriptDir, 'reconcile.jsonl'), 'reconcile', { onEntry: recordTranscriptEntry(join(transcriptDir, 'reconcile.jsonl')) }),
|
|
1606
|
+
buildToolSet: () => buildReconcileToolSetWithHeartbeat(),
|
|
1527
1607
|
modelRole: 'reconcile',
|
|
1528
1608
|
stepBudget: 60,
|
|
1529
1609
|
sourceKey: job.sourceKey,
|
|
1530
1610
|
jobId: job.jobId,
|
|
1531
1611
|
force: !!overrideReport,
|
|
1532
|
-
|
|
1533
|
-
? ({ stepIndex, stepBudget }) => {
|
|
1534
|
-
emitStageProgress('reconciliation', 85, `Reconciling results: step ${stepIndex}/${stepBudget}`, {
|
|
1535
|
-
transient: true,
|
|
1536
|
-
});
|
|
1537
|
-
void stage4.updateProgress(stepIndex / stepBudget, `Reconciling results · step ${stepIndex}`);
|
|
1538
|
-
}
|
|
1539
|
-
: undefined,
|
|
1612
|
+
abortSignal: ctx?.abortSignal,
|
|
1540
1613
|
});
|
|
1541
1614
|
}
|
|
1542
1615
|
await runTrace.event('debug', 'reconciliation', 'reconciliation_executed', {
|
|
@@ -1890,6 +1963,7 @@ export class IngestBundleRunner {
|
|
|
1890
1963
|
repairKind: 'final_artifact_gate',
|
|
1891
1964
|
maxAttempts: 1,
|
|
1892
1965
|
stepBudget: 16,
|
|
1966
|
+
abortSignal: ctx?.abortSignal,
|
|
1893
1967
|
});
|
|
1894
1968
|
isolatedDiffSummary.gateRepairAttempts += gateRepair.attempts;
|
|
1895
1969
|
if (gateRepair.status === 'failed') {
|
|
@@ -85,18 +85,88 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
catch (semanticError) {
|
|
88
|
-
|
|
89
|
-
await input.integrationGit.resetHardTo(preApplyHead);
|
|
90
|
-
}
|
|
88
|
+
const reason = errorMessage(semanticError);
|
|
91
89
|
await input.trace.event('error', 'integration', 'patch_semantic_conflict_after_textual_resolution', {
|
|
92
90
|
unitKey: input.unitKey,
|
|
93
91
|
patchPath: input.patchPath,
|
|
94
92
|
touchedPaths: textualResolution.changedPaths,
|
|
95
|
-
reason
|
|
93
|
+
reason,
|
|
96
94
|
});
|
|
95
|
+
// A textual conflict and a semantic-gate failure can co-occur: the resolver
|
|
96
|
+
// reconciles the text but can leave wiki sl_refs pointing at measures the
|
|
97
|
+
// merged source no longer defines. Recover via the same gate repair the
|
|
98
|
+
// clean-apply branch uses, instead of hard-failing the whole job.
|
|
99
|
+
if (input.repairGateFailure) {
|
|
100
|
+
const gateRepair = await input.repairGateFailure({
|
|
101
|
+
unitKey: input.unitKey,
|
|
102
|
+
patchPath: input.patchPath,
|
|
103
|
+
touchedPaths: textualResolution.changedPaths,
|
|
104
|
+
reason,
|
|
105
|
+
});
|
|
106
|
+
if (gateRepair.status !== 'failed') {
|
|
107
|
+
// The resolver wrote its merge to the worktree (unstaged); the repair
|
|
108
|
+
// edited a subset on top. Commit the union so neither is dropped.
|
|
109
|
+
const resolvedAndRepairedPaths = [
|
|
110
|
+
...new Set([...textualResolution.changedPaths, ...gateRepair.changedPaths]),
|
|
111
|
+
].sort();
|
|
112
|
+
try {
|
|
113
|
+
await traceTimed(input.trace, 'integration', 'semantic_gate_after_gate_repair', { unitKey: input.unitKey, touchedPaths: gateRepair.changedPaths }, async () => {
|
|
114
|
+
await input.validateAppliedTree(gateRepair.changedPaths);
|
|
115
|
+
});
|
|
116
|
+
const commit = await input.integrationGit.commitFiles(resolvedAndRepairedPaths, `ingest: resolve WorkUnit ${input.unitKey} conflict`, input.author.name, input.author.email);
|
|
117
|
+
if (commit.created) {
|
|
118
|
+
await input.trace.event('debug', 'integration', 'patch_accepted_after_textual_resolution', {
|
|
119
|
+
unitKey: input.unitKey,
|
|
120
|
+
commitSha: commit.commitHash,
|
|
121
|
+
touchedPaths: resolvedAndRepairedPaths,
|
|
122
|
+
attempts: textualResolution.attempts,
|
|
123
|
+
gateRepairAttempts: gateRepair.attempts,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
status: 'accepted',
|
|
127
|
+
commitSha: commit.commitHash,
|
|
128
|
+
touchedPaths: resolvedAndRepairedPaths,
|
|
129
|
+
textualResolution,
|
|
130
|
+
gateRepair,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (repairValidationError) {
|
|
135
|
+
if (preApplyHead) {
|
|
136
|
+
await input.integrationGit.resetHardTo(preApplyHead);
|
|
137
|
+
}
|
|
138
|
+
await input.trace.event('error', 'integration', 'patch_semantic_conflict_after_textual_resolution', {
|
|
139
|
+
unitKey: input.unitKey,
|
|
140
|
+
patchPath: input.patchPath,
|
|
141
|
+
touchedPaths: gateRepair.changedPaths,
|
|
142
|
+
reason: errorMessage(repairValidationError),
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
status: 'semantic_conflict',
|
|
146
|
+
reason: errorMessage(repairValidationError),
|
|
147
|
+
touchedPaths: gateRepair.changedPaths,
|
|
148
|
+
textualResolution,
|
|
149
|
+
gateRepair,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (preApplyHead) {
|
|
154
|
+
await input.integrationGit.resetHardTo(preApplyHead);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
status: 'semantic_conflict',
|
|
158
|
+
reason: gateRepair.status === 'failed' ? gateRepair.reason : reason,
|
|
159
|
+
touchedPaths: textualResolution.changedPaths,
|
|
160
|
+
textualResolution,
|
|
161
|
+
gateRepair,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (preApplyHead) {
|
|
165
|
+
await input.integrationGit.resetHardTo(preApplyHead);
|
|
166
|
+
}
|
|
97
167
|
return {
|
|
98
168
|
status: 'semantic_conflict',
|
|
99
|
-
reason
|
|
169
|
+
reason,
|
|
100
170
|
touchedPaths: textualResolution.changedPaths,
|
|
101
171
|
textualResolution,
|
|
102
172
|
};
|
|
@@ -19,5 +19,6 @@ export interface ResolveTextualConflictInput {
|
|
|
19
19
|
reason: string;
|
|
20
20
|
maxAttempts?: number;
|
|
21
21
|
stepBudget?: number;
|
|
22
|
+
abortSignal?: AbortSignal;
|
|
22
23
|
}
|
|
23
24
|
export declare function resolveTextualConflict(input: ResolveTextualConflictInput): Promise<TextualConflictResolutionResult>;
|
|
@@ -165,6 +165,7 @@ export async function resolveTextualConflict(input) {
|
|
|
165
165
|
jobId: input.trace.context.jobId,
|
|
166
166
|
unitKey: input.unitKey,
|
|
167
167
|
},
|
|
168
|
+
abortSignal: input.abortSignal,
|
|
168
169
|
}));
|
|
169
170
|
if (result.stopReason === 'error') {
|
|
170
171
|
lastFailure = result.error?.message ?? 'resolver agent loop errored';
|
|
@@ -9,6 +9,7 @@ export interface RunIsolatedWorkUnitInput {
|
|
|
9
9
|
patchDir: string;
|
|
10
10
|
trace: IngestTraceWriter;
|
|
11
11
|
workUnit: WorkUnit;
|
|
12
|
+
abortSignal?: AbortSignal;
|
|
12
13
|
run(child: IngestSessionWorktree): Promise<WorkUnitOutcome>;
|
|
13
14
|
afterSuccess?(child: IngestSessionWorktree): Promise<void>;
|
|
14
15
|
}
|
|
@@ -7,6 +7,7 @@ import { DbtSourceAdapter } from './adapters/dbt/dbt.adapter.js';
|
|
|
7
7
|
import { FakeSourceAdapter } from './adapters/fake/fake.adapter.js';
|
|
8
8
|
import { HistoricSqlSourceAdapter } from './adapters/historic-sql/historic-sql.adapter.js';
|
|
9
9
|
import { PostgresPgssReader } from './adapters/historic-sql/postgres-pgss-reader.js';
|
|
10
|
+
import { resolveQueryHistoryScopeFloor } from './adapters/historic-sql/scope-floor.js';
|
|
10
11
|
import { HISTORIC_SQL_SOURCE_KEY, historicSqlUnifiedPullConfigSchema, } from './adapters/historic-sql/types.js';
|
|
11
12
|
import { createDaemonLiveDatabaseIntrospection, } from './adapters/live-database/daemon-introspection.js';
|
|
12
13
|
import { LiveDatabaseSourceAdapter } from './adapters/live-database/live-database.adapter.js';
|
|
@@ -113,14 +114,30 @@ function queryHistoryRecord(connection) {
|
|
|
113
114
|
const queryHistory = isRecord(context?.queryHistory) ? context.queryHistory : null;
|
|
114
115
|
return queryHistory;
|
|
115
116
|
}
|
|
116
|
-
function queryHistoryPullConfig(connection) {
|
|
117
|
+
async function queryHistoryPullConfig(project, connectionId, connection) {
|
|
117
118
|
const queryHistory = queryHistoryRecord(connection);
|
|
118
119
|
if (queryHistory?.enabled !== true || !isRecord(connection))
|
|
119
120
|
return null;
|
|
120
|
-
const
|
|
121
|
+
const driver = String(connection.driver ?? '').toLowerCase();
|
|
122
|
+
const dialect = historicSqlDialectByDriver.get(driver);
|
|
121
123
|
if (!dialect)
|
|
122
124
|
return null;
|
|
123
|
-
|
|
125
|
+
const scopeFloor = await resolveQueryHistoryScopeFloor({
|
|
126
|
+
projectDir: project.projectDir,
|
|
127
|
+
connectionId,
|
|
128
|
+
driver,
|
|
129
|
+
connection,
|
|
130
|
+
storedQueryHistory: queryHistory,
|
|
131
|
+
});
|
|
132
|
+
const { enabled: _enabled, dialect: _dialect, enabledTables: _enabledTables, enabledSchemas: _enabledSchemas, scopeFloorWarnings: _scopeFloorWarnings, ...stored } = queryHistory;
|
|
133
|
+
return {
|
|
134
|
+
...stored,
|
|
135
|
+
dialect,
|
|
136
|
+
...(scopeFloor.enabledTables.length > 0 ? { enabledTables: scopeFloor.enabledTables } : {}),
|
|
137
|
+
...(scopeFloor.enabledSchemas.length > 0 ? { enabledSchemas: scopeFloor.enabledSchemas } : {}),
|
|
138
|
+
...(scopeFloor.modeledTableCatalog.length > 0 ? { modeledTableCatalog: scopeFloor.modeledTableCatalog } : {}),
|
|
139
|
+
...(scopeFloor.warnings.length > 0 ? { scopeFloorWarnings: scopeFloor.warnings } : {}),
|
|
140
|
+
};
|
|
124
141
|
}
|
|
125
142
|
function stringField(value) {
|
|
126
143
|
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
|
|
@@ -168,7 +185,7 @@ export async function localPullConfigForAdapter(project, adapter, connectionId,
|
|
|
168
185
|
if (options.historicSqlPullConfigOverride) {
|
|
169
186
|
return historicSqlUnifiedPullConfigSchema.parse(options.historicSqlPullConfigOverride);
|
|
170
187
|
}
|
|
171
|
-
const queryHistory = queryHistoryPullConfig(connection);
|
|
188
|
+
const queryHistory = await queryHistoryPullConfig(project, connectionId, connection);
|
|
172
189
|
if (!queryHistory) {
|
|
173
190
|
throw new Error(`Connection "${connectionId}" does not have context.queryHistory.enabled: true`);
|
|
174
191
|
}
|
|
@@ -8,6 +8,7 @@ import { SessionWorktreeService } from '../../context/core/session-worktree.serv
|
|
|
8
8
|
import { createRuntimeToolDescriptorFromAiTool } from '../../context/llm/runtime-tools.js';
|
|
9
9
|
import { createLocalKtxLlmRuntimeFromConfig } from '../../context/llm/local-config.js';
|
|
10
10
|
import { KtxIngestEmbeddingPortAdapter } from '../../context/llm/embedding-port.js';
|
|
11
|
+
import { createRateLimitGovernorConfig, RateLimitGovernor } from '../../context/llm/rate-limit-governor.js';
|
|
11
12
|
import { RuntimeAgentRunner } from '../../context/llm/runtime-port.js';
|
|
12
13
|
import { ktxLocalStateDbPath } from '../../context/project/local-state-db.js';
|
|
13
14
|
import { PromptService } from '../../context/prompts/prompt.service.js';
|
|
@@ -488,17 +489,19 @@ function nextLocalJobId() {
|
|
|
488
489
|
}
|
|
489
490
|
function localIngestLlmProviderGuardMessage(projectDir) {
|
|
490
491
|
return [
|
|
491
|
-
'ktx ingest requires llm.provider.backend: anthropic, vertex, gateway,
|
|
492
|
-
'Configure a local Claude Code session or API-backed LLM, then rerun ingest:',
|
|
492
|
+
'ktx ingest requires llm.provider.backend: anthropic, vertex, gateway, claude-code, or codex, or an injected agentRunner.',
|
|
493
|
+
'Configure a local Claude Code/Codex session or API-backed LLM, then rerun ingest:',
|
|
493
494
|
` ktx setup --project-dir ${projectDir} --llm-backend claude-code --no-input`,
|
|
494
|
-
` ktx setup --project-dir ${projectDir} --llm-backend
|
|
495
|
+
` ktx setup --project-dir ${projectDir} --llm-backend codex --no-input`,
|
|
496
|
+
` ktx setup --project-dir ${projectDir} --llm-backend anthropic --anthropic-api-key-env ANTHROPIC_API_KEY --no-input`,
|
|
495
497
|
].join('\n');
|
|
496
498
|
}
|
|
497
|
-
function resolveAgentRunner(options) {
|
|
499
|
+
function resolveAgentRunner(options, rateLimitGovernor) {
|
|
498
500
|
const llmRuntime = options.llmRuntime ??
|
|
499
501
|
(options.createLlmRuntime ?? createLocalKtxLlmRuntimeFromConfig)(options.project.config.llm, {
|
|
500
502
|
projectDir: options.project.projectDir,
|
|
501
503
|
env: process.env,
|
|
504
|
+
rateLimitGovernor,
|
|
502
505
|
}) ??
|
|
503
506
|
undefined;
|
|
504
507
|
if (options.agentRunner) {
|
|
@@ -535,7 +538,11 @@ export function createLocalBundleIngestRuntime(options) {
|
|
|
535
538
|
const knowledgeIndex = new LocalKnowledgeIndex(options.project, embedding);
|
|
536
539
|
const knowledgeEvents = new NoopKnowledgeEventPort();
|
|
537
540
|
const wikiService = new KnowledgeWikiService(rootFileStore, embedding, knowledgeIndex, options.project.git, logger);
|
|
538
|
-
const
|
|
541
|
+
const rateLimitGovernor = new RateLimitGovernor(createRateLimitGovernorConfig({
|
|
542
|
+
...options.project.config.ingest.rateLimit,
|
|
543
|
+
maxConcurrency: options.project.config.ingest.workUnits.maxConcurrency,
|
|
544
|
+
}));
|
|
545
|
+
const { agentRunner, llmRuntime } = resolveAgentRunner(options, rateLimitGovernor);
|
|
539
546
|
const promptService = new PromptService({ promptsDir, partials: [], logger });
|
|
540
547
|
const storage = new LocalIngestStorage(options.project);
|
|
541
548
|
const registry = registerAdapters(options.adapters);
|
|
@@ -574,6 +581,7 @@ export function createLocalBundleIngestRuntime(options) {
|
|
|
574
581
|
workUnitMaxConcurrency: options.project.config.ingest.workUnits.maxConcurrency,
|
|
575
582
|
workUnitStepBudget: options.project.config.ingest.workUnits.stepBudget,
|
|
576
583
|
workUnitFailureMode: options.project.config.ingest.workUnits.failureMode,
|
|
584
|
+
rateLimitGovernor,
|
|
577
585
|
profileIngest: options.project.config.ingest.profile,
|
|
578
586
|
ingestTraceLevel: ingestTraceLevelFromEnv(),
|
|
579
587
|
},
|
|
@@ -25,6 +25,7 @@ export interface RunLocalIngestOptions {
|
|
|
25
25
|
queryExecutor?: KtxSqlQueryExecutorPort;
|
|
26
26
|
logger?: KtxLogger;
|
|
27
27
|
embeddingProvider?: import('../../llm/types.js').KtxEmbeddingProvider | null;
|
|
28
|
+
abortSignal?: AbortSignal;
|
|
28
29
|
}
|
|
29
30
|
export interface LocalIngestResult {
|
|
30
31
|
result: IngestBundleResult;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { cp, mkdir, rm } from 'node:fs/promises';
|
|
3
3
|
import { isAbsolute, resolve } from 'node:path';
|
|
4
|
+
import { createAbortError, isAbortError } from '../../context/core/abort.js';
|
|
4
5
|
import { ktxLocalStateDbPath } from '../../context/project/local-state-db.js';
|
|
5
6
|
import { planMetabaseFanoutChildren } from './adapters/metabase/fanout-planner.js';
|
|
6
7
|
import { KtxYamlMetabaseSourceStateReader, LocalMetabaseDiscoveryCache } from './adapters/metabase/local-source-state-store.js';
|
|
@@ -36,10 +37,11 @@ function findAdapter(adapters, source) {
|
|
|
36
37
|
}
|
|
37
38
|
return adapter;
|
|
38
39
|
}
|
|
39
|
-
function localJobContext(jobId, memoryFlow) {
|
|
40
|
+
function localJobContext(jobId, memoryFlow, abortSignal) {
|
|
40
41
|
return {
|
|
41
42
|
jobId,
|
|
42
43
|
...(memoryFlow ? { memoryFlow } : {}),
|
|
44
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
43
45
|
startPhase() {
|
|
44
46
|
return new LocalIngestPhase();
|
|
45
47
|
},
|
|
@@ -62,7 +64,7 @@ async function runScheduledPullJob(options) {
|
|
|
62
64
|
sourceKey: options.adapter.source,
|
|
63
65
|
trigger: options.trigger ?? 'manual_resync',
|
|
64
66
|
bundleRef: { kind: 'scheduled_pull', config: options.pullConfig },
|
|
65
|
-
}, localJobContext(jobId, options.memoryFlow));
|
|
67
|
+
}, localJobContext(jobId, options.memoryFlow, options.abortSignal));
|
|
66
68
|
const report = await runtime.store.findByJobId(jobId);
|
|
67
69
|
if (!report) {
|
|
68
70
|
throw new Error(`Local ingest report for job "${jobId}" was not created`);
|
|
@@ -102,6 +104,7 @@ export async function runLocalIngest(options) {
|
|
|
102
104
|
queryExecutor: options.queryExecutor,
|
|
103
105
|
logger: options.logger,
|
|
104
106
|
embeddingProvider: options.embeddingProvider,
|
|
107
|
+
abortSignal: options.abortSignal,
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
const result = await runtime.runner.run({
|
|
@@ -110,7 +113,7 @@ export async function runLocalIngest(options) {
|
|
|
110
113
|
sourceKey: adapter.source,
|
|
111
114
|
trigger: options.trigger ?? (options.sourceDir ? 'upload' : 'manual_resync'),
|
|
112
115
|
bundleRef,
|
|
113
|
-
}, localJobContext(jobId, options.memoryFlow));
|
|
116
|
+
}, localJobContext(jobId, options.memoryFlow, options.abortSignal));
|
|
114
117
|
const report = await runtime.store.findByJobId(jobId);
|
|
115
118
|
if (!report) {
|
|
116
119
|
throw new Error(`Local ingest report for job "${jobId}" was not created`);
|
|
@@ -226,6 +229,9 @@ export async function runLocalMetabaseIngest(options) {
|
|
|
226
229
|
});
|
|
227
230
|
const children = [];
|
|
228
231
|
for (const childPlan of childPlans) {
|
|
232
|
+
if (options.abortSignal?.aborted) {
|
|
233
|
+
throw createAbortError();
|
|
234
|
+
}
|
|
229
235
|
const targetConnectionId = safeSegment('target connection id', childPlan.targetConnectionId);
|
|
230
236
|
if (!options.project.config.connections[targetConnectionId]) {
|
|
231
237
|
throw new Error(`Target connection "${targetConnectionId}" is not configured in ktx.yaml`);
|
|
@@ -255,9 +261,13 @@ export async function runLocalMetabaseIngest(options) {
|
|
|
255
261
|
queryExecutor: options.queryExecutor,
|
|
256
262
|
logger: options.logger,
|
|
257
263
|
embeddingProvider: options.embeddingProvider,
|
|
264
|
+
abortSignal: options.abortSignal,
|
|
258
265
|
});
|
|
259
266
|
}
|
|
260
267
|
catch (error) {
|
|
268
|
+
if (isAbortError(error)) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
261
271
|
child = await recordLocalMetabaseChildFailure({
|
|
262
272
|
project: options.project,
|
|
263
273
|
jobId: childJobId,
|
|
@@ -127,7 +127,7 @@ export function ingestReportToMemoryFlowReplay(report, options = {}) {
|
|
|
127
127
|
}
|
|
128
128
|
const actions = allReportActions(report);
|
|
129
129
|
const workUnitEvents = report.body.workUnits.flatMap((workUnit) => [
|
|
130
|
-
{ type: 'work_unit_started', unitKey: workUnit.unitKey, skills: []
|
|
130
|
+
{ type: 'work_unit_started', unitKey: workUnit.unitKey, skills: [] },
|
|
131
131
|
...workUnit.actions.map((action) => ({
|
|
132
132
|
type: 'candidate_action',
|
|
133
133
|
unitKey: workUnit.unitKey,
|
|
@@ -64,17 +64,22 @@ const memoryFlowEventSchema = z.discriminatedUnion('type', [
|
|
|
64
64
|
message: z.string().min(1),
|
|
65
65
|
transient: z.boolean().optional(),
|
|
66
66
|
}),
|
|
67
|
+
eventSchema({
|
|
68
|
+
type: z.literal('rate_limit_wait'),
|
|
69
|
+
provider: z.string(),
|
|
70
|
+
rateLimitType: z.string().optional(),
|
|
71
|
+
resumeAtMs: z.number().int().nonnegative(),
|
|
72
|
+
remainingMs: z.number().int().nonnegative(),
|
|
73
|
+
}),
|
|
67
74
|
eventSchema({
|
|
68
75
|
type: z.literal('work_unit_started'),
|
|
69
76
|
unitKey: z.string().min(1),
|
|
70
77
|
skills: z.array(z.string().min(1)),
|
|
71
|
-
stepBudget: z.number().int().min(0),
|
|
72
78
|
}),
|
|
73
79
|
eventSchema({
|
|
74
80
|
type: z.literal('work_unit_step'),
|
|
75
81
|
unitKey: z.string().min(1),
|
|
76
|
-
|
|
77
|
-
stepBudget: z.number().int().min(0),
|
|
82
|
+
toolCalls: z.number().int().min(0),
|
|
78
83
|
}),
|
|
79
84
|
eventSchema({
|
|
80
85
|
type: z.literal('candidate_action'),
|