ultimate-pi 0.19.1 → 0.22.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/.agents/skills/harness-decisions/SKILL.md +68 -2
- package/.agents/skills/harness-git-commit/SKILL.md +72 -0
- package/.agents/skills/harness-governor/SKILL.md +2 -2
- package/.agents/skills/harness-ls-lint-setup/SKILL.md +59 -0
- package/.agents/skills/harness-plan/SKILL.md +13 -11
- package/.agents/skills/harness-review/SKILL.md +1 -1
- package/.agents/skills/harness-sentrux-repair/SKILL.md +48 -0
- package/.agents/skills/sentrux/SKILL.md +4 -2
- package/.agents/skills/wiki-save/SKILL.md +1 -1
- package/.pi/PACKAGING.md +6 -0
- package/.pi/SYSTEM.md +21 -3
- package/.pi/agents/harness/ls-lint-steward.md +49 -0
- package/.pi/agents/harness/planning/decompose.md +4 -4
- package/.pi/agents/harness/reviewing/evaluator.md +1 -1
- package/.pi/agents/harness/running/executor.md +43 -2
- package/.pi/agents/harness/sentrux-repair-advisor.md +50 -0
- package/.pi/agents/pi-pi/prompt-expert.md +17 -2
- package/.pi/auto-commit.json +9 -2
- package/.pi/extensions/debate-orchestrator.ts +3 -0
- package/.pi/extensions/harness-anchored-edit.ts +139 -0
- package/.pi/extensions/harness-ask-user.ts +13 -34
- package/.pi/extensions/harness-debate-tools.ts +43 -4
- package/.pi/extensions/harness-live-widget.ts +28 -19
- package/.pi/extensions/harness-run-context.ts +278 -115
- package/.pi/extensions/harness-web-tools.ts +598 -471
- package/.pi/extensions/ls-lint-rules-sync.ts +103 -0
- package/.pi/extensions/observation-bus.ts +4 -0
- package/.pi/extensions/policy-gate.ts +270 -229
- package/.pi/extensions/sentrux-rules-sync.ts +2 -0
- package/.pi/extensions/soundboard.ts +48 -48
- package/.pi/harness/README.md +4 -0
- package/.pi/harness/agents.manifest.json +15 -7
- package/.pi/harness/agents.policy.yaml +47 -81
- package/.pi/harness/docs/adrs/0051-hash-anchored-executor-edits.md +41 -0
- package/.pi/harness/docs/adrs/0052-ls-lint-naming-lifecycle.md +45 -0
- package/.pi/harness/docs/adrs/0052-sentrux-structured-repair.md +38 -0
- package/.pi/harness/docs/adrs/0053-plan-task-clarification-gate.md +39 -0
- package/.pi/harness/docs/adrs/0054-harness-native-ask-user.md +40 -0
- package/.pi/harness/docs/adrs/0055-auto-commit-coauthor-lifecycle.md +40 -0
- package/.pi/harness/docs/adrs/README.md +7 -0
- package/.pi/harness/docs/practice-map.md +21 -5
- package/.pi/harness/evals/smoke/ls-lint-stub.json +10 -0
- package/.pi/harness/evolution/self-healing-rules.json +16 -0
- package/.pi/harness/ls-lint/naming.manifest.json +128 -0
- package/.pi/harness/sentrux/architecture.manifest.json +1 -1
- package/.pi/harness/specs/auto-commit.schema.json +63 -0
- package/.pi/harness/specs/ls-lint-manifest-proposal.schema.json +80 -0
- package/.pi/harness/specs/ls-lint-signal.schema.json +47 -0
- package/.pi/harness/specs/naming-manifest.schema.json +54 -0
- package/.pi/harness/specs/plan-task-clarification.schema.json +88 -0
- package/.pi/harness/specs/sentrux-diagnostics.schema.json +173 -0
- package/.pi/harness/specs/sentrux-repair-plan.schema.json +133 -0
- package/.pi/harness/specs/sentrux-report.schema.json +119 -0
- package/.pi/harness/specs/sentrux-signal.schema.json +34 -1
- package/.pi/lib/agents-policy.d.mts +26 -47
- package/.pi/lib/agents-policy.mjs +84 -29
- package/.pi/lib/agents-policy.ts +1 -0
- package/.pi/lib/agt/build-evaluation-context.ts +136 -64
- package/.pi/lib/ask-user/constants.mjs +3 -0
- package/.pi/lib/ask-user/constants.ts +4 -0
- package/.pi/lib/ask-user/contracts/glimpse-parse.ts +56 -0
- package/.pi/lib/ask-user/contracts/glimpse-payload-build.ts +58 -0
- package/.pi/lib/ask-user/contracts/glimpse-payload.ts +38 -0
- package/.pi/lib/ask-user/core/questionnaire.ts +74 -0
- package/.pi/lib/ask-user/dialog.ts +2 -314
- package/.pi/lib/ask-user/fallback.ts +2 -78
- package/.pi/lib/ask-user/format.ts +85 -0
- package/.pi/lib/ask-user/glimpseui.d.ts +10 -0
- package/.pi/lib/ask-user/index.ts +114 -0
- package/.pi/lib/ask-user/merge-task-clarification.ts +98 -0
- package/.pi/lib/ask-user/policy.mjs +43 -0
- package/.pi/lib/ask-user/policy.ts +104 -0
- package/.pi/lib/ask-user/presenters/glimpse.ts +130 -0
- package/.pi/lib/ask-user/presenters/headless.ts +131 -0
- package/.pi/lib/ask-user/presenters/select.ts +60 -0
- package/.pi/lib/ask-user/presenters/tui.ts +373 -0
- package/.pi/lib/ask-user/presenters/types.ts +13 -0
- package/.pi/lib/ask-user/render.ts +40 -9
- package/.pi/lib/ask-user/schema.ts +66 -13
- package/.pi/lib/ask-user/types.ts +60 -3
- package/.pi/lib/ask-user/validate-core.mjs +193 -7
- package/.pi/lib/ask-user/validate.ts +53 -34
- package/.pi/lib/harness-anchored-edit/.hash_anchors +1721 -0
- package/.pi/lib/harness-anchored-edit/anchor-state.ts +320 -0
- package/.pi/lib/harness-anchored-edit/apply-anchored-edits.ts +161 -0
- package/.pi/lib/harness-anchored-edit/edit-executor.ts +146 -0
- package/.pi/lib/harness-anchored-edit/index.ts +9 -0
- package/.pi/lib/harness-anchored-edit/line-protocol.ts +38 -0
- package/.pi/lib/harness-anchored-edit/package.json +3 -0
- package/.pi/lib/harness-anchored-edit/settings.ts +1 -0
- package/.pi/lib/harness-anchored-edit/task-id.ts +8 -0
- package/.pi/lib/harness-anchored-edit/types.ts +19 -0
- package/.pi/lib/harness-artifact-gate.ts +75 -21
- package/.pi/lib/harness-auto-commit-config.mjs +321 -0
- package/.pi/lib/harness-lens/clients/anchored-edit-autopatch.ts +158 -0
- package/.pi/lib/harness-lens/clients/lsp/client.ts +62 -39
- package/.pi/lib/harness-lens/clients/tool-policy.ts +73 -181
- package/.pi/lib/harness-lens/index.ts +246 -96
- package/.pi/lib/harness-lens/tools/lsp-navigation.ts +10 -8
- package/.pi/lib/harness-repair-brief.ts +84 -25
- package/.pi/lib/harness-run-context.ts +42 -52
- package/.pi/lib/harness-sentrux-parse.mjs +272 -0
- package/.pi/lib/harness-sentrux-root.mjs +78 -0
- package/.pi/lib/harness-slash-completions.ts +116 -0
- package/.pi/lib/harness-spawn-topology.ts +121 -87
- package/.pi/lib/harness-subagent-submit-registry.ts +10 -0
- package/.pi/lib/harness-subagents-bridge.ts +11 -6
- package/.pi/lib/harness-ui-state.ts +95 -48
- package/.pi/lib/plan-approval/dialog.ts +5 -0
- package/.pi/lib/plan-approval/validate.ts +1 -1
- package/.pi/lib/plan-approval-readiness.ts +32 -0
- package/.pi/lib/plan-debate-gate.ts +154 -114
- package/.pi/lib/plan-task-clarification.ts +158 -0
- package/.pi/prompts/harness-auto.md +2 -2
- package/.pi/prompts/harness-ls-lint-steward.md +43 -0
- package/.pi/prompts/harness-plan.md +58 -8
- package/.pi/prompts/harness-review.md +40 -6
- package/.pi/prompts/harness-run.md +33 -11
- package/.pi/prompts/harness-setup.md +72 -3
- package/.pi/prompts/harness-steer.md +3 -2
- package/.pi/prompts/wiki-save.md +5 -4
- package/.pi/scripts/README.md +8 -0
- package/.pi/scripts/generate-agents-policy-yaml.mjs +14 -2
- package/.pi/scripts/harness-anchored-edit-smoke.mjs +45 -0
- package/.pi/scripts/harness-auto-commit-bootstrap.mjs +96 -0
- package/.pi/scripts/harness-cli-verify.sh +47 -0
- package/.pi/scripts/harness-git-churn.mjs +77 -0
- package/.pi/scripts/harness-git-commit.mjs +173 -0
- package/.pi/scripts/harness-ls-lint-bootstrap.mjs +142 -0
- package/.pi/scripts/harness-ls-lint-cli.mjs +184 -0
- package/.pi/scripts/harness-seed-project-contracts.mjs +47 -0
- package/.pi/scripts/harness-sentrux-diagnostics.mjs +230 -0
- package/.pi/scripts/harness-sentrux-report.mjs +256 -0
- package/.pi/scripts/harness-verify.mjs +347 -117
- package/.pi/scripts/ls-lint-rules-sync.mjs +265 -0
- package/.pi/scripts/run-tests.mjs +65 -0
- package/.pi/settings.example.json +1 -0
- package/.sentrux/rules.toml +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +31 -0
- package/README.md +13 -4
- package/THIRD_PARTY_NOTICES.md +7 -0
- package/package.json +8 -3
- package/vendor/pi-subagents/src/agents.ts +5 -0
- package/vendor/pi-subagents/src/subagents.ts +22 -3
- package/vendor/pi-vcc/src/hooks/before-compact.ts +86 -60
- package/.pi/scripts/release.sh +0 -338
|
@@ -7,6 +7,11 @@ import * as nodeFs from "node:fs";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { anchoredEditTaskId } from "../harness-anchored-edit/task-id.js";
|
|
11
|
+
import {
|
|
12
|
+
applyAnchoredEditAutopatch,
|
|
13
|
+
isAnchoredEditToolInput,
|
|
14
|
+
} from "./clients/anchored-edit-autopatch.js";
|
|
10
15
|
import {
|
|
11
16
|
tryCorrectIndentationMismatch,
|
|
12
17
|
tryCorrectIndentationMismatchFromContent,
|
|
@@ -331,57 +336,101 @@ function applyEditAutopatch(
|
|
|
331
336
|
return undefined;
|
|
332
337
|
}
|
|
333
338
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
getFlag?: (name: string) => boolean | undefined;
|
|
341
|
-
};
|
|
342
|
-
const piFlags = pi as PiWithFlags;
|
|
343
|
-
const readCliFlag = (name: string): boolean | undefined => {
|
|
344
|
-
if (typeof piFlags.getFlag === "function") return piFlags.getFlag(name);
|
|
345
|
-
return process.argv.includes(`--${name}`) ? true : undefined;
|
|
346
|
-
};
|
|
347
|
-
const getFlag = (name: string) =>
|
|
348
|
-
getLensFlag(name, readCliFlag, globalConfig);
|
|
349
|
-
|
|
339
|
+
function registerLensRuntimePart1(
|
|
340
|
+
pi: ExtensionAPI,
|
|
341
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
342
|
+
runtime: RuntimeCoordinator,
|
|
343
|
+
lensEnabledRef: { current: boolean },
|
|
344
|
+
) {
|
|
350
345
|
pi.registerFlag("no-lens", {
|
|
351
346
|
description: "Disable harness-lens for this session.",
|
|
352
347
|
type: "boolean",
|
|
353
348
|
default: false,
|
|
354
349
|
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function registerLensRuntimePart2(
|
|
353
|
+
pi: ExtensionAPI,
|
|
354
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
355
|
+
runtime: RuntimeCoordinator,
|
|
356
|
+
lensEnabledRef: { current: boolean },
|
|
357
|
+
) {
|
|
355
358
|
pi.registerFlag("no-lsp", {
|
|
356
359
|
description: "Disable LSP auto-touch and lsp_* tools backing servers.",
|
|
357
360
|
type: "boolean",
|
|
358
361
|
default: false,
|
|
359
362
|
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function registerLensRuntimePart3(
|
|
366
|
+
pi: ExtensionAPI,
|
|
367
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
368
|
+
runtime: RuntimeCoordinator,
|
|
369
|
+
lensEnabledRef: { current: boolean },
|
|
370
|
+
) {
|
|
360
371
|
pi.registerFlag("no-autoformat", {
|
|
361
372
|
description: "Disable auto-format on write/edit.",
|
|
362
373
|
type: "boolean",
|
|
363
374
|
default: false,
|
|
364
375
|
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function registerLensRuntimePart4(
|
|
379
|
+
pi: ExtensionAPI,
|
|
380
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
381
|
+
runtime: RuntimeCoordinator,
|
|
382
|
+
lensEnabledRef: { current: boolean },
|
|
383
|
+
) {
|
|
365
384
|
pi.registerFlag("immediate-format", {
|
|
366
385
|
description: "Format during tool_result instead of deferring to agent_end.",
|
|
367
386
|
type: "boolean",
|
|
368
387
|
default: false,
|
|
369
388
|
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function registerLensRuntimePart5(
|
|
392
|
+
pi: ExtensionAPI,
|
|
393
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
394
|
+
runtime: RuntimeCoordinator,
|
|
395
|
+
lensEnabledRef: { current: boolean },
|
|
396
|
+
) {
|
|
370
397
|
pi.registerFlag("lens-guard", {
|
|
371
398
|
description: "Block git commit/push while unresolved lens blockers exist.",
|
|
372
399
|
type: "boolean",
|
|
373
400
|
default: false,
|
|
374
401
|
});
|
|
402
|
+
}
|
|
375
403
|
|
|
404
|
+
function registerLensRuntimePart6(
|
|
405
|
+
pi: ExtensionAPI,
|
|
406
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
407
|
+
runtime: RuntimeCoordinator,
|
|
408
|
+
lensEnabledRef: { current: boolean },
|
|
409
|
+
) {
|
|
376
410
|
pi.registerTool(createLspDiagnosticsTool());
|
|
377
|
-
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function registerLensRuntimePart7(
|
|
414
|
+
pi: ExtensionAPI,
|
|
415
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
416
|
+
runtime: RuntimeCoordinator,
|
|
417
|
+
lensEnabledRef: { current: boolean },
|
|
418
|
+
) {
|
|
419
|
+
pi.registerTool(createLspNavigationTool(getFlag));
|
|
420
|
+
}
|
|
378
421
|
|
|
422
|
+
function registerLensRuntimePart8(
|
|
423
|
+
pi: ExtensionAPI,
|
|
424
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
425
|
+
runtime: RuntimeCoordinator,
|
|
426
|
+
lensEnabledRef: { current: boolean },
|
|
427
|
+
) {
|
|
379
428
|
pi.on("session_start", async (_event, ctx) => {
|
|
380
429
|
if (getFlag("no-lens")) {
|
|
381
|
-
|
|
430
|
+
lensEnabledRef.current = false;
|
|
382
431
|
return;
|
|
383
432
|
}
|
|
384
|
-
|
|
433
|
+
lensEnabledRef.current = true;
|
|
385
434
|
runtime.resetForSession();
|
|
386
435
|
clearWidgetState();
|
|
387
436
|
resetFormatService();
|
|
@@ -411,15 +460,126 @@ export default function harnessLensExtension(pi: ExtensionAPI): void {
|
|
|
411
460
|
});
|
|
412
461
|
}
|
|
413
462
|
});
|
|
463
|
+
}
|
|
414
464
|
|
|
465
|
+
function registerLensRuntimePart9(
|
|
466
|
+
pi: ExtensionAPI,
|
|
467
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
468
|
+
runtime: RuntimeCoordinator,
|
|
469
|
+
lensEnabledRef: { current: boolean },
|
|
470
|
+
) {
|
|
415
471
|
pi.on("turn_start", () => {
|
|
416
|
-
if (!
|
|
472
|
+
if (!lensEnabledRef.current) return;
|
|
417
473
|
runtime.beginTurn();
|
|
418
474
|
clearLastAnalyzedStateCache();
|
|
419
475
|
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function ensureToolCallLspConfig(args: {
|
|
479
|
+
getFlag: (name: string) => boolean | string | undefined;
|
|
480
|
+
filePath: string | undefined;
|
|
481
|
+
ctx: any;
|
|
482
|
+
runtime: RuntimeCoordinator;
|
|
483
|
+
}): Promise<void> {
|
|
484
|
+
if (args.getFlag("no-lsp")) return;
|
|
485
|
+
try {
|
|
486
|
+
await ensureLSPConfigInitialized(
|
|
487
|
+
args.filePath
|
|
488
|
+
? path.dirname(args.filePath)
|
|
489
|
+
: (args.ctx.cwd ?? args.runtime.projectRoot),
|
|
490
|
+
);
|
|
491
|
+
} catch (err) {
|
|
492
|
+
dbg(`tool_call lsp config init failed: ${err}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function maybeAutoTouchLspOnToolCall(args: {
|
|
497
|
+
getFlag: (name: string) => boolean | string | undefined;
|
|
498
|
+
toolName: string;
|
|
499
|
+
filePath: string;
|
|
500
|
+
runtime: RuntimeCoordinator;
|
|
501
|
+
}): void {
|
|
502
|
+
if (
|
|
503
|
+
args.getFlag("no-lsp") ||
|
|
504
|
+
!isLspCapableFile(args.filePath) ||
|
|
505
|
+
shouldSkipLspAutoTouch(args.filePath, args.runtime.projectRoot)
|
|
506
|
+
) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const shouldWarmRead =
|
|
510
|
+
args.toolName === "read" && args.runtime.shouldWarmLspOnRead(args.filePath);
|
|
511
|
+
const shouldTouch =
|
|
512
|
+
args.toolName === "write" ||
|
|
513
|
+
args.toolName === "edit" ||
|
|
514
|
+
args.toolName === "lsp_navigation" ||
|
|
515
|
+
shouldWarmRead;
|
|
516
|
+
if (!shouldTouch) return;
|
|
517
|
+
try {
|
|
518
|
+
const content = nodeFs.readFileSync(args.filePath, "utf-8");
|
|
519
|
+
if (args.toolName === "read")
|
|
520
|
+
args.runtime.markLspReadWarmStarted(args.filePath);
|
|
521
|
+
void getLSPService()
|
|
522
|
+
.touchFile(args.filePath, content, {
|
|
523
|
+
diagnostics: "none",
|
|
524
|
+
source: `tool_call:${args.toolName}`,
|
|
525
|
+
})
|
|
526
|
+
.then((result) => {
|
|
527
|
+
if (args.toolName !== "read") return;
|
|
528
|
+
if (result === undefined)
|
|
529
|
+
args.runtime.clearLspReadWarmState(args.filePath);
|
|
530
|
+
else args.runtime.markLspReadWarmCompleted(args.filePath);
|
|
531
|
+
})
|
|
532
|
+
.catch((err) => {
|
|
533
|
+
if (args.toolName === "read") {
|
|
534
|
+
args.runtime.clearLspReadWarmState(args.filePath);
|
|
535
|
+
}
|
|
536
|
+
dbg(`lsp auto-touch failed: ${err}`);
|
|
537
|
+
});
|
|
538
|
+
} catch {
|
|
539
|
+
if (args.toolName === "read")
|
|
540
|
+
args.runtime.clearLspReadWarmState(args.filePath);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
420
543
|
|
|
544
|
+
function applyEditAutopatchForToolCall(
|
|
545
|
+
filePath: string,
|
|
546
|
+
event: unknown,
|
|
547
|
+
ctx: unknown,
|
|
548
|
+
) {
|
|
549
|
+
if (
|
|
550
|
+
!isToolCallEventType(
|
|
551
|
+
"edit",
|
|
552
|
+
event as Parameters<typeof isToolCallEventType>[1],
|
|
553
|
+
)
|
|
554
|
+
) {
|
|
555
|
+
return undefined;
|
|
556
|
+
}
|
|
557
|
+
const editInput = (event as { input?: unknown }).input;
|
|
558
|
+
if (isAnchoredEditToolInput(editInput)) {
|
|
559
|
+
return applyAnchoredEditAutopatch(
|
|
560
|
+
filePath,
|
|
561
|
+
editInput,
|
|
562
|
+
anchoredEditTaskId({
|
|
563
|
+
sessionId: (ctx as { sessionId?: string }).sessionId,
|
|
564
|
+
}),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
const legacyInput = editInput as {
|
|
568
|
+
oldText?: string;
|
|
569
|
+
newText?: string;
|
|
570
|
+
edits?: Array<{ oldText?: string; newText?: string }>;
|
|
571
|
+
};
|
|
572
|
+
return applyEditAutopatch(filePath, legacyInput);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function registerLensRuntimePart10(
|
|
576
|
+
pi: ExtensionAPI,
|
|
577
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
578
|
+
runtime: RuntimeCoordinator,
|
|
579
|
+
lensEnabledRef: { current: boolean },
|
|
580
|
+
) {
|
|
421
581
|
pi.on("tool_call", async (event, ctx) => {
|
|
422
|
-
if (!
|
|
582
|
+
if (!lensEnabledRef.current) return;
|
|
423
583
|
|
|
424
584
|
const toolName = (event as { toolName?: string }).toolName ?? "";
|
|
425
585
|
if (
|
|
@@ -437,88 +597,33 @@ export default function harnessLensExtension(pi: ExtensionAPI): void {
|
|
|
437
597
|
runtime.projectRoot,
|
|
438
598
|
);
|
|
439
599
|
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
await ensureLSPConfigInitialized(
|
|
443
|
-
filePath ? path.dirname(filePath) : (ctx.cwd ?? runtime.projectRoot),
|
|
444
|
-
);
|
|
445
|
-
} catch (err) {
|
|
446
|
-
dbg(`tool_call lsp config init failed: ${err}`);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
600
|
+
await ensureToolCallLspConfig({ getFlag, filePath, ctx, runtime });
|
|
450
601
|
if (!filePath || !nodeFs.existsSync(filePath)) return;
|
|
451
602
|
if (isPathIgnoredByProject(filePath, runtime.projectRoot, false)) return;
|
|
452
603
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
!shouldSkipLspAutoTouch(filePath, runtime.projectRoot)
|
|
457
|
-
) {
|
|
458
|
-
const shouldWarmRead =
|
|
459
|
-
toolName === "read" && runtime.shouldWarmLspOnRead(filePath);
|
|
460
|
-
const shouldTouch =
|
|
461
|
-
toolName === "write" ||
|
|
462
|
-
toolName === "edit" ||
|
|
463
|
-
toolName === "lsp_navigation" ||
|
|
464
|
-
shouldWarmRead;
|
|
465
|
-
if (shouldTouch) {
|
|
466
|
-
try {
|
|
467
|
-
const content = nodeFs.readFileSync(filePath, "utf-8");
|
|
468
|
-
if (toolName === "read") runtime.markLspReadWarmStarted(filePath);
|
|
469
|
-
void getLSPService()
|
|
470
|
-
.touchFile(filePath, content, {
|
|
471
|
-
diagnostics: "none",
|
|
472
|
-
source: `tool_call:${toolName}`,
|
|
473
|
-
})
|
|
474
|
-
.then((result) => {
|
|
475
|
-
if (toolName === "read") {
|
|
476
|
-
if (result === undefined) {
|
|
477
|
-
runtime.clearLspReadWarmState(filePath);
|
|
478
|
-
} else {
|
|
479
|
-
runtime.markLspReadWarmCompleted(filePath);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
})
|
|
483
|
-
.catch((err) => {
|
|
484
|
-
if (toolName === "read") runtime.clearLspReadWarmState(filePath);
|
|
485
|
-
dbg(`lsp auto-touch failed: ${err}`);
|
|
486
|
-
});
|
|
487
|
-
} catch {
|
|
488
|
-
if (toolName === "read") runtime.clearLspReadWarmState(filePath);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
isToolCallEventType(
|
|
495
|
-
"edit",
|
|
496
|
-
event as Parameters<typeof isToolCallEventType>[1],
|
|
497
|
-
)
|
|
498
|
-
) {
|
|
499
|
-
const editInput = (event as { input?: unknown }).input as {
|
|
500
|
-
oldText?: string;
|
|
501
|
-
newText?: string;
|
|
502
|
-
edits?: Array<{ oldText?: string; newText?: string }>;
|
|
503
|
-
};
|
|
504
|
-
const block = applyEditAutopatch(filePath, editInput);
|
|
505
|
-
if (block) return block;
|
|
506
|
-
}
|
|
604
|
+
maybeAutoTouchLspOnToolCall({ getFlag, toolName, filePath, runtime });
|
|
605
|
+
const block = applyEditAutopatchForToolCall(filePath, event, ctx);
|
|
606
|
+
if (block) return block;
|
|
507
607
|
});
|
|
608
|
+
}
|
|
508
609
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
});
|
|
518
|
-
});
|
|
610
|
+
function registerLensRuntimePart11(
|
|
611
|
+
pi: ExtensionAPI,
|
|
612
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
613
|
+
runtime: RuntimeCoordinator,
|
|
614
|
+
lensEnabledRef: { current: boolean },
|
|
615
|
+
) {
|
|
616
|
+
pi as any;
|
|
617
|
+
}
|
|
519
618
|
|
|
619
|
+
function registerLensRuntimePart12(
|
|
620
|
+
pi: ExtensionAPI,
|
|
621
|
+
getFlag: (name: string) => boolean | string | undefined,
|
|
622
|
+
runtime: RuntimeCoordinator,
|
|
623
|
+
lensEnabledRef: { current: boolean },
|
|
624
|
+
) {
|
|
520
625
|
pi.on("agent_end", async (_event, ctx) => {
|
|
521
|
-
if (!
|
|
626
|
+
if (!lensEnabledRef.current) return;
|
|
522
627
|
await handleAgentEnd({
|
|
523
628
|
ctxCwd: ctx.cwd,
|
|
524
629
|
getFlag,
|
|
@@ -530,3 +635,48 @@ export default function harnessLensExtension(pi: ExtensionAPI): void {
|
|
|
530
635
|
});
|
|
531
636
|
});
|
|
532
637
|
}
|
|
638
|
+
|
|
639
|
+
function registerHarnessLensRuntime(
|
|
640
|
+
pi: ExtensionAPI,
|
|
641
|
+
args: {
|
|
642
|
+
getFlag: (name: string) => boolean | string | undefined;
|
|
643
|
+
runtime: RuntimeCoordinator;
|
|
644
|
+
lensEnabledRef: { current: boolean };
|
|
645
|
+
},
|
|
646
|
+
) {
|
|
647
|
+
const { getFlag, runtime, lensEnabledRef } = args;
|
|
648
|
+
registerLensRuntimePart1(pi, getFlag, runtime, lensEnabledRef);
|
|
649
|
+
registerLensRuntimePart2(pi, getFlag, runtime, lensEnabledRef);
|
|
650
|
+
registerLensRuntimePart3(pi, getFlag, runtime, lensEnabledRef);
|
|
651
|
+
registerLensRuntimePart4(pi, getFlag, runtime, lensEnabledRef);
|
|
652
|
+
registerLensRuntimePart5(pi, getFlag, runtime, lensEnabledRef);
|
|
653
|
+
registerLensRuntimePart6(pi, getFlag, runtime, lensEnabledRef);
|
|
654
|
+
registerLensRuntimePart7(pi, getFlag, runtime, lensEnabledRef);
|
|
655
|
+
registerLensRuntimePart8(pi, getFlag, runtime, lensEnabledRef);
|
|
656
|
+
registerLensRuntimePart9(pi, getFlag, runtime, lensEnabledRef);
|
|
657
|
+
registerLensRuntimePart10(pi, getFlag, runtime, lensEnabledRef);
|
|
658
|
+
registerLensRuntimePart11(pi, getFlag, runtime, lensEnabledRef);
|
|
659
|
+
registerLensRuntimePart12(pi, getFlag, runtime, lensEnabledRef);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export default function harnessLensExtension(pi: ExtensionAPI): void {
|
|
663
|
+
initLensEvents(pi);
|
|
664
|
+
const globalConfig = loadPiLensGlobalConfig();
|
|
665
|
+
const lensEnabledRef = { current: !globalConfig.noLens };
|
|
666
|
+
|
|
667
|
+
type PiWithFlags = ExtensionAPI & {
|
|
668
|
+
getFlag?: (name: string) => boolean | string | undefined;
|
|
669
|
+
};
|
|
670
|
+
const piFlags = pi as PiWithFlags;
|
|
671
|
+
const readCliFlag = (name: string): boolean | undefined => {
|
|
672
|
+
if (typeof piFlags.getFlag === "function") {
|
|
673
|
+
const flag = piFlags.getFlag(name);
|
|
674
|
+
return typeof flag === "boolean" ? flag : undefined;
|
|
675
|
+
}
|
|
676
|
+
return process.argv.includes(`--${name}`) ? true : undefined;
|
|
677
|
+
};
|
|
678
|
+
const getFlag = (name: string) =>
|
|
679
|
+
getLensFlag(name, readCliFlag, globalConfig);
|
|
680
|
+
|
|
681
|
+
registerHarnessLensRuntime(pi, { getFlag, runtime, lensEnabledRef });
|
|
682
|
+
}
|
|
@@ -723,6 +723,15 @@ function buildNavigationOutput(args: {
|
|
|
723
723
|
return { output, actionStats, isEmpty, resultCount };
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
+
function isDirectoryPath(filePath: string): boolean {
|
|
727
|
+
if (!filePath) return false;
|
|
728
|
+
try {
|
|
729
|
+
return nodeFs.statSync(filePath).isDirectory();
|
|
730
|
+
} catch {
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
726
735
|
export function createLspNavigationTool(
|
|
727
736
|
getFlag: (name: string) => boolean | string | undefined,
|
|
728
737
|
) {
|
|
@@ -1021,14 +1030,7 @@ export function createLspNavigationTool(
|
|
|
1021
1030
|
: path.resolve(ctx.cwd || ".", rawPath)
|
|
1022
1031
|
: "";
|
|
1023
1032
|
|
|
1024
|
-
|
|
1025
|
-
if (filePath) {
|
|
1026
|
-
try {
|
|
1027
|
-
filePathIsDirectory = nodeFs.statSync(filePath).isDirectory();
|
|
1028
|
-
} catch {
|
|
1029
|
-
// non-existent path — existing error paths handle this
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1033
|
+
const filePathIsDirectory = isDirectoryPath(filePath);
|
|
1032
1034
|
|
|
1033
1035
|
const lspService = getLSPService();
|
|
1034
1036
|
if (operation === "workspaceDiagnostics") {
|
|
@@ -64,27 +64,23 @@ export async function synthesizeRepairBrief(
|
|
|
64
64
|
const planRel =
|
|
65
65
|
input.planPacketPath?.replace(/\\/g, "/") ?? "plan-packet.yaml";
|
|
66
66
|
const plan = await readArtifactYaml(runRoot, planRel, "plan-packet");
|
|
67
|
+
const sentruxRepair = await readArtifactYaml(
|
|
68
|
+
runRoot,
|
|
69
|
+
"artifacts/sentrux-repair-plan.yaml",
|
|
70
|
+
"sentrux-repair-plan",
|
|
71
|
+
);
|
|
67
72
|
|
|
68
73
|
const remediation =
|
|
69
74
|
(typeof review?.remediation_class === "string" &&
|
|
70
75
|
review.remediation_class) ||
|
|
71
76
|
"implementation_gap";
|
|
72
77
|
|
|
73
|
-
const sourceArtifacts
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
input.evalVerdictPath ?? "artifacts/eval-verdict.yaml";
|
|
80
|
-
}
|
|
81
|
-
if (adversary) {
|
|
82
|
-
sourceArtifacts["adversary-report"] =
|
|
83
|
-
input.adversaryReportPath ?? "artifacts/adversary-report.yaml";
|
|
84
|
-
}
|
|
85
|
-
if (plan) {
|
|
86
|
-
sourceArtifacts["plan-packet"] = planRel;
|
|
87
|
-
}
|
|
78
|
+
const sourceArtifacts = buildSourceArtifacts(input, planRel, {
|
|
79
|
+
evalDoc,
|
|
80
|
+
adversary,
|
|
81
|
+
plan,
|
|
82
|
+
sentruxRepair,
|
|
83
|
+
});
|
|
88
84
|
|
|
89
85
|
const failedIds = [
|
|
90
86
|
...stringList(review?.failed_acceptance_check_ids),
|
|
@@ -93,7 +89,7 @@ export async function synthesizeRepairBrief(
|
|
|
93
89
|
];
|
|
94
90
|
const uniqueFailed = [...new Set(failedIds)];
|
|
95
91
|
|
|
96
|
-
const fixDirectives: string[] =
|
|
92
|
+
const fixDirectives: string[] = sentruxFixDirectives(sentruxRepair);
|
|
97
93
|
for (const key of [
|
|
98
94
|
"fix_directives",
|
|
99
95
|
"repair_directives",
|
|
@@ -117,15 +113,7 @@ export async function synthesizeRepairBrief(
|
|
|
117
113
|
);
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
const
|
|
121
|
-
const priorityLakeIds = stringList(execPlan?.critical_path_lake_ids);
|
|
122
|
-
if (priorityLakeIds.length === 0) {
|
|
123
|
-
const lakes = Array.isArray(execPlan?.lakes) ? execPlan.lakes : [];
|
|
124
|
-
for (const lake of lakes) {
|
|
125
|
-
const l = asRecord(lake);
|
|
126
|
-
if (l && typeof l.id === "string") priorityLakeIds.push(l.id);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
116
|
+
const priorityLakeIds = collectPriorityLakeIds(plan);
|
|
129
117
|
|
|
130
118
|
const brief: Record<string, unknown> = {
|
|
131
119
|
schema_version: REPAIR_BRIEF_SCHEMA,
|
|
@@ -143,3 +131,74 @@ export async function synthesizeRepairBrief(
|
|
|
143
131
|
}
|
|
144
132
|
return brief;
|
|
145
133
|
}
|
|
134
|
+
|
|
135
|
+
function buildSourceArtifacts(
|
|
136
|
+
input: SynthesizeRepairBriefInput,
|
|
137
|
+
planRel: string,
|
|
138
|
+
docs: {
|
|
139
|
+
evalDoc: Record<string, unknown> | null;
|
|
140
|
+
adversary: Record<string, unknown> | null;
|
|
141
|
+
plan: Record<string, unknown> | null;
|
|
142
|
+
sentruxRepair: Record<string, unknown> | null;
|
|
143
|
+
},
|
|
144
|
+
): Record<string, string> {
|
|
145
|
+
const sourceArtifacts: Record<string, string> = {
|
|
146
|
+
"review-outcome":
|
|
147
|
+
input.reviewOutcomePath ?? "artifacts/review-outcome.yaml",
|
|
148
|
+
};
|
|
149
|
+
if (docs.evalDoc)
|
|
150
|
+
sourceArtifacts["eval-verdict"] =
|
|
151
|
+
input.evalVerdictPath ?? "artifacts/eval-verdict.yaml";
|
|
152
|
+
if (docs.adversary)
|
|
153
|
+
sourceArtifacts["adversary-report"] =
|
|
154
|
+
input.adversaryReportPath ?? "artifacts/adversary-report.yaml";
|
|
155
|
+
if (docs.plan) sourceArtifacts["plan-packet"] = planRel;
|
|
156
|
+
if (docs.sentruxRepair)
|
|
157
|
+
sourceArtifacts["sentrux-repair-plan"] =
|
|
158
|
+
"artifacts/sentrux-repair-plan.yaml";
|
|
159
|
+
return sourceArtifacts;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function sentruxFixDirectives(
|
|
163
|
+
sentruxRepair: Record<string, unknown> | null,
|
|
164
|
+
): string[] {
|
|
165
|
+
if (!sentruxRepair) return [];
|
|
166
|
+
const out: string[] = [];
|
|
167
|
+
const actions = Array.isArray(sentruxRepair.actions)
|
|
168
|
+
? sentruxRepair.actions
|
|
169
|
+
: [];
|
|
170
|
+
for (const raw of actions) {
|
|
171
|
+
const action = asRecord(raw);
|
|
172
|
+
if (!action) continue;
|
|
173
|
+
const id = typeof action.id === "string" ? action.id : "action";
|
|
174
|
+
const target = typeof action.target === "string" ? action.target : "";
|
|
175
|
+
const instruction =
|
|
176
|
+
typeof action.instruction === "string" ? action.instruction : "";
|
|
177
|
+
if (instruction)
|
|
178
|
+
out.push(`[sentrux:${id}] ${target}: ${instruction}`.trim());
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
typeof sentruxRepair.summary === "string" &&
|
|
182
|
+
sentruxRepair.summary.trim()
|
|
183
|
+
) {
|
|
184
|
+
out.unshift(`[sentrux] ${sentruxRepair.summary.trim()}`);
|
|
185
|
+
}
|
|
186
|
+
for (const v of stringList(sentruxRepair.verification)) {
|
|
187
|
+
out.push(`[sentrux:verify] ${v}`);
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function collectPriorityLakeIds(
|
|
193
|
+
plan: Record<string, unknown> | null,
|
|
194
|
+
): string[] {
|
|
195
|
+
const execPlan = asRecord(plan?.execution_plan);
|
|
196
|
+
const ids = stringList(execPlan?.critical_path_lake_ids);
|
|
197
|
+
if (ids.length > 0) return ids;
|
|
198
|
+
const lakes = Array.isArray(execPlan?.lakes) ? execPlan.lakes : [];
|
|
199
|
+
for (const lake of lakes) {
|
|
200
|
+
const record = asRecord(lake);
|
|
201
|
+
if (record && typeof record.id === "string") ids.push(record.id);
|
|
202
|
+
}
|
|
203
|
+
return ids;
|
|
204
|
+
}
|