@opengeni/api-router 3.0.1-canary.0 → 3.0.1-canary.2
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/dist/app.js +1 -1
- package/dist/{chunk-XZGTKYOA.js → chunk-PRCUIL5L.js} +2644 -2581
- package/dist/{chunk-XZGTKYOA.js.map → chunk-PRCUIL5L.js.map} +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/marketplace-plugin.d.ts +7 -7
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/routes/sessions.d.ts +24 -2
- package/package.json +19 -19
- package/src/editable-artifact-production.ts +6 -0
- package/src/mcp/editable-artifacts.ts +7 -2
- package/src/mcp/server.ts +47 -10
- package/src/routes/sessions.ts +30 -3
|
@@ -52,8 +52,8 @@ export declare function marketplacePlugin(url: string): {
|
|
|
52
52
|
category: string;
|
|
53
53
|
keywords: never[];
|
|
54
54
|
author: {
|
|
55
|
-
name: string;
|
|
56
55
|
url?: never;
|
|
56
|
+
name: string;
|
|
57
57
|
email?: never;
|
|
58
58
|
};
|
|
59
59
|
version: string;
|
|
@@ -78,9 +78,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
78
78
|
installation: string;
|
|
79
79
|
upstream: {
|
|
80
80
|
source: {
|
|
81
|
+
url?: never;
|
|
81
82
|
source: string;
|
|
82
83
|
path: string;
|
|
83
|
-
url?: never;
|
|
84
84
|
};
|
|
85
85
|
policy: {
|
|
86
86
|
installation: string;
|
|
@@ -124,9 +124,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
124
124
|
installation: string;
|
|
125
125
|
upstream: {
|
|
126
126
|
source: {
|
|
127
|
+
url?: never;
|
|
127
128
|
source: string;
|
|
128
129
|
path: string;
|
|
129
|
-
url?: never;
|
|
130
130
|
};
|
|
131
131
|
policy: {
|
|
132
132
|
installation: string;
|
|
@@ -170,9 +170,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
170
170
|
installation: string;
|
|
171
171
|
upstream: {
|
|
172
172
|
source: {
|
|
173
|
+
url?: never;
|
|
173
174
|
source: string;
|
|
174
175
|
path: string;
|
|
175
|
-
url?: never;
|
|
176
176
|
};
|
|
177
177
|
policy: {
|
|
178
178
|
installation: string;
|
|
@@ -216,9 +216,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
216
216
|
installation: string;
|
|
217
217
|
upstream: {
|
|
218
218
|
source: {
|
|
219
|
+
url?: never;
|
|
219
220
|
source: string;
|
|
220
221
|
path: string;
|
|
221
|
-
url?: never;
|
|
222
222
|
};
|
|
223
223
|
policy: {
|
|
224
224
|
installation: string;
|
|
@@ -262,9 +262,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
262
262
|
installation: string;
|
|
263
263
|
upstream: {
|
|
264
264
|
source: {
|
|
265
|
+
url?: never;
|
|
265
266
|
source: string;
|
|
266
267
|
path: string;
|
|
267
|
-
url?: never;
|
|
268
268
|
};
|
|
269
269
|
policy: {
|
|
270
270
|
installation: string;
|
|
@@ -308,9 +308,9 @@ export declare function marketplacePlugin(url: string): {
|
|
|
308
308
|
installation: string;
|
|
309
309
|
upstream: {
|
|
310
310
|
source: {
|
|
311
|
+
url?: never;
|
|
311
312
|
source: string;
|
|
312
313
|
path: string;
|
|
313
|
-
url?: never;
|
|
314
314
|
};
|
|
315
315
|
policy: {
|
|
316
316
|
installation: string;
|
|
@@ -82,8 +82,8 @@ export declare function scheduledTaskMcpSummary(task: ScheduledTask): {
|
|
|
82
82
|
runMode: "existing_session" | "new_session_per_run" | "reusable_session";
|
|
83
83
|
overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
|
|
84
84
|
action: {
|
|
85
|
-
readonly sourceId?: never;
|
|
86
85
|
kind: "agent_turn";
|
|
86
|
+
sourceId?: never;
|
|
87
87
|
sourceGeneration?: never;
|
|
88
88
|
sourceLifecycleGeneration?: never;
|
|
89
89
|
destination?: never;
|
|
@@ -205,8 +205,8 @@ export declare function boundScheduledTaskDetailMcp(task: ScheduledTask): {
|
|
|
205
205
|
runMode: "existing_session" | "new_session_per_run" | "reusable_session";
|
|
206
206
|
overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
|
|
207
207
|
action: {
|
|
208
|
-
readonly sourceId?: never;
|
|
209
208
|
kind: "agent_turn";
|
|
209
|
+
sourceId?: never;
|
|
210
210
|
sourceGeneration?: never;
|
|
211
211
|
sourceLifecycleGeneration?: never;
|
|
212
212
|
destination?: never;
|
|
@@ -389,8 +389,8 @@ export declare function boundScheduledTaskMcpPage(input: {
|
|
|
389
389
|
runMode: "existing_session" | "new_session_per_run" | "reusable_session";
|
|
390
390
|
overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
|
|
391
391
|
action: {
|
|
392
|
-
readonly sourceId?: never;
|
|
393
392
|
kind: "agent_turn";
|
|
393
|
+
sourceId?: never;
|
|
394
394
|
sourceGeneration?: never;
|
|
395
395
|
sourceLifecycleGeneration?: never;
|
|
396
396
|
destination?: never;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ClientSessionEvent, SteerSessionMessageRequest, type SessionStatus, type WorkClaimSubjectFilter, type SessionGoalRevision, type SessionAuthorizationOperation } from "@opengeni/contracts";
|
|
2
|
-
import { type SessionDiscoveryCursor } from "@opengeni/db";
|
|
1
|
+
import { ClientSessionEvent, SessionScopeSubjectId, SteerSessionMessageRequest, type SessionStatus, type WorkClaimSubjectFilter, type SessionGoalRevision, type SessionAuthorizationOperation } from "@opengeni/contracts";
|
|
2
|
+
import { decodeSessionListCursor, type SessionDiscoveryCursor } from "@opengeni/db";
|
|
3
3
|
import type { Context, Hono } from "hono";
|
|
4
4
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
5
5
|
import { type ViewerServices } from "../sandbox/viewer.js";
|
|
@@ -13,6 +13,28 @@ export declare function goalProposalMatchesExpectedRevision(revision: Pick<Sessi
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function sessionAuthorizationOperationForHttp(method: string, pathname: string, sessionId: string): SessionAuthorizationOperation | null;
|
|
15
15
|
export declare function sessionTenancyHttpError(error: unknown): Error;
|
|
16
|
+
export declare function sessionListQuery(query: Record<string, string>, allowCursor?: boolean): {
|
|
17
|
+
originSiteId: string | undefined;
|
|
18
|
+
limit: string | undefined;
|
|
19
|
+
parentSessionId: string | null | undefined;
|
|
20
|
+
cursor: ReturnType<typeof decodeSessionListCursor> | undefined;
|
|
21
|
+
search: string | undefined;
|
|
22
|
+
pinsOnly: boolean;
|
|
23
|
+
archivedOnly: boolean;
|
|
24
|
+
sortBy: "updatedAt" | "createdAt" | "name" | undefined;
|
|
25
|
+
archiveStatus: "active" | "archived" | "all" | undefined;
|
|
26
|
+
channelId: string | null | undefined;
|
|
27
|
+
createdBy: {
|
|
28
|
+
kind: "subject" | "service";
|
|
29
|
+
subjectId: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
updatedFrom: Date | undefined;
|
|
32
|
+
updatedBefore: Date | undefined;
|
|
33
|
+
createdFrom: Date | undefined;
|
|
34
|
+
createdBefore: Date | undefined;
|
|
35
|
+
scopeSubjectId: SessionScopeSubjectId | undefined;
|
|
36
|
+
hasPageFilters: boolean;
|
|
37
|
+
};
|
|
16
38
|
export type AgentTopologyCursorEnvelope = {
|
|
17
39
|
cursor: SessionDiscoveryCursor;
|
|
18
40
|
parentSessionId: string | null | "all";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/api-router",
|
|
3
|
-
"version": "3.0.1-canary.
|
|
3
|
+
"version": "3.0.1-canary.2",
|
|
4
4
|
"description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -43,24 +43,24 @@
|
|
|
43
43
|
"@hono/zod-validator": "^0.7.6",
|
|
44
44
|
"@llamaindex/liteparse": "2.14.2",
|
|
45
45
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
46
|
-
"@opengeni/agent-proto": "^0.6.0-canary.
|
|
47
|
-
"@opengeni/artifact-tool": "^0.3.29-canary.
|
|
48
|
-
"@opengeni/capabilities": "^0.3.4-canary.
|
|
49
|
-
"@opengeni/codemode": "^0.5.9-canary.
|
|
50
|
-
"@opengeni/codex": "^0.2.23-canary.
|
|
51
|
-
"@opengeni/config": "^1.2.2-canary.
|
|
52
|
-
"@opengeni/contracts": "^4.1.0-canary.
|
|
53
|
-
"@opengeni/core": "^3.0.1-canary.
|
|
54
|
-
"@opengeni/db": "^5.0.1-canary.
|
|
55
|
-
"@opengeni/documents": "^0.8.30-canary.
|
|
56
|
-
"@opengeni/events": "^0.4.28-canary.
|
|
57
|
-
"@opengeni/github": "^0.7.13-canary.
|
|
58
|
-
"@opengeni/network": "^0.3.1-canary.
|
|
59
|
-
"@opengeni/observability": "^0.8.29-canary.
|
|
60
|
-
"@opengeni/runtime": "^3.0.1-canary.
|
|
61
|
-
"@opengeni/storage": "^0.2.130-canary.
|
|
62
|
-
"@opengeni/tool-gateway": "^0.1.10-canary.
|
|
63
|
-
"@opengeni/xai-subscription": "^0.1.4-canary.
|
|
46
|
+
"@opengeni/agent-proto": "^0.6.0-canary.7",
|
|
47
|
+
"@opengeni/artifact-tool": "^0.3.29-canary.2",
|
|
48
|
+
"@opengeni/capabilities": "^0.3.4-canary.2",
|
|
49
|
+
"@opengeni/codemode": "^0.5.9-canary.2",
|
|
50
|
+
"@opengeni/codex": "^0.2.23-canary.4",
|
|
51
|
+
"@opengeni/config": "^1.2.2-canary.2",
|
|
52
|
+
"@opengeni/contracts": "^4.1.0-canary.2",
|
|
53
|
+
"@opengeni/core": "^3.0.1-canary.2",
|
|
54
|
+
"@opengeni/db": "^5.0.1-canary.2",
|
|
55
|
+
"@opengeni/documents": "^0.8.30-canary.2",
|
|
56
|
+
"@opengeni/events": "^0.4.28-canary.2",
|
|
57
|
+
"@opengeni/github": "^0.7.13-canary.2",
|
|
58
|
+
"@opengeni/network": "^0.3.1-canary.19",
|
|
59
|
+
"@opengeni/observability": "^0.8.29-canary.2",
|
|
60
|
+
"@opengeni/runtime": "^3.0.1-canary.2",
|
|
61
|
+
"@opengeni/storage": "^0.2.130-canary.2",
|
|
62
|
+
"@opengeni/tool-gateway": "^0.1.10-canary.2",
|
|
63
|
+
"@opengeni/xai-subscription": "^0.1.4-canary.19",
|
|
64
64
|
"@temporalio/client": "^1.17.0",
|
|
65
65
|
"better-auth": "^1.6.14",
|
|
66
66
|
"hono": "^4.12.18",
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
PostgresEditableArtifactStore,
|
|
48
48
|
listEditableArtifactIdsForSession,
|
|
49
49
|
touchEditableArtifactSessionLink,
|
|
50
|
+
recordNativeDocumentInspection,
|
|
50
51
|
dbSql,
|
|
51
52
|
withRlsContext,
|
|
52
53
|
type Database,
|
|
@@ -167,6 +168,11 @@ export async function createStandaloneEditableArtifactApplication(input: {
|
|
|
167
168
|
await touchEditableArtifactSessionLink(input.db, scope, sessionId, artifactId),
|
|
168
169
|
} satisfies EditableArtifactAgentAssociationPort),
|
|
169
170
|
inspector: kernel,
|
|
171
|
+
recordInspection: async (inspection) => {
|
|
172
|
+
if (inspection.actor.kind !== "agent")
|
|
173
|
+
throw new Error("Inspection proof requires an agent attempt");
|
|
174
|
+
return recordNativeDocumentInspection(input.db, { ...inspection, actor: inspection.actor });
|
|
175
|
+
},
|
|
170
176
|
officeImports,
|
|
171
177
|
workspaceFiles: new EditableArtifactWorkspaceFileAdapter({
|
|
172
178
|
db: input.db,
|
|
@@ -38,6 +38,7 @@ const ArtifactCommand = z
|
|
|
38
38
|
const ArtifactMetadata = z
|
|
39
39
|
.object({
|
|
40
40
|
id: ArtifactId,
|
|
41
|
+
artifactReference: z.string(),
|
|
41
42
|
modality: Modality,
|
|
42
43
|
title: z.string(),
|
|
43
44
|
lifecycle: z.enum(["active", "archived"]),
|
|
@@ -203,13 +204,17 @@ export function registerEditableArtifactAgentTools(
|
|
|
203
204
|
{
|
|
204
205
|
title: "Inspect editable artifact",
|
|
205
206
|
description:
|
|
206
|
-
"Query the artifact's current canonical head through the same native kernel used by the browser. Use bounded modality queries; results are current, not a sandbox-file snapshot.",
|
|
207
|
+
"Query the artifact's current canonical head through the same native kernel used by the browser. Use bounded modality queries; results are current, not a sandbox-file snapshot. Native document queries return a server-issued inspectionReceiptId; use a post-edit body inspection receipt for declared report completion. If the document changes during inspection, retry against its current head.",
|
|
207
208
|
inputSchema: {
|
|
208
209
|
artifactId: ArtifactId,
|
|
209
210
|
modality: Modality,
|
|
210
211
|
request: EditableArtifactQueryRequestSchema,
|
|
211
212
|
},
|
|
212
|
-
outputSchema: {
|
|
213
|
+
outputSchema: {
|
|
214
|
+
artifact: ArtifactMetadata,
|
|
215
|
+
projection: z.unknown(),
|
|
216
|
+
inspectionReceiptId: z.string().uuid().optional(),
|
|
217
|
+
},
|
|
213
218
|
annotations: readOnlyAnnotations("Inspect editable artifact"),
|
|
214
219
|
},
|
|
215
220
|
async ({ artifactId, modality, request }, extra) =>
|
package/src/mcp/server.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
CreateScheduledTaskRequest,
|
|
19
19
|
CreateSessionRequest,
|
|
20
20
|
GoalSpec,
|
|
21
|
+
SessionGoalReportRequirements,
|
|
22
|
+
SessionGoalReportDeliveries,
|
|
21
23
|
boundSessionMcpText as capSessionDiscoveryText,
|
|
22
24
|
compactSessionMcpListRow,
|
|
23
25
|
sessionMcpIncludesRelatedWork,
|
|
@@ -160,6 +162,7 @@ import type { AnySchema, ZodRawShapeCompat } from "@modelcontextprotocol/sdk/ser
|
|
|
160
162
|
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
161
163
|
import { HTTPException } from "hono/http-exception";
|
|
162
164
|
import * as z4 from "zod/v4";
|
|
165
|
+
import { editableArtifactActorForGrant } from "../routes/editable-artifacts";
|
|
163
166
|
import { registerKnowledgeEntryTools } from "./knowledge-entries";
|
|
164
167
|
import {
|
|
165
168
|
FIRST_PARTY_TOOL_AUTHORIZATION,
|
|
@@ -2420,7 +2423,7 @@ function registerGoalTools(
|
|
|
2420
2423
|
"goal_set",
|
|
2421
2424
|
{
|
|
2422
2425
|
description:
|
|
2423
|
-
"Create a goal when this session has none, or replace a completed goal with a new one. While active, idle moments synthesize continuation turns until goal_complete or goal_pause. To change an active or paused goal, use goal_update with its objective revision, a change kind, and rationale.",
|
|
2426
|
+
"Create a goal when this session has none, or replace a completed goal with a new one. Declare user-facing native document reports with reportRequirements before producing them. While active, idle moments synthesize continuation turns until goal_complete or goal_pause. To change an active or paused goal, use goal_update with its objective revision, a change kind, and rationale.",
|
|
2424
2427
|
// `maxAutoContinuations` is deliberately not agent-facing: the ceiling is
|
|
2425
2428
|
// API/scheduled-task pacing configuration, and an agent that set its own
|
|
2426
2429
|
// cap used to silence its orchestration for hours. Continuation pacing is
|
|
@@ -2428,9 +2431,10 @@ function registerGoalTools(
|
|
|
2428
2431
|
inputSchema: {
|
|
2429
2432
|
text: goalText,
|
|
2430
2433
|
successCriteria: successCriteriaSchema.optional(),
|
|
2434
|
+
reportRequirements: SessionGoalReportRequirements.optional(),
|
|
2431
2435
|
},
|
|
2432
2436
|
},
|
|
2433
|
-
async ({ text, successCriteria }) => {
|
|
2437
|
+
async ({ text, successCriteria, reportRequirements }) => {
|
|
2434
2438
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2435
2439
|
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
2436
2440
|
const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
|
|
@@ -2444,15 +2448,24 @@ function registerGoalTools(
|
|
|
2444
2448
|
? (grant.metadata["turnId"] as string)
|
|
2445
2449
|
: null;
|
|
2446
2450
|
await assertGoalReactivationAllowed(deps, grant.workspaceId, sessionId, callerTurnId);
|
|
2451
|
+
const context = exactAgentCommandContext(grant, sessionId);
|
|
2447
2452
|
const { goal, replaced, events } = await upsertSessionGoalWithEvent(deps.db, {
|
|
2448
2453
|
accountId: grant.accountId,
|
|
2449
2454
|
workspaceId: grant.workspaceId,
|
|
2450
2455
|
sessionId,
|
|
2451
2456
|
text,
|
|
2452
2457
|
successCriteria: successCriteria ?? null,
|
|
2458
|
+
...(reportRequirements !== undefined ? { reportRequirements } : {}),
|
|
2453
2459
|
maxAutoContinuations: null,
|
|
2454
2460
|
createdBy: "agent",
|
|
2455
2461
|
actor: "agent",
|
|
2462
|
+
commandActor: {
|
|
2463
|
+
type: "agent_attempt",
|
|
2464
|
+
sessionId: context.callerSessionId,
|
|
2465
|
+
turnId: context.callerTurnId,
|
|
2466
|
+
attemptId: context.callerAttemptId,
|
|
2467
|
+
executionGeneration: context.callerExecutionGeneration,
|
|
2468
|
+
},
|
|
2456
2469
|
});
|
|
2457
2470
|
if (events.length > 0) {
|
|
2458
2471
|
await deps.bus.publish(grant.workspaceId, sessionId, events);
|
|
@@ -2540,13 +2553,14 @@ function registerGoalTools(
|
|
|
2540
2553
|
"goal_progress",
|
|
2541
2554
|
{
|
|
2542
2555
|
description:
|
|
2543
|
-
"Record concrete progress toward the unchanged active goal. This does not change goal text, success criteria, mutation policy, or objective revision. Do not use it merely to keep the continuation loop alive.",
|
|
2556
|
+
"Record concrete progress toward the unchanged active goal. Optionally append reportRequirements for secondary user-facing reports discovered during other work; existing requirement IDs and titles cannot be changed or removed. This does not change goal text, success criteria, mutation policy, or objective revision. Do not use it merely to keep the continuation loop alive.",
|
|
2544
2557
|
inputSchema: {
|
|
2545
2558
|
progressNote: progressNoteSchema,
|
|
2546
2559
|
idempotencyKey: z4.string().uuid(),
|
|
2560
|
+
reportRequirements: SessionGoalReportRequirements.optional(),
|
|
2547
2561
|
},
|
|
2548
2562
|
},
|
|
2549
|
-
async ({ progressNote, idempotencyKey }) => {
|
|
2563
|
+
async ({ progressNote, idempotencyKey, reportRequirements }) => {
|
|
2550
2564
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2551
2565
|
const context = exactAgentCommandContext(grant, sessionId);
|
|
2552
2566
|
const { goal, events, operationId, replay } = await recordSessionGoalProgressWithEvent(
|
|
@@ -2555,6 +2569,7 @@ function registerGoalTools(
|
|
|
2555
2569
|
sessionId,
|
|
2556
2570
|
{
|
|
2557
2571
|
progressNote,
|
|
2572
|
+
...(reportRequirements !== undefined ? { reportRequirements } : {}),
|
|
2558
2573
|
command: {
|
|
2559
2574
|
accountId: grant.accountId,
|
|
2560
2575
|
actor: {
|
|
@@ -2635,16 +2650,23 @@ function registerGoalTools(
|
|
|
2635
2650
|
"goal_complete",
|
|
2636
2651
|
{
|
|
2637
2652
|
description:
|
|
2638
|
-
"Mark the session goal as completed
|
|
2639
|
-
inputSchema: {
|
|
2653
|
+
"Mark the session goal as completed with concrete evidence. Every persisted report requirement must have a matching reportDeliveries entry containing a native document artifactId and its server-issued inspectionReceiptId from a post-edit body inspection. Missing, stale, inaccessible or summary-only proof fails; inspect again after an edit. Omit reportDeliveries only when no reports were declared. Successful completion returns report artifact references and prevents further continuation turns.",
|
|
2654
|
+
inputSchema: {
|
|
2655
|
+
evidence: z4.string().min(1),
|
|
2656
|
+
reportDeliveries: SessionGoalReportDeliveries.optional(),
|
|
2657
|
+
},
|
|
2640
2658
|
},
|
|
2641
|
-
async ({ evidence }) => {
|
|
2659
|
+
async ({ evidence, reportDeliveries }) => {
|
|
2642
2660
|
await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
|
|
2643
2661
|
await requireSession(deps.db, grant.workspaceId, sessionId);
|
|
2644
2662
|
const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
|
|
2645
2663
|
if (!existing) {
|
|
2646
2664
|
throw new Error("this session has no goal; use goal_set first");
|
|
2647
2665
|
}
|
|
2666
|
+
const context = exactAgentCommandContext(grant, sessionId);
|
|
2667
|
+
const reportArtifactActor = editableArtifactActorForGrant(grant, "0000000000000001");
|
|
2668
|
+
if (reportArtifactActor.kind !== "agent")
|
|
2669
|
+
throw new Error("Goal completion requires exact agent authority");
|
|
2648
2670
|
const { goal, events } = await setSessionGoalStatusWithEvent(
|
|
2649
2671
|
deps.db,
|
|
2650
2672
|
grant.workspaceId,
|
|
@@ -2652,6 +2674,15 @@ function registerGoalTools(
|
|
|
2652
2674
|
{
|
|
2653
2675
|
status: "completed",
|
|
2654
2676
|
evidence,
|
|
2677
|
+
...(reportDeliveries !== undefined ? { reportDeliveries } : {}),
|
|
2678
|
+
reportArtifactActor,
|
|
2679
|
+
commandActor: {
|
|
2680
|
+
type: "agent_attempt",
|
|
2681
|
+
sessionId: context.callerSessionId,
|
|
2682
|
+
turnId: context.callerTurnId,
|
|
2683
|
+
attemptId: context.callerAttemptId,
|
|
2684
|
+
executionGeneration: context.callerExecutionGeneration,
|
|
2685
|
+
},
|
|
2655
2686
|
event: { type: "goal.completed", evidence },
|
|
2656
2687
|
},
|
|
2657
2688
|
);
|
|
@@ -2659,8 +2690,8 @@ function registerGoalTools(
|
|
|
2659
2690
|
if (events.length > 0) {
|
|
2660
2691
|
await deps.bus.publish(grant.workspaceId, sessionId, events);
|
|
2661
2692
|
}
|
|
2662
|
-
return json(
|
|
2663
|
-
mcpMutationReceipt({
|
|
2693
|
+
return json({
|
|
2694
|
+
...mcpMutationReceipt({
|
|
2664
2695
|
operation: "goal_complete",
|
|
2665
2696
|
committed: true,
|
|
2666
2697
|
outcome: changed ? "updated" : "unchanged",
|
|
@@ -2675,7 +2706,13 @@ function registerGoalTools(
|
|
|
2675
2706
|
idempotency: { status: "not_supported" },
|
|
2676
2707
|
nextAction: { tool: "session_get", arguments: { sessionId } },
|
|
2677
2708
|
}),
|
|
2678
|
-
|
|
2709
|
+
reportDeliveries: SessionGoalReportDeliveries.parse(
|
|
2710
|
+
goal.metadata.reportDeliveriesV1 ?? [],
|
|
2711
|
+
).map((delivery) => ({
|
|
2712
|
+
...delivery,
|
|
2713
|
+
artifactReference: `[Open report](/workspaces/${grant.workspaceId}/artifacts/editable/${delivery.artifactId})`,
|
|
2714
|
+
})),
|
|
2715
|
+
});
|
|
2679
2716
|
},
|
|
2680
2717
|
);
|
|
2681
2718
|
|
package/src/routes/sessions.ts
CHANGED
|
@@ -708,6 +708,8 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
708
708
|
...(query.search ? { search: query.search } : {}),
|
|
709
709
|
...(query.pinsOnly ? { pinsOnly: true } : {}),
|
|
710
710
|
...(query.archivedOnly ? { archivedOnly: true } : {}),
|
|
711
|
+
...(query.sortBy ? { sortBy: query.sortBy } : {}),
|
|
712
|
+
...(query.archiveStatus ? { archiveStatus: query.archiveStatus } : {}),
|
|
711
713
|
...(query.parentSessionId !== undefined ? { parentSessionId: query.parentSessionId } : {}),
|
|
712
714
|
...(query.channelId !== undefined ? { channelId: query.channelId } : {}),
|
|
713
715
|
...(query.originSiteId ? { originSiteId: query.originSiteId } : {}),
|
|
@@ -2569,10 +2571,17 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2569
2571
|
|
|
2570
2572
|
app.delete("/v1/workspaces/:workspaceId/sessions/:sessionId/goal", async (c) => {
|
|
2571
2573
|
const workspaceId = c.req.param("workspaceId");
|
|
2572
|
-
await requireAccessGrant(c, deps, workspaceId, "sessions:control");
|
|
2574
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "sessions:control");
|
|
2573
2575
|
const sessionId = c.req.param("sessionId");
|
|
2574
2576
|
await assertSessionExists(db, workspaceId, sessionId);
|
|
2575
|
-
const { event } = await clearSessionGoal(db, workspaceId, sessionId
|
|
2577
|
+
const { event } = await clearSessionGoal(db, workspaceId, sessionId, {
|
|
2578
|
+
actor: grant.principalKind === "agent_attempt" ? "agent" : "api",
|
|
2579
|
+
}).catch((error: unknown) => {
|
|
2580
|
+
if (error instanceof SessionControlConflictError) {
|
|
2581
|
+
throw new HTTPException(409, { message: error.message, cause: error });
|
|
2582
|
+
}
|
|
2583
|
+
throw error;
|
|
2584
|
+
});
|
|
2576
2585
|
if (event) {
|
|
2577
2586
|
try {
|
|
2578
2587
|
await bus.publish(workspaceId, sessionId, [event]);
|
|
@@ -4795,7 +4804,7 @@ function eventEnumList<T extends string>(
|
|
|
4795
4804
|
});
|
|
4796
4805
|
}
|
|
4797
4806
|
|
|
4798
|
-
function sessionListQuery(
|
|
4807
|
+
export function sessionListQuery(
|
|
4799
4808
|
query: Record<string, string>,
|
|
4800
4809
|
allowCursor = true,
|
|
4801
4810
|
): {
|
|
@@ -4806,6 +4815,8 @@ function sessionListQuery(
|
|
|
4806
4815
|
search: string | undefined;
|
|
4807
4816
|
pinsOnly: boolean;
|
|
4808
4817
|
archivedOnly: boolean;
|
|
4818
|
+
sortBy: "updatedAt" | "createdAt" | "name" | undefined;
|
|
4819
|
+
archiveStatus: "active" | "archived" | "all" | undefined;
|
|
4809
4820
|
channelId: string | null | undefined;
|
|
4810
4821
|
createdBy: { kind: "subject" | "service"; subjectId: string } | undefined;
|
|
4811
4822
|
updatedFrom: Date | undefined;
|
|
@@ -4852,6 +4863,20 @@ function sessionListQuery(
|
|
|
4852
4863
|
throw new HTTPException(400, { message: 'archivedOnly must be the literal "true"' });
|
|
4853
4864
|
}
|
|
4854
4865
|
const archivedOnly = query.archivedOnly === "true";
|
|
4866
|
+
const sortBy = z.enum(["updatedAt", "createdAt", "name"]).optional().safeParse(query.sortBy);
|
|
4867
|
+
const archiveStatus = z
|
|
4868
|
+
.enum(["active", "archived", "all"])
|
|
4869
|
+
.optional()
|
|
4870
|
+
.safeParse(query.archiveStatus);
|
|
4871
|
+
if (!sortBy.success || !archiveStatus.success) {
|
|
4872
|
+
throw new HTTPException(400, { message: "Invalid session sortBy or archiveStatus" });
|
|
4873
|
+
}
|
|
4874
|
+
if (archivedOnly && archiveStatus.data !== undefined && archiveStatus.data !== "archived") {
|
|
4875
|
+
throw new HTTPException(400, { message: "archivedOnly conflicts with archiveStatus" });
|
|
4876
|
+
}
|
|
4877
|
+
if (pinsOnly && archiveStatus.data === "archived") {
|
|
4878
|
+
throw new HTTPException(400, { message: "pinsOnly cannot be combined with archived status" });
|
|
4879
|
+
}
|
|
4855
4880
|
const channelId = query.channelId;
|
|
4856
4881
|
if (
|
|
4857
4882
|
channelId !== undefined &&
|
|
@@ -4956,6 +4981,8 @@ function sessionListQuery(
|
|
|
4956
4981
|
search: search || undefined,
|
|
4957
4982
|
pinsOnly,
|
|
4958
4983
|
archivedOnly,
|
|
4984
|
+
sortBy: sortBy.data,
|
|
4985
|
+
archiveStatus: archiveStatus.data,
|
|
4959
4986
|
channelId: channelId === undefined ? undefined : channelId === "null" ? null : channelId,
|
|
4960
4987
|
createdBy:
|
|
4961
4988
|
createdByKind && createdBySubjectId
|