opencode-agent-skill 15.0.0 → 15.1.1

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.
Files changed (43) hide show
  1. package/README.md +5 -5
  2. package/docs/V15-MANAGED-RUNTIME.md +18 -0
  3. package/global-config/agents/verifier.md +6 -2
  4. package/lib/affected-tests.mjs +58 -8
  5. package/lib/capability-fabric.mjs +107 -48
  6. package/lib/code-intelligence/index.mjs +4 -15
  7. package/lib/code-intelligence/lsp-pool.mjs +694 -0
  8. package/lib/code-intelligence/lsp-provider.mjs +481 -67
  9. package/lib/compaction-resume-guard.mjs +327 -0
  10. package/lib/completion-auditor.mjs +20 -0
  11. package/lib/context-engine-v11.mjs +20 -7
  12. package/lib/context-manifest.mjs +38 -21
  13. package/lib/decision-policy.mjs +40 -2
  14. package/lib/evidence-store.mjs +121 -31
  15. package/lib/executable-probe.mjs +69 -0
  16. package/lib/execution-contract.mjs +29 -16
  17. package/lib/hierarchical-context.mjs +31 -3
  18. package/lib/learning-engine.mjs +164 -65
  19. package/lib/memory-engine.mjs +348 -98
  20. package/lib/model-config.mjs +86 -7
  21. package/lib/performance-fabric.mjs +354 -107
  22. package/lib/permission-policy.mjs +232 -0
  23. package/lib/pi-rpc-pool.mjs +170 -5
  24. package/lib/provider-recovery.mjs +51 -0
  25. package/lib/repo-graph.mjs +38 -5
  26. package/lib/runtime-events.mjs +31 -10
  27. package/lib/semantic-index.mjs +67 -20
  28. package/lib/skill-compiler.mjs +84 -18
  29. package/lib/subagent-artifacts.mjs +182 -0
  30. package/lib/task-policy.mjs +80 -7
  31. package/lib/trajectory.mjs +25 -1
  32. package/lib/untrusted-output.mjs +106 -0
  33. package/lib/verification-broker.mjs +32 -27
  34. package/lib/workspace-fingerprint.mjs +43 -8
  35. package/lib/workspace-hygiene.mjs +481 -0
  36. package/package.json +8 -6
  37. package/pi/extensions/ues-child-runtime.ts +103 -5
  38. package/pi/extensions/ues.ts +984 -95
  39. package/scripts/benchmark-lsp-pool.mjs +128 -0
  40. package/scripts/check-release-consistency.mjs +16 -4
  41. package/scripts/check-source-integrity.mjs +60 -1
  42. package/scripts/run-test-suite.mjs +143 -0
  43. package/scripts/smoke-package-closure.mjs +4 -0
@@ -1,11 +1,16 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { Type } from "typebox";
3
3
  import { readFile, stat } from "node:fs/promises";
4
+ import path from "node:path";
4
5
  import { compactReversibleOutput } from "../../lib/performance-fabric.mjs";
5
6
  import { getEvidenceSelected } from "../../lib/evidence-store.mjs";
6
7
  import { recordVerification } from "../../lib/verification-broker.mjs";
7
8
  import { runtimeWorkspaceFingerprint } from "../../lib/workspace-fingerprint.mjs";
8
9
  import { destructiveShellRisk } from "../../lib/safety.mjs";
10
+ import { getUesConfigDir } from "../../lib/runtime-config.mjs";
11
+ import { PermissionPolicyStore, permissionRecoveryHint, toolPermissionRequest } from "../../lib/permission-policy.mjs";
12
+ import { analyzeUntrustedOutput, renderUntrustedOutputWarning } from "../../lib/untrusted-output.mjs";
13
+ import { mcpExecutionPolicy } from "../../lib/mcp-tool-policy.mjs";
9
14
  import { crossToolTempPathRisk, isLocalEnvPath, localEnvWriteRisk } from "../../lib/execution-contract.mjs";
