summaryrefslogtreecommitdiffhomepage
path: root/packages/system-prompt/src/types.ts
blob: ef57ee09508591f12b478d6d0dba18f8ad4c735b (plain)
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
 * System-prompt service handle + interface.
 *
 * The service is the single-responder anchor the session-orchestrator obtains
 * (lazily, via `host.getService(systemPromptHandle)`) to construct and read the
 * per-conversation resolved system prompt.
 */
import { defineService, type ServiceHandle } from "@dispatch/kernel";

/**
 * The system-prompt service.
 *
 * `construct` resolves the template once (first turn / compaction) and persists
 * the result; `get` reads the persisted result on subsequent turns (cache-safe —
 * no per-turn reconstruction).
 */
export interface SystemPromptService {
  /**
   * Resolve the template against the current environment and persist the
   * result under `resolved:<conversationId>`. Returns the resolved string.
   * When no template is stored, the built-in default template is used. An
   * empty template yields an empty string.
   *
   * When `context.computerId` is set, the resolver uses remote-backed adapters
   * (reading the remote's `/etc/os-release`, `hostname`, `uname`, `git` via
   * the ExecBackend/SSH) so the system prompt reflects the REMOTE machine.
   */
  construct(
    conversationId: string,
    cwd: string,
    context?: {
      readonly model?: string;
      readonly workspaceId?: string;
      readonly computerId?: string;
    },
  ): Promise<string>;

  /**
   * Resolve an ARBITRARY template string against the current environment,
   * using the SAME variable resolver + adapter set as `construct` (so a
   * caller that owns its own prompt — e.g. the heartbeat extension — gets
   * `[type:name]` placeholders substituted identically to the global
   * template). Pure resolution: nothing is persisted (the result is the
   * caller's to use). An empty template yields an empty string.
   *
   * Like `construct`, when `context.computerId` is set the resolver uses
   * remote-backed adapters (reading the remote's OS/hostname/git via SSH).
   */
  resolveText(
    template: string,
    cwd: string,
    context?: {
      readonly model?: string;
      readonly conversationId?: string;
      readonly workspaceId?: string;
      readonly computerId?: string;
    },
  ): Promise<string>;

  /** Read the persisted resolved system prompt, or `null` if never constructed. */
  get(conversationId: string): Promise<string | null>;

  /**
   * Read the persisted resolved system prompt AND the cwd + computerId it was
   * built against. Returns `{ prompt: null, cwd: null, computerId: null }` if
   * never constructed. Consumers use this to detect whether the cached prompt
   * is stale relative to the current effective cwd or computerId.
   */
  getWithMeta(conversationId: string): Promise<{
    readonly prompt: string | null;
    readonly cwd: string | null;
    readonly computerId: string | null;
  }>;

  /** Read the global template (or `DEFAULT_TEMPLATE` when none is stored). */
  getTemplate(): Promise<string>;

  /** Set (upsert) the global template. An empty string means "no system prompt". */
  setTemplate(template: string): Promise<void>;
}

/**
 * Typed handle anchoring the system-prompt service. The single symbol the
 * session-orchestrator imports to reach the builder — no string-keyed lookup.
 */
export const systemPromptHandle: ServiceHandle<SystemPromptService> =
  defineService<SystemPromptService>("system-prompt");