@1yefuwang1/dsh-worktrees 0.0.0-stage → 0.2.13
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/CHANGELOG.md +218 -0
- package/LICENSE +21 -0
- package/README.md +471 -2
- package/client.js +1398 -0
- package/cordis.patch.yml +6 -0
- package/dist/context.d.ts +20 -0
- package/dist/context.js +70 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +30 -0
- package/dist/first-message.d.ts +19 -0
- package/dist/first-message.js +121 -0
- package/dist/git.d.ts +45 -0
- package/dist/git.js +806 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +183 -0
- package/dist/naming.d.ts +25 -0
- package/dist/naming.js +151 -0
- package/dist/project-context.d.ts +20 -0
- package/dist/project-context.js +82 -0
- package/dist/project-rpc.d.ts +10 -0
- package/dist/project-rpc.js +103 -0
- package/dist/project-store.d.ts +121 -0
- package/dist/project-store.js +32 -0
- package/dist/projects.d.ts +149 -0
- package/dist/projects.js +603 -0
- package/dist/quiet-rpc.d.ts +7 -0
- package/dist/quiet-rpc.js +125 -0
- package/dist/rpc.d.ts +10 -0
- package/dist/rpc.js +418 -0
- package/dist/runtime.d.ts +22 -0
- package/dist/runtime.js +123 -0
- package/dist/schema.d.ts +50 -0
- package/dist/schema.js +86 -0
- package/dist/service.d.ts +98 -0
- package/dist/service.js +943 -0
- package/dist/sessions.d.ts +31 -0
- package/dist/sessions.js +130 -0
- package/dist/store.d.ts +114 -0
- package/dist/store.js +44 -0
- package/dist/types.d.ts +262 -0
- package/dist/types.js +1 -0
- package/icon.svg +1 -0
- package/locale/en.json +6 -0
- package/package.json +106 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
3
|
+
import type { SessionSettings } from './types.js';
|
|
4
|
+
export type { SessionSettings } from './types.js';
|
|
5
|
+
export declare class SessionBootstrap {
|
|
6
|
+
private readonly owner;
|
|
7
|
+
private readonly handles;
|
|
8
|
+
constructor(owner: Context);
|
|
9
|
+
actor(id: string): Agent;
|
|
10
|
+
isLive(agent: Agent): boolean;
|
|
11
|
+
settings(agent: Agent, signal?: AbortSignal): Promise<SessionSettings>;
|
|
12
|
+
assertBlank(agent: Agent): Promise<void>;
|
|
13
|
+
create(input: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
title: string;
|
|
16
|
+
source: Agent;
|
|
17
|
+
settings: SessionSettings;
|
|
18
|
+
mode: 'new' | 'continue';
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
onCreated?: (result: {
|
|
21
|
+
agent: Agent;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
workspaceId: string;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
agent: Agent;
|
|
27
|
+
sessionId: string;
|
|
28
|
+
workspaceId: string;
|
|
29
|
+
}>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import { SessionId, SessionSeq, SessionLogOffset, buildForkSeed } from '@deepseek-ai/dsh-session';
|
|
4
|
+
import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
|
|
5
|
+
import { setApprovalPolicy } from '@deepseek-ai/dsh-user-approval';
|
|
6
|
+
import { hashValue } from './schema.js';
|
|
7
|
+
import { WorktreeError } from './errors.js';
|
|
8
|
+
import { capability, required, policyOf } from './runtime.js';
|
|
9
|
+
export class SessionBootstrap {
|
|
10
|
+
owner;
|
|
11
|
+
handles = new Map();
|
|
12
|
+
constructor(owner) { this.owner = owner; }
|
|
13
|
+
actor(id) {
|
|
14
|
+
const agent = required(this.owner, 'agents').get(id);
|
|
15
|
+
if (agent === undefined)
|
|
16
|
+
throw new WorktreeError('SESSION_NOT_LIVE', 'The source session is no longer live. Open it and retry.');
|
|
17
|
+
if (agent.session.header.origin === 'subagent')
|
|
18
|
+
throw new WorktreeError('SUBAGENT_SOURCE', 'New top-level worktree conversations require an ordinary source session.');
|
|
19
|
+
return agent;
|
|
20
|
+
}
|
|
21
|
+
isLive(agent) { return required(this.owner, 'agents').get(agent.id) === agent; }
|
|
22
|
+
async settings(agent, signal) {
|
|
23
|
+
const query = required(this.owner, 'sessionQuery');
|
|
24
|
+
const observation = await query.observeSession(agent.id, { signal, projectionMode: 'all' });
|
|
25
|
+
try {
|
|
26
|
+
if (!this.isLive(agent))
|
|
27
|
+
throw new WorktreeError('SESSION_CHANGED', 'The source session generation changed.');
|
|
28
|
+
const values = observation.projections?.values;
|
|
29
|
+
const selection = values?.modelSelection;
|
|
30
|
+
const candidate = selection?.next ?? (agent.options.provider && agent.options.model ? {
|
|
31
|
+
provider: agent.options.provider, model: agent.options.model,
|
|
32
|
+
...(agent.options.reasoningEffort === undefined ? {} : { reasoningEffort: String(agent.options.reasoningEffort) }),
|
|
33
|
+
} : null);
|
|
34
|
+
const model = candidate === null ? null : {
|
|
35
|
+
provider: candidate.provider, model: candidate.model,
|
|
36
|
+
...(candidate.reasoningEffort === undefined ? {} : { reasoningEffort: candidate.reasoningEffort }),
|
|
37
|
+
};
|
|
38
|
+
if (model !== null && (typeof model.provider !== 'string' || typeof model.model !== 'string' || !model.provider || !model.model))
|
|
39
|
+
throw new WorktreeError('INVALID_MODEL', 'The source model selection is not valid. Select a model before preparing a worktree.');
|
|
40
|
+
const presetValue = values?.agentPreset ?? observation.header.agentPreset;
|
|
41
|
+
const preset = typeof presetValue === 'string' ? presetValue : null;
|
|
42
|
+
const approval = capability(agent.ctx, 'approval');
|
|
43
|
+
const policy = approval?.overrideOf(agent.session) ?? approval?.config.policy ?? 'ask';
|
|
44
|
+
const plan = capability(agent.ctx, 'planMode')?.get(agent);
|
|
45
|
+
const base = { model, preset, sandbox: policyOf(agent).mode, approval: policy, plan: plan?.pending ?? plan?.active ?? false };
|
|
46
|
+
return { ...base, hash: hashValue(base) };
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
observation[Symbol.dispose]();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async assertBlank(agent) {
|
|
53
|
+
if (!this.isLive(agent) || agent.status !== 'idle' || agent.inbox.nextStep.length > 0 || agent.inbox.nextTurn.length > 0)
|
|
54
|
+
throw new WorktreeError('SOURCE_BUSY', 'The source must remain an idle blank conversation without queued input.');
|
|
55
|
+
const observation = await required(this.owner, 'sessionQuery').observeSession(agent.id, { projectionMode: 'all' });
|
|
56
|
+
try {
|
|
57
|
+
if (observation.events.some(e => e.type === 'turn/start'))
|
|
58
|
+
throw new WorktreeError('SOURCE_STARTED', 'The source conversation has already started. Prepare another New Conversation instead.');
|
|
59
|
+
if (!this.isLive(agent) || agent.status !== 'idle' || agent.inbox.nextStep.length > 0 || agent.inbox.nextTurn.length > 0)
|
|
60
|
+
throw new WorktreeError('SOURCE_BUSY', 'The source changed while its blank state was checked.');
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
observation[Symbol.dispose]();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async create(input) {
|
|
67
|
+
const cwd = await realpath(input.cwd);
|
|
68
|
+
if (!(await stat(cwd)).isDirectory())
|
|
69
|
+
throw new WorktreeError('MISSING_PROJECT_DIRECTORY', 'The chosen remote tree does not contain the project directory.');
|
|
70
|
+
if (input.signal?.aborted)
|
|
71
|
+
throw new WorktreeError('CANCELLED', 'Session creation was cancelled.');
|
|
72
|
+
const workspace = await required(this.owner, 'workspaceRegistry').create(cwd, input.title);
|
|
73
|
+
if (workspace.path !== cwd)
|
|
74
|
+
throw new WorktreeError('WORKSPACE_IDENTITY', 'The canonical Workspace path does not match the execution directory.');
|
|
75
|
+
let options = {
|
|
76
|
+
sessionId: SessionId(randomUUID()),
|
|
77
|
+
meta: { cwd, ...(input.settings.preset === null ? {} : { agentPreset: input.settings.preset }) },
|
|
78
|
+
signal: input.signal,
|
|
79
|
+
agentOptions: input.settings.model === null ? {} : {
|
|
80
|
+
provider: input.settings.model.provider, model: input.settings.model.model,
|
|
81
|
+
...(input.settings.model.reasoningEffort === undefined ? {} : { reasoningEffort: input.settings.model.reasoningEffort }),
|
|
82
|
+
},
|
|
83
|
+
setup: async (agentCtx, child) => {
|
|
84
|
+
const presets = capability(this.owner, 'agentPresets');
|
|
85
|
+
if (presets !== undefined) {
|
|
86
|
+
const selected = await presets.resolve(input.settings.preset ?? undefined);
|
|
87
|
+
await presets.mount(agentCtx, selected.id);
|
|
88
|
+
}
|
|
89
|
+
else if (input.settings.preset !== null)
|
|
90
|
+
throw new WorktreeError('MISSING_PRESET_SERVICE', 'The source preset cannot be mounted in this profile.');
|
|
91
|
+
child.inbox.clear();
|
|
92
|
+
setSandboxMode(child.session, input.settings.sandbox);
|
|
93
|
+
setApprovalPolicy(child.session, input.settings.approval);
|
|
94
|
+
child.session.append('plan/mode', { active: input.settings.plan });
|
|
95
|
+
if (input.settings.model !== null)
|
|
96
|
+
child.session.append('model/selection', input.settings.model);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
if (input.mode === 'continue') {
|
|
100
|
+
const observation = await required(this.owner, 'sessionQuery').observeSession(input.source.id, { signal: input.signal, projectionMode: 'all' });
|
|
101
|
+
try {
|
|
102
|
+
let boundary = -1;
|
|
103
|
+
for (let i = 0; i < observation.events.length; i++)
|
|
104
|
+
if (observation.events[i]?.type === 'turn/end')
|
|
105
|
+
boundary = i;
|
|
106
|
+
if (boundary < 0)
|
|
107
|
+
throw new WorktreeError('NO_COMPLETED_TURN', 'This conversation has no completed turn to inherit. Start a fresh conversation instead.');
|
|
108
|
+
if (observation.events[boundary]?.seq !== boundary)
|
|
109
|
+
throw new WorktreeError('INVALID_FORK_BOUNDARY', 'The source log does not have a contiguous completed-turn boundary.');
|
|
110
|
+
options = { ...options, seed: buildForkSeed(observation.events, SessionSeq(boundary)), inheritedEventCount: SessionLogOffset(boundary + 1), meta: { ...options.meta, parentSession: input.source.id, isSeeded: true } };
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
observation[Symbol.dispose]();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const handle = await required(this.owner, 'agents').create(options);
|
|
117
|
+
this.handles.set(handle.agent.id, handle);
|
|
118
|
+
await input.onCreated?.({ agent: handle.agent, sessionId: handle.agent.id, workspaceId: workspace.id });
|
|
119
|
+
await workspace.attachSession(handle.agent.id);
|
|
120
|
+
const flushed = await required(this.owner, 'sessions').flush(handle.agent.session);
|
|
121
|
+
if (!flushed)
|
|
122
|
+
throw new WorktreeError('MISSING_DURABILITY_BARRIER', 'No persistence listener participated in the session durability checkpoint.');
|
|
123
|
+
return { agent: handle.agent, sessionId: handle.agent.id, workspaceId: workspace.id };
|
|
124
|
+
}
|
|
125
|
+
async close() {
|
|
126
|
+
const values = [...this.handles.values()];
|
|
127
|
+
this.handles.clear();
|
|
128
|
+
await Promise.allSettled(values.map(async (h) => { h.agent.cancel({ kind: 'disposed' }); await h.dispose(); await h.agent.whenIdle(); }));
|
|
129
|
+
}
|
|
130
|
+
}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { WorktreeRecord, OperationRecord } from './types.js';
|
|
2
|
+
export declare const worktreeDomain: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: number;
|
|
5
|
+
layout: "per-record";
|
|
6
|
+
tables: {
|
|
7
|
+
worktrees: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<string, {
|
|
8
|
+
id: string;
|
|
9
|
+
operationId: string;
|
|
10
|
+
repoRoot: string;
|
|
11
|
+
commonDir: string;
|
|
12
|
+
projectSubdir: string;
|
|
13
|
+
checkoutRoot: string;
|
|
14
|
+
effectiveCwd: string;
|
|
15
|
+
remote: string;
|
|
16
|
+
remoteIdentity: string;
|
|
17
|
+
remoteBranch: string;
|
|
18
|
+
baseOid: string;
|
|
19
|
+
baseRef: string;
|
|
20
|
+
fetchedAt: number;
|
|
21
|
+
createdAt: number;
|
|
22
|
+
sessionIds: string[];
|
|
23
|
+
workspaceId: string | null;
|
|
24
|
+
branch: string | null;
|
|
25
|
+
protected: boolean;
|
|
26
|
+
archived: boolean;
|
|
27
|
+
state: "creating" | "ready" | "recovery-required" | "missing";
|
|
28
|
+
error: string | null;
|
|
29
|
+
displayName?: string | undefined;
|
|
30
|
+
naming?: {
|
|
31
|
+
title: string;
|
|
32
|
+
slug: string;
|
|
33
|
+
directoryName: string;
|
|
34
|
+
branch: string;
|
|
35
|
+
source: "model" | "fallback";
|
|
36
|
+
fallbackReason?: "disabled" | "unavailable" | "timeout" | "provider-failure" | "invalid-output" | "output-limit" | "incomplete-output" | undefined;
|
|
37
|
+
} | undefined;
|
|
38
|
+
projectId?: string | undefined;
|
|
39
|
+
folderId?: string | undefined;
|
|
40
|
+
firstMessageNaming?: {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
initialBranch: string;
|
|
43
|
+
phase: "waiting" | "generating" | "generated" | "renaming" | "complete" | "skipped";
|
|
44
|
+
messageSeq?: number | undefined;
|
|
45
|
+
messageId?: string | undefined;
|
|
46
|
+
messageHash?: string | undefined;
|
|
47
|
+
generatedName?: {
|
|
48
|
+
title: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
directoryName: string;
|
|
51
|
+
branch: string;
|
|
52
|
+
source: "model" | "fallback";
|
|
53
|
+
fallbackReason?: "disabled" | "unavailable" | "timeout" | "provider-failure" | "invalid-output" | "output-limit" | "incomplete-output" | undefined;
|
|
54
|
+
} | undefined;
|
|
55
|
+
reason?: string | undefined;
|
|
56
|
+
} | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
operations: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<string, {
|
|
59
|
+
id: string;
|
|
60
|
+
kind: "create" | "handoff";
|
|
61
|
+
requestHash: string;
|
|
62
|
+
actorId: string;
|
|
63
|
+
worktreeId: string;
|
|
64
|
+
settingsHash: string;
|
|
65
|
+
sourceSessionId: string;
|
|
66
|
+
sessionMode: "new" | "continue";
|
|
67
|
+
workspaceId: string | null;
|
|
68
|
+
settings: {
|
|
69
|
+
model: {
|
|
70
|
+
provider: string;
|
|
71
|
+
model: string;
|
|
72
|
+
reasoningEffort?: string | undefined;
|
|
73
|
+
} | null;
|
|
74
|
+
preset: string | null;
|
|
75
|
+
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
76
|
+
approval: "ask" | "never";
|
|
77
|
+
plan: boolean;
|
|
78
|
+
hash: string;
|
|
79
|
+
};
|
|
80
|
+
targetRoot: string | null;
|
|
81
|
+
previewId: string | null;
|
|
82
|
+
appliedHead: string | null;
|
|
83
|
+
appliedFingerprint: string | null;
|
|
84
|
+
phase: "fetching" | "ready" | "recovery-required" | "planned" | "checkout-created" | "session-created" | "applying" | "code-applied" | "cancel-requested" | "cancelled-before-effects";
|
|
85
|
+
createdAt: number;
|
|
86
|
+
updatedAt: number;
|
|
87
|
+
sessionId: string | null;
|
|
88
|
+
error: {
|
|
89
|
+
code: string;
|
|
90
|
+
message: string;
|
|
91
|
+
} | null;
|
|
92
|
+
generatedName?: {
|
|
93
|
+
title: string;
|
|
94
|
+
slug: string;
|
|
95
|
+
directoryName: string;
|
|
96
|
+
branch: string;
|
|
97
|
+
source: "model" | "fallback";
|
|
98
|
+
fallbackReason?: "disabled" | "unavailable" | "timeout" | "provider-failure" | "invalid-output" | "output-limit" | "incomplete-output" | undefined;
|
|
99
|
+
} | undefined;
|
|
100
|
+
namingStarted?: boolean | undefined;
|
|
101
|
+
}>;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export interface RecordTable<T> {
|
|
105
|
+
get(key: string): T | undefined;
|
|
106
|
+
entries(): IterableIterator<[string, T]>;
|
|
107
|
+
put(key: string, value: T): Promise<void>;
|
|
108
|
+
delete(key: string): Promise<boolean>;
|
|
109
|
+
}
|
|
110
|
+
export interface WorktreeStore {
|
|
111
|
+
worktrees: RecordTable<WorktreeRecord>;
|
|
112
|
+
operations: RecordTable<OperationRecord>;
|
|
113
|
+
close(): Promise<void>;
|
|
114
|
+
}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
|
|
3
|
+
const str = z.string().max(4096);
|
|
4
|
+
const namingSchema = z.object({
|
|
5
|
+
title: z.string().min(1).max(60), slug: z.string().min(1).max(40).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
|
|
6
|
+
directoryName: z.string().min(10).max(49).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/u),
|
|
7
|
+
branch: z.string().min(19).max(58).regex(/^worktree\/[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/u),
|
|
8
|
+
source: z.enum(['model', 'fallback']),
|
|
9
|
+
fallbackReason: z.enum(['disabled', 'unavailable', 'timeout', 'provider-failure', 'invalid-output', 'output-limit', 'incomplete-output']).optional(),
|
|
10
|
+
}).strict();
|
|
11
|
+
const worktreeSchema = z.object({
|
|
12
|
+
id: z.string().uuid(), operationId: z.string().uuid(), repoRoot: str, commonDir: str, projectSubdir: str,
|
|
13
|
+
checkoutRoot: str, effectiveCwd: str, remote: str, remoteIdentity: str, remoteBranch: str,
|
|
14
|
+
baseOid: str, baseRef: str, fetchedAt: z.number().int().nonnegative(), createdAt: z.number().int().nonnegative(),
|
|
15
|
+
sessionIds: z.array(str), workspaceId: str.nullable(), branch: str.nullable(), protected: z.boolean(), archived: z.boolean(),
|
|
16
|
+
state: z.enum(['creating', 'ready', 'recovery-required', 'missing']), error: str.nullable(),
|
|
17
|
+
displayName: z.string().min(1).max(60).optional(), naming: namingSchema.optional(),
|
|
18
|
+
projectId: z.string().uuid().optional(), folderId: z.string().min(1).max(256).optional(),
|
|
19
|
+
firstMessageNaming: z.object({
|
|
20
|
+
sessionId: str, initialBranch: str,
|
|
21
|
+
phase: z.enum(['waiting', 'generating', 'generated', 'renaming', 'complete', 'skipped']),
|
|
22
|
+
messageSeq: z.number().int().nonnegative().optional(), messageId: str.optional(),
|
|
23
|
+
messageHash: z.string().regex(/^[0-9a-f]{64}$/u).optional(), generatedName: namingSchema.optional(),
|
|
24
|
+
reason: z.string().max(128).optional(),
|
|
25
|
+
}).strict().optional(),
|
|
26
|
+
}).strict();
|
|
27
|
+
const settingsSchema = z.object({
|
|
28
|
+
model: z.object({ provider: str, model: str, reasoningEffort: str.optional() }).strict().nullable(),
|
|
29
|
+
preset: str.nullable(), sandbox: z.enum(['read-only', 'workspace-write', 'danger-full-access']),
|
|
30
|
+
approval: z.enum(['ask', 'never']), plan: z.boolean(), hash: str,
|
|
31
|
+
}).strict();
|
|
32
|
+
const operationSchema = z.object({
|
|
33
|
+
id: z.string().uuid(), kind: z.enum(['create', 'handoff']), requestHash: str, actorId: str, worktreeId: z.string().uuid(),
|
|
34
|
+
settingsHash: str, sourceSessionId: str, sessionMode: z.enum(['new', 'continue']), workspaceId: str.nullable(),
|
|
35
|
+
settings: settingsSchema, targetRoot: str.nullable(), previewId: str.nullable(), appliedHead: str.nullable(), appliedFingerprint: str.nullable(),
|
|
36
|
+
phase: z.enum(['planned', 'fetching', 'checkout-created', 'session-created', 'ready', 'applying', 'code-applied', 'cancel-requested', 'cancelled-before-effects', 'recovery-required']),
|
|
37
|
+
createdAt: z.number().int().nonnegative(), updatedAt: z.number().int().nonnegative(), sessionId: str.nullable(),
|
|
38
|
+
error: z.object({ code: str, message: str }).strict().nullable(),
|
|
39
|
+
generatedName: namingSchema.optional(), namingStarted: z.boolean().optional(),
|
|
40
|
+
}).strict();
|
|
41
|
+
export const worktreeDomain = defineDomain({
|
|
42
|
+
name: 'dsh_worktrees', version: 1, layout: 'per-record',
|
|
43
|
+
tables: { worktrees: domainTable(worktreeSchema), operations: domainTable(operationSchema) },
|
|
44
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
export interface WorktreeConfig {
|
|
2
|
+
root: string;
|
|
3
|
+
gitExecutable: string;
|
|
4
|
+
defaultRemote: string;
|
|
5
|
+
defaultBranch: string;
|
|
6
|
+
commandTimeoutMs: number;
|
|
7
|
+
fetchTimeoutMs: number;
|
|
8
|
+
operationTimeoutMs: number;
|
|
9
|
+
maxSnapshotBytes: number;
|
|
10
|
+
maxFiles: number;
|
|
11
|
+
maxRefBytes: number;
|
|
12
|
+
namingEnabled?: boolean;
|
|
13
|
+
namingProvider?: string;
|
|
14
|
+
namingModel?: string;
|
|
15
|
+
namingTimeoutMs?: number;
|
|
16
|
+
namingMaxTokens?: number;
|
|
17
|
+
}
|
|
18
|
+
export type NamingFallbackReason = 'disabled' | 'unavailable' | 'timeout' | 'provider-failure' | 'invalid-output' | 'output-limit' | 'incomplete-output';
|
|
19
|
+
/** Safe, durable result of one auxiliary call; never stores task text or provider errors. */
|
|
20
|
+
export interface GeneratedWorktreeName {
|
|
21
|
+
title: string;
|
|
22
|
+
slug: string;
|
|
23
|
+
directoryName: string;
|
|
24
|
+
branch: string;
|
|
25
|
+
source: 'model' | 'fallback';
|
|
26
|
+
fallbackReason?: NamingFallbackReason;
|
|
27
|
+
}
|
|
28
|
+
/** Git execution is injected; production uses the managed subprocess/sandbox capabilities. */
|
|
29
|
+
export interface GitRunSpec {
|
|
30
|
+
cwd: string;
|
|
31
|
+
args: readonly string[];
|
|
32
|
+
env?: NodeJS.ProcessEnv;
|
|
33
|
+
stdin?: string | Uint8Array;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
maxBytes?: number;
|
|
37
|
+
/** Git's documented nonzero outcomes must be opted into per command. */
|
|
38
|
+
allowedExitCodes?: readonly number[];
|
|
39
|
+
}
|
|
40
|
+
export interface GitRunResult {
|
|
41
|
+
stdout: Buffer;
|
|
42
|
+
stderr: Buffer;
|
|
43
|
+
exitCode: number;
|
|
44
|
+
}
|
|
45
|
+
export type GitExecutor = (spec: GitRunSpec) => Promise<GitRunResult>;
|
|
46
|
+
export interface RepositoryInfo {
|
|
47
|
+
root: string;
|
|
48
|
+
commonDir: string;
|
|
49
|
+
projectSubdir: string;
|
|
50
|
+
head: string;
|
|
51
|
+
branch: string | null;
|
|
52
|
+
}
|
|
53
|
+
export interface RemoteInfo {
|
|
54
|
+
name: string;
|
|
55
|
+
identity: string;
|
|
56
|
+
}
|
|
57
|
+
export interface RemoteBranch {
|
|
58
|
+
name: string;
|
|
59
|
+
oid: string;
|
|
60
|
+
}
|
|
61
|
+
export interface RemoteBranches {
|
|
62
|
+
remote: string;
|
|
63
|
+
remoteIdentity: string;
|
|
64
|
+
defaultBranch: string | null;
|
|
65
|
+
branches: RemoteBranch[];
|
|
66
|
+
observedAt: number;
|
|
67
|
+
}
|
|
68
|
+
export interface CheckoutState {
|
|
69
|
+
head: string;
|
|
70
|
+
branch: string | null;
|
|
71
|
+
dirty: boolean;
|
|
72
|
+
changes: number;
|
|
73
|
+
untracked: number;
|
|
74
|
+
}
|
|
75
|
+
/** Internal observation only; progress carries no caller authority or task data. */
|
|
76
|
+
export type SetupProgressStage = 'fetching' | 'creating' | 'naming' | 'opening' | 'ready';
|
|
77
|
+
export interface WorktreeSetupProgress {
|
|
78
|
+
readonly stage: SetupProgressStage;
|
|
79
|
+
readonly operationId: string;
|
|
80
|
+
readonly worktreeId?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface CreateCheckoutInput {
|
|
83
|
+
id: string;
|
|
84
|
+
repository: RepositoryInfo;
|
|
85
|
+
destination: string;
|
|
86
|
+
remote: string;
|
|
87
|
+
remoteIdentity: string;
|
|
88
|
+
remoteBranch: string;
|
|
89
|
+
baseRef: string;
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
/** Observer failures are ignored; never used to authorize or change Git effects. */
|
|
92
|
+
onProgress?: (stage: 'fetching' | 'creating') => void;
|
|
93
|
+
}
|
|
94
|
+
export interface CreatedCheckout {
|
|
95
|
+
checkoutRoot: string;
|
|
96
|
+
effectiveCwd: string;
|
|
97
|
+
baseOid: string;
|
|
98
|
+
baseRef: string;
|
|
99
|
+
fetchedAt: number;
|
|
100
|
+
}
|
|
101
|
+
export interface PatchFile {
|
|
102
|
+
path: string;
|
|
103
|
+
status: string;
|
|
104
|
+
binary: boolean;
|
|
105
|
+
}
|
|
106
|
+
export interface WorktreePreview {
|
|
107
|
+
id: string;
|
|
108
|
+
worktreeId: string;
|
|
109
|
+
sourceSessionId: string;
|
|
110
|
+
sourceHead: string;
|
|
111
|
+
sourceFingerprint: string;
|
|
112
|
+
sourceRoot: string;
|
|
113
|
+
sourceCommonDir: string;
|
|
114
|
+
targetCommonDir: string;
|
|
115
|
+
finalTree: string;
|
|
116
|
+
snapshotRoot: string;
|
|
117
|
+
targetRoot: string;
|
|
118
|
+
targetHead: string;
|
|
119
|
+
baseOid: string;
|
|
120
|
+
patchPath: string;
|
|
121
|
+
patchHash: string;
|
|
122
|
+
bytes: number;
|
|
123
|
+
files: PatchFile[];
|
|
124
|
+
createdAt: number;
|
|
125
|
+
}
|
|
126
|
+
export interface SnapshotInput {
|
|
127
|
+
worktreeId: string;
|
|
128
|
+
sourceSessionId: string;
|
|
129
|
+
repositoryRoot: string;
|
|
130
|
+
checkoutRoot: string;
|
|
131
|
+
targetRoot: string;
|
|
132
|
+
baseOid: string;
|
|
133
|
+
snapshotRoot: string;
|
|
134
|
+
signal?: AbortSignal;
|
|
135
|
+
}
|
|
136
|
+
/** One durable, source-log-bound naming opportunity; task text stays only in the session log. */
|
|
137
|
+
export interface FirstMessageNaming {
|
|
138
|
+
sessionId: string;
|
|
139
|
+
initialBranch: string;
|
|
140
|
+
phase: 'waiting' | 'generating' | 'generated' | 'renaming' | 'complete' | 'skipped';
|
|
141
|
+
messageSeq?: number;
|
|
142
|
+
messageId?: string;
|
|
143
|
+
messageHash?: string;
|
|
144
|
+
generatedName?: GeneratedWorktreeName;
|
|
145
|
+
reason?: string;
|
|
146
|
+
}
|
|
147
|
+
export interface WorktreeRecord {
|
|
148
|
+
id: string;
|
|
149
|
+
operationId: string;
|
|
150
|
+
repoRoot: string;
|
|
151
|
+
commonDir: string;
|
|
152
|
+
projectSubdir: string;
|
|
153
|
+
checkoutRoot: string;
|
|
154
|
+
effectiveCwd: string;
|
|
155
|
+
remote: string;
|
|
156
|
+
remoteIdentity: string;
|
|
157
|
+
remoteBranch: string;
|
|
158
|
+
baseOid: string;
|
|
159
|
+
baseRef: string;
|
|
160
|
+
fetchedAt: number;
|
|
161
|
+
createdAt: number;
|
|
162
|
+
sessionIds: string[];
|
|
163
|
+
workspaceId: string | null;
|
|
164
|
+
branch: string | null;
|
|
165
|
+
displayName?: string;
|
|
166
|
+
naming?: GeneratedWorktreeName;
|
|
167
|
+
firstMessageNaming?: FirstMessageNaming;
|
|
168
|
+
/** Logical project membership; execution cwd and native Workspace stay independent. */
|
|
169
|
+
projectId?: string;
|
|
170
|
+
folderId?: string;
|
|
171
|
+
protected: boolean;
|
|
172
|
+
archived: boolean;
|
|
173
|
+
state: 'creating' | 'ready' | 'recovery-required' | 'missing';
|
|
174
|
+
error: string | null;
|
|
175
|
+
}
|
|
176
|
+
export type OperationPhase = 'planned' | 'fetching' | 'checkout-created' | 'session-created' | 'ready' | 'applying' | 'code-applied' | 'cancel-requested' | 'cancelled-before-effects' | 'recovery-required';
|
|
177
|
+
export interface SessionSettings {
|
|
178
|
+
model: {
|
|
179
|
+
provider: string;
|
|
180
|
+
model: string;
|
|
181
|
+
reasoningEffort?: string;
|
|
182
|
+
} | null;
|
|
183
|
+
preset: string | null;
|
|
184
|
+
sandbox: 'read-only' | 'workspace-write' | 'danger-full-access';
|
|
185
|
+
approval: 'ask' | 'never';
|
|
186
|
+
plan: boolean;
|
|
187
|
+
hash: string;
|
|
188
|
+
}
|
|
189
|
+
export interface OperationRecord {
|
|
190
|
+
id: string;
|
|
191
|
+
kind: 'create' | 'handoff';
|
|
192
|
+
requestHash: string;
|
|
193
|
+
actorId: string;
|
|
194
|
+
worktreeId: string;
|
|
195
|
+
settingsHash: string;
|
|
196
|
+
sourceSessionId: string;
|
|
197
|
+
sessionMode: 'new' | 'continue';
|
|
198
|
+
workspaceId: string | null;
|
|
199
|
+
settings: SessionSettings;
|
|
200
|
+
targetRoot: string | null;
|
|
201
|
+
previewId: string | null;
|
|
202
|
+
appliedHead: string | null;
|
|
203
|
+
appliedFingerprint: string | null;
|
|
204
|
+
generatedName?: GeneratedWorktreeName;
|
|
205
|
+
/** Durable checkout-first inference intent; interrupted creates never infer again. */
|
|
206
|
+
namingStarted?: boolean;
|
|
207
|
+
phase: OperationPhase;
|
|
208
|
+
createdAt: number;
|
|
209
|
+
updatedAt: number;
|
|
210
|
+
sessionId: string | null;
|
|
211
|
+
error: {
|
|
212
|
+
code: string;
|
|
213
|
+
message: string;
|
|
214
|
+
} | null;
|
|
215
|
+
}
|
|
216
|
+
export type WorktreeAction = 'list' | 'status' | 'branches' | 'create' | 'start' | 'branch' | 'protect' | 'preview' | 'export' | 'handoff' | 'archive';
|
|
217
|
+
export interface WorktreeRequest {
|
|
218
|
+
action: WorktreeAction;
|
|
219
|
+
repoPath?: string;
|
|
220
|
+
id?: string;
|
|
221
|
+
operationId?: string;
|
|
222
|
+
remote?: string;
|
|
223
|
+
remoteIdentity?: string;
|
|
224
|
+
remoteBranch?: string;
|
|
225
|
+
sourceSessionId?: string;
|
|
226
|
+
sessionMode?: 'new' | 'continue';
|
|
227
|
+
requireBlankSource?: boolean;
|
|
228
|
+
settingsHash?: string;
|
|
229
|
+
firstPrompt?: string;
|
|
230
|
+
/** Pair on create; membership does not change execution or permission policy. */
|
|
231
|
+
projectId?: string;
|
|
232
|
+
folderId?: string;
|
|
233
|
+
name?: string;
|
|
234
|
+
protected?: boolean;
|
|
235
|
+
archived?: boolean;
|
|
236
|
+
previewId?: string;
|
|
237
|
+
targetPath?: string;
|
|
238
|
+
query?: string;
|
|
239
|
+
cursor?: string;
|
|
240
|
+
limit?: number;
|
|
241
|
+
includeArchived?: boolean;
|
|
242
|
+
}
|
|
243
|
+
export type CommandEnvelope = {
|
|
244
|
+
v: 1;
|
|
245
|
+
ok: true;
|
|
246
|
+
data: unknown;
|
|
247
|
+
} | {
|
|
248
|
+
v: 1;
|
|
249
|
+
ok: false;
|
|
250
|
+
error: {
|
|
251
|
+
code: string;
|
|
252
|
+
message: string;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
/** Result of create/start/handoff; clients must open this exact session identity. */
|
|
256
|
+
export interface SessionResult {
|
|
257
|
+
worktree: WorktreeRecord;
|
|
258
|
+
sessionId: string;
|
|
259
|
+
workspaceId: string;
|
|
260
|
+
settingsHash: string;
|
|
261
|
+
operation: OperationRecord;
|
|
262
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/icon.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><g fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M14 11v26M14 25c0-10 20 0 20-14v-1"/><circle cx="14" cy="8" r="4"/><circle cx="14" cy="40" r="4"/><circle cx="34" cy="8" r="4"/></g></svg>
|