10
15
  import {
11
16
  canonicalVerificationCommand,
@@ -14,9 +19,11 @@ import {
14
19
  import {
15
20
  applyAnchoredFileEdits,
16
21
  diagnoseCode,
22
+ lspOperation,
17
23
  probeCodeIntelligence,
18
24
  readAnchoredCode,
19
25
  searchCodeIntelligence,
26
+ shutdownLspPool,
20
27
  } from "../../lib/code-intelligence/index.mjs";
21
28
  import { ingestDocument } from "../../lib/document-ingestion.mjs";
22
29
  import { compactContext, expandContext, searchContext } from "../../lib/reversible-context.mjs";
@@ -31,6 +38,16 @@ import {
31
38
  waitForService,
32
39
  } from "../../lib/service-manager.mjs";
33
40
 
41
+ const PERMISSION_POLICY = new PermissionPolicyStore(
42
+ path.join(getUesConfigDir(), ".ues", "permissions.json"),
43
+ );
44
+ const EXTERNAL_TOOL_NAMES = new Set(
45
+ String(process.env.UES_CHILD_EXTERNAL_TOOL_NAMES || "")
46
+ .split(",")
47
+ .map((value) => value.trim())
48
+ .filter(Boolean),
49
+ );
50
+
34
51
  const toolExecutionState = new Map<string, {
35
52
  startedAt: number;
36
53
  workspaceBefore?: string;
@@ -101,12 +118,45 @@ export default function (pi: ExtensionAPI) {
101
118
  pi.on("session_start", clearExecutionState);
102
119
  pi.on("session_shutdown", async (_event, ctx) => {
103
120
  clearExecutionState();
104
- await stopAllServices(ctx.cwd).catch(() => []);
121
+ await Promise.all([
122
+ stopAllServices(ctx.cwd).catch(() => []),
123
+ shutdownLspPool(ctx.cwd).catch(() => ({ stopped: 0, remaining: 0 })),
124
+ ]);
105
125
  });
106
126
 
107
127
  pi.on("tool_call", async (event, ctx) => {
108
128
  const toolName = String(event.toolName || "");
109
129
  const input: any = event.input || {};
130
+ const permissionRequest = toolPermissionRequest(toolName, input);
131
+ const configuredPermission: any = await PERMISSION_POLICY.evaluate(
132
+ permissionRequest,
133
+ { agent: String(process.env.UES_CHILD_AGENT || "ues-child") },
134
+ ).catch((error) => ({
135
+ configured: true,
136
+ decision: null,
137
+ error: error instanceof Error ? error.message : String(error),
138
+ }));
139
+ if (configuredPermission.error) {
140
+ toolExecutionState.delete(String(event.toolCallId || ""));
141
+ return {
142
+ block: true,
143
+ reason: "UES permission policy is invalid: " + configuredPermission.error,
144
+ };
145
+ }
146
+ if (configuredPermission?.decision?.effect === "deny") {
147
+ toolExecutionState.delete(String(event.toolCallId || ""));
148
+ return {
149
+ block: true,
150
+ reason: permissionRecoveryHint(permissionRequest, configuredPermission.decision, { effect: "deny" }),
151
+ };
152
+ }
153
+ if (configuredPermission?.decision?.effect === "ask") {
154
+ toolExecutionState.delete(String(event.toolCallId || ""));
155
+ return {
156
+ block: true,
157
+ reason: permissionRecoveryHint(permissionRequest, configuredPermission.decision, { effect: "ask" }),
158
+ };
159
+ }
110
160
  const localEnvAllowed = String(process.env.UES_CHILD_ALLOW_LOCAL_ENV_WRITE || "") === "1";
111
161
  const writeTool = ["edit", "write", "write_file", "apply_patch", "ues_code_edit"].includes(toolName);
112
162
  const fileTool = ["read", "edit", "write", "write_file", "apply_patch", "ues_code", "ues_code_edit"].includes(toolName);
@@ -185,11 +235,37 @@ export default function (pi: ExtensionAPI) {
185
235
 
186
236
  pi.on("tool_result", async (event, ctx) => {
187
237
  const toolName = String(event.toolName || "");
238
+ const shownText = visibleText(event);
239
+ const allTools = typeof (pi as any).getAllTools === "function" ? (pi as any).getAllTools() : [];
240
+ const descriptor = allTools.find((tool: any) => String(tool?.name || "") === toolName);
241
+ const externalPolicy = mcpExecutionPolicy(descriptor || { name: toolName });
242
+ const externalBoundary =
243
+ EXTERNAL_TOOL_NAMES.has(toolName) ||
244
+ externalPolicy.externalEvidenceBoundary === true;
245
+ if (externalBoundary && shownText) {
246
+ const analysis = analyzeUntrustedOutput(shownText, { source: toolName });
247
+ if (analysis.flagged) {
248
+ const originalContent = Array.isArray(event.content)
249
+ ? event.content
250
+ : [{ type: "text", text: shownText }];
251
+ return {
252
+ content: [
253
+ { type: "text", text: renderUntrustedOutputWarning(analysis, { source: toolName }) },
254
+ ...originalContent,
255
+ ],
256
+ details: {
257
+ ...(event.details && typeof event.details === "object" ? event.details : {}),
258
+ uesUntrustedOutputBoundary: analysis,
259
+ },
260
+ isError: event.isError,
261
+ usage: event.usage,
262
+ };
263
+ }
264
+ }
265
+
188
266
  if (!["bash", "powershell", "grep", "find", "ls"].includes(toolName)) {
189
267
  return undefined;
190
268
  }
191
-
192
- const shownText = visibleText(event);
193
269
  const capture = await capturedText(event, shownText);
194
270
  const rawText = capture.text;
195
271
  const images = (event.content || []).filter((part: any) => part?.type !== "text");
@@ -272,13 +348,20 @@ export default function (pi: ExtensionAPI) {
272
348
  name: "ues_code",
273
349
  label: "UES Code Intelligence",
274
350
  description:
275
- "Bounded code/document/context intelligence for weak models: semantic/AST search, hash-anchored reads, optional LSP diagnostics, optional MarkItDown ingestion, and reversible context recovery.",
351
+ "Bounded code/document/context intelligence for weak models: semantic/AST search, hash-anchored reads, deterministic LSP definition/references/symbols/hover/rename-preview/call hierarchy, diagnostics, optional MarkItDown ingestion, and reversible context recovery.",
276
352
  parameters: Type.Object({
277
353
  action: Type.Union([
278
354
  Type.Literal("status"),
279
355
  Type.Literal("search"),
280
356
  Type.Literal("read"),
281
357
  Type.Literal("diagnostics"),
358
+ Type.Literal("definition"),
359
+ Type.Literal("references"),
360
+ Type.Literal("symbols"),
361
+ Type.Literal("hover"),
362
+ Type.Literal("rename-preview"),
363
+ Type.Literal("incoming-calls"),
364
+ Type.Literal("outgoing-calls"),
282
365
  Type.Literal("document"),
283
366
  Type.Literal("context-expand"),
284
367
  Type.Literal("context-search"),
@@ -289,6 +372,10 @@ export default function (pi: ExtensionAPI) {
289
372
  language: Type.Optional(Type.String()),
290
373
  startLine: Type.Optional(Type.Number({ minimum: 1 })),
291
374
  endLine: Type.Optional(Type.Number({ minimum: 1 })),
375
+ line: Type.Optional(Type.Number({ minimum: 1, description: "1-based source line for LSP operations" })),
376
+ character: Type.Optional(Type.Number({ minimum: 1, description: "1-based source character for LSP operations" })),
377
+ newName: Type.Optional(Type.String({ minLength: 1, maxLength: 512 })),
378
+ includeDeclaration: Type.Optional(Type.Boolean()),
292
379
  ref: Type.Optional(Type.String()),
293
380
  maxBytes: Type.Optional(Type.Number({ minimum: 1, maximum: 128000 })),
294
381
  }),
@@ -320,7 +407,18 @@ export default function (pi: ExtensionAPI) {
320
407
  };
321
408
  } else if (params.action === "diagnostics") {
322
409
  if (!params.file) throw new Error("ues_code diagnostics requires file");
323
- result = await diagnoseCode(ctx.cwd, params.file, { timeoutMs: 3000, maxDiagnostics: 40 });
410
+ result = await diagnoseCode(ctx.cwd, params.file, { timeoutMs: 5000, maxResults: 80 });
411
+ } else if (["definition", "references", "symbols", "hover", "rename-preview", "incoming-calls", "outgoing-calls"].includes(params.action)) {
412
+ if (!params.file) throw new Error(`ues_code ${params.action} requires file`);
413
+ if (params.action === "rename-preview" && !params.newName) throw new Error("ues_code rename-preview requires newName");
414
+ result = await lspOperation(ctx.cwd, params.file, params.action, {
415
+ line: params.line || 1,
416
+ character: params.character || 1,
417
+ newName: params.newName,
418
+ includeDeclaration: params.includeDeclaration,
419
+ timeoutMs: 7000,
420
+ maxResults: 120,
421
+ });
324
422
  } else if (params.action === "document") {
325
423
  if (!params.file) throw new Error("ues_code document requires file");
326
424
  const document = await ingestDocument(ctx.cwd, params.file, { maxBytes: Math.min(Number(params.maxBytes || 4 * 1024 * 1024), 4 * 1024 * 1024) });