File size: 2,841 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
 * `sessionManager` — App-scope session lifecycle after the Workspace-domain
 * split. It creates, resumes, closes, archives, restores, deletes, and forks
 * sessions through the App-owned manager; create/resume/restore return scope
 * handles on the wire (`{ id, kind }`), fork/createChild return the forked
 * session's metadata directly.
 */

import { z } from 'zod';

import { maybe, noResult } from '../helpers.js';
import { mcpServerConfigSchema } from '../mcp.js';
import type { ServiceContract } from '../types.js';
import { sessionMetaSchema } from './metadata.js';

export const createSessionOptionsSchema = z.object({
  sessionId: z.string().optional(),
  workDir: z.string(),
  additionalDirs: z.array(z.string()).optional(),
  /**
   * Ephemeral per-session MCP servers (engine `CreateSessionOptions.mcpServers`):
   * connected only for the created session, never persisted.
   */
  mcpServers: z.record(z.string(), mcpServerConfigSchema).optional(),
});

/** Same fields as `ResumeSessionOptions` in the engine — keep in sync. */
export const resumeSessionOptionsSchema = z.object({
  additionalDirs: z.array(z.string()).optional(),
  /**
   * Ephemeral per-session MCP servers, applied when resume re-materializes a
   * cold session (ignored when the session is already live).
   */
  mcpServers: z.record(z.string(), mcpServerConfigSchema).optional(),
});

/** Same fields as `ForkSessionOptions` in the engine — keep in sync. */
export const forkSessionOptionsSchema = z.object({
  sourceSessionId: z.string(),
  newSessionId: z.string().optional(),
  title: z.string().optional(),
  metadata: z.record(z.string(), z.unknown()).optional(),
  turnIndex: z.number().optional(),
});

/** Same fields as `ForkSessionOptions` in the engine, minus the fork-only truncation. */
export const createChildSessionOptionsSchema = forkSessionOptionsSchema.omit({ turnIndex: true });

/** `IScopeHandle` as it survives JSON — `{ id, kind }` plus extras. */
export const handleWireSchema = z.looseObject({
  id: z.string(),
  kind: z.string(),
});

export const sessionManagerContract = {
  create: { input: z.tuple([createSessionOptionsSchema]), output: handleWireSchema },
  resume: {
    input: z.tuple([z.string(), resumeSessionOptionsSchema.optional()]),
    output: maybe(handleWireSchema),
  },
  close: { input: z.tuple([z.string()]), output: noResult },
  archive: { input: z.tuple([z.string()]), output: noResult },
  restore: {
    input: z.tuple([z.string(), resumeSessionOptionsSchema.optional()]),
    output: maybe(handleWireSchema),
  },
  delete: { input: z.tuple([z.string()]), output: noResult },
  fork: { input: z.tuple([forkSessionOptionsSchema]), output: sessionMetaSchema },
  createChild: { input: z.tuple([createChildSessionOptionsSchema]), output: sessionMetaSchema },
} satisfies ServiceContract;