negotium 0.22.7 → 0.23.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/registry.js CHANGED
@@ -132,7 +132,7 @@ var codexRegistryOperations = {
132
132
  return { sessionId: threadId, rolloutPath };
133
133
  },
134
134
  async forkSession({ parentSessionId }) {
135
- const { forkCodexSession } = await import("./chunk-1gznvrth.js");
135
+ const { forkCodexSession } = await import("./chunk-qmpbb92y.js");
136
136
  return await forkCodexSession(parentSessionId);
137
137
  },
138
138
  async cleanupRollouts({ sessionIds }) {
@@ -49,6 +49,7 @@ export {
49
49
  THREAD_READ_DEFAULT_LIMIT,
50
50
  THREAD_READ_MAX_LIMIT,
51
51
  } from "#runtime/thread-read";
52
+ export { isVisualsShowImageTool, visualMediaInputError } from "#runtime/visuals";
52
53
  export { isSensitivePath } from "#security/sensitive-path";
53
54
  export {
54
55
  appendApiMessage,
@@ -123,10 +123,19 @@ export function buildVideoHtml(mimeType: string): string {
123
123
  </html>`;
124
124
  }
125
125
 
126
- export function resolveVisualMediaInput(
126
+ type VisualMediaInput = { file_path?: unknown; file_id?: unknown };
127
+
128
+ /**
129
+ * Where a media visual's bytes come from, checked but not yet stored: an upload
130
+ * already in the topic, or a readable workspace file.
131
+ */
132
+ function locateVisualMediaInput(
127
133
  topicId: string,
128
- input: { file_path?: unknown; file_id?: unknown },
129
- ): { fileId: string; mimeType: string; source: string } | { error: string } {
134
+ input: VisualMediaInput,
135
+ ):
136
+ | { kind: "upload"; fileId: string; mimeType: string }
137
+ | { kind: "file"; path: string; rawPath: string }
138
+ | { error: string } {
130
139
  if (typeof input.file_id === "string" && input.file_id.trim()) {
131
140
  const fileId = input.file_id.trim().toLowerCase();
132
141
  if (!isUploadFileId(fileId)) return { error: "file_id must be a full uploaded-file UUID" };
@@ -136,11 +145,7 @@ export function resolveVisualMediaInput(
136
145
  const attachment = resolveAttachmentByFileId(fileId);
137
146
  const filePath = resolveUploadedFilePathByFileId(fileId);
138
147
  if (!attachment || !filePath) return { error: `uploaded file not found: ${fileId}` };
139
- return {
140
- fileId: attachment.id,
141
- mimeType: attachment.mimeType,
142
- source: `file_id:${attachment.id}`,
143
- };
148
+ return { kind: "upload", fileId: attachment.id, mimeType: attachment.mimeType };
144
149
  }
145
150
 
146
151
  if (typeof input.file_path !== "string" || !input.file_path.trim()) {
@@ -149,18 +154,49 @@ export function resolveVisualMediaInput(
149
154
  const rawPath = input.file_path.trim();
150
155
  const cwd = workspaceCwdFor(topicId);
151
156
  const candidate = isAbsolute(rawPath) ? rawPath : resolve(cwd, rawPath);
152
- if (!isPathInside(cwd, candidate)) {
153
- return { error: "file_path must be inside the topic workspace" };
154
- }
157
+ // Existence first: the containment check resolves both ends on disk, so a
158
+ // missing file would otherwise be reported as being outside the workspace.
155
159
  let realCandidate: string;
156
160
  try {
157
161
  realCandidate = realpathSync(candidate);
158
162
  } catch {
159
163
  return { error: `file not found: ${rawPath}` };
160
164
  }
165
+ if (!isPathInside(cwd, realCandidate)) {
166
+ return {
167
+ error: `file_path must be inside the topic workspace (${cwd}); copy the file into it and pass the copy`,
168
+ };
169
+ }
161
170
  if (isSensitivePath(realCandidate)) {
162
171
  return { error: "file_path matches the sensitive-file blacklist" };
163
172
  }
173
+ return { kind: "file", path: realCandidate, rawPath };
174
+ }
175
+
176
+ /**
177
+ * Why a media visual call cannot be displayed, or null when it can. The same
178
+ * checks the turn runs before rendering, so the tool can answer the agent with
179
+ * the real outcome instead of a success it never had.
180
+ */
181
+ export function visualMediaInputError(topicId: string, input: VisualMediaInput): string | null {
182
+ const located = locateVisualMediaInput(topicId, input);
183
+ return "error" in located ? located.error : null;
184
+ }
185
+
186
+ export function resolveVisualMediaInput(
187
+ topicId: string,
188
+ input: VisualMediaInput,
189
+ ): { fileId: string; mimeType: string; source: string } | { error: string } {
190
+ const located = locateVisualMediaInput(topicId, input);
191
+ if ("error" in located) return located;
192
+ if (located.kind === "upload") {
193
+ return {
194
+ fileId: located.fileId,
195
+ mimeType: located.mimeType,
196
+ source: `file_id:${located.fileId}`,
197
+ };
198
+ }
199
+ const { path: realCandidate, rawPath } = located;
164
200
  const attachment = storeLocalFileAsUpload(realCandidate, { topicId });
165
201
  if (!attachment) return { error: `failed to store media file: ${rawPath}` };
166
202
  const filePath = resolveUploadedFilePathByFileId(attachment.id);
@@ -1 +1 @@
1
- export const NEGOTIUM_VERSION = "0.22.7";
1
+ export const NEGOTIUM_VERSION = "0.23.0";
@@ -14,10 +14,17 @@ export declare function topicAllowsVisualFileId(topicId: string, fileId: string)
14
14
  export declare function isPathInside(baseDir: string, filePath: string): boolean;
15
15
  export declare function buildImageHtml(alt: string): string;
16
16
  export declare function buildVideoHtml(mimeType: string): string;
17
- export declare function resolveVisualMediaInput(topicId: string, input: {
17
+ type VisualMediaInput = {
18
18
  file_path?: unknown;
19
19
  file_id?: unknown;
20
- }): {
20
+ };
21
+ /**
22
+ * Why a media visual call cannot be displayed, or null when it can. The same
23
+ * checks the turn runs before rendering, so the tool can answer the agent with
24
+ * the real outcome instead of a success it never had.
25
+ */
26
+ export declare function visualMediaInputError(topicId: string, input: VisualMediaInput): string | null;
27
+ export declare function resolveVisualMediaInput(topicId: string, input: VisualMediaInput): {
21
28
  fileId: string;
22
29
  mimeType: string;
23
30
  source: string;
@@ -1 +1 @@
1
- export declare const NEGOTIUM_VERSION = "0.22.7";
1
+ export declare const NEGOTIUM_VERSION = "0.23.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "negotium",
3
- "version": "0.22.7",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "description": "Install the Negotium multi-agent runtime and CLI with one package",
6
6
  "license": "Apache-2.0",