@roj-ai/sdk 0.2.18 → 0.2.20
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/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -9
- package/dist/bootstrap.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/core/image/types.d.ts +6 -0
- package/dist/core/image/types.d.ts.map +1 -1
- package/dist/core/sessions/session-manager.d.ts +2 -0
- package/dist/core/sessions/session-manager.d.ts.map +1 -1
- package/dist/core/sessions/session-manager.js +3 -0
- package/dist/core/sessions/session-manager.js.map +1 -1
- package/dist/core/system.d.ts +1 -0
- package/dist/core/system.d.ts.map +1 -1
- package/dist/core/system.js +2 -1
- package/dist/core/system.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/platform/index.d.ts +7 -0
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/index.js.map +1 -1
- package/dist/plugins/limits-guard/plugin.d.ts.map +1 -1
- package/dist/plugins/limits-guard/plugin.js +3 -9
- package/dist/plugins/limits-guard/plugin.js.map +1 -1
- package/dist/plugins/uploads/plugin.d.ts +2 -0
- package/dist/plugins/uploads/plugin.d.ts.map +1 -1
- package/dist/plugins/uploads/plugin.js +7 -5
- package/dist/plugins/uploads/plugin.js.map +1 -1
- package/dist/testing/test-harness.d.ts +1 -0
- package/dist/testing/test-harness.d.ts.map +1 -1
- package/dist/testing/test-harness.js +1 -0
- package/dist/testing/test-harness.js.map +1 -1
- package/package.json +2 -2
- package/src/bootstrap.ts +7 -9
- package/src/config.ts +5 -0
- package/src/core/image/types.ts +6 -0
- package/src/core/sessions/session-manager.ts +4 -0
- package/src/core/system.ts +3 -0
- package/src/index.ts +3 -0
- package/src/platform/index.ts +7 -0
- package/src/plugins/limits-guard/plugin.ts +4 -13
- package/src/plugins/uploads/plugin.ts +9 -4
- package/src/testing/test-harness.ts +2 -0
package/src/core/image/types.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface ImageResizeOptions {
|
|
|
9
9
|
signal?: AbortSignal
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Paths are on the host's `FileSystem`: the caller reads the result through it and unlinks
|
|
14
|
+
* `tempFile` there once the image is encoded. When an image cannot be shrunk, return the original
|
|
15
|
+
* path: the LLM path treats a throw as an unreadable image.
|
|
16
|
+
*/
|
|
12
17
|
export interface ImageResizer {
|
|
13
18
|
resize(filePath: string, mimeType: string, options?: ImageResizeOptions): Promise<ImageResizeResult>
|
|
14
19
|
}
|
|
@@ -16,6 +21,7 @@ export interface ImageResizer {
|
|
|
16
21
|
export interface ImageResizeResult {
|
|
17
22
|
path: string
|
|
18
23
|
mimeType: string
|
|
24
|
+
/** A file the resizer created; the caller owns it from here and deletes it. */
|
|
19
25
|
tempFile?: string
|
|
20
26
|
}
|
|
21
27
|
|
|
@@ -83,6 +83,7 @@ export interface SessionManagerOptions {
|
|
|
83
83
|
onUserOutput?: UserOutputCallback
|
|
84
84
|
preprocessorRegistry?: PreprocessorRegistry
|
|
85
85
|
uploadArchiveLimits?: ArchiveLimitOverrides
|
|
86
|
+
uploadMaxFileSize?: number
|
|
86
87
|
resourceArchiveLimits?: ArchiveLimitOverrides
|
|
87
88
|
llmLogger?: LLMLogger
|
|
88
89
|
portPool?: PortPool
|
|
@@ -142,6 +143,7 @@ export class SessionManager {
|
|
|
142
143
|
private readonly onUserOutput?: UserOutputCallback
|
|
143
144
|
private readonly preprocessorRegistry?: PreprocessorRegistry
|
|
144
145
|
private readonly uploadArchiveLimits?: ArchiveLimitOverrides
|
|
146
|
+
private readonly uploadMaxFileSize?: number
|
|
145
147
|
private readonly resourceArchiveLimits?: ArchiveLimitOverrides
|
|
146
148
|
private readonly llmLogger?: LLMLogger
|
|
147
149
|
private readonly portPool?: PortPool
|
|
@@ -171,6 +173,7 @@ export class SessionManager {
|
|
|
171
173
|
this.onUserOutput = options.onUserOutput
|
|
172
174
|
this.preprocessorRegistry = options.preprocessorRegistry
|
|
173
175
|
this.uploadArchiveLimits = options.uploadArchiveLimits
|
|
176
|
+
this.uploadMaxFileSize = options.uploadMaxFileSize
|
|
174
177
|
this.resourceArchiveLimits = options.resourceArchiveLimits
|
|
175
178
|
this.llmLogger = options.llmLogger
|
|
176
179
|
this.portPool = options.portPool
|
|
@@ -1079,6 +1082,7 @@ export class SessionManager {
|
|
|
1079
1082
|
dataFileStore: this.dataFileStore,
|
|
1080
1083
|
preprocessorRegistry: this.preprocessorRegistry,
|
|
1081
1084
|
archiveLimits: this.uploadArchiveLimits,
|
|
1085
|
+
maxFileSize: this.uploadMaxFileSize,
|
|
1082
1086
|
})
|
|
1083
1087
|
|
|
1084
1088
|
if (this.resourceArchiveLimits) {
|
package/src/core/system.ts
CHANGED
|
@@ -121,6 +121,7 @@ export interface CreateSystemOptions<TPlugins extends readonly PluginDefinition<
|
|
|
121
121
|
onUserOutput?: UserOutputCallback
|
|
122
122
|
preprocessorRegistry?: PreprocessorRegistry
|
|
123
123
|
uploadArchiveLimits?: ArchiveLimitOverrides
|
|
124
|
+
uploadMaxFileSize?: number
|
|
124
125
|
resourceArchiveLimits?: ArchiveLimitOverrides
|
|
125
126
|
llmLogger?: LLMLogger
|
|
126
127
|
portPool?: PortPool
|
|
@@ -155,6 +156,7 @@ export function createSystem<const TPlugins extends readonly PluginDefinition<st
|
|
|
155
156
|
onUserOutput,
|
|
156
157
|
preprocessorRegistry,
|
|
157
158
|
uploadArchiveLimits,
|
|
159
|
+
uploadMaxFileSize,
|
|
158
160
|
resourceArchiveLimits,
|
|
159
161
|
llmLogger,
|
|
160
162
|
portPool,
|
|
@@ -181,6 +183,7 @@ export function createSystem<const TPlugins extends readonly PluginDefinition<st
|
|
|
181
183
|
onUserOutput,
|
|
182
184
|
preprocessorRegistry,
|
|
183
185
|
uploadArchiveLimits,
|
|
186
|
+
uploadMaxFileSize,
|
|
184
187
|
resourceArchiveLimits,
|
|
185
188
|
llmLogger,
|
|
186
189
|
portPool,
|
package/src/index.ts
CHANGED
package/src/platform/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* runtime packages (e.g. `@roj-ai/sdk/bun-platform`) and wired in at bootstrap time.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { ImageResizer } from '../core/image/types.js'
|
|
9
10
|
import type { FileSystem } from './fs.js'
|
|
10
11
|
import type { FsRevision } from './fs-revision.js'
|
|
11
12
|
import type { GitClient } from './git.js'
|
|
@@ -39,6 +40,7 @@ export type { LiveScheduler, Scheduler, WakeHandler } from './scheduler.js'
|
|
|
39
40
|
export type { SessionLogPage, SessionLogStore } from './session-log.js'
|
|
40
41
|
export type { ShellConfinement, ShellGrant, ShellLimits, ShellRunner, ShellRunOptions, ShellRunResult } from './shell.js'
|
|
41
42
|
|
|
43
|
+
export type { ImageResizeOptions, ImageResizer, ImageResizeResult } from '../core/image/types.js'
|
|
42
44
|
export type { ServiceExecutorPort } from '../plugins/services/service.js'
|
|
43
45
|
export type { PortPool } from '../plugins/services/port-pool.js'
|
|
44
46
|
export type { ServiceConfig, ServiceStatus, ServiceStopSource } from '../plugins/services/schema.js'
|
|
@@ -77,6 +79,11 @@ export interface Platform {
|
|
|
77
79
|
* writing `sessions/<id>/calls/<callId>.json` and listing that directory.
|
|
78
80
|
*/
|
|
79
81
|
llmCallLog?: LLMCallStore
|
|
82
|
+
/**
|
|
83
|
+
* Shrinks images before they reach a model. Absent on hosts with a process table,
|
|
84
|
+
* which keep resizing with the `vipsheader` / `vipsthumbnail` binaries.
|
|
85
|
+
*/
|
|
86
|
+
imageResizer?: ImageResizer
|
|
80
87
|
/** Absolute path to the OS temp directory (equivalent to `os.tmpdir()` on Node/Bun). */
|
|
81
88
|
tmpDir: string
|
|
82
89
|
}
|
|
@@ -122,18 +122,11 @@ export type BudgetExceededEvent = (typeof limitsEvents)['Events']['budget_exceed
|
|
|
122
122
|
// ============================================================================
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
125
|
+
* Name of the tool behind a tool_completed/tool_failed event. Plugin reducers run after the
|
|
126
|
+
* core reducer, which has already cleared the running call and recorded the named result.
|
|
127
127
|
*/
|
|
128
|
-
function resolveToolName(
|
|
129
|
-
sessionState
|
|
130
|
-
agentId: AgentId,
|
|
131
|
-
toolCallId: string,
|
|
132
|
-
): string {
|
|
133
|
-
const agent = sessionState.agents.get(agentId)
|
|
134
|
-
if (!agent) return 'unknown'
|
|
135
|
-
return agent.executingToolCall?.toolName
|
|
136
|
-
?? agent.pendingToolCalls.find((tc) => tc.id === toolCallId)?.name
|
|
128
|
+
function resolveToolName(sessionState: SessionState, agentId: AgentId, toolCallId: ToolCallId): string {
|
|
129
|
+
return sessionState.agents.get(agentId)?.pendingToolResults.find((result) => result.toolCallId === toolCallId)?.toolName
|
|
137
130
|
?? 'unknown'
|
|
138
131
|
}
|
|
139
132
|
|
|
@@ -279,7 +272,6 @@ export const limitsGuardPlugin = definePlugin('limits-guard')
|
|
|
279
272
|
const counters = limits.get(event.agentId)
|
|
280
273
|
if (!counters) return limits
|
|
281
274
|
|
|
282
|
-
// Resolve tool name from agent state (must run before core clears executingToolCall)
|
|
283
275
|
const toolName = resolveToolName(sessionState, event.agentId, event.toolCallId)
|
|
284
276
|
const { [toolName]: _, ...restFailures } = counters.consecutiveToolFailures
|
|
285
277
|
|
|
@@ -296,7 +288,6 @@ export const limitsGuardPlugin = definePlugin('limits-guard')
|
|
|
296
288
|
const counters = limits.get(event.agentId)
|
|
297
289
|
if (!counters) return limits
|
|
298
290
|
|
|
299
|
-
// Resolve tool name from agent state (must run before core clears executingToolCall)
|
|
300
291
|
const toolName = resolveToolName(sessionState, event.agentId, event.toolCallId)
|
|
301
292
|
const currentEntry = counters.consecutiveToolFailures[toolName]
|
|
302
293
|
|
|
@@ -31,7 +31,7 @@ const statusChangedSchema = z.object({
|
|
|
31
31
|
// Constants
|
|
32
32
|
// ============================================================================
|
|
33
33
|
|
|
34
|
-
const
|
|
34
|
+
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB
|
|
35
35
|
|
|
36
36
|
/** Allowed MIME type patterns */
|
|
37
37
|
const ALLOWED_MIME_TYPES = [
|
|
@@ -68,6 +68,8 @@ const uploadStorageError = (): DomainError => createDomainError('upload_storage_
|
|
|
68
68
|
export interface UploadsPluginConfig {
|
|
69
69
|
dataFileStore: FileStore
|
|
70
70
|
preprocessorRegistry?: PreprocessorRegistry
|
|
71
|
+
/** Largest accepted file in bytes. Defaults to 10 MB. */
|
|
72
|
+
maxFileSize?: number
|
|
71
73
|
processingTimeoutMs?: number
|
|
72
74
|
processingAbortGraceMs?: number
|
|
73
75
|
/** Bound in ms on the close-time wait for in-flight uploads and mutations. Must be positive; anything else uses the 30s default. */
|
|
@@ -181,9 +183,9 @@ function toStoredFilename(filename: string): string {
|
|
|
181
183
|
return filename.normalize('NFC')
|
|
182
184
|
}
|
|
183
185
|
|
|
184
|
-
function validateUploadInput(input: Pick<UploadInput, 'size' | 'mimeType' | 'filename'
|
|
186
|
+
function validateUploadInput(input: Pick<UploadInput, 'size' | 'mimeType' | 'filename'>, maxFileSize: number): string | undefined {
|
|
185
187
|
if (!isBasename(input.filename)) return 'Invalid filename'
|
|
186
|
-
if (input.size >
|
|
188
|
+
if (input.size > maxFileSize) return `File too large: max ${maxFileSize / (1024 * 1024)}MB`
|
|
187
189
|
if (!isAllowedMimeType(input.mimeType)) return `Unsupported file type: ${input.mimeType}`
|
|
188
190
|
return undefined
|
|
189
191
|
}
|
|
@@ -290,11 +292,12 @@ function runTrackedMutation<T>(args: {
|
|
|
290
292
|
async function prepareUpload(args: {
|
|
291
293
|
pluginContext: UploadsPluginContext
|
|
292
294
|
dataFileStore: FileStore
|
|
295
|
+
maxFileSize: number
|
|
293
296
|
logger: Logger
|
|
294
297
|
input: UploadInput
|
|
295
298
|
}): Promise<Result<PreparedUpload, DomainError>> {
|
|
296
299
|
const filename = toStoredFilename(args.input.filename)
|
|
297
|
-
const validationError = validateUploadInput({ ...args.input, filename })
|
|
300
|
+
const validationError = validateUploadInput({ ...args.input, filename }, args.maxFileSize)
|
|
298
301
|
if (validationError) return Err(ValidationErrors.invalid(validationError))
|
|
299
302
|
|
|
300
303
|
const uploadId = generateUploadId()
|
|
@@ -1013,6 +1016,7 @@ export const uploadsPlugin = definePlugin('uploads')
|
|
|
1013
1016
|
const preparedResult = await prepareUpload({
|
|
1014
1017
|
pluginContext: ctx.pluginContext,
|
|
1015
1018
|
dataFileStore: ctx.pluginConfig.dataFileStore,
|
|
1019
|
+
maxFileSize: ctx.pluginConfig.maxFileSize ?? DEFAULT_MAX_FILE_SIZE,
|
|
1016
1020
|
logger: ctx.logger,
|
|
1017
1021
|
input,
|
|
1018
1022
|
})
|
|
@@ -1076,6 +1080,7 @@ export const uploadsPlugin = definePlugin('uploads')
|
|
|
1076
1080
|
const preparedResult = await prepareUpload({
|
|
1077
1081
|
pluginContext: ctx.pluginContext,
|
|
1078
1082
|
dataFileStore: ctx.pluginConfig.dataFileStore,
|
|
1083
|
+
maxFileSize: ctx.pluginConfig.maxFileSize ?? DEFAULT_MAX_FILE_SIZE,
|
|
1079
1084
|
logger: ctx.logger,
|
|
1080
1085
|
input,
|
|
1081
1086
|
})
|
|
@@ -58,6 +58,7 @@ export class TestHarness {
|
|
|
58
58
|
eventStore?: MemoryEventStore
|
|
59
59
|
/** Resident session idle timeout. Zero or absent disables eviction. */
|
|
60
60
|
sessionIdleTimeoutMs?: number
|
|
61
|
+
uploadMaxFileSize?: number
|
|
61
62
|
/** Extra notification sink, run after the recorder. Throwing here simulates a hostile embedder. */
|
|
62
63
|
onUserOutput?: (notification: PluginNotification) => void
|
|
63
64
|
}) {
|
|
@@ -116,6 +117,7 @@ export class TestHarness {
|
|
|
116
117
|
llmLogger: options.llmLogger,
|
|
117
118
|
platform,
|
|
118
119
|
sessionIdleTimeoutMs: options.sessionIdleTimeoutMs,
|
|
120
|
+
uploadMaxFileSize: options.uploadMaxFileSize,
|
|
119
121
|
systemPlugins: mergeSystemPlugins([...fullPlugins, ...(options.systemPlugins ?? [])]),
|
|
120
122
|
})
|
|
121
123
|
}
|