-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.ts
More file actions
127 lines (118 loc) · 5.24 KB
/
Copy pathtypes.ts
File metadata and controls
127 lines (118 loc) · 5.24 KB
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* The structured agent→app tool side channel, domain-seamed.
*
* Every Tangle agent product needs the same thing: a way for the in-sandbox
* agent to reach the host app with a STRUCTURED, validated tool call (not a
* fenced `:::` block scraped from prose). Four canonical tools cover it:
*
* - `submit_proposal` — route a regulated/state-changing action to a human
* approval queue (the human-in-the-loop gate).
* - `schedule_followup` — register a dated cadence step (executes immediately).
* - `render_ui` — persist a generated view as an artifact (immediate).
* - `add_citation` — anchor a grounding reference (immediate).
*
* The shapes, validation, OpenAI tool definitions, MCP-server wiring, HTTP
* route handling, and runtime-loop executor are GENERIC and live here. The
* persistence (which DB, which tables) and the product's proposal taxonomy are
* the DOMAIN SEAM: each product supplies {@link AppToolHandlers} +
* {@link AppToolTaxonomy}. This package imports no product code and no agent
* runtime — it depends only on the Web `Request`/`Response` types.
*/
/** Server-set, trusted per-turn context. Recovered from request headers (HTTP
* path) or supplied directly (runtime path) — never from model tool args, so
* the model cannot forge identity or target another workspace. */
export interface AppToolContext {
userId: string
workspaceId: string
threadId: string | null
}
/** The product's proposal taxonomy — the only domain-specific vocabulary the
* generic layer needs (to validate `submit_proposal.type` and label the
* regulated subset). */
export interface AppToolTaxonomy {
/** Every accepted proposal type. */
proposalTypes: readonly string[]
/** The subset that cannot execute without a named certified human (regulated
* steps). Used only to label the tool result; enforcement is the product's
* approval executor. */
regulatedTypes: readonly string[]
}
export interface SubmitProposalArgs {
type: string
title: string
description?: string | null
/** Stamped by dispatch from the approval policy (needsApproval predicate or
* taxonomy.regulatedTypes). Handlers MUST queue (never auto-execute) when
* true. Products don't set this; dispatch owns it — fail-closed. */
regulated?: boolean
}
export interface SubmitProposalResult {
proposalId: string
/** True when an identical (workspace, title) proposal already existed. */
deduped: boolean
/** Handlers that execute a proposal type immediately (an unregulated
* generate-style action) return 'executed'; omitted/'queued_for_approval'
* means a human gate. Dispatch reports this verbatim to the model. */
status?: 'queued_for_approval' | 'executed'
/** Product-specific result fields (e.g. datasetId) — passed through to the
* tool outcome so the model and UI see what the handler actually did. */
[extra: string]: unknown
}
export interface ScheduleFollowupArgs {
title: string
dueDate: string
priority?: string
}
export interface ScheduleFollowupResult {
id: string
dueDate: string
deduped: boolean
}
export interface RenderUiArgs {
title: string
schema: unknown
}
export interface RenderUiResult {
/** The persisted artifact path. */
path: string
/** The exact persisted body — surfaced as the `artifact` produced event so a
* consumer's completion oracle sees the real content. */
content: string
}
export interface AddCitationArgs {
path: string
quote: string
label?: string
}
export interface AddCitationResult {
citationId: string
path: string
}
/**
* The domain seam. Each product implements these against its own D1/KV/vault.
* A handler MAY throw {@link ToolInputError} for correctable bad input (mapped
* to a 4xx / failed tool_result); any other throw is an internal error.
* `submitProposal` is the only handler whose result feeds the approval queue;
* the others execute immediately.
*/
export interface AppToolHandlers {
submitProposal(args: SubmitProposalArgs, ctx: AppToolContext): Promise<SubmitProposalResult>
scheduleFollowup(args: ScheduleFollowupArgs, ctx: AppToolContext): Promise<ScheduleFollowupResult>
renderUi(args: RenderUiArgs, ctx: AppToolContext): Promise<RenderUiResult>
addCitation(args: AddCitationArgs, ctx: AppToolContext): Promise<AddCitationResult>
}
/** Produced-state events the runtime executor emits at the real side-effect
* site, so a consumer's eval/completion oracle credits a persisted proposal or
* artifact. Deliberately substrate-free (no RuntimeStreamEvent import); the
* consumer maps these onto its own telemetry shape. */
export type AppToolProducedEvent =
| { type: 'proposal_created'; proposalId: string; title: string; status: 'pending' | 'executed' }
| { type: 'artifact'; path: string; content: string }
/** Outcome of one tool dispatch — structurally identical to the agent-runtime
* tool-loop's `ToolCallOutcome`, so a dispatched outcome folds straight into
* the loop's `role: 'tool'` result message. Defined here (not imported) to keep
* the `tools/` module substrate-free: it depends only on Web `Request`/
* `Response`, never on the agent runtime. */
export type AppToolOutcome =
| { ok: true; result: unknown }
| { ok: false; code: string; message: string; status?: number }