@agimon-ai/po-git 0.0.0-alpha.3 → 0.0.0-alpha.30
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/LICENSE +149 -21
- package/README.md +16 -4
- package/dist/extensions/native.mjs +1642 -456
- package/dist/extensions/native.mjs.map +1 -1
- package/dist/extensions/server.cjs +241 -73
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +240 -72
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +1373 -1292
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{worktreeOperations-2PaaLPTF.mjs → worktreeOperations-BqLLypS5.mjs} +86 -41
- package/dist/worktreeOperations-BqLLypS5.mjs.map +1 -0
- package/dist/{worktreeOperations-C6_xikos.cjs → worktreeOperations-CU0iV2h-.cjs} +124 -49
- package/dist/worktreeOperations-CU0iV2h-.cjs.map +1 -0
- package/llms.txt +1 -1
- package/package.json +21 -20
- package/src/prompts/{po-use-git → codemode-how-to-git}/SKILL.md +34 -31
- package/dist/worktreeOperations-2PaaLPTF.mjs.map +0 -1
- package/dist/worktreeOperations-C6_xikos.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region generated/server.d.ts
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const serverPlugin: import("@agimon-ai/po-core/serverPlugin").PoServerPlugin;
|
|
3
3
|
//#endregion
|
|
4
|
-
export {
|
|
4
|
+
export { serverPlugin as default };
|
|
5
5
|
//# sourceMappingURL=server.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAuBa,wDAAY"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as READ_TIMEOUT_MS, E as runGit, S as NETWORK_TIMEOUT_MS, T as createWorktreeGit, a as GIT_WORKTREE_LIFECYCLE_EVENT, c as isWorktreeLifecycleEvent, d as registryFile, f as readJson, g as invalidRequest, h as PoGitExpectedError, i as GIT_SESSION_CHANGED_EVENT, l as credentialsFile, m as HubUnavailableError, n as createWorktreeRegistry, o as createWorktreeMessageInbox, p as writeJsonAtomic, r as validRefName, s as isSessionChangedEvent, t as createWorktreeOperations, u as poGitRoot, w as REBASE_TIMEOUT_MS } from "../worktreeOperations-BqLLypS5.mjs";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { composeServerProviderOverrides, defineServerPlugin, packageResourcePath, readPackageResource } from "@agimon-ai/po-core/serverPlugin";
|
|
6
|
+
import { composeRootHooks, composeRootInjects, defineChannel, defineHook, defineResource, defineRoot, defineRoute, defineServerTool } from "@agimon-ai/po-core/extensionFile";
|
|
7
|
+
import { PO_CODEMODE_SERVICE, readPoCodemode } from "@agimon-ai/po-core/codemode";
|
|
8
|
+
import { readPoSessionDelivery } from "@agimon-ai/po-session";
|
|
8
9
|
import { Type } from "typebox";
|
|
9
10
|
//#region src/schemas/runWorktreeTool.ts
|
|
10
11
|
/**
|
|
@@ -247,7 +248,8 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
247
248
|
content: text(describe(record, context.sessionId), `Session: ${record.sessionId}`, dirtyFiles.length === 0 ? "Clean." : `${String(dirtyFiles.length)} uncommitted file(s): ${dirtyFiles.slice(0, 20).join(", ")}`),
|
|
248
249
|
details: {
|
|
249
250
|
action: params.action,
|
|
250
|
-
records: [record]
|
|
251
|
+
records: [record],
|
|
252
|
+
dirtyFiles
|
|
251
253
|
}
|
|
252
254
|
};
|
|
253
255
|
}
|
|
@@ -271,7 +273,10 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
271
273
|
const waiting = await operations.messages(context, params.id);
|
|
272
274
|
return {
|
|
273
275
|
content: waiting.length === 0 ? text("No messages.") : text(`${String(waiting.length)} message(s):`, ...waiting.map((entry) => `[${entry.sentAt}] ${entry.from}: ${entry.text}`)),
|
|
274
|
-
details: {
|
|
276
|
+
details: {
|
|
277
|
+
action: params.action,
|
|
278
|
+
messages: waiting
|
|
279
|
+
}
|
|
275
280
|
};
|
|
276
281
|
}
|
|
277
282
|
case WORKTREE_ACTIONS.prune: {
|
|
@@ -286,12 +291,172 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
286
291
|
`Untracked directories, left alone: ${plan.untracked.length === 0 ? "none" : plan.untracked.join(", ")}`,
|
|
287
292
|
...plan.keptBranches.length === 0 ? [] : [`Branches kept, they hold commits: ${plan.keptBranches.join(", ")}`]
|
|
288
293
|
]),
|
|
289
|
-
details: {
|
|
294
|
+
details: {
|
|
295
|
+
action: params.action,
|
|
296
|
+
plan
|
|
297
|
+
}
|
|
290
298
|
};
|
|
291
299
|
}
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
302
|
//#endregion
|
|
303
|
+
//#region src/services/worktreeNamespace/index.ts
|
|
304
|
+
const DECLARATIONS = `type WorktreeStatus = 'spawning' | 'running' | 'closing' | 'orphaned';
|
|
305
|
+
|
|
306
|
+
interface Worktree {
|
|
307
|
+
id: string;
|
|
308
|
+
branch: string;
|
|
309
|
+
baseRef: string;
|
|
310
|
+
/** Absolute path of the checkout, under ~/.po/git/worktrees. */
|
|
311
|
+
path: string;
|
|
312
|
+
repositoryRoot: string;
|
|
313
|
+
/** The session running inside the worktree. */
|
|
314
|
+
sessionId: string;
|
|
315
|
+
parentSessionId?: string;
|
|
316
|
+
status: WorktreeStatus;
|
|
317
|
+
createdAt: string;
|
|
318
|
+
/** How your session relates to it: 'parent' spawned it, 'child' runs inside it, 'other' is neither. */
|
|
319
|
+
relationship: 'parent' | 'child' | 'other';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
interface WorktreeMessage {
|
|
323
|
+
worktreeId: string;
|
|
324
|
+
fromSessionId: string;
|
|
325
|
+
from: 'parent' | 'child';
|
|
326
|
+
text: string;
|
|
327
|
+
sentAt: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
interface PrunePlan {
|
|
331
|
+
remove: Worktree[];
|
|
332
|
+
forget: Worktree[];
|
|
333
|
+
/** Kept because they hold uncommitted work. */
|
|
334
|
+
keptDirty: Worktree[];
|
|
335
|
+
untracked: string[];
|
|
336
|
+
/** Branches kept because they hold commits. */
|
|
337
|
+
keptBranches: string[];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
declare const worktrees: {
|
|
341
|
+
/** Every worktree for this repository. */
|
|
342
|
+
list(): Promise<Worktree[]>;
|
|
343
|
+
/** One worktree and its uncommitted files. */
|
|
344
|
+
status(id: string): Promise<{ worktree: Worktree; dirtyFiles: string[] }>;
|
|
345
|
+
/**
|
|
346
|
+
* Create a worktree on a new branch and start a Po session in it, nested under this one. baseRef defaults to
|
|
347
|
+
* the current branch's remote-tracking ref, then the remote default branch; name labels it in the rail; task is
|
|
348
|
+
* the new session's first message. Setup can be slow, so give exec a larger timeoutMs.
|
|
349
|
+
*/
|
|
350
|
+
spawn(request: { branch: string; baseRef?: string; name?: string; task?: string }): Promise<Worktree>;
|
|
351
|
+
/** Stop the worktree's session and delete its checkout. Refused on a dirty tree unless force is true. */
|
|
352
|
+
close(id: string, options?: { force?: boolean }): Promise<Worktree>;
|
|
353
|
+
/** Merge the worktree's branch into this checkout. Refused when this checkout is dirty. */
|
|
354
|
+
merge(id: string, options?: { message?: string }): Promise<Worktree>;
|
|
355
|
+
/** Remove orphaned worktrees. With dryRun, only return the plan. */
|
|
356
|
+
prune(options?: { dryRun?: boolean }): Promise<PrunePlan>;
|
|
357
|
+
/**
|
|
358
|
+
* Message the other side of a worktree. Use the record with relationship 'child' to report to your parent,
|
|
359
|
+
* and 'parent' to reach your own children. 'other' records are not communication edges.
|
|
360
|
+
*/
|
|
361
|
+
send(id: string, message: string): Promise<void>;
|
|
362
|
+
/** Messages waiting for you from a worktree. */
|
|
363
|
+
messages(id: string): Promise<WorktreeMessage[]>;
|
|
364
|
+
};`;
|
|
365
|
+
const json = (value) => JSON.parse(JSON.stringify(value));
|
|
366
|
+
/** Git worktrees for code mode: the actions of `run_worktree` as methods, returning records instead of text. */
|
|
367
|
+
function createWorktreeNamespace(options) {
|
|
368
|
+
return {
|
|
369
|
+
name: "worktrees",
|
|
370
|
+
summary: "Git worktrees, each with its own Po session nested under this one: worktrees.list, status, spawn, close, merge, prune, send and messages.",
|
|
371
|
+
declarations: DECLARATIONS,
|
|
372
|
+
bind(run) {
|
|
373
|
+
if (run.sessionId !== options.execution.sessionId) throw new Error("Worktrees namespace belongs to another session.");
|
|
374
|
+
let disposed = false;
|
|
375
|
+
run.onDispose(() => {
|
|
376
|
+
disposed = true;
|
|
377
|
+
});
|
|
378
|
+
const view = (record) => ({
|
|
379
|
+
...record,
|
|
380
|
+
relationship: record.parentSessionId === options.execution.sessionId ? "parent" : record.sessionId === options.execution.sessionId ? "child" : "other"
|
|
381
|
+
});
|
|
382
|
+
/** One `run_worktree` action through the tool's own code, so the tool and the namespace cannot drift. */
|
|
383
|
+
const act = async (name, action, input) => {
|
|
384
|
+
run.signal.throwIfAborted();
|
|
385
|
+
if (disposed) throw new Error("Worktrees namespace run is disposed.");
|
|
386
|
+
options.assertAvailable();
|
|
387
|
+
const params = validateParams({
|
|
388
|
+
action,
|
|
389
|
+
...typeof input === "object" && input !== null ? input : {}
|
|
390
|
+
});
|
|
391
|
+
const context = {
|
|
392
|
+
cwd: options.execution.cwd,
|
|
393
|
+
sessionId: options.execution.sessionId
|
|
394
|
+
};
|
|
395
|
+
const started = Date.now();
|
|
396
|
+
try {
|
|
397
|
+
const result = await executeRunWorktreeTool(params, options.operations, context, { signal: run.signal });
|
|
398
|
+
options.assertAvailable();
|
|
399
|
+
run.record?.({
|
|
400
|
+
name: `worktrees.${name}`,
|
|
401
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
402
|
+
args: params,
|
|
403
|
+
ok: true,
|
|
404
|
+
ms: Date.now() - started,
|
|
405
|
+
result
|
|
406
|
+
});
|
|
407
|
+
return result;
|
|
408
|
+
} catch (error) {
|
|
409
|
+
run.record?.({
|
|
410
|
+
name: `worktrees.${name}`,
|
|
411
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
412
|
+
args: params,
|
|
413
|
+
ok: false,
|
|
414
|
+
ms: Date.now() - started,
|
|
415
|
+
error: error instanceof Error ? error.message : String(error)
|
|
416
|
+
});
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const record = (result) => view(result.details.records[0]);
|
|
421
|
+
const members = Object.create(null);
|
|
422
|
+
members.list = async () => json((await act("list", "list", {})).details.records.map(view));
|
|
423
|
+
members.status = async (id) => {
|
|
424
|
+
const { details } = await act("status", "status", { id });
|
|
425
|
+
return json({
|
|
426
|
+
worktree: view(details.records[0]),
|
|
427
|
+
dirtyFiles: details.dirtyFiles ?? []
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
members.spawn = async (request) => json(record(await act("spawn", "spawn_worktree", request)));
|
|
431
|
+
members.close = async (id, settings) => json(record(await act("close", "close_worktree", {
|
|
432
|
+
...settings,
|
|
433
|
+
id
|
|
434
|
+
})));
|
|
435
|
+
members.merge = async (id, settings) => json(record(await act("merge", "merge", {
|
|
436
|
+
...settings,
|
|
437
|
+
id
|
|
438
|
+
})));
|
|
439
|
+
members.prune = async (settings) => {
|
|
440
|
+
const { plan } = (await act("prune", "prune", settings)).details;
|
|
441
|
+
return json({
|
|
442
|
+
...plan,
|
|
443
|
+
remove: plan.remove.map(view),
|
|
444
|
+
forget: plan.forget.map(view),
|
|
445
|
+
keptDirty: plan.keptDirty.map(view)
|
|
446
|
+
});
|
|
447
|
+
};
|
|
448
|
+
members.send = async (id, message) => {
|
|
449
|
+
await act("send", "send", {
|
|
450
|
+
id,
|
|
451
|
+
message
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
members.messages = async (id) => json((await act("messages", "messages", { id })).details.messages);
|
|
455
|
+
return members;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
//#endregion
|
|
295
460
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
|
|
296
461
|
function progressResult(action, label) {
|
|
297
462
|
return {
|
|
@@ -302,9 +467,9 @@ function progressResult(action, label) {
|
|
|
302
467
|
details: { action }
|
|
303
468
|
};
|
|
304
469
|
}
|
|
305
|
-
function createGitSession({ context, host: serverHost }) {
|
|
306
|
-
if (serverHost.context.directEvents === void 0) throw new Error("Git
|
|
307
|
-
if (serverHost.context.sessionId === void 0) throw new Error("Git
|
|
470
|
+
function createGitSession({ context, host: serverHost, agent, signal }) {
|
|
471
|
+
if (serverHost.context.directEvents === void 0) throw new Error("Git server plugin requires the session direct event bus.");
|
|
472
|
+
if (serverHost.context.sessionId === void 0) throw new Error("Git server plugin requires a session identity.");
|
|
308
473
|
const sessionId = serverHost.context.sessionId;
|
|
309
474
|
const directEvents = serverHost.context.directEvents;
|
|
310
475
|
const messageInbox = createWorktreeMessageInbox(directEvents, sessionId);
|
|
@@ -312,23 +477,15 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
312
477
|
git: createWorktreeGit(),
|
|
313
478
|
sessionService: serverHost.context.sessionService,
|
|
314
479
|
messageInbox,
|
|
315
|
-
sessionDelivery: () =>
|
|
480
|
+
sessionDelivery: () => readPoSessionDelivery(context)
|
|
316
481
|
});
|
|
317
482
|
const resources = [{
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
attribution: {
|
|
321
|
-
kind: "minor",
|
|
322
|
-
mode: "help"
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
name: "po-git",
|
|
326
|
-
kind: "context",
|
|
327
|
-
read: () => readPackageResource(import.meta.url, "llms.txt")
|
|
328
|
-
}, {
|
|
329
|
-
name: "po-use-git",
|
|
483
|
+
name: "codemode-how-to-git",
|
|
484
|
+
description: "Use the worktrees namespace to create, inspect, message, merge, and close Git worktree sessions through code mode.",
|
|
330
485
|
kind: "skill",
|
|
331
|
-
|
|
486
|
+
pinned: true,
|
|
487
|
+
path: packageResourcePath(import.meta.url, "src/prompts/codemode-how-to-git/SKILL.md"),
|
|
488
|
+
read: () => readPackageResource(import.meta.url, "src/prompts/codemode-how-to-git/SKILL.md")
|
|
332
489
|
}];
|
|
333
490
|
const tool = {
|
|
334
491
|
name: RUN_WORKTREE_TOOL_NAME,
|
|
@@ -348,17 +505,37 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
348
505
|
const changed = (_event, execution) => {
|
|
349
506
|
directEvents.publish(GIT_SESSION_CHANGED_EVENT, execution.sessionId, { version: 1 });
|
|
350
507
|
};
|
|
508
|
+
const hooks = [{
|
|
509
|
+
event: "tool_execution_end",
|
|
510
|
+
handle: changed
|
|
511
|
+
}, {
|
|
512
|
+
event: "agent_settled",
|
|
513
|
+
handle: changed
|
|
514
|
+
}];
|
|
515
|
+
if (agent) {
|
|
516
|
+
const namespace = createWorktreeNamespace({
|
|
517
|
+
operations,
|
|
518
|
+
execution: agent.context,
|
|
519
|
+
assertAvailable: () => {
|
|
520
|
+
if (signal?.aborted) throw new Error("Git session is unavailable.");
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
context.inject([PO_CODEMODE_SERVICE], (scoped) => {
|
|
524
|
+
const codemode = readPoCodemode(scoped);
|
|
525
|
+
if (!codemode) return void 0;
|
|
526
|
+
const registration = codemode.register("@agimon-ai/po-git", namespace);
|
|
527
|
+
const restriction = agent.registerToolRestriction({ excludedTools: [RUN_WORKTREE_TOOL_NAME] });
|
|
528
|
+
return () => {
|
|
529
|
+
restriction.dispose();
|
|
530
|
+
registration.dispose();
|
|
531
|
+
};
|
|
532
|
+
});
|
|
533
|
+
}
|
|
351
534
|
return {
|
|
352
535
|
value: {
|
|
353
536
|
resources,
|
|
354
537
|
tool,
|
|
355
|
-
hooks
|
|
356
|
-
event: "tool_execution_end",
|
|
357
|
-
handle: changed
|
|
358
|
-
}, {
|
|
359
|
-
event: "agent_settled",
|
|
360
|
-
handle: changed
|
|
361
|
-
}]
|
|
538
|
+
hooks
|
|
362
539
|
},
|
|
363
540
|
onDispose: () => messageInbox.close()
|
|
364
541
|
};
|
|
@@ -928,7 +1105,7 @@ function createGitAuthStore(homeDir = os.homedir()) {
|
|
|
928
1105
|
const key = workspaceKey(workspaceRoot);
|
|
929
1106
|
const current = readFile();
|
|
930
1107
|
const next = parseGitAuthSave(request, current.workspaces[key] ?? { method: "none" }, homeDir);
|
|
931
|
-
const root =
|
|
1108
|
+
const root = poGitRoot(homeDir);
|
|
932
1109
|
fs.mkdirSync(root, {
|
|
933
1110
|
recursive: true,
|
|
934
1111
|
mode: OWNER_ONLY_DIRECTORY
|
|
@@ -1100,7 +1277,7 @@ function badRequest(message) {
|
|
|
1100
1277
|
return Response.json({ error: message }, { status: 400 });
|
|
1101
1278
|
}
|
|
1102
1279
|
function failed(error) {
|
|
1103
|
-
if (error instanceof
|
|
1280
|
+
if (error instanceof PoGitExpectedError) return Response.json({
|
|
1104
1281
|
error: error.message,
|
|
1105
1282
|
code: error.code
|
|
1106
1283
|
}, { status: error.retryable ? 503 : 409 });
|
|
@@ -1191,7 +1368,7 @@ var route_server_default = defineRoute({
|
|
|
1191
1368
|
try {
|
|
1192
1369
|
return Response.json(gitAuth.save(root, await request.json().catch(() => void 0)));
|
|
1193
1370
|
} catch (error) {
|
|
1194
|
-
if (error instanceof
|
|
1371
|
+
if (error instanceof PoGitExpectedError && error.code === "invalid_request") {
|
|
1195
1372
|
const text = error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1196
1373
|
return Response.json({
|
|
1197
1374
|
error: text,
|
|
@@ -1366,12 +1543,12 @@ const RULES = [
|
|
|
1366
1543
|
}
|
|
1367
1544
|
];
|
|
1368
1545
|
function classifyGitFailure(operation, result, mismatch) {
|
|
1369
|
-
if (result.timedOut) return new
|
|
1546
|
+
if (result.timedOut) return new PoGitExpectedError("git_timeout", `git ${operation} took too long and was stopped.`, true, "Check the network, then try again.");
|
|
1370
1547
|
const text = `${result.stderr}\n${result.stdout}`.toLowerCase();
|
|
1371
1548
|
const rule = RULES.find((candidate) => candidate.phrases.some((phrase) => text.includes(phrase)));
|
|
1372
|
-
if (rule === void 0) return new
|
|
1549
|
+
if (rule === void 0) return new PoGitExpectedError("git_failed", `git ${operation} failed (exit ${String(result.code)}).`, false, "Run the same command in a terminal in this checkout to see what git says.");
|
|
1373
1550
|
const recovery = rule.code === "auth_failed" && mismatch !== void 0 ? `${mismatch} ${rule.recovery}` : rule.recovery;
|
|
1374
|
-
return new
|
|
1551
|
+
return new PoGitExpectedError(rule.code, rule.message, rule.retryable, recovery);
|
|
1375
1552
|
}
|
|
1376
1553
|
//#endregion
|
|
1377
1554
|
//#region src/services/gitSync/index.ts
|
|
@@ -1394,7 +1571,7 @@ function classifyGitFailure(operation, result, mismatch) {
|
|
|
1394
1571
|
*/
|
|
1395
1572
|
const DONE = { outcome: "done" };
|
|
1396
1573
|
function expected(code, message, recovery) {
|
|
1397
|
-
return new
|
|
1574
|
+
return new PoGitExpectedError(code, message, false, recovery);
|
|
1398
1575
|
}
|
|
1399
1576
|
async function readLine(cwd, args) {
|
|
1400
1577
|
const result = await runGit(cwd, args, { timeout: READ_TIMEOUT_MS });
|
|
@@ -1583,9 +1760,9 @@ const GIT_CHANGES_TYPE = "git_changes";
|
|
|
1583
1760
|
* DESIGN PATTERNS:
|
|
1584
1761
|
* - Apart from the worktrees channel. Worktrees are repository management;
|
|
1585
1762
|
* this is the session's own code change.
|
|
1586
|
-
* - Push, not poll. The session
|
|
1587
|
-
* tool call and settled turn; recounts are coalesced to one in
|
|
1588
|
-
* one rerun, so a burst of tool calls costs at most two.
|
|
1763
|
+
* - Push, not poll. The session's server plugin publishes a "changed" event
|
|
1764
|
+
* after every tool call and settled turn; recounts are coalesced to one in
|
|
1765
|
+
* flight plus one rerun, so a burst of tool calls costs at most two.
|
|
1589
1766
|
* - One sync command per session. A rebase and a push must never overlap.
|
|
1590
1767
|
* - Auth is read fresh for each command from the session's workspace, and goes
|
|
1591
1768
|
* only into that command's git child.
|
|
@@ -1621,13 +1798,13 @@ const PENDING = {
|
|
|
1621
1798
|
/** The error target a failure maps to: a rejected plain push asks for a confirmed lease force. */
|
|
1622
1799
|
function errorTargetFor$1(command, error) {
|
|
1623
1800
|
if (command.action !== "push") return { action: command.action };
|
|
1624
|
-
return error instanceof
|
|
1801
|
+
return error instanceof PoGitExpectedError && error.code === "push_needs_force" && command.force !== true ? {
|
|
1625
1802
|
action: "push",
|
|
1626
1803
|
forceRequired: true
|
|
1627
1804
|
} : { action: "push" };
|
|
1628
1805
|
}
|
|
1629
1806
|
function failureText$1(error) {
|
|
1630
|
-
if (error instanceof
|
|
1807
|
+
if (error instanceof PoGitExpectedError) return error.message.replace(/^\[[a-z_]+\] /u, "").replace("\nRecovery: ", " ");
|
|
1631
1808
|
return "The git command failed.";
|
|
1632
1809
|
}
|
|
1633
1810
|
function createGitChangesChannel(options = {}) {
|
|
@@ -1745,7 +1922,7 @@ function createGitChangesChannel(options = {}) {
|
|
|
1745
1922
|
errorTarget: void 0
|
|
1746
1923
|
});
|
|
1747
1924
|
run(scope, command).then(() => update(scope.sessionId, { pending: void 0 })).catch((error) => {
|
|
1748
|
-
host.onNotice(`git ${command.action} failed for ${scope.sessionId} (${error instanceof
|
|
1925
|
+
host.onNotice(`git ${command.action} failed for ${scope.sessionId} (${error instanceof PoGitExpectedError ? error.code : "unexpected"})`);
|
|
1749
1926
|
update(scope.sessionId, {
|
|
1750
1927
|
pending: void 0,
|
|
1751
1928
|
error: failureText$1(error),
|
|
@@ -1853,11 +2030,15 @@ function parseCommand(payload) {
|
|
|
1853
2030
|
/**
|
|
1854
2031
|
* What the reader is told when a command fails.
|
|
1855
2032
|
*
|
|
1856
|
-
* An expected failure explains itself
|
|
1857
|
-
*
|
|
2033
|
+
* An expected failure explains itself, minus the `[code]` tag and recovery line
|
|
2034
|
+
* written for the agent's tool result. A dirty close is said in the person's
|
|
2035
|
+
* terms, because its message tells the agent to pass `force`. Anything else is
|
|
2036
|
+
* summarised, never echoed: a git error can carry a remote URL with a token in it.
|
|
1858
2037
|
*/
|
|
1859
2038
|
function failureText(error) {
|
|
1860
|
-
|
|
2039
|
+
if (!(error instanceof PoGitExpectedError)) return "The worktree operation failed.";
|
|
2040
|
+
if (error.code === "worktree_dirty") return "The worktree has uncommitted changes.";
|
|
2041
|
+
return error.message.replace(/^\[[a-z_]+\] /u, "").split("\nRecovery:")[0] ?? error.message;
|
|
1861
2042
|
}
|
|
1862
2043
|
/** Starts one direct-event subscription per managed session. */
|
|
1863
2044
|
function createWorktreesChannel(options = {}) {
|
|
@@ -2028,20 +2209,8 @@ var agent_settled_server_default = defineHook((context) => context.root.hooks[1]
|
|
|
2028
2209
|
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts
|
|
2029
2210
|
var tool_execution_end_server_default = defineHook((context) => context.root.hooks[0]);
|
|
2030
2211
|
//#endregion
|
|
2031
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGit.server.ts
|
|
2032
|
-
var poGit_server_default$1 = (context) => context.root.resources[0];
|
|
2033
|
-
//#endregion
|
|
2034
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGit.server.ts
|
|
2035
|
-
var poGit_server_default = defineResource(poGit_server_default$1);
|
|
2036
|
-
//#endregion
|
|
2037
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGitReadme.server.ts
|
|
2038
|
-
var poGitReadme_server_default$1 = (context) => context.root.resources[2];
|
|
2039
|
-
//#endregion
|
|
2040
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGitReadme.server.ts
|
|
2041
|
-
var poGitReadme_server_default = defineResource(poGitReadme_server_default$1);
|
|
2042
|
-
//#endregion
|
|
2043
2212
|
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poUseGit.server.ts
|
|
2044
|
-
var poUseGit_server_default$1 = (context) => context.root.resources[
|
|
2213
|
+
var poUseGit_server_default$1 = (context) => context.root.resources[0];
|
|
2045
2214
|
//#endregion
|
|
2046
2215
|
//#region src/extensions/workspaces/sessions/(backend)/resource/poUseGit.server.ts
|
|
2047
2216
|
var poUseGit_server_default = defineResource(poUseGit_server_default$1);
|
|
@@ -2051,19 +2220,22 @@ var runWorktree_server_default = defineServerTool((context) => context.root.tool
|
|
|
2051
2220
|
//#endregion
|
|
2052
2221
|
//#region generated/server.ts
|
|
2053
2222
|
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
2223
|
+
/* v8 ignore next -- generated normalization branch */
|
|
2224
|
+
const defined = (values) => values.filter((value) => value !== void 0);
|
|
2054
2225
|
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
2055
2226
|
...identity,
|
|
2056
2227
|
...value
|
|
2057
2228
|
};
|
|
2058
|
-
const
|
|
2229
|
+
const serverPlugin = defineServerPlugin({
|
|
2059
2230
|
name: "@agimon-ai/po-git",
|
|
2060
2231
|
providerOverrides: composeServerProviderOverrides(root_server_default),
|
|
2232
|
+
injects: composeRootInjects({ session: [root_server_default] }),
|
|
2061
2233
|
global: (context) => ({
|
|
2062
2234
|
get api() {
|
|
2063
2235
|
return [at(route_server_default, context)];
|
|
2064
2236
|
},
|
|
2065
2237
|
get channels() {
|
|
2066
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2238
|
+
return [() => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))];
|
|
2067
2239
|
}
|
|
2068
2240
|
}),
|
|
2069
2241
|
workspace: (context) => ({
|
|
@@ -2071,11 +2243,11 @@ const facet = defineServerPlugin({
|
|
|
2071
2243
|
return [at(route_server_default, context)];
|
|
2072
2244
|
},
|
|
2073
2245
|
get channels() {
|
|
2074
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2246
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2075
2247
|
}
|
|
2076
2248
|
}),
|
|
2077
2249
|
session: async (context) => {
|
|
2078
|
-
const scopeSession = await root_server_default
|
|
2250
|
+
const scopeSession = await context.scopeRoot("session", root_server_default, context);
|
|
2079
2251
|
const contextSession = {
|
|
2080
2252
|
...context,
|
|
2081
2253
|
root: scopeSession.value
|
|
@@ -2086,17 +2258,13 @@ const facet = defineServerPlugin({
|
|
|
2086
2258
|
return [at(route_server_default, context)];
|
|
2087
2259
|
},
|
|
2088
2260
|
get channels() {
|
|
2089
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2261
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2090
2262
|
},
|
|
2091
2263
|
get hooks() {
|
|
2092
2264
|
return [via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)), via({ event: "tool_execution_end" }, at(tool_execution_end_server_default, contextSession))];
|
|
2093
2265
|
},
|
|
2094
2266
|
get resources() {
|
|
2095
|
-
return [
|
|
2096
|
-
at(poGit_server_default, contextSession),
|
|
2097
|
-
at(poGitReadme_server_default, contextSession),
|
|
2098
|
-
at(poUseGit_server_default, contextSession)
|
|
2099
|
-
];
|
|
2267
|
+
return [at(poUseGit_server_default, contextSession)];
|
|
2100
2268
|
},
|
|
2101
2269
|
get tools() {
|
|
2102
2270
|
return [via({ name: "run_worktree" }, at(runWorktree_server_default, contextSession))];
|
|
@@ -2109,6 +2277,6 @@ const facet = defineServerPlugin({
|
|
|
2109
2277
|
}
|
|
2110
2278
|
});
|
|
2111
2279
|
//#endregion
|
|
2112
|
-
export {
|
|
2280
|
+
export { serverPlugin as default, serverPlugin };
|
|
2113
2281
|
|
|
2114
2282
|
//# sourceMappingURL=server.mjs.map
|