@arhen/pi-core-subagent 1.3.64 → 1.3.66

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.
@@ -0,0 +1,30 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { TaskSnapshot } from "./types.ts";
3
+
4
+ export function textFingerprint(text: string): string {
5
+ return createHash("sha256").update(text.trim()).digest("hex");
6
+ }
7
+
8
+ export function matchesFinalText(task: TaskSnapshot, text: string): boolean {
9
+ return textFingerprint(text) === (task.finalTextFingerprint ?? textFingerprint(task.finalText ?? ""));
10
+ }
11
+
12
+ export function artifactFingerprint(task: TaskSnapshot): string | undefined {
13
+ if (!task.changedFiles?.length && !task.diffStat?.trim() && !task.worktreeError) return undefined;
14
+ return textFingerprint(
15
+ JSON.stringify([task.branch, [...(task.changedFiles ?? [])].sort(), task.diffStat, task.worktreeError]),
16
+ );
17
+ }
18
+
19
+ export function outcomeFingerprint(task: TaskSnapshot): string {
20
+ return textFingerprint(
21
+ JSON.stringify([
22
+ task.startedAt,
23
+ task.status,
24
+ task.toolCalls,
25
+ task.finalTextFingerprint ?? textFingerprint(task.finalText ?? ""),
26
+ task.error,
27
+ artifactFingerprint(task),
28
+ ]),
29
+ );
30
+ }
@@ -7,17 +7,18 @@ import type {
7
7
  ToolLoadoutChanges,
8
8
  ToolNamespace,
9
9
  } from "@earendil-works/pi-coding-agent";
10
-
11
- /**
12
- * Presentation preference for the subagent toolset.
13
- * - auto: native codemode presentation when the codemode tool is active, direct otherwise.
14
- * - direct (default): legacy native declarations and active-tool script callability.
15
- * - codemode: subagent tools are callable from codemode scripts but not declared or listed.
16
- */
17
- export type SubagentMode = "auto" | "direct" | "codemode";
10
+ import {
11
+ DEFAULT_SUBAGENT_MODE,
12
+ isSubagentMode,
13
+ readStoredMode,
14
+ type SubagentMode,
15
+ writeSubagentConfig,
16
+ } from "./config.ts";
17
+
18
+ export { isSubagentMode, type SubagentMode } from "./config.ts";
18
19
  export type EffectiveMode = "direct" | "codemode";
19
20
 
20
- /** Branch-scoped custom entry holding the preference, so resume/tree keep it. */
21
+ /** Branch-scoped custom entry holding the legacy preference; the global config file now wins. */
21
22
  export const MODE_ENTRY_TYPE = "subagent-mode";
22
23
  export const CODEMODE_TOOL_NAME = "codemode";
23
24
 
