gentle-pi 2.6.4 → 3.0.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/README.md +25 -7
- package/assets/agents/gentle-ai-worker.md +5 -1
- package/assets/agents/sdd-apply.md +9 -7
- package/assets/agents/sdd-archive.md +42 -23
- package/assets/agents/sdd-proposal.md +2 -2
- package/assets/agents/sdd-remediate.md +4 -4
- package/assets/agents/sdd-research.md +20 -48
- package/assets/agents/sdd-tasks.md +5 -5
- package/assets/agents/sdd-verify.md +6 -28
- package/assets/chains/sdd-full.chain.md +4 -22
- package/assets/chains/sdd-verify.chain.md +3 -12
- package/assets/orchestrator-delegation.md +33 -3
- package/assets/orchestrator-memory.md +20 -7
- package/assets/orchestrator.md +5 -3
- package/assets/sdd-orchestrator-workflow.md +25 -58
- package/assets/support/sdd-status-contract.md +9 -12
- package/docs/gentle-shell.md +41 -19
- package/docs/readme-reference.md +175 -36
- package/extensions/codegraph-tools.ts +2 -0
- package/extensions/gentle-agents.ts +289 -361
- package/extensions/gentle-ai.ts +588 -117
- package/extensions/gentle-shell.ts +123 -97
- package/extensions/pi-pretty.ts +63 -14
- package/extensions/quiet-tools.ts +1 -2
- package/extensions/startup-banner.ts +10 -9
- package/lib/agent-home.ts +8 -0
- package/lib/agent-profile-pin.ts +336 -0
- package/lib/agent-profiles.ts +28 -8
- package/lib/agents-config.ts +24 -2
- package/lib/agents-history.ts +3 -97
- package/lib/agents-keys.ts +27 -0
- package/lib/agents-protocol.ts +2 -15
- package/lib/agents-runner.ts +74 -113
- package/lib/agents-session-transport.ts +691 -0
- package/lib/command-palette-catalog.ts +87 -0
- package/lib/command-palette.ts +346 -0
- package/lib/native-choice-list.ts +5 -0
- package/lib/native-review-cli.ts +19 -97
- package/lib/review-publication-gate.ts +11 -1
- package/lib/review-repository.ts +1 -1
- package/lib/review-snapshot.ts +1 -0
- package/lib/review-transaction.ts +4 -2
- package/lib/sdd-preflight.ts +2 -1
- package/lib/sdd-research-capabilities.ts +18 -152
- package/lib/sdd-status.ts +7 -779
- package/lib/session-change-capture.ts +88 -0
- package/lib/session-changes.ts +147 -0
- package/lib/shell-bar.ts +24 -10
- package/lib/shell-card.ts +8 -12
- package/lib/shell-changes-view.ts +2 -1
- package/lib/shell-changes.ts +5 -2
- package/lib/shell-prompt.ts +25 -8
- package/lib/shell-sidebar-banner.ts +2 -2
- package/lib/shell-sidebar-layout.ts +5 -2
- package/lib/windows-session-transport.ts +877 -0
- package/package.json +3 -3
- package/runtime/native-review-cli.mjs +18 -96
- package/runtime/windows-session-transport.ps1 +791 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/test-packed-runner.mjs +1668 -20
- package/scripts/verify-package-files.mjs +2 -3
- package/tests/agent-home.test.ts +52 -0
- package/tests/agent-profiles.test.ts +30 -1
- package/tests/agents-config.test.ts +44 -0
- package/tests/agents-history.test.ts +12 -24
- package/tests/agents-runner.test.ts +321 -58
- package/tests/agents-session-transport-process.test.ts +249 -0
- package/tests/agents-session-transport.test.ts +823 -0
- package/tests/artifact-language.test.ts +10 -7
- package/tests/command-palette.test.ts +378 -0
- package/tests/delegated-key-learnings-contract.test.ts +2 -2
- package/tests/fixtures/agents-session-transport-process.mjs +108 -0
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +54 -0
- package/tests/fixtures/windows-session-bootstrap.ps1 +129 -0
- package/tests/fixtures/windows-session-compile.ps1 +110 -0
- package/tests/gentle-agents.test.ts +883 -356
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai.test.ts +472 -4
- package/tests/gentle-shell.test.ts +338 -205
- package/tests/native-choice-list.test.ts +13 -0
- package/tests/native-review-capability-contract.test.ts +15 -1
- package/tests/native-review-cli.test.ts +0 -33
- package/tests/odd-routing-contract.test.ts +208 -0
- package/tests/orchestrator-budget.test.ts +17 -2
- package/tests/package-manifest.test.ts +119 -31
- package/tests/persona-single-channel.test.ts +3 -3
- package/tests/pi-pretty.test.ts +45 -0
- package/tests/profile-pin.test.ts +370 -0
- package/tests/quiet-tool-rendering.test.ts +32 -5
- package/tests/review-contract-prompt.test.ts +9 -0
- package/tests/review-controller.test.ts +0 -44
- package/tests/review-session-standing-permission-ipc.test.ts +427 -13
- package/tests/runtime-harness.mjs +4 -4
- package/tests/sdd-agent-tools.test.ts +15 -36
- package/tests/sdd-archive-replay.test.ts +82 -0
- package/tests/sdd-classical-continuation.test.ts +74 -0
- package/tests/sdd-execution-routing-contract.test.ts +18 -2
- package/tests/sdd-managed-runtime-settlement.test.ts +42 -330
- package/tests/sdd-native-managed-uptake.test.ts +11 -21
- package/tests/sdd-no-attempts-contract.test.ts +15 -0
- package/tests/sdd-odd-integration.test.ts +33 -0
- package/tests/sdd-optional-research.test.ts +124 -0
- package/tests/sdd-planning-routing-contract.test.ts +1 -1
- package/tests/sdd-preflight-rpc-input.test.ts +125 -0
- package/tests/sdd-preflight.test.ts +1 -1
- package/tests/sdd-research-capabilities.test.ts +20 -162
- package/tests/sdd-selection-transport.test.ts +180 -88
- package/tests/sdd-status.test.ts +5 -778
- package/tests/sdd-task-truth.test.ts +43 -0
- package/tests/session-change-capture.test.ts +86 -0
- package/tests/session-changes-shell.test.ts +38 -0
- package/tests/session-changes.test.ts +114 -0
- package/tests/shell-bar.test.ts +35 -0
- package/tests/shell-card.test.ts +8 -6
- package/tests/shell-changes.test.ts +8 -0
- package/tests/shell-prompt.test.ts +41 -7
- package/tests/shell-sidebar-banner.test.ts +4 -4
- package/tests/shell-sidebar-layout.test.ts +97 -13
- package/tests/startup-banner.test.ts +55 -2
- package/tests/windows-hidden-processes.test.ts +303 -0
- package/tests/windows-session-bootstrap.test.ts +1772 -0
- package/tests/windows-session-compile.test.ts +170 -0
- package/tests/windows-session-transport.test.ts +754 -0
- package/assets/agents/sdd-sync.md +0 -146
- package/lib/openspec-guardrails.ts +0 -99
- package/tests/native-sdd-attempt-authority.test.ts +0 -240
- package/tests/openspec-guardrails.test.ts +0 -71
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { FIXED_WINDOWS_POWERSHELL } from "../lib/windows-session-transport.ts";
|
|
8
|
+
|
|
9
|
+
const fixture = fileURLToPath(new URL("fixtures/windows-session-compile.ps1", import.meta.url));
|
|
10
|
+
const runtime = fileURLToPath(new URL("../runtime/windows-session-transport.ps1", import.meta.url));
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
const maxOutputBytes = 4096;
|
|
13
|
+
const maxErrors = 8;
|
|
14
|
+
const maxLocation = 1_000_000;
|
|
15
|
+
|
|
16
|
+
type CompilerEntry = Readonly<{ code: string; line: number; column: number }>;
|
|
17
|
+
type CompilerOutcome = "not-compiled" | "compile-success" | "compile-errors";
|
|
18
|
+
type CompilerControl = Readonly<{
|
|
19
|
+
kind: "windows-session-csharp-compile-control";
|
|
20
|
+
success: boolean;
|
|
21
|
+
sourceExtracted: boolean;
|
|
22
|
+
stage: "completed" | "source-parse" | "source-contract" | "compile" | "control-failure";
|
|
23
|
+
outcome: CompilerOutcome;
|
|
24
|
+
errorlist: readonly CompilerEntry[];
|
|
25
|
+
warnings?: readonly CompilerEntry[];
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
function isCompilerEntry(value: unknown): value is CompilerEntry {
|
|
29
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
30
|
+
const entry = value as Record<string, unknown>;
|
|
31
|
+
return Object.keys(entry).sort().join(",") === "code,column,line"
|
|
32
|
+
&& typeof entry.code === "string" && /^CS[0-9]{4}(?![\s\S])/.test(entry.code)
|
|
33
|
+
&& typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 && entry.line <= maxLocation
|
|
34
|
+
&& typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 && entry.column <= maxLocation;
|
|
35
|
+
}
|
|
36
|
+
function isCompilerEntries(value: unknown): value is CompilerEntry[] {
|
|
37
|
+
return Array.isArray(value) && value.length > 0 && value.length <= maxErrors && value.every(isCompilerEntry);
|
|
38
|
+
}
|
|
39
|
+
function compilerStage(value: unknown): CompilerControl["stage"] | undefined {
|
|
40
|
+
switch (value) {
|
|
41
|
+
case "completed": case "source-parse": case "source-contract": case "compile": case "control-failure": return value;
|
|
42
|
+
default: return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function compilerOutcome(value: unknown): CompilerOutcome | undefined {
|
|
46
|
+
switch (value) {
|
|
47
|
+
case "not-compiled": case "compile-success": case "compile-errors": return value;
|
|
48
|
+
default: return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function parseCompilerControl(stdout: string): CompilerControl | undefined {
|
|
53
|
+
if (Buffer.byteLength(stdout, "utf8") > maxOutputBytes || !stdout.endsWith("\n")) return undefined;
|
|
54
|
+
const lines = stdout.slice(0, -1).split("\n");
|
|
55
|
+
if (lines.length !== 1 || lines[0].length === 0) return undefined;
|
|
56
|
+
let value: unknown;
|
|
57
|
+
try { value = JSON.parse(lines[0]); } catch { return undefined; }
|
|
58
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
59
|
+
const record = value as Record<string, unknown>;
|
|
60
|
+
const allowed = new Set(["kind", "success", "sourceExtracted", "stage", "outcome", "errorlist", "warnings"]);
|
|
61
|
+
if (Object.keys(record).some((key) => !allowed.has(key))
|
|
62
|
+
|| record.kind !== "windows-session-csharp-compile-control"
|
|
63
|
+
|| typeof record.success !== "boolean"
|
|
64
|
+
|| typeof record.sourceExtracted !== "boolean"
|
|
65
|
+
|| compilerStage(record.stage) === undefined
|
|
66
|
+
|| compilerOutcome(record.outcome) === undefined
|
|
67
|
+
|| !Array.isArray(record.errorlist) || record.errorlist.length > maxErrors || !record.errorlist.every(isCompilerEntry)) return undefined;
|
|
68
|
+
const warnings = record.warnings;
|
|
69
|
+
const stage = compilerStage(record.stage);
|
|
70
|
+
const outcome = compilerOutcome(record.outcome);
|
|
71
|
+
if (stage === undefined || outcome === undefined) return undefined;
|
|
72
|
+
const compileSuccess = outcome === "compile-success" && record.sourceExtracted && record.success && stage === "completed" && record.errorlist.length === 0;
|
|
73
|
+
const compileErrors = outcome === "compile-errors" && record.sourceExtracted && !record.success && stage === "compile" && record.errorlist.length > 0;
|
|
74
|
+
const notCompiledStage = (stage === "source-parse" && !record.sourceExtracted)
|
|
75
|
+
|| (stage === "source-contract" && !record.sourceExtracted)
|
|
76
|
+
|| stage === "control-failure";
|
|
77
|
+
const notCompiled = outcome === "not-compiled" && !record.success && record.errorlist.length === 0 && warnings === undefined && notCompiledStage;
|
|
78
|
+
if (!compileSuccess && !compileErrors && !notCompiled) return undefined;
|
|
79
|
+
const base: Omit<CompilerControl, "warnings"> = {
|
|
80
|
+
kind: "windows-session-csharp-compile-control",
|
|
81
|
+
success: record.success,
|
|
82
|
+
sourceExtracted: record.sourceExtracted,
|
|
83
|
+
stage,
|
|
84
|
+
outcome,
|
|
85
|
+
errorlist: Object.freeze([...record.errorlist]),
|
|
86
|
+
};
|
|
87
|
+
if (warnings === undefined) return Object.freeze(base);
|
|
88
|
+
if (!isCompilerEntries(warnings)) return undefined;
|
|
89
|
+
return Object.freeze({ ...base, warnings: Object.freeze([...warnings]) });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function control(value: Record<string, unknown>): string {
|
|
93
|
+
return `${JSON.stringify({ kind: "windows-session-csharp-compile-control", success: false, sourceExtracted: true, stage: "compile", outcome: "compile-errors", errorlist: [{ code: "CS1002", line: 12, column: 3 }], ...value })}\n`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
test("Windows bootstrap CSharp compile control parser accepts only consistent bounded records", () => {
|
|
97
|
+
const parsed = parseCompilerControl(control({}));
|
|
98
|
+
assert.ok(parsed);
|
|
99
|
+
assert.equal(parsed.outcome, "compile-errors");
|
|
100
|
+
assert.equal(parsed.success, false);
|
|
101
|
+
assert.equal(parsed.errorlist.length, 1);
|
|
102
|
+
for (const malformed of [
|
|
103
|
+
"null\n",
|
|
104
|
+
"[]\n",
|
|
105
|
+
"{}\n",
|
|
106
|
+
control({ unexpected: true }),
|
|
107
|
+
control({ outcome: "compile-success" }),
|
|
108
|
+
control({ sourceExtracted: false }),
|
|
109
|
+
control({ stage: "source-parse" }),
|
|
110
|
+
control({ stage: "source-contract", outcome: "not-compiled", errorlist: [] }),
|
|
111
|
+
control({ stage: "control-failure" }),
|
|
112
|
+
control({ errorlist: [] }),
|
|
113
|
+
control({ errorlist: Array.from({ length: maxErrors + 1 }, () => ({ code: "CS1002", line: 1, column: 1 })) }),
|
|
114
|
+
control({ errorlist: [{ code: "cs1002", line: 1, column: 1 }] }),
|
|
115
|
+
control({ errorlist: [{ code: "CS1002\n", line: 1, column: 1 }] }),
|
|
116
|
+
control({ errorlist: [{ code: "CS1002\r\n", line: 1, column: 1 }] }),
|
|
117
|
+
control({ errorlist: [{ code: "CS1002", line: 1.5, column: 1 }] }),
|
|
118
|
+
control({ errorlist: [{ code: "CS1002", line: -1, column: 1 }] }),
|
|
119
|
+
control({ errorlist: [{ code: "CS1002", line: maxLocation + 1, column: 1 }] }),
|
|
120
|
+
control({ errorlist: [{ code: "CS1002", line: 1, column: -1 }] }),
|
|
121
|
+
control({ errorlist: [{ code: "CS1002", line: 1, column: 1.5 }] }),
|
|
122
|
+
control({ errorlist: [{ code: "CS1002", line: 1, column: maxLocation + 1 }] }),
|
|
123
|
+
`${control({}).trimEnd()}\nextra\n`,
|
|
124
|
+
control({}).trimEnd(),
|
|
125
|
+
`${"x".repeat(maxOutputBytes)}\n`,
|
|
126
|
+
]) assert.equal(parseCompilerControl(malformed), undefined);
|
|
127
|
+
assert.equal(parseCompilerControl('{"kind":"windows-session-csharp-compile-control","success":false,"sourceExtracted":false,"stage":"source-contract","outcome":"not-compiled","errorlist":[]}\n')?.outcome, "not-compiled");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("Windows bootstrap pinned enumeration has no unreachable post-loop return", async () => {
|
|
131
|
+
const source = await readFile(runtime, "utf8");
|
|
132
|
+
const start = source.indexOf("static string[] EnumeratePinned(IntPtr directory)");
|
|
133
|
+
const end = source.indexOf("public static int EnumeratePresence()", start);
|
|
134
|
+
assert.ok(start >= 0 && end > start, "source guard: pinned enumeration bounds were not found");
|
|
135
|
+
const enumeration = source.slice(start, end);
|
|
136
|
+
assert.equal((enumeration.match(/return names\.ToArray\(\);/g) ?? []).length, 1, "source guard: the infinite enumeration loop must not have a post-loop return");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("Windows bootstrap CSharp compile control source guard uses an owned created temp root", async () => {
|
|
140
|
+
const source = await readFile(fixture, "utf8");
|
|
141
|
+
assert.match(source, /\[System\.Management\.Automation\.Language\.Parser\]::ParseFile\(/);
|
|
142
|
+
assert.match(source, /\[Microsoft\.CSharp\.CSharpCodeProvider\]::new\(\)/);
|
|
143
|
+
assert.match(source, /CompileAssemblyFromSource\(/);
|
|
144
|
+
assert.match(source, /GenerateInMemory\s*=\s*\$true/);
|
|
145
|
+
assert.match(source, /ReferencedAssemblies\.Add\("System\.dll"\)/);
|
|
146
|
+
assert.match(source, /ReferencedAssemblies\.Add\("System\.Core\.dll"\)/);
|
|
147
|
+
const createRoot = source.indexOf("CreateDirectory($tempRoot)");
|
|
148
|
+
const createCollection = source.indexOf("TempFileCollection]::new($tempRoot, $false)");
|
|
149
|
+
assert.ok(createRoot >= 0 && createRoot < createCollection, "source guard: fixture creates its owned temp root before CodeDom receives it");
|
|
150
|
+
assert.match(source, /\$provider\.Dispose\(\)/);
|
|
151
|
+
assert.match(source, /\$tempFiles\.Dispose\(\)/);
|
|
152
|
+
assert.doesNotMatch(source, /Invoke-Expression|\bInvoke-Command\b|\.\s+\$\w+|\[WindowsSessionBootstrap\]::/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("Windows bootstrap CSharp compile control reports comparison outcome", { skip: process.platform !== "win32", timeout: 40_000 }, async (t) => {
|
|
156
|
+
let result: { stdout: string; stderr: string };
|
|
157
|
+
try {
|
|
158
|
+
// execFile resolves only after its owned child has exited and its stdio is closed.
|
|
159
|
+
result = await execFileAsync(FIXED_WINDOWS_POWERSHELL, ["-NoLogo", "-NoProfile", "-NonInteractive", "-File", fixture, "-SourcePath", runtime], { shell: false, windowsHide: true, timeout: 30_000, maxBuffer: maxOutputBytes });
|
|
160
|
+
} catch (error: unknown) {
|
|
161
|
+
const captured = error as Readonly<{ stdout?: string; stderr?: string }>;
|
|
162
|
+
result = { stdout: captured.stdout ?? "", stderr: captured.stderr ?? "" };
|
|
163
|
+
}
|
|
164
|
+
assert.equal(result.stderr, "", "compile control emitted non-JSON diagnostics");
|
|
165
|
+
const parsed = parseCompilerControl(result.stdout);
|
|
166
|
+
assert.ok(parsed, "compile control did not return a valid bounded result");
|
|
167
|
+
t.diagnostic(JSON.stringify({ outcome: parsed.outcome, success: parsed.success, errorlist: parsed.errorlist, warnings: parsed.warnings }));
|
|
168
|
+
assert.equal(parsed.sourceExtracted, true, "compile control did not extract the Add-Type literal");
|
|
169
|
+
assert.ok(parsed.outcome === "compile-success" || parsed.outcome === "compile-errors", "compile control did not complete compilation");
|
|
170
|
+
});
|