@canonmsg/codex-plugin 0.11.3 → 0.11.4

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,32 @@
1
+ import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalResult, ApprovalRisk } from '@canonmsg/core';
2
+ /**
3
+ * Mapping for Codex app-server native approval requests.
4
+ *
5
+ * The current Canon Codex host still uses `codex exec --json`, which cannot
6
+ * block on these methods. Keep this as metadata/decision plumbing for the
7
+ * future app-server transport, not as an advertised `exec --json` UI mode.
8
+ */
9
+ export type CodexAppServerApprovalMethod = 'item/commandExecution/requestApproval' | 'item/fileChange/requestApproval' | 'item/permissions/requestApproval' | 'execCommandApproval' | 'applyPatchApproval';
10
+ export interface CodexNativeApprovalRequest {
11
+ method: CodexAppServerApprovalMethod;
12
+ params: Record<string, unknown>;
13
+ }
14
+ export interface CanonCodexApprovalRequest {
15
+ toolName: string;
16
+ toolInput: Record<string, unknown>;
17
+ toolSummary: string;
18
+ category: ApprovalRequestCategory;
19
+ risk: ApprovalRisk;
20
+ riskLevel: 'normal' | 'destructive';
21
+ native: ApprovalNativeRequestMetadata;
22
+ details: ApprovalRequestDetail[];
23
+ }
24
+ export type CodexApprovalDecision = {
25
+ decision: 'accept';
26
+ } | {
27
+ decision: 'acceptForSession';
28
+ } | {
29
+ decision: 'decline';
30
+ };
31
+ export declare function mapCodexAppServerApprovalRequest(request: CodexNativeApprovalRequest): CanonCodexApprovalRequest | null;
32
+ export declare function mapCanonApprovalResultToCodexDecision(result: ApprovalResult): CodexApprovalDecision;
@@ -0,0 +1,168 @@
1
+ function isRecord(value) {
2
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
3
+ }
4
+ function readString(record, keys) {
5
+ for (const key of keys) {
6
+ const value = record[key];
7
+ if (typeof value === 'string' && value.trim())
8
+ return value.trim();
9
+ }
10
+ return undefined;
11
+ }
12
+ function firstString(value) {
13
+ if (typeof value === 'string' && value.trim())
14
+ return value.trim();
15
+ if (Array.isArray(value)) {
16
+ return value.map((entry) => (typeof entry === 'string' ? entry.trim() : '')).find(Boolean);
17
+ }
18
+ return undefined;
19
+ }
20
+ function stringifyPreview(value, maxLength = 500) {
21
+ if (typeof value === 'string')
22
+ return value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
23
+ const json = JSON.stringify(value ?? {});
24
+ return json.length > maxLength ? `${json.slice(0, maxLength - 3)}...` : json;
25
+ }
26
+ function nativeHandle(method, params) {
27
+ const native = {
28
+ runtime: 'codex',
29
+ method,
30
+ };
31
+ const requestId = readString(params, ['requestId', 'id', 'callId']);
32
+ const provider = readString(params, ['provider']);
33
+ const origin = readString(params, ['origin']);
34
+ const surface = readString(params, ['surface']);
35
+ const threadId = readString(params, ['threadId', 'conversationId']);
36
+ const turnId = readString(params, ['turnId']);
37
+ const runId = readString(params, ['runId']);
38
+ const itemId = readString(params, ['itemId']);
39
+ const toolCallId = readString(params, ['toolCallId', 'toolUseId']);
40
+ const approvalId = readString(params, ['approvalId']);
41
+ const pluginId = readString(params, ['pluginId']);
42
+ const sessionKey = readString(params, ['sessionKey', 'sessionId']);
43
+ const cwd = readString(params, ['cwd', 'workingDirectory']);
44
+ const model = readString(params, ['model']);
45
+ if (requestId)
46
+ native.requestId = requestId;
47
+ if (provider)
48
+ native.provider = provider;
49
+ if (origin)
50
+ native.origin = origin;
51
+ if (surface)
52
+ native.surface = surface;
53
+ if (threadId)
54
+ native.threadId = threadId;
55
+ if (turnId)
56
+ native.turnId = turnId;
57
+ if (runId)
58
+ native.runId = runId;
59
+ if (itemId)
60
+ native.itemId = itemId;
61
+ if (toolCallId)
62
+ native.toolCallId = toolCallId;
63
+ if (approvalId)
64
+ native.approvalId = approvalId;
65
+ if (pluginId)
66
+ native.pluginId = pluginId;
67
+ if (sessionKey)
68
+ native.sessionKey = sessionKey;
69
+ if (cwd)
70
+ native.cwd = cwd;
71
+ if (model)
72
+ native.model = model;
73
+ return native;
74
+ }
75
+ function commandRisk(command) {
76
+ if (/\brm\s+(-rf?|--force)|\bgit\s+reset\s+--hard|\bgit\s+push\s+--force|\bdrop\s+(table|database)\b/i.test(command)) {
77
+ return 'destructive';
78
+ }
79
+ return 'high';
80
+ }
81
+ function detail(label, value, monospace = true) {
82
+ return value ? [{ label, value, monospace }] : [];
83
+ }
84
+ function mapCommandRequest(method, params) {
85
+ const command = readString(params, ['command', 'cmd'])
86
+ ?? firstString(params.argv)
87
+ ?? firstString(params.args)
88
+ ?? stringifyPreview(params);
89
+ const cwd = readString(params, ['cwd', 'workingDirectory']);
90
+ const risk = commandRisk(command);
91
+ return {
92
+ toolName: 'codex.command',
93
+ toolInput: {
94
+ command,
95
+ ...(cwd ? { cwd } : {}),
96
+ },
97
+ toolSummary: command,
98
+ category: 'command',
99
+ risk,
100
+ riskLevel: risk === 'destructive' ? 'destructive' : 'normal',
101
+ native: nativeHandle(method, params),
102
+ details: [
103
+ ...detail('Command', command),
104
+ ...detail('Working directory', cwd),
105
+ ],
106
+ };
107
+ }
108
+ function mapFileRequest(method, params) {
109
+ const path = readString(params, ['path', 'filePath', 'targetPath']);
110
+ const summary = path
111
+ ?? firstString(params.paths)
112
+ ?? stringifyPreview(params.changes ?? params.patch ?? params);
113
+ const patch = readString(params, ['patch', 'diff']);
114
+ const destructive = /\b(delete|remove|unlink)\b/i.test(stringifyPreview(params, 1000));
115
+ return {
116
+ toolName: method === 'applyPatchApproval' ? 'codex.applyPatch' : 'codex.fileChange',
117
+ toolInput: {
118
+ ...(path ? { path } : {}),
119
+ ...(patch ? { patch } : {}),
120
+ },
121
+ toolSummary: summary,
122
+ category: 'file',
123
+ risk: destructive ? 'destructive' : 'high',
124
+ riskLevel: destructive ? 'destructive' : 'normal',
125
+ native: nativeHandle(method, params),
126
+ details: [
127
+ ...detail('Path', path),
128
+ ...detail('Patch', patch),
129
+ ],
130
+ };
131
+ }
132
+ function mapPermissionRequest(method, params) {
133
+ const action = readString(params, ['action', 'permission', 'toolName', 'tool'])
134
+ ?? stringifyPreview(params);
135
+ return {
136
+ toolName: 'codex.permission',
137
+ toolInput: { action },
138
+ toolSummary: action,
139
+ category: 'tool',
140
+ risk: 'normal',
141
+ riskLevel: 'normal',
142
+ native: nativeHandle(method, params),
143
+ details: detail('Permission', action, false),
144
+ };
145
+ }
146
+ export function mapCodexAppServerApprovalRequest(request) {
147
+ if (!isRecord(request.params))
148
+ return null;
149
+ if (request.method === 'item/commandExecution/requestApproval'
150
+ || request.method === 'execCommandApproval') {
151
+ return mapCommandRequest(request.method, request.params);
152
+ }
153
+ if (request.method === 'item/fileChange/requestApproval'
154
+ || request.method === 'applyPatchApproval') {
155
+ return mapFileRequest(request.method, request.params);
156
+ }
157
+ if (request.method === 'item/permissions/requestApproval') {
158
+ return mapPermissionRequest(request.method, request.params);
159
+ }
160
+ return null;
161
+ }
162
+ export function mapCanonApprovalResultToCodexDecision(result) {
163
+ if (result.decision === 'deny')
164
+ return { decision: 'decline' };
165
+ if (result.sessionRule)
166
+ return { decision: 'acceptForSession' };
167
+ return { decision: 'accept' };
168
+ }
package/dist/host.js CHANGED
@@ -413,10 +413,12 @@ export async function main() {
413
413
  ]);
