@canonmsg/codex-plugin 0.30.1 → 0.31.0
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/host.d.ts +5 -0
- package/dist/host.js +32 -13
- package/package.json +5 -5
package/dist/host.d.ts
CHANGED
|
@@ -247,6 +247,11 @@ export declare function shouldRouteCodexTurnArtifacts(input: {
|
|
|
247
247
|
artifactRoutingMode: TurnArtifactRoutingMode | undefined;
|
|
248
248
|
silenced: boolean;
|
|
249
249
|
finalText: string | null | undefined;
|
|
250
|
+
currentAudience?: {
|
|
251
|
+
memberIds?: readonly string[];
|
|
252
|
+
agentId: string;
|
|
253
|
+
ownerId: string | null | undefined;
|
|
254
|
+
};
|
|
250
255
|
}): TurnArtifactRoutingDecision;
|
|
251
256
|
export declare function main(): Promise<void>;
|
|
252
257
|
export {};
|
package/dist/host.js
CHANGED
|
@@ -6,7 +6,7 @@ import { dirname } from 'node:path';
|
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
7
|
import { getCodexImagePath, materializeMessageMedia, materializeReplyContextMedia, sendMediaFileMessage, } from '@canonmsg/agent-sdk';
|
|
8
8
|
import { buildTrailBlockId, buildUndeliverableFinalNotice, captureTurnArtifactSnapshot, createTurnArtifactRouter, IDLE_TIMEOUT_MS, PLAN_BLOCK_TITLE, resolveTurnArtifactRouting, collectMissedInboundMessages, createRecoveryCheckpointTracker, createReconnectRecoveryCoordinator, STARTUP_RECOVERY_PAGE_SIZE, } from '@canonmsg/coding-agent-host';
|
|
9
|
-
import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, resolveQuestionAllowOther, buildCanonTurnContextV2, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, buildCanonInboundFrameV1, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, decideAutoReply, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, markLocalRuntimeStopped, normalizeTurnMetadata, parseTurnVerbosityConfig, RuntimeRequestManager, prepareConversationEnvironment, releaseConversationEnvironment, resolveLocalRuntimeSessionState, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState, publishHostSessionSnapshots, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveConfiguredWorkspaceCwd, isSilentTurnSuppressed, resolveSilentTurnDelivery, resolveTurnVerbosity, shouldTriggerAgentTurn, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
9
|
+
import { RUNTIME_NEW_SESSION_ACTION, RUNTIME_STOP_ACTION, RUNTIME_STOP_AND_DROP_ACTION, buildRuntimeCardOutcome, resolveQuestionAllowOther, buildCanonTurnContextV2, buildCanonGroupContext, buildCompactGroupContextLines, buildConfiguredWorkspaceOptionsWithRoots, buildFirstPartyCodingRuntimeDescriptor, buildHydratedInboundContext, diffCanonMemberIds, buildPublicWorkspaceRoots, buildPublicWorkspaceOptions, buildRuntimePresentationPolicy, buildCanonInboundFrameV1, DEFAULT_FIRST_PARTY_RUNTIME_PRESENTATION, createConversationMetadataLoader, createRuntimeStatePublisher, createRuntimeHeartbeat, createTypingStatusPublisher, EXECUTION_ENVIRONMENT_MODES, ExecutionEnvironmentError, CanonClient, CanonStream, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT, DEFAULT_RUNTIME_CAPABILITIES, FINAL_MESSAGE_HANDOFF_MS, getActiveProfileLock, decideAutoReply, initRTDBAuth, buildLocalRuntimeId, heartbeatLocalRuntimeEntry, markLocalRuntimeStopped, normalizeTurnMetadata, parseTurnVerbosityConfig, RuntimeRequestManager, prepareConversationEnvironment, releaseConversationEnvironment, resolveLocalRuntimeSessionState, resolveCanonAgent, verifyResolvedAgentEnvironment, CanonApiError, loadRuntimeSessionState, sendMessageWithRetry, sendMessageWithRetryChunked, saveRuntimeSessionState, publishHostSessionSnapshots, renderCanonHostInboundContent, renderCodingHostInboundPrompt, resolveConfiguredWorkspaceCwd, isSilentTurnSuppressed, resolveSilentTurnDelivery, resolveTurnVerbosity, shouldTriggerAgentTurn, upsertLocalRuntimeEntry, } from '@canonmsg/core';
|
|
10
10
|
import { validateCard } from '@canonmsg/rich-cards';
|
|
11
11
|
import { CodexConversationAdapter, } from './adapter.js';
|
|
12
12
|
import { CodexAppServerAdapter, } from './app-server-adapter.js';
|
|
@@ -599,8 +599,12 @@ export function selectCodexNativeImagePaths(imagePaths, nativeVisionEnabled) {
|
|
|
599
599
|
* does not currently carry into the catch without a race.
|
|
600
600
|
*/
|
|
601
601
|
export function shouldRouteCodexTurnArtifacts(input) {
|
|
602
|
+
const audience = input.currentAudience;
|
|
603
|
+
const ownerPair = !audience || (audience.ownerId !== audience.agentId
|
|
604
|
+
&& Boolean(audience.ownerId) && audience.memberIds?.length === 2
|
|
605
|
+
&& audience.memberIds.includes(audience.agentId) && audience.memberIds.includes(audience.ownerId));
|
|
602
606
|
return resolveTurnArtifactRouting({
|
|
603
|
-
artifactRoutingMode: input.artifactRoutingMode,
|
|
607
|
+
artifactRoutingMode: ownerPair ? input.artifactRoutingMode : 'disabled',
|
|
604
608
|
silenced: isSilentTurnSuppressed({ silenced: input.silenced, finalText: input.finalText }),
|
|
605
609
|
});
|
|
606
610
|
}
|
|
@@ -854,6 +858,7 @@ export async function main() {
|
|
|
854
858
|
if (cached) {
|
|
855
859
|
conversationCache.set(payload.conversationId, {
|
|
856
860
|
...cached,
|
|
861
|
+
...payload.changes,
|
|
857
862
|
memberIds,
|
|
858
863
|
});
|
|
859
864
|
}
|
|
@@ -862,11 +867,12 @@ export async function main() {
|
|
|
862
867
|
}
|
|
863
868
|
if (!memberIds.includes(agentId)) {
|
|
864
869
|
knownConversationIds.delete(payload.conversationId);
|
|
865
|
-
|
|
870
|
+
pendingMembershipChanges.delete(payload.conversationId);
|
|
871
|
+
closeSession(payload.conversationId);
|
|
866
872
|
}
|
|
867
873
|
}
|
|
868
874
|
function getGroupContextMode(conversationId, conversation) {
|
|
869
|
-
if (conversation
|
|
875
|
+
if (!conversation)
|
|
870
876
|
return undefined;
|
|
871
877
|
if (pendingMembershipChanges.has(conversationId))
|
|
872
878
|
return 'membership_change';
|
|
@@ -884,7 +890,7 @@ export async function main() {
|
|
|
884
890
|
}
|
|
885
891
|
async function loadHydratedInboundContext(input) {
|
|
886
892
|
const [conversation, page] = await Promise.all([
|
|
887
|
-
getConversationMeta(input.conversationId),
|
|
893
|
+
getConversationMeta(input.conversationId, { refreshIfMemberMissing: agentId }),
|
|
888
894
|
input.hydratedPage
|
|
889
895
|
? Promise.resolve(input.hydratedPage)
|
|
890
896
|
: client.getMessagesPage(input.conversationId, DEFAULT_PARTICIPATION_HISTORY_FETCH_LIMIT).catch(() => null),
|
|
@@ -893,6 +899,7 @@ export async function main() {
|
|
|
893
899
|
agentId,
|
|
894
900
|
conversationId: input.conversationId,
|
|
895
901
|
conversation,
|
|
902
|
+
requireAgentMembership: true,
|
|
896
903
|
page,
|
|
897
904
|
activeSelfContextId: input.activeSelfContextId,
|
|
898
905
|
selfContexts: input.selfContexts,
|
|
@@ -1204,6 +1211,10 @@ export async function main() {
|
|
|
1204
1211
|
}
|
|
1205
1212
|
}
|
|
1206
1213
|
async function getOrCreateSession(conversationId) {
|
|
1214
|
+
const current = conversationCache.get(conversationId);
|
|
1215
|
+
if (current && !current.memberIds.includes(agentId)) {
|
|
1216
|
+
throw new Error('Agent is no longer a member of this conversation');
|
|
1217
|
+
}
|
|
1207
1218
|
knownConversationIds.add(conversationId);
|
|
1208
1219
|
const existing = sessions.get(conversationId);
|
|
1209
1220
|
if (existing && !existing.closed) {
|
|
@@ -1930,8 +1941,9 @@ export async function main() {
|
|
|
1930
1941
|
replyAuthority: input.replyAuthority ?? null,
|
|
1931
1942
|
});
|
|
1932
1943
|
}
|
|
1933
|
-
function sendTurnArtifactFile(session, file) {
|
|
1944
|
+
function sendTurnArtifactFile(session, file, canPublish) {
|
|
1934
1945
|
return sendMediaFileMessage(client, session.conversationId, file.path, '', {
|
|
1946
|
+
canPublish,
|
|
1935
1947
|
...(session.currentReplyAuthority ? { replyAuthority: session.currentReplyAuthority } : {}),
|
|
1936
1948
|
...(session.activeSelfContextId ? { selfContextId: session.activeSelfContextId } : {}),
|
|
1937
1949
|
metadata: {
|
|
@@ -2001,22 +2013,29 @@ export async function main() {
|
|
|
2001
2013
|
// the collect-and-post loop, so there is no un-gated path left to the
|
|
2002
2014
|
// workspace: the failure branches still send Canon's notice, and none of
|
|
2003
2015
|
// them can post the artifacts of a turn that chose silence.
|
|
2016
|
+
const decideArtifactRouting = () => shouldRouteCodexTurnArtifacts({
|
|
2017
|
+
artifactRoutingMode: nextTurn.artifactRoutingMode,
|
|
2018
|
+
silenced: session.currentTurnSilenced,
|
|
2019
|
+
finalText: turnFinalText,
|
|
2020
|
+
currentAudience: { memberIds: conversationCache.get(session.conversationId)?.memberIds, agentId, ownerId },
|
|
2021
|
+
});
|
|
2004
2022
|
const artifactRouter = createTurnArtifactRouter({
|
|
2005
|
-
decide:
|
|
2006
|
-
artifactRoutingMode: nextTurn.artifactRoutingMode,
|
|
2007
|
-
silenced: session.currentTurnSilenced,
|
|
2008
|
-
finalText: turnFinalText,
|
|
2009
|
-
}),
|
|
2023
|
+
decide: decideArtifactRouting,
|
|
2010
2024
|
baseline: () => artifactBaseline,
|
|
2011
2025
|
cwd: () => session.cwd,
|
|
2012
|
-
send: (file) => sendTurnArtifactFile(session, file),
|
|
2026
|
+
send: (file) => sendTurnArtifactFile(session, file, () => decideArtifactRouting().route),
|
|
2013
2027
|
log: (line) => console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] ${line}`),
|
|
2014
2028
|
});
|
|
2015
2029
|
const routeArtifactsOnce = artifactRouter.route;
|
|
2016
2030
|
try {
|
|
2017
2031
|
const turnId = session.currentTurnId ?? randomUUID();
|
|
2018
2032
|
session.currentTurnId = turnId;
|
|
2019
|
-
|
|
2033
|
+
// Queueing does not freeze the audience: refresh at the native turn
|
|
2034
|
+
// boundary while retaining the same thread and its existing memory.
|
|
2035
|
+
const roster = buildCanonGroupContext({
|
|
2036
|
+
conversation: conversationCache.get(session.conversationId), messages: [], agentId, ownerId, ownerName,
|
|
2037
|
+
});
|
|
2038
|
+
let turnPrompt = [nextTurn.prompt, ...(roster ? buildCompactGroupContextLines(roster, 'initial') : [])].join('\n\n');
|
|
2020
2039
|
if (nextTurn.artifactRoutingMode === 'workspace-generated') {
|
|
2021
2040
|
artifactBaseline = await captureTurnArtifactSnapshot({ cwd: session.cwd }).catch((error) => {
|
|
2022
2041
|
console.error(`[canon-codex] [${session.conversationId.slice(0, 8)}] Artifact snapshot failed:`, error instanceof Error ? error.message : error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonmsg/codex-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Canon host integration for Codex CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"prepack": "npm run build"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@canonmsg/agent-sdk": "^10.
|
|
35
|
-
"@canonmsg/agent-tools": "^0.
|
|
36
|
-
"@canonmsg/coding-agent-host": "^0.
|
|
37
|
-
"@canonmsg/core": "^12.
|
|
34
|
+
"@canonmsg/agent-sdk": "^10.4.0",
|
|
35
|
+
"@canonmsg/agent-tools": "^0.10.0",
|
|
36
|
+
"@canonmsg/coding-agent-host": "^0.8.0",
|
|
37
|
+
"@canonmsg/core": "^12.5.0",
|
|
38
38
|
"@canonmsg/rich-cards": "^0.10.5",
|
|
39
39
|
"ws": "^8.21.3"
|
|
40
40
|
},
|