@agimon-ai/po-approval 0.0.0-alpha.43

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.
@@ -0,0 +1,250 @@
1
+ import { defineFill, defineNativePlugin } from "@agimon-ai/po-core/native";
2
+ import { View } from "react-native-unistyles/components/native/View";
3
+ import { Text } from "react-native-unistyles/components/native/Text";
4
+ import { Button, fontOf } from "@agimon-ai/po-native-components";
5
+ import { StyleSheet } from "react-native-unistyles";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { useEffect, useState, useSyncExternalStore } from "react";
8
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ApprovalPromptView.native.tsx
9
+ function ApprovalPromptView({ title, summary, details, requirement, seconds, expired, decision, disabled, onApprove, onDeny, voiceHint }) {
10
+ const inactive = disabled || expired || Boolean(decision);
11
+ return /* @__PURE__ */ jsxs(View, {
12
+ style: styles.prompt,
13
+ accessibilityLabel: "Approval request",
14
+ children: [
15
+ /* @__PURE__ */ jsx(Text, {
16
+ style: styles.label,
17
+ children: "Your approval"
18
+ }),
19
+ /* @__PURE__ */ jsx(Text, {
20
+ accessibilityRole: "header",
21
+ style: styles.title,
22
+ children: title
23
+ }),
24
+ /* @__PURE__ */ jsx(Text, {
25
+ style: styles.summary,
26
+ children: summary
27
+ }),
28
+ /* @__PURE__ */ jsx(View, {
29
+ style: styles.details,
30
+ children: details.map(({ label, value }) => /* @__PURE__ */ jsxs(View, {
31
+ style: styles.detail,
32
+ children: [/* @__PURE__ */ jsx(Text, {
33
+ style: styles.meta,
34
+ children: label
35
+ }), /* @__PURE__ */ jsx(Text, {
36
+ selectable: true,
37
+ style: styles.value,
38
+ children: value
39
+ })]
40
+ }, label))
41
+ }),
42
+ /* @__PURE__ */ jsxs(View, {
43
+ style: styles.footer,
44
+ children: [
45
+ /* @__PURE__ */ jsx(Text, {
46
+ accessibilityLiveRegion: "polite",
47
+ style: styles.meta,
48
+ children: decision || (expired ? "This request expired. Nothing ran." : seconds !== void 0 ? `${seconds} seconds remaining. Nothing runs if time runs out.` : requirement === "human" ? "Only you can approve this in the app." : "Po will wait for your decision.")
49
+ }),
50
+ /* @__PURE__ */ jsxs(View, {
51
+ style: styles.actions,
52
+ children: [/* @__PURE__ */ jsx(Button, {
53
+ variant: "primary",
54
+ style: styles.action,
55
+ disabled: inactive,
56
+ onPress: onApprove,
57
+ children: "Approve"
58
+ }), /* @__PURE__ */ jsx(Button, {
59
+ variant: "outline",
60
+ style: styles.action,
61
+ disabled: inactive,
62
+ onPress: onDeny,
63
+ children: "Deny"
64
+ })]
65
+ }),
66
+ requirement === "ask" && voiceHint && !inactive ? /* @__PURE__ */ jsx(Text, {
67
+ style: styles.meta,
68
+ children: voiceHint
69
+ }) : null
70
+ ]
71
+ })
72
+ ]
73
+ });
74
+ }
75
+ const styles = StyleSheet.create((theme) => ({
76
+ prompt: {
77
+ gap: theme.spacing(2),
78
+ padding: theme.spacing(4),
79
+ borderWidth: 1,
80
+ borderColor: theme.colors["edge-magenta"],
81
+ borderRadius: theme.borderRadius.lg,
82
+ backgroundColor: theme.colors.panel,
83
+ uni__dependencies: [0]
84
+ },
85
+ label: {
86
+ fontFamily: fontOf(theme.fontFamily.sans),
87
+ fontSize: theme.fontSize.sm,
88
+ color: theme.colors.magenta,
89
+ fontWeight: theme.fontWeight.medium,
90
+ uni__dependencies: [0]
91
+ },
92
+ title: {
93
+ fontFamily: fontOf(theme.fontFamily.sans),
94
+ fontSize: theme.fontSize.lg,
95
+ lineHeight: theme.lineHeight.lg,
96
+ color: theme.colors.hi,
97
+ fontWeight: theme.fontWeight.semibold,
98
+ uni__dependencies: [0]
99
+ },
100
+ summary: {
101
+ fontFamily: fontOf(theme.fontFamily.sans),
102
+ fontSize: theme.fontSize.lg,
103
+ lineHeight: theme.lineHeightRelaxed.lg,
104
+ color: theme.colors.text,
105
+ uni__dependencies: [0]
106
+ },
107
+ details: {
108
+ gap: theme.spacing(3),
109
+ paddingVertical: theme.spacing(2),
110
+ uni__dependencies: [0]
111
+ },
112
+ detail: {
113
+ gap: theme.spacing(1),
114
+ uni__dependencies: [0]
115
+ },
116
+ meta: {
117
+ fontFamily: fontOf(theme.fontFamily.sans),
118
+ fontSize: theme.fontSize.sm,
119
+ lineHeight: theme.lineHeightRelaxed.sm,
120
+ color: theme.colors.dim,
121
+ uni__dependencies: [0]
122
+ },
123
+ value: {
124
+ fontFamily: fontOf(theme.fontFamily.sans),
125
+ fontSize: theme.fontSize.base,
126
+ lineHeight: theme.lineHeightRelaxed.base,
127
+ color: theme.colors.hi,
128
+ uni__dependencies: [0]
129
+ },
130
+ footer: {
131
+ borderTopWidth: 1,
132
+ borderTopColor: theme.colors["border-soft"],
133
+ paddingTop: theme.spacing(3),
134
+ gap: theme.spacing(3),
135
+ uni__dependencies: [0]
136
+ },
137
+ actions: {
138
+ flexDirection: "row",
139
+ gap: theme.spacing(2),
140
+ uni__dependencies: [0]
141
+ },
142
+ action: {
143
+ minHeight: theme.spacing(11),
144
+ flex: 1,
145
+ uni__dependencies: [0]
146
+ }
147
+ }));
148
+ //#endregion
149
+ //#region src/types/voiceHint.ts
150
+ function parseApprovalVoiceHintSource(input) {
151
+ const source = input;
152
+ return typeof source?.subscribe === "function" && typeof source.isActive === "function" ? source : null;
153
+ }
154
+ /** Voice fills this while installed; the hint shows only for `ask` approvals and only while a source is active. */
155
+ const approvalVoiceHintSlot = {
156
+ slot: "approval.voice-hint",
157
+ parse: parseApprovalVoiceHintSource
158
+ };
159
+ //#endregion
160
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/approvalDeadline.ts
161
+ /** Whole seconds left before `expiresAt`, or undefined when the request has no deadline. */
162
+ function remainingSeconds(expiresAt, now) {
163
+ return expiresAt === void 0 ? void 0 : Math.max(0, Math.ceil((expiresAt - now) / 1e3));
164
+ }
165
+ //#endregion
166
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/useApprovalPrompt.ts
167
+ const VOICE_HINT = "Or say “approve” or “deny”.";
168
+ const NO_SOURCES = () => () => void 0;
169
+ /** Display-only countdown. The server's deadline decides; this only stops offering a stale choice. */
170
+ function useDeadline(expiresAt) {
171
+ const [seconds, setSeconds] = useState(() => remainingSeconds(expiresAt, Date.now()));
172
+ useEffect(() => {
173
+ setSeconds(remainingSeconds(expiresAt, Date.now()));
174
+ if (expiresAt === void 0) return void 0;
175
+ const timer = setInterval(() => {
176
+ const next = remainingSeconds(expiresAt, Date.now());
177
+ setSeconds(next);
178
+ if (next === 0) clearInterval(timer);
179
+ }, 1e3);
180
+ return () => clearInterval(timer);
181
+ }, [expiresAt]);
182
+ return seconds;
183
+ }
184
+ function useVoiceActive(sources, sessionId) {
185
+ const subscribe = sources.length === 0 ? NO_SOURCES : (listener) => {
186
+ const stops = sources.map((source) => source.subscribe(listener));
187
+ return () => {
188
+ for (const stop of stops) stop();
189
+ };
190
+ };
191
+ const read = () => sources.some((source) => source.isActive(sessionId));
192
+ return useSyncExternalStore(subscribe, read, read);
193
+ }
194
+ /** The approved view's props for one live dialog. Callbacks answer with the exact select labels. */
195
+ function useApprovalPrompt(props) {
196
+ const approval = props.dialog.approval;
197
+ const seconds = useDeadline(approval?.expiresAt);
198
+ const voice = useVoiceActive(props.slotData(approvalVoiceHintSlot).map((fill) => fill.data), props.sessionId);
199
+ if (approval === void 0) return void 0;
200
+ return {
201
+ title: approval.title,
202
+ summary: approval.summary,
203
+ details: approval.details ?? [],
204
+ requirement: approval.requirement,
205
+ ...seconds === void 0 || seconds === 0 ? {} : { seconds },
206
+ expired: seconds === 0,
207
+ onApprove: () => props.answer("Approve"),
208
+ onDeny: () => props.answer("Deny"),
209
+ onCancel: props.cancel,
210
+ ...approval.requirement === "ask" && voice ? { voiceHint: VOICE_HINT } : {}
211
+ };
212
+ }
213
+ //#endregion
214
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/ApprovalDialogPrompt.native.tsx
215
+ /** The approved inline prompt, replacing the composer while the session waits on one approval. */
216
+ function ApprovalDialogPrompt(props) {
217
+ const view = useApprovalPrompt(props);
218
+ return view === void 0 ? null : /* @__PURE__ */ jsx(ApprovalPromptView, { ...view });
219
+ }
220
+ //#endregion
221
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/approvalClaim.ts
222
+ /** Only a select carrying a parsed approval envelope with both exact choices. */
223
+ function claimsApproval(dialog) {
224
+ return dialog.method === "select" && dialog.approval !== void 0 && dialog.options.includes("Approve") && dialog.options.includes("Deny");
225
+ }
226
+ //#endregion
227
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/Approval.dialog-prompt.native.tsx
228
+ var Approval_dialog_prompt_native_default = defineFill({ data: {
229
+ claims: claimsApproval,
230
+ component: ApprovalDialogPrompt
231
+ } });
232
+ //#endregion
233
+ //#region generated/native.ts
234
+ const via = (identity, value) => value === void 0 ? void 0 : {
235
+ ...identity,
236
+ ...value
237
+ };
238
+ const nativePlugin = defineNativePlugin({
239
+ id: "approval",
240
+ session: { get fills() {
241
+ return [via({
242
+ slot: "dialog-prompt",
243
+ id: "approval"
244
+ }, Approval_dialog_prompt_native_default)];
245
+ } }
246
+ });
247
+ //#endregion
248
+ export { nativePlugin };
249
+
250
+ //# sourceMappingURL=native.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.mjs","names":["Button","fontOf","StyleSheet","ApprovalPromptViewProps","ApprovalPromptView","title","summary","details","requirement","seconds","expired","decision","disabled","onApprove","onDeny","voiceHint","inactive","Boolean","styles","prompt","label","map","value","detail","meta","footer","undefined","actions","action","create","theme","gap","spacing","padding","borderWidth","borderColor","colors","borderRadius","lg","backgroundColor","panel","uni__dependencies","fontFamily","sans","fontSize","sm","color","magenta","fontWeight","medium","lineHeight","hi","semibold","lineHeightRelaxed","text","paddingVertical","dim","base","borderTopWidth","borderTopColor","paddingTop","flexDirection","minHeight","flex","fillApproval"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/_components/ApprovalPromptView.native.tsx","../../src/types/voiceHint.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/approvalDeadline.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/useApprovalPrompt.ts","../../src/extensions/workspaces/sessions/(frontend)/_components/ApprovalDialogPrompt.native.tsx","../../src/extensions/workspaces/sessions/(frontend)/_lib/approvalClaim.ts","../../src/extensions/workspaces/sessions/(frontend)/fill/Approval.dialog-prompt.native.tsx","../../generated/native.ts"],"sourcesContent":["import { Button, fontOf } from '@agimon-ai/po-native-components';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport type { ApprovalPromptViewProps } from './ApprovalPromptView.types';\n\nexport function ApprovalPromptView({\n title,\n summary,\n details,\n requirement,\n seconds,\n expired,\n decision,\n disabled,\n onApprove,\n onDeny,\n voiceHint,\n}: ApprovalPromptViewProps) {\n const inactive = disabled || expired || Boolean(decision);\n return (\n <View style={styles.prompt} accessibilityLabel=\"Approval request\">\n <Text style={styles.label}>Your approval</Text>\n <Text accessibilityRole=\"header\" style={styles.title}>\n {title}\n </Text>\n <Text style={styles.summary}>{summary}</Text>\n <View style={styles.details}>\n {details.map(({ label, value }) => (\n <View key={label} style={styles.detail}>\n <Text style={styles.meta}>{label}</Text>\n <Text selectable style={styles.value}>\n {value}\n </Text>\n </View>\n ))}\n </View>\n <View style={styles.footer}>\n <Text accessibilityLiveRegion=\"polite\" style={styles.meta}>\n {decision ||\n (expired\n ? 'This request expired. Nothing ran.'\n : seconds !== undefined\n ? `${seconds} seconds remaining. Nothing runs if time runs out.`\n : requirement === 'human'\n ? 'Only you can approve this in the app.'\n : 'Po will wait for your decision.')}\n </Text>\n <View style={styles.actions}>\n <Button variant=\"primary\" style={styles.action} disabled={inactive} onPress={onApprove}>\n Approve\n </Button>\n <Button variant=\"outline\" style={styles.action} disabled={inactive} onPress={onDeny}>\n Deny\n </Button>\n </View>\n {requirement === 'ask' && voiceHint && !inactive ? <Text style={styles.meta}>{voiceHint}</Text> : null}\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n prompt: {\n gap: theme.spacing(2),\n padding: theme.spacing(4),\n borderWidth: 1,\n borderColor: theme.colors['edge-magenta'],\n borderRadius: theme.borderRadius.lg,\n backgroundColor: theme.colors.panel,\n },\n label: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n color: theme.colors.magenta,\n fontWeight: theme.fontWeight.medium,\n },\n title: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.lg,\n lineHeight: theme.lineHeight.lg,\n color: theme.colors.hi,\n fontWeight: theme.fontWeight.semibold,\n },\n summary: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.lg,\n lineHeight: theme.lineHeightRelaxed.lg,\n color: theme.colors.text,\n },\n details: { gap: theme.spacing(3), paddingVertical: theme.spacing(2) },\n detail: { gap: theme.spacing(1) },\n meta: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n color: theme.colors.dim,\n },\n value: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeightRelaxed.base,\n color: theme.colors.hi,\n },\n footer: {\n borderTopWidth: 1,\n borderTopColor: theme.colors['border-soft'],\n paddingTop: theme.spacing(3),\n gap: theme.spacing(3),\n },\n actions: { flexDirection: 'row', gap: theme.spacing(2) },\n action: { minHeight: theme.spacing(11), flex: 1 },\n}));\n","import type { SlotDeclaration } from '@agimon-ai/po-core/web';\n\n/** Whether a voice surface can take a spoken approve or deny for a session right now. */\nexport interface ApprovalVoiceHintSource {\n subscribe(listener: () => void): () => void;\n isActive(sessionId: string | null): boolean;\n}\n\nfunction parseApprovalVoiceHintSource(input: unknown): ApprovalVoiceHintSource | null {\n const source = input as Partial<ApprovalVoiceHintSource> | null;\n return typeof source?.subscribe === 'function' && typeof source.isActive === 'function'\n ? (source as ApprovalVoiceHintSource)\n : null;\n}\n\n/** Voice fills this while installed; the hint shows only for `ask` approvals and only while a source is active. */\nexport const approvalVoiceHintSlot: SlotDeclaration<ApprovalVoiceHintSource> = {\n slot: 'approval.voice-hint',\n parse: parseApprovalVoiceHintSource,\n};\n","/** Whole seconds left before `expiresAt`, or undefined when the request has no deadline. */\nexport function remainingSeconds(expiresAt: number | undefined, now: number): number | undefined {\n return expiresAt === undefined ? undefined : Math.max(0, Math.ceil((expiresAt - now) / 1000));\n}\n","import type { DialogPromptRenderProps } from '@agimon-ai/po-core/web';\nimport { useEffect, useState, useSyncExternalStore } from 'react';\n\nimport type { ApprovalVoiceHintSource } from '../../../../../types/voiceHint';\nimport { approvalVoiceHintSlot } from '../../../../../types/voiceHint';\nimport { remainingSeconds } from '../_lib/approvalDeadline';\nimport type { ApprovalPromptViewProps } from './ApprovalPromptView.types';\n\nconst VOICE_HINT = 'Or say “approve” or “deny”.';\nconst NO_SOURCES = (): (() => void) => () => undefined;\n\n/** Display-only countdown. The server's deadline decides; this only stops offering a stale choice. */\nfunction useDeadline(expiresAt: number | undefined): number | undefined {\n const [seconds, setSeconds] = useState(() => remainingSeconds(expiresAt, Date.now()));\n useEffect(() => {\n setSeconds(remainingSeconds(expiresAt, Date.now()));\n if (expiresAt === undefined) return undefined;\n const timer = setInterval(() => {\n const next = remainingSeconds(expiresAt, Date.now());\n setSeconds(next);\n if (next === 0) clearInterval(timer);\n }, 1000);\n return () => clearInterval(timer);\n }, [expiresAt]);\n return seconds;\n}\n\nfunction useVoiceActive(sources: readonly ApprovalVoiceHintSource[], sessionId: string | null): boolean {\n const subscribe =\n sources.length === 0\n ? NO_SOURCES\n : (listener: () => void) => {\n const stops = sources.map((source) => source.subscribe(listener));\n return () => {\n for (const stop of stops) stop();\n };\n };\n const read = () => sources.some((source) => source.isActive(sessionId));\n return useSyncExternalStore(subscribe, read, read);\n}\n\n/** The approved view's props for one live dialog. Callbacks answer with the exact select labels. */\nexport function useApprovalPrompt(props: DialogPromptRenderProps): ApprovalPromptViewProps | undefined {\n const approval = props.dialog.approval;\n const seconds = useDeadline(approval?.expiresAt);\n const voice = useVoiceActive(\n props.slotData(approvalVoiceHintSlot).map((fill) => fill.data),\n props.sessionId,\n );\n if (approval === undefined) return undefined;\n return {\n title: approval.title,\n summary: approval.summary,\n details: approval.details ?? [],\n requirement: approval.requirement,\n ...(seconds === undefined || seconds === 0 ? {} : { seconds }),\n expired: seconds === 0,\n onApprove: () => props.answer('Approve'),\n onDeny: () => props.answer('Deny'),\n onCancel: props.cancel,\n ...(approval.requirement === 'ask' && voice ? { voiceHint: VOICE_HINT } : {}),\n };\n}\n","import type { DialogPromptRenderProps } from '@agimon-ai/po-core/native';\n\nimport { ApprovalPromptView } from './ApprovalPromptView.native';\nimport { useApprovalPrompt } from './useApprovalPrompt';\n\n/** The approved inline prompt, replacing the composer while the session waits on one approval. */\nexport function ApprovalDialogPrompt(props: DialogPromptRenderProps) {\n const view = useApprovalPrompt(props);\n return view === undefined ? null : <ApprovalPromptView {...view} />;\n}\n","import type { ToolPromptDialog } from '@agimon-ai/po-core/web';\n\n/** Only a select carrying a parsed approval envelope with both exact choices. */\nexport function claimsApproval(dialog: ToolPromptDialog): boolean {\n return (\n dialog.method === 'select' &&\n dialog.approval !== undefined &&\n dialog.options.includes('Approve') &&\n dialog.options.includes('Deny')\n );\n}\n","import { defineFill } from '@agimon-ai/po-core/native';\n\nimport { ApprovalDialogPrompt } from '../_components/ApprovalDialogPrompt.native';\nimport { claimsApproval } from '../_lib/approvalClaim';\n\nexport default defineFill({ data: { claims: claimsApproval, component: ApprovalDialogPrompt } });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport fillApproval from '../src/extensions/workspaces/sessions/(frontend)/fill/Approval.dialog-prompt.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'approval',\n session: {\n get fills(): WebPluginContributions['fills'] { return [via({ slot: 'dialog-prompt', id: 'approval' }, fillApproval)]; },\n },\n});\n"],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_approvalCoordinator = require("../approvalCoordinator-Be6utzFg.cjs");
6
+ let _agimon_ai_po_core_serverPlugin = require("@agimon-ai/po-core/serverPlugin");
7
+ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
8
+ let _agimon_ai_po_core_approval = require("@agimon-ai/po-core/approval");
9
+ let typebox_value = require("typebox/value");
10
+ let _agimon_ai_po_core_apiContracts = require("@agimon-ai/po-core/apiContracts");
11
+ let typebox = require("typebox");
12
+ //#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
13
+ /** Binds approvals only where an agent session has a client to ask; elsewhere `human` calls stay refused. */
14
+ function createApprovalSession({ context, agent }) {
15
+ const execution = agent?.context;
16
+ if (execution === void 0) return { value: void 0 };
17
+ const coordinator = require_approvalCoordinator.createApprovalCoordinator(() => execution.client);
18
+ context.inject([_agimon_ai_po_core_serverPlugin.PO_SERVER_SCOPE_SERVICE], (scoped) => (0, _agimon_ai_po_core_serverPlugin.requirePoServerScope)(scoped).provide(_agimon_ai_po_core_approval.PO_APPROVAL_CONTRACT, coordinator));
19
+ return { value: coordinator };
20
+ }
21
+ //#endregion
22
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
23
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(createApprovalSession);
24
+ //#endregion
25
+ //#region src/extensions/workspaces/sessions/(backend)/contract/approval.server.ts
26
+ var approval_server_default = (0, _agimon_ai_po_core_extensionFile.defineServiceContract)(_agimon_ai_po_core_approval.PO_APPROVAL_SERVICE, { scope: "session" });
27
+ //#endregion
28
+ //#region src/schemas/apiContracts.ts
29
+ const VoiceApprovalAnswerSchema = typebox.Type.Object({
30
+ approvalId: typebox.Type.String({
31
+ minLength: 1,
32
+ maxLength: 200
33
+ }),
34
+ decision: typebox.Type.Union([typebox.Type.Literal("approved"), typebox.Type.Literal("denied")]),
35
+ channel: typebox.Type.Literal("voice")
36
+ });
37
+ (0, _agimon_ai_po_core_apiContracts.defineApiContract)({
38
+ version: 1,
39
+ http: [{
40
+ id: "approval.current",
41
+ scope: "session",
42
+ basePath: "approval",
43
+ path: "/current",
44
+ method: "GET",
45
+ authentication: "owner",
46
+ description: "Read the visible head approval, or null when another dialog is at the head.",
47
+ responses: (0, _agimon_ai_po_core_apiContracts.jsonApiResponses)(typebox.Type.Union([_agimon_ai_po_core_approval.PoApprovalPromptSchema, typebox.Type.Null()]))
48
+ }, {
49
+ id: "approval.answer",
50
+ scope: "session",
51
+ basePath: "approval",
52
+ path: "/current",
53
+ method: "POST",
54
+ authentication: "owner",
55
+ description: "Answer the current ask approval through Voice. Human approvals are app-only.",
56
+ body: {
57
+ required: true,
58
+ contentType: "application/json",
59
+ schema: VoiceApprovalAnswerSchema
60
+ },
61
+ responses: {
62
+ ...(0, _agimon_ai_po_core_apiContracts.jsonApiResponses)(typebox.Type.Object({ answered: typebox.Type.Boolean() })),
63
+ "400": { description: "Invalid approval answer." }
64
+ }
65
+ }],
66
+ sockets: [],
67
+ dynamic: []
68
+ });
69
+ //#endregion
70
+ //#region src/extensions/workspaces/sessions/(backend)/api/approval/route.server.ts
71
+ var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoute)(({ agent }) => ({
72
+ basePath: "approval",
73
+ start: () => ({
74
+ async fetch(request) {
75
+ if (new URL(request.url).pathname !== "/current") return new Response(null, { status: 404 });
76
+ const client = agent?.context.client;
77
+ if (request.method === "GET") return Response.json(client?.pendingApproval?.() ?? null);
78
+ if (request.method !== "POST") return new Response(null, { status: 405 });
79
+ let body;
80
+ try {
81
+ body = await request.json();
82
+ } catch {
83
+ return Response.json({ error: "Invalid approval answer" }, { status: 400 });
84
+ }
85
+ if (!(0, typebox_value.Check)(VoiceApprovalAnswerSchema, body)) return new Response(null, { status: 400 });
86
+ const { approvalId, decision } = body;
87
+ return Response.json({ answered: client?.answerApproval?.(approvalId, decision, "voice") === true });
88
+ },
89
+ close() {}
90
+ })
91
+ }));
92
+ //#endregion
93
+ //#region generated/server.ts
94
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
95
+ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
96
+ name: "@agimon-ai/po-approval",
97
+ providerOverrides: (0, _agimon_ai_po_core_serverPlugin.composeServerProviderOverrides)(root_server_default),
98
+ provides: { session: [approval_server_default] },
99
+ injects: (0, _agimon_ai_po_core_extensionFile.composeRootInjects)({ session: [root_server_default] }),
100
+ session: async (context) => {
101
+ const scopeSession = await context.scopeRoot("session", root_server_default, context);
102
+ const contextSession = {
103
+ ...context,
104
+ root: scopeSession.value
105
+ };
106
+ return {
107
+ ...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
108
+ get api() {
109
+ return [at(route_server_default, contextSession)];
110
+ },
111
+ services: [...scopeSession.services ?? []],
112
+ get activities() {
113
+ return [...scopeSession.activities ?? []];
114
+ }
115
+ };
116
+ }
117
+ });
118
+ //#endregion
119
+ exports.default = serverPlugin;
120
+ exports.serverPlugin = serverPlugin;
121
+
122
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","names":["createApprovalCoordinator","PO_SERVER_SCOPE_SERVICE","requirePoServerScope","PO_APPROVAL_CONTRACT","defineRoot","defineServiceContract","PO_APPROVAL_SERVICE","Type","defineApiContract","jsonApiResponses","PoApprovalPromptSchema","defineRoute","Check","defineServerPlugin","composeServerProviderOverrides","rootSession","contractApproval","composeRootInjects","composeRootHooks","apiRoute"],"sources":["../../src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/contract/approval.server.ts","../../src/schemas/apiContracts.ts","../../src/extensions/workspaces/sessions/(backend)/api/approval/route.server.ts","../../generated/server.ts"],"sourcesContent":["import { PO_APPROVAL_CONTRACT } from '@agimon-ai/po-core/approval';\nimport {\n PO_SERVER_SCOPE_SERVICE,\n requirePoServerScope,\n type PoServerPluginContext,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { createApprovalCoordinator } from '../../../../../services/approvalCoordinator';\n\n/** Binds approvals only where an agent session has a client to ask; elsewhere `human` calls stay refused. */\nexport function createApprovalSession({ context, agent }: PoServerPluginContext) {\n const execution = agent?.context;\n if (execution === undefined) return { value: undefined };\n const coordinator = createApprovalCoordinator(() => execution.client);\n // The binding unwinds with this injected fiber, so the root needs no withdraw of its own.\n context.inject([PO_SERVER_SCOPE_SERVICE], (scoped) =>\n requirePoServerScope(scoped).provide(PO_APPROVAL_CONTRACT, coordinator),\n );\n return { value: coordinator };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createApprovalSession } from './_lib/root.server';\n\nexport default defineRoot(createApprovalSession);\n","import { PO_APPROVAL_SERVICE, type PoApprovalService } from '@agimon-ai/po-core/approval';\nimport { defineServiceContract } from '@agimon-ai/po-core/extensionFile';\n\nexport default defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, { scope: 'session' });\n","import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { PoApprovalPromptSchema } from '@agimon-ai/po-core/approval';\nimport { Type } from 'typebox';\n\nexport const VoiceApprovalAnswerSchema = Type.Object({\n approvalId: Type.String({ minLength: 1, maxLength: 200 }),\n decision: Type.Union([Type.Literal('approved'), Type.Literal('denied')]),\n channel: Type.Literal('voice'),\n});\n\nexport const apiContracts = defineApiContract({\n version: 1,\n http: [\n {\n id: 'approval.current',\n scope: 'session',\n basePath: 'approval',\n path: '/current',\n method: 'GET',\n authentication: 'owner',\n description: 'Read the visible head approval, or null when another dialog is at the head.',\n responses: jsonApiResponses(Type.Union([PoApprovalPromptSchema, Type.Null()])),\n },\n {\n id: 'approval.answer',\n scope: 'session',\n basePath: 'approval',\n path: '/current',\n method: 'POST',\n authentication: 'owner',\n description: 'Answer the current ask approval through Voice. Human approvals are app-only.',\n body: { required: true, contentType: 'application/json', schema: VoiceApprovalAnswerSchema },\n responses: {\n ...jsonApiResponses(Type.Object({ answered: Type.Boolean() })),\n '400': { description: 'Invalid approval answer.' },\n },\n },\n ],\n sockets: [],\n dynamic: [],\n});\nexport default apiContracts;\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport { Check } from 'typebox/value';\n\nimport { VoiceApprovalAnswerSchema } from '../../../../../../schemas/apiContracts';\nexport default defineRoute(({ agent }: PoServerPluginContext) => ({\n basePath: 'approval',\n start: () => ({\n async fetch(request: Request) {\n if (new URL(request.url).pathname !== '/current') return new Response(null, { status: 404 });\n const client = agent?.context.client;\n if (request.method === 'GET') return Response.json(client?.pendingApproval?.() ?? null);\n if (request.method !== 'POST') return new Response(null, { status: 405 });\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return Response.json({ error: 'Invalid approval answer' }, { status: 400 });\n }\n if (!Check(VoiceApprovalAnswerSchema, body)) return new Response(null, { status: 400 });\n const { approvalId, decision } = body;\n // The app uses its existing select answer. This route never accepts an app-channel claim from Voice.\n return Response.json({ answered: client?.answerApproval?.(approvalId, decision, 'voice') === true });\n },\n close() {},\n }),\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport contractApproval from '../src/extensions/workspaces/sessions/(backend)/contract/approval.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/approval/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-approval',\n providerOverrides: composeServerProviderOverrides(rootSession),\n provides: {\n session: [contractApproval],\n },\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ //#region generated/server.d.ts
2
+ export declare const serverPlugin: import("@agimon-ai/po-core/serverPlugin").PoServerPlugin;
3
+ //#endregion
4
+ export { serverPlugin as default };
5
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAaa,wDAAY"}
@@ -0,0 +1,117 @@
1
+ import { n as createApprovalCoordinator } from "../approvalCoordinator-CRbOm-PY.mjs";
2
+ import { PO_SERVER_SCOPE_SERVICE, composeServerProviderOverrides, defineServerPlugin, requirePoServerScope } from "@agimon-ai/po-core/serverPlugin";
3
+ import { composeRootHooks, composeRootInjects, defineRoot, defineRoute, defineServiceContract } from "@agimon-ai/po-core/extensionFile";
4
+ import { PO_APPROVAL_CONTRACT, PO_APPROVAL_SERVICE, PoApprovalPromptSchema } from "@agimon-ai/po-core/approval";
5
+ import { Check } from "typebox/value";
6
+ import { defineApiContract, jsonApiResponses } from "@agimon-ai/po-core/apiContracts";
7
+ import { Type } from "typebox";
8
+ //#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
9
+ /** Binds approvals only where an agent session has a client to ask; elsewhere `human` calls stay refused. */
10
+ function createApprovalSession({ context, agent }) {
11
+ const execution = agent?.context;
12
+ if (execution === void 0) return { value: void 0 };
13
+ const coordinator = createApprovalCoordinator(() => execution.client);
14
+ context.inject([PO_SERVER_SCOPE_SERVICE], (scoped) => requirePoServerScope(scoped).provide(PO_APPROVAL_CONTRACT, coordinator));
15
+ return { value: coordinator };
16
+ }
17
+ //#endregion
18
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
19
+ var root_server_default = defineRoot(createApprovalSession);
20
+ //#endregion
21
+ //#region src/extensions/workspaces/sessions/(backend)/contract/approval.server.ts
22
+ var approval_server_default = defineServiceContract(PO_APPROVAL_SERVICE, { scope: "session" });
23
+ //#endregion
24
+ //#region src/schemas/apiContracts.ts
25
+ const VoiceApprovalAnswerSchema = Type.Object({
26
+ approvalId: Type.String({
27
+ minLength: 1,
28
+ maxLength: 200
29
+ }),
30
+ decision: Type.Union([Type.Literal("approved"), Type.Literal("denied")]),
31
+ channel: Type.Literal("voice")
32
+ });
33
+ defineApiContract({
34
+ version: 1,
35
+ http: [{
36
+ id: "approval.current",
37
+ scope: "session",
38
+ basePath: "approval",
39
+ path: "/current",
40
+ method: "GET",
41
+ authentication: "owner",
42
+ description: "Read the visible head approval, or null when another dialog is at the head.",
43
+ responses: jsonApiResponses(Type.Union([PoApprovalPromptSchema, Type.Null()]))
44
+ }, {
45
+ id: "approval.answer",
46
+ scope: "session",
47
+ basePath: "approval",
48
+ path: "/current",
49
+ method: "POST",
50
+ authentication: "owner",
51
+ description: "Answer the current ask approval through Voice. Human approvals are app-only.",
52
+ body: {
53
+ required: true,
54
+ contentType: "application/json",
55
+ schema: VoiceApprovalAnswerSchema
56
+ },
57
+ responses: {
58
+ ...jsonApiResponses(Type.Object({ answered: Type.Boolean() })),
59
+ "400": { description: "Invalid approval answer." }
60
+ }
61
+ }],
62
+ sockets: [],
63
+ dynamic: []
64
+ });
65
+ //#endregion
66
+ //#region src/extensions/workspaces/sessions/(backend)/api/approval/route.server.ts
67
+ var route_server_default = defineRoute(({ agent }) => ({
68
+ basePath: "approval",
69
+ start: () => ({
70
+ async fetch(request) {
71
+ if (new URL(request.url).pathname !== "/current") return new Response(null, { status: 404 });
72
+ const client = agent?.context.client;
73
+ if (request.method === "GET") return Response.json(client?.pendingApproval?.() ?? null);
74
+ if (request.method !== "POST") return new Response(null, { status: 405 });
75
+ let body;
76
+ try {
77
+ body = await request.json();
78
+ } catch {
79
+ return Response.json({ error: "Invalid approval answer" }, { status: 400 });
80
+ }
81
+ if (!Check(VoiceApprovalAnswerSchema, body)) return new Response(null, { status: 400 });
82
+ const { approvalId, decision } = body;
83
+ return Response.json({ answered: client?.answerApproval?.(approvalId, decision, "voice") === true });
84
+ },
85
+ close() {}
86
+ })
87
+ }));
88
+ //#endregion
89
+ //#region generated/server.ts
90
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
91
+ const serverPlugin = defineServerPlugin({
92
+ name: "@agimon-ai/po-approval",
93
+ providerOverrides: composeServerProviderOverrides(root_server_default),
94
+ provides: { session: [approval_server_default] },
95
+ injects: composeRootInjects({ session: [root_server_default] }),
96
+ session: async (context) => {
97
+ const scopeSession = await context.scopeRoot("session", root_server_default, context);
98
+ const contextSession = {
99
+ ...context,
100
+ root: scopeSession.value
101
+ };
102
+ return {
103
+ ...composeRootHooks(scopeSession),
104
+ get api() {
105
+ return [at(route_server_default, contextSession)];
106
+ },
107
+ services: [...scopeSession.services ?? []],
108
+ get activities() {
109
+ return [...scopeSession.activities ?? []];
110
+ }
111
+ };
112
+ }
113
+ });
114
+ //#endregion
115
+ export { serverPlugin as default, serverPlugin };
116
+
117
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","names":["rootSession","contractApproval","apiRoute"],"sources":["../../src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/contract/approval.server.ts","../../src/schemas/apiContracts.ts","../../src/extensions/workspaces/sessions/(backend)/api/approval/route.server.ts","../../generated/server.ts"],"sourcesContent":["import { PO_APPROVAL_CONTRACT } from '@agimon-ai/po-core/approval';\nimport {\n PO_SERVER_SCOPE_SERVICE,\n requirePoServerScope,\n type PoServerPluginContext,\n} from '@agimon-ai/po-core/serverPlugin';\n\nimport { createApprovalCoordinator } from '../../../../../services/approvalCoordinator';\n\n/** Binds approvals only where an agent session has a client to ask; elsewhere `human` calls stay refused. */\nexport function createApprovalSession({ context, agent }: PoServerPluginContext) {\n const execution = agent?.context;\n if (execution === undefined) return { value: undefined };\n const coordinator = createApprovalCoordinator(() => execution.client);\n // The binding unwinds with this injected fiber, so the root needs no withdraw of its own.\n context.inject([PO_SERVER_SCOPE_SERVICE], (scoped) =>\n requirePoServerScope(scoped).provide(PO_APPROVAL_CONTRACT, coordinator),\n );\n return { value: coordinator };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\n\nimport { createApprovalSession } from './_lib/root.server';\n\nexport default defineRoot(createApprovalSession);\n","import { PO_APPROVAL_SERVICE, type PoApprovalService } from '@agimon-ai/po-core/approval';\nimport { defineServiceContract } from '@agimon-ai/po-core/extensionFile';\n\nexport default defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, { scope: 'session' });\n","import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { PoApprovalPromptSchema } from '@agimon-ai/po-core/approval';\nimport { Type } from 'typebox';\n\nexport const VoiceApprovalAnswerSchema = Type.Object({\n approvalId: Type.String({ minLength: 1, maxLength: 200 }),\n decision: Type.Union([Type.Literal('approved'), Type.Literal('denied')]),\n channel: Type.Literal('voice'),\n});\n\nexport const apiContracts = defineApiContract({\n version: 1,\n http: [\n {\n id: 'approval.current',\n scope: 'session',\n basePath: 'approval',\n path: '/current',\n method: 'GET',\n authentication: 'owner',\n description: 'Read the visible head approval, or null when another dialog is at the head.',\n responses: jsonApiResponses(Type.Union([PoApprovalPromptSchema, Type.Null()])),\n },\n {\n id: 'approval.answer',\n scope: 'session',\n basePath: 'approval',\n path: '/current',\n method: 'POST',\n authentication: 'owner',\n description: 'Answer the current ask approval through Voice. Human approvals are app-only.',\n body: { required: true, contentType: 'application/json', schema: VoiceApprovalAnswerSchema },\n responses: {\n ...jsonApiResponses(Type.Object({ answered: Type.Boolean() })),\n '400': { description: 'Invalid approval answer.' },\n },\n },\n ],\n sockets: [],\n dynamic: [],\n});\nexport default apiContracts;\n","import { defineRoute } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext } from '@agimon-ai/po-core/serverPlugin';\nimport { Check } from 'typebox/value';\n\nimport { VoiceApprovalAnswerSchema } from '../../../../../../schemas/apiContracts';\nexport default defineRoute(({ agent }: PoServerPluginContext) => ({\n basePath: 'approval',\n start: () => ({\n async fetch(request: Request) {\n if (new URL(request.url).pathname !== '/current') return new Response(null, { status: 404 });\n const client = agent?.context.client;\n if (request.method === 'GET') return Response.json(client?.pendingApproval?.() ?? null);\n if (request.method !== 'POST') return new Response(null, { status: 405 });\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return Response.json({ error: 'Invalid approval answer' }, { status: 400 });\n }\n if (!Check(VoiceApprovalAnswerSchema, body)) return new Response(null, { status: 400 });\n const { approvalId, decision } = body;\n // The app uses its existing select answer. This route never accepts an app-channel claim from Voice.\n return Response.json({ answered: client?.answerApproval?.(approvalId, decision, 'voice') === true });\n },\n close() {},\n }),\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport contractApproval from '../src/extensions/workspaces/sessions/(backend)/contract/approval.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/approval/route.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-approval',\n providerOverrides: composeServerProviderOverrides(rootSession),\n provides: {\n session: [contractApproval],\n },\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}