@@ -30,7 +31,7 @@ export const SUBAGENT_NAMESPACE: ToolNamespace = {
30
31
  - direct (default): legacy native tool declarations and active-tool script callability; activating codemode alone does not switch profiles.
31
32
  - auto (opt-in): codemode presentation while the \`codemode\` tool is active, direct otherwise.
32
33
  - codemode: active tools are callable from scripts but not declared; inactive tools stay model-only and are not callable at all.
33
- Switch with \`/subagents mode auto|direct|codemode\`. The preference is stored on the session branch and restored on reload, resume and tree navigation.
34
+ Switch with \`/subagents mode auto|direct|codemode\`. The preference is stored in the global \`subagents-config.json\` and applies to every session — reload, resume and tree navigation included.
34
35
 
35
36
  ## Operations
36
37
  Object arguments, exactly as validated when issued by the model:
@@ -67,10 +68,6 @@ export const CODEMODE_DISCOVERY_GUIDELINE =
67
68
  /** The full-reference pointer only resolves while codemode scripts can reach `describeNamespace`. */
68
69
  const NAMESPACE_REFERENCE = / Full reference: `describeNamespace\('subagents'\)`\./;
69
70
 
70
- export function isSubagentMode(value: unknown): value is SubagentMode {
71
- return value === "auto" || value === "direct" || value === "codemode";
72
- }
73
-
74
71
  export type AnyToolDefinition = ToolDefinition<any, any, any>;
75
72
 
76
73
  export interface Presentation {
@@ -82,7 +79,7 @@ export interface Presentation {
82
79
  /** Report preference, effective mode and codemode availability. */
83
80
  describe(): string;
84
81
  setPreference(mode: SubagentMode): void;
85
- /** Restore the latest preference stored on the session branch. */
82
+ /** Restore the globally stored preference, with the session branch as the legacy fallback. */
86
83
  restore(ctx: ExtensionContext): void;
87
84
  /** Register the tools for the current effective mode; no-op when already applied. */
88
85
  sync(): boolean;
@@ -90,6 +87,17 @@ export interface Presentation {
90
87
  registerInitial(): void;
91
88
  }
92
89
 
90
+ /** Latest mode stored by an older package version on this session branch, if any. */
91
+ function branchPreference(ctx: ExtensionContext): SubagentMode | undefined {
92
+ let restored: SubagentMode | undefined;
93
+ for (const entry of ctx.sessionManager.getBranch()) {
94
+ const candidate = entry as { type?: string; customType?: string; data?: { mode?: unknown } };
95
+ if (candidate.type === "custom" && candidate.customType === MODE_ENTRY_TYPE && isSubagentMode(candidate.data?.mode))
96
+ restored = candidate.data.mode;
97
+ }
98
+ return restored;
99
+ }
100
+
93
101
  /**
94
102
  * Owns the subagent exposure profile. Tools stay registered while only their exposure changes, so
95
103
  * the active selection (and the running manager) is preserved across mode switches.
@@ -169,25 +177,19 @@ export function createPresentation(pi: ExtensionAPI, definitions: readonly AnyTo
169
177
  const availability = available ? "codemode is active" : "codemode is not active";
170
178
  const fallback = !available && preference === "codemode" ? "; using direct fallback" : "";
171
179
  const pending = target !== shown ? `; pending ${target} at the next request boundary` : "";
172
- return `Subagent exposure: mode ${preference} → effective ${shown} (${availability}${fallback}${pending}). Use /subagents mode auto|direct|codemode.`;
180
+ return `Subagent exposure: mode ${preference} → effective ${shown} (${availability}${fallback}${pending}). Saved for all sessions. Use /subagents mode auto|direct|codemode.`;
173
181
  },
174
182
  setPreference(mode) {
175
183
  preference = mode;
184
+ // The global config file is authoritative for every session; the branch entry stays as a
185
+ // fallback for sessions that predate it and for readers of older package versions.
186
+ void writeSubagentConfig({ mode }).catch(() => {});
176
187
  pi.appendEntry(MODE_ENTRY_TYPE, { mode });
177
188
  },
178
189
  restore(ctx) {
179
- let restored: SubagentMode | undefined;
180
- for (const entry of ctx.sessionManager.getBranch()) {
181
- const candidate = entry as { type?: string; customType?: string; data?: { mode?: unknown } };
182
- if (
183
- candidate.type === "custom" &&
184
- candidate.customType === MODE_ENTRY_TYPE &&
185
- isSubagentMode(candidate.data?.mode)
186
- )
187
- restored = candidate.data.mode;
188
- }
189
- // Branch-sensitive state: a branch without an entry uses the package default again.
190
- preference = restored ?? "direct";
190
+ // Global preference wins over the branch; the branch entry is the legacy fallback for
191
+ // sessions that chose a mode before the preference moved into the config file.
192
+ preference = readStoredMode() ?? branchPreference(ctx) ?? DEFAULT_SUBAGENT_MODE;
191
193
  },
192
194
  sync() {
193
195
  const own = new Set(ownNames());
package/src/types.ts CHANGED
@@ -30,7 +30,16 @@ export interface TaskSnapshot {
30
30
  toolCalls: number;
31
31
  lastActivity?: string;
32
32
  finalText?: string;
33
+ finalTextFingerprint?: string;
33
34
  notifiedParent?: boolean;
35
+ finalParentReport?: {
36
+ body: string;
37
+ message: string;
38
+ toolCalls: number;
39
+ fingerprint?: string;
40
+ artifacts?: string;
41
+ delivery: "pending" | "message" | "parked" | "lost" | "awaited";
42
+ };
34
43
  error?: string;
35
44
  model?: string;
36
45
  provider?: string;
@@ -62,6 +71,7 @@ export interface RunSnapshot {
62
71
  tasks: TaskSnapshot[];
63
72
  aggregateUsage: UsageStats;
64
73
  awaited?: boolean;
74
+ completionAwaited?: boolean;
65
75
  }
66
76
 
67
77
  export interface RunDetails {