414
414
  return buildHydratedInboundContext({
415
415
  agentId,
416
+ conversationId: input.conversationId,
416
417
  conversation,
417
418
  page,
418
419
  activeSelfContextId: input.activeSelfContextId,
419
420
  selfContexts: input.selfContexts,
421
+ provenance: input.provenance,
420
422
  message: input.message,
421
423
  senderName: input.senderName,
422
424
  isOwner: input.isOwner,
@@ -687,6 +689,7 @@ export async function main() {
687
689
  isOwner: input.isOwner,
688
690
  activeSelfContextId: input.activeSelfContextId,
689
691
  selfContexts: input.selfContexts,
692
+ provenance: input.provenance,
690
693
  hydratedPage: input.hydratedPage,
691
694
  });
692
695
  const behavior = input.behavior ?? hydrated.behavior;
@@ -1096,6 +1099,7 @@ export async function main() {
1096
1099
  behavior: payload.behavior,
1097
1100
  activeSelfContextId: payload.activeSelfContextId,
1098
1101
  selfContexts: payload.selfContexts,
1102
+ provenance: payload.provenance,
1099
1103
  });
1100
1104
  if (message.id) {
1101
1105
  saveRuntimeSessionState(runtimeId, {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { main as hostMain } from './host.js';
2
2
  export { main as registerMain } from './register.js';
3
3
  export { main as setupMain } from './setup.js';
4
+ export { mapCanonApprovalResultToCodexDecision, mapCodexAppServerApprovalRequest, } from './app-server-approval.js';
5
+ export type { CanonCodexApprovalRequest, CodexAppServerApprovalMethod, CodexApprovalDecision, CodexNativeApprovalRequest, } from './app-server-approval.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { main as hostMain } from './host.js';
2
2
  export { main as registerMain } from './register.js';
3
3
  export { main as setupMain } from './setup.js';
4
+ export { mapCanonApprovalResultToCodexDecision, mapCodexAppServerApprovalRequest, } from './app-server-approval.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/codex-plugin",
3
- "version": "0.11.3",
3
+ "version": "0.11.4",
4
4
  "description": "Canon host integration for Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,8 +29,8 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@canonmsg/agent-sdk": "^1.4.1",
33
- "@canonmsg/core": "^0.18.1"
32
+ "@canonmsg/agent-sdk": "^1.5.0",
33
+ "@canonmsg/core": "^0.19.0"
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=18.0.0"