@giladbarnea/pi-user-agents 0.0.14 → 0.0.16
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/INTERNALS.md +1 -1
- package/attach.ts +6 -3
- package/autocomplete.ts +11 -2
- package/package.json +1 -1
package/INTERNALS.md
CHANGED
|
@@ -73,7 +73,7 @@ The file itself appears on the child's first assistant message (`_persist` defer
|
|
|
73
73
|
|
|
74
74
|
The session file does not persist the dispatch options, so every dispatch writes a `pi-user-agents-dispatch` custom entry into the child — the forwarded pi tokens, the plain task, and the isolate flag. At attach the record replays through the same projections dispatch used (`buildChildResourceLoaderOptions`, `resolveToolOptions`), so `--tools`, `--no-extensions`, `--system-prompt` and friends survive the round trip instead of silently widening back to pi defaults. The model and thinking level deliberately come from the file, not the record: reattachment is pi's own resume path — `SessionManager.open` plus `createAgentSessionFromServices` on a non-empty manager takes `createAgentSession`'s has-existing-session branch — and the file reflects any later changes. A record-less child (dispatched before the record existed, or `/fork`-made) still attaches, with pi's default tools and resources.
|
|
75
75
|
|
|
76
|
-
The dispatch preamble — built at the dispatch call site, sent verbatim by `runChildTurns`, or its pane counterpart for `-h` children — doubles as the boundary marker: `splitAtDispatchBoundary` finds the first user message carrying either and recovers the child's own conversation for the overlay and the inherited base whose fingerprint re-arms the rebase fast-forward. The fingerprint compares content, not file bookkeeping: rebuilding custom, compaction, and branch summaries from a session file re-stamps their timestamps (and a branch summary's `fromId`), so `conversationFingerprint` drops those fields or a round-tripped base would never equal main's live context. Without a recognizable boundary (a fork child, or a child whose compaction summarized it away) the whole context attaches and the base is a sentinel that never matches, so `r` stays withheld.
|
|
76
|
+
The dispatch preamble — built at the dispatch call site, sent verbatim by `runChildTurns`, or its pane counterpart for `-h` children — doubles as the boundary marker: `splitAtDispatchBoundary` finds the first user message carrying either and recovers the child's own conversation for the overlay and the inherited base whose fingerprint re-arms the rebase fast-forward. The system messages directly before that prompt belong to the child's conversation: Pi persists the child's prompt and tool changes there on its first request, and counting them as base made every reattached child look drifted. The fingerprint compares content, not file bookkeeping: rebuilding custom, compaction, and branch summaries from a session file re-stamps their timestamps (and a branch summary's `fromId`), so `conversationFingerprint` drops those fields or a round-tripped base would never equal main's live context. Without a recognizable boundary (a fork child, or a child whose compaction summarized it away) the whole context attaches and the base is a sentinel that never matches, so `r` stays withheld.
|
|
77
77
|
|
|
78
78
|
The attached agent parks idle first (`runAttachedTurns` waits for an instruction before running the shared turn loop), and its latest persisted response seeds `pendingSquashMessage`, so steering, `s`, and `r` work immediately without a new turn. A successful attach leaves an `Attached session …` breadcrumb — the detach line's counterpart — and both success states end with a "View agent?" confirmation that opens the overlay.
|
|
79
79
|
|
package/attach.ts
CHANGED
|
@@ -53,7 +53,8 @@ export type DispatchBoundary = {
|
|
|
53
53
|
/**
|
|
54
54
|
* Split a child's resolved context at its dispatch boundary — the first user message carrying
|
|
55
55
|
* a dispatch preamble. Undefined when no message carries one (a /fork child, or a child whose
|
|
56
|
-
* own compaction summarized the boundary away).
|
|
56
|
+
* own compaction summarized the boundary away). The system messages right before that prompt are
|
|
57
|
+
* the child's own: Pi persists its prompt and tool changes there on the child's first request.
|
|
57
58
|
*/
|
|
58
59
|
export function splitAtDispatchBoundary(
|
|
59
60
|
messages: readonly AgentMessage[],
|
|
@@ -63,9 +64,11 @@ export function splitAtDispatchBoundary(
|
|
|
63
64
|
const text = userMessageText(message);
|
|
64
65
|
const preamble = DISPATCH_PREAMBLES.find((candidate) => text.startsWith(candidate));
|
|
65
66
|
if (preamble === undefined) continue;
|
|
67
|
+
let conversationStart = index;
|
|
68
|
+
while (messages[conversationStart - 1]?.role === "system") conversationStart -= 1;
|
|
66
69
|
return {
|
|
67
|
-
base: messages.slice(0,
|
|
68
|
-
conversation: messages.slice(
|
|
70
|
+
base: messages.slice(0, conversationStart),
|
|
71
|
+
conversation: messages.slice(conversationStart),
|
|
69
72
|
task: text.slice(preamble.length),
|
|
70
73
|
};
|
|
71
74
|
}
|
package/autocomplete.ts
CHANGED
|
@@ -29,6 +29,7 @@ type CompletionTool = { name: string; description: string };
|
|
|
29
29
|
type CompletionResourceCommand = ReturnType<ExtensionAPI["getCommands"]>[number];
|
|
30
30
|
type ModelScopeSource = Pick<ExtensionContext["modelRegistry"], "getAvailable">;
|
|
31
31
|
type AutocompleteRuntimeEditor = EditorComponent & {
|
|
32
|
+
cancelAutocomplete?: () => void;
|
|
32
33
|
forceFileAutocomplete?: (explicitTab?: boolean) => void;
|
|
33
34
|
getCursor?: () => { line: number; col: number };
|
|
34
35
|
isShowingAutocomplete?: () => boolean;
|
|
@@ -449,15 +450,19 @@ function decorateAutocompleteEditor<T extends EditorComponent>(
|
|
|
449
450
|
): T {
|
|
450
451
|
const runtimeEditor = editor as DecoratedEditor;
|
|
451
452
|
if (runtimeEditor[DECORATED_EDITOR]) return editor;
|
|
453
|
+
const cancelAutocomplete = runtimeEditor.cancelAutocomplete;
|
|
454
|
+
if (typeof cancelAutocomplete !== "function")
|
|
455
|
+
throw new Error("pi-user-agents autocomplete requires editor.cancelAutocomplete()");
|
|
452
456
|
const handleInput = editor.handleInput.bind(editor);
|
|
453
457
|
editor.handleInput = (data) => {
|
|
454
458
|
const getCursor = runtimeEditor.getCursor;
|
|
455
459
|
if (typeof getCursor !== "function")
|
|
456
460
|
throw new Error("pi-user-agents autocomplete requires editor.getCursor()");
|
|
457
461
|
const beforeCursor = getCursor.call(editor);
|
|
462
|
+
const beforeText = editor.getText();
|
|
458
463
|
const beforeExpectation =
|
|
459
464
|
beforeCursor.line === 0
|
|
460
|
-
? analyzeAgentEditorInput(
|
|
465
|
+
? analyzeAgentEditorInput(beforeText, beforeCursor.col)
|
|
461
466
|
: undefined;
|
|
462
467
|
const wasShowingAutocomplete = runtimeEditor.isShowingAutocomplete?.call(editor) ?? false;
|
|
463
468
|
const printableCharacter =
|
|
@@ -466,8 +471,12 @@ function decorateAutocompleteEditor<T extends EditorComponent>(
|
|
|
466
471
|
handleInput(data);
|
|
467
472
|
|
|
468
473
|
const cursor = getCursor.call(editor);
|
|
474
|
+
const expectation =
|
|
475
|
+
cursor.line === 0 ? analyzeAgentEditorInput(editor.getText(), cursor.col) : undefined;
|
|
476
|
+
// Pi's word and line jumps do not refresh an open menu. Dismiss it before the next key.
|
|
477
|
+
if (wasShowingAutocomplete && beforeExpectation && !expectation && editor.getText() === beforeText)
|
|
478
|
+
cancelAutocomplete.call(editor);
|
|
469
479
|
if (cursor.line !== 0) return;
|
|
470
|
-
const expectation = analyzeAgentEditorInput(editor.getText(), cursor.col);
|
|
471
480
|
const typedOptionDash = printableCharacter === "-" && expectation?.kind === "option";
|
|
472
481
|
const completionAcceptance = isCompletionAcceptance(data);
|
|
473
482
|
const typedEagerValueBoundary =
|