fix(cli): 继承 Aipod 会话持久身份
This commit is contained in:
+71
-1
@@ -1570,7 +1570,13 @@ async function submitQueueTaskWithAipod(args: ParsedArgs, aipod: string): Promis
|
||||
|
||||
async function sessionSendWithAipod(args: ParsedArgs, positionalSessionId: string | null, aipod: string): Promise<JsonRecord> {
|
||||
const sessionId = positionalSessionId ?? optionalFlag(args, "session-id") ?? newSessionId();
|
||||
const rendered = await renderAipodForCommand(args, aipod, positionalSessionId ? 3 : 2, { sessionId });
|
||||
const existing = await fetchDurableSessionForAipodSend(args, sessionId);
|
||||
const renderInput = inheritDurableSessionAipodIdentity(
|
||||
await aipodRenderInput(args, positionalSessionId ? 3 : 2, { sessionId }),
|
||||
sessionId,
|
||||
existing,
|
||||
);
|
||||
const rendered = await client(args).post(`/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, renderInput) as RenderedAipodQueueTask;
|
||||
const task = rendered.queueTask;
|
||||
const profile = String(task.backendProfile);
|
||||
const sessionRef = objectField(task as unknown as JsonRecord, "sessionRef", {});
|
||||
@@ -1605,6 +1611,70 @@ async function sessionSendWithAipod(args: ParsedArgs, positionalSessionId: strin
|
||||
return { ...summarizeSessionSendResult(result, sessionId, profile, aipod), modelResolution: rendered.modelResolution };
|
||||
}
|
||||
|
||||
async function fetchDurableSessionForAipodSend(args: ParsedArgs, sessionId: string): Promise<JsonRecord | null> {
|
||||
try {
|
||||
return await client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}`) as JsonRecord;
|
||||
} catch (error) {
|
||||
if (error instanceof AgentRunError && error.httpStatus === 404) return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function inheritDurableSessionAipodIdentity(
|
||||
renderInput: RenderAipodInput,
|
||||
sessionId: string,
|
||||
existing: JsonRecord | null,
|
||||
): RenderAipodInput {
|
||||
if (existing === null) return renderInput;
|
||||
const durableSessionId = stringValue(existing.sessionId);
|
||||
const tenantId = stringValue(existing.tenantId);
|
||||
const projectId = stringValue(existing.projectId);
|
||||
if (durableSessionId !== sessionId || tenantId === null || projectId === null) {
|
||||
throw new AgentRunError("schema-invalid", `session ${sessionId} durable identity is incomplete or mismatched`, {
|
||||
httpStatus: 400,
|
||||
details: { sessionId, valuesPrinted: false },
|
||||
});
|
||||
}
|
||||
assertDurableSessionBoundary(renderInput.tenantId, tenantId, sessionId, "tenantId");
|
||||
assertDurableSessionBoundary(renderInput.projectId, projectId, sessionId, "projectId");
|
||||
const requestedSessionRef = jsonRecordValue(renderInput.sessionRef) ?? {};
|
||||
const requestedSessionId = stringValue(requestedSessionRef.sessionId);
|
||||
if (requestedSessionId !== null && requestedSessionId !== sessionId) {
|
||||
throw durableSessionBoundaryError(sessionId, "sessionRef.sessionId");
|
||||
}
|
||||
const conversationId = stringValue(existing.conversationId);
|
||||
const threadId = stringValue(existing.threadId);
|
||||
const expiresAt = stringValue(existing.expiresAt);
|
||||
return {
|
||||
...renderInput,
|
||||
tenantId,
|
||||
projectId,
|
||||
sessionId,
|
||||
sessionRef: {
|
||||
sessionId,
|
||||
...(conversationId === null ? {} : { conversationId }),
|
||||
...(threadId === null ? {} : { threadId }),
|
||||
...(expiresAt === null ? {} : { expiresAt }),
|
||||
metadata: {
|
||||
...(jsonRecordValue(requestedSessionRef.metadata) ?? {}),
|
||||
...(jsonRecordValue(existing.metadata) ?? {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function assertDurableSessionBoundary(value: string | undefined, durable: string, sessionId: string, field: string): void {
|
||||
if (value === undefined || value === durable) return;
|
||||
throw durableSessionBoundaryError(sessionId, field);
|
||||
}
|
||||
|
||||
function durableSessionBoundaryError(sessionId: string, field: string): AgentRunError {
|
||||
return new AgentRunError("tenant-policy-denied", "sessionRef cannot be reused across tenant or project boundary", {
|
||||
httpStatus: 403,
|
||||
details: { sessionId, field, valuesPrinted: false },
|
||||
});
|
||||
}
|
||||
|
||||
async function submitQueueTask(args: ParsedArgs): Promise<JsonValue> {
|
||||
const aipod = optionalFlag(args, "aipod") ?? optionalFlag(args, "aipod-spec");
|
||||
if (aipod) return submitQueueTaskWithAipod(args, aipod);
|
||||
|
||||
Reference in New Issue
Block a user