@tangle-network/agent-app 0.44.18 → 0.44.21
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/.claude/skills/eval-campaign/SKILL.md +58 -95
- package/.claude/skills/surface-evolution/SKILL.md +29 -23
- package/README.md +8 -7
- package/dist/assistant/index.d.ts +2 -2
- package/dist/assistant/index.js +3 -3
- package/dist/{attachment-validation-Zw8eslhN.d.ts → attachment-validation-Dvc_Livy.d.ts} +1 -1
- package/dist/chat-routes/index.d.ts +17 -10
- package/dist/chat-routes/index.js +89 -12
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +2 -2
- package/dist/chat-store/index.js +1 -1
- package/dist/{chunk-JXIQSGOV.js → chunk-2ZSSOYXP.js} +2 -6
- package/dist/chunk-2ZSSOYXP.js.map +1 -0
- package/dist/{chunk-VM6VLJH6.js → chunk-BI6NKSO4.js} +2 -2
- package/dist/{chunk-DV2FA2PW.js → chunk-C3SRFZGL.js} +18 -9
- package/dist/chunk-C3SRFZGL.js.map +1 -0
- package/dist/{chunk-34M7AUWO.js → chunk-M3UFMQ7D.js} +1 -1
- package/dist/chunk-M3UFMQ7D.js.map +1 -0
- package/dist/{chunk-7SMPAAIT.js → chunk-PRKSYTMQ.js} +3 -3
- package/dist/eval-campaign/index.d.ts +6 -8
- package/dist/eval-campaign/index.js +9 -5
- package/dist/eval-campaign/index.js.map +1 -1
- package/dist/forms/index.d.ts +333 -0
- package/dist/forms/index.js +343 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/{parts-S1OINlRP.d.ts → parts-fyPPdDdK.d.ts} +3 -4
- package/dist/profile/index.d.ts +5 -6
- package/dist/profile/index.js +5 -3
- package/dist/profile/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +3 -7
- package/dist/sandbox/index.js +1 -1
- package/dist/skills/index.d.ts +4 -3
- package/dist/skills/index.js +1 -1
- package/dist/skills-placement/index.d.ts +0 -1
- package/dist/skills-placement/index.js +1 -1
- package/dist/teams-react/index.js +3 -3
- package/dist/web-react/index.d.ts +3 -3
- package/dist/web-react/index.js +3 -3
- package/package.json +29 -18
- package/dist/chunk-34M7AUWO.js.map +0 -1
- package/dist/chunk-DV2FA2PW.js.map +0 -1
- package/dist/chunk-JXIQSGOV.js.map +0 -1
- /package/dist/{chunk-VM6VLJH6.js.map → chunk-BI6NKSO4.js.map} +0 -0
- /package/dist/{chunk-7SMPAAIT.js.map → chunk-PRKSYTMQ.js.map} +0 -0
|
@@ -1,116 +1,79 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: eval-campaign
|
|
3
|
-
description: Wire
|
|
3
|
+
description: Wire product measurement and improvement to Agent Eval campaigns, official optimizers, and explicit release decisions.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Wire Eval Campaigns
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Use `@tangle-network/agent-app/eval-campaign` as the product-facing import.
|
|
9
|
+
It composes Agent Eval `0.133.0`.
|
|
10
|
+
Do not recreate execution, scoring, data splitting, or optimization.
|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## Choose The Entry Point
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
| Job | Use |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Measure one fixed agent | `runCampaign` |
|
|
17
|
+
| Optimize with official GEPA | `selfImprove` with `gepaOptimizationMethod(...)` |
|
|
18
|
+
| Optimize with Microsoft SkillOpt | `selfImprove` with `skillOptOptimizationMethod(...)` |
|
|
19
|
+
| Adapt another text optimizer | `selfImprove` with `externalTextOptimizationMethod(...)` |
|
|
20
|
+
| Let product code generate candidates | `selfImprove` with an explicit `SurfaceProposer` |
|
|
21
|
+
| Compare complete methods on the same cases | `compareOptimizationMethods` |
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- the **held-out production gate** (default `defaultProductionGate`, `deltaThreshold` 0.05),
|
|
17
|
-
- **durable provenance** + optional hosted ingest,
|
|
18
|
-
- every budget/seed/storage default.
|
|
23
|
+
There is no default candidate generator.
|
|
24
|
+
When `budget.generations` is greater than zero, pass exactly one of `method` or `proposer`.
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
## Supply Product Inputs
|
|
21
27
|
|
|
22
|
-
1.
|
|
23
|
-
2.
|
|
24
|
-
3.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
1. Define `scenarios` with stable IDs.
|
|
29
|
+
2. Implement `agent(surface, scenario, ctx)` against the real execution path.
|
|
30
|
+
3. Report paid calls through `ctx.cost`.
|
|
31
|
+
4. Define a `JudgeConfig`, or use `buildEnsembleJudge` for repeated independent judges.
|
|
32
|
+
5. Keep method-selection cases separate from final comparison cases.
|
|
33
|
+
6. Set a dollar limit and bounded concurrency.
|
|
34
|
+
7. Promote only when `gateDecision` is `ship`.
|
|
29
35
|
|
|
30
36
|
```ts
|
|
31
37
|
import {
|
|
32
|
-
selfImprove,
|
|
33
38
|
buildEnsembleJudge,
|
|
34
|
-
|
|
35
|
-
type
|
|
39
|
+
selfImprove,
|
|
40
|
+
type OptimizationMethod,
|
|
36
41
|
} from '@tangle-network/agent-app/eval-campaign'
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
> Requires `@tangle-network/agent-eval >= 0.100.0` (peer; current published is 0.107.0). The scaffold composes the substrate downward; never import a product package from agent-eval (layering rule).
|
|
40
|
-
|
|
41
|
-
## Minimal wiring (copy, then fill the three blanks)
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
const RUBRIC = ['accuracy', 'grounding', 'tone'] as const
|
|
45
|
-
type Dim = (typeof RUBRIC)[number]
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if (result.gate.decision === 'ship') await ship(result.winnerSurface)
|
|
43
|
+
async function optimize(
|
|
44
|
+
method: OptimizationMethod<MyScenario, MyArtifact>,
|
|
45
|
+
) {
|
|
46
|
+
const judge = buildEnsembleJudge<MyArtifact, MyScenario, 'accuracy'>({
|
|
47
|
+
name: 'answer-quality',
|
|
48
|
+
rubric: ['accuracy'],
|
|
49
|
+
scoreOne: scoreOneAnswer,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return selfImprove({
|
|
53
|
+
scenarios,
|
|
54
|
+
selectionScenarios,
|
|
55
|
+
agent: runUnderSurface,
|
|
56
|
+
judge,
|
|
57
|
+
baselineSurface,
|
|
58
|
+
method,
|
|
59
|
+
budget: {
|
|
60
|
+
generations: 1,
|
|
61
|
+
dollars: 10,
|
|
62
|
+
maxConcurrency: 2,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
}
|
|
73
66
|
```
|
|
74
67
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
- `scoreOne` is called `judgeReps` times per artifact; **vary the model by `rep`** so the ensemble is uncorrelated (judges sharing a base model share its bias).
|
|
78
|
-
- Return `{ model, perDimension: null }` to record a judge failure **without** killing the ensemble — the reducer means over survivors.
|
|
79
|
-
- The reducer (`aggregateJudgeVerdicts`) **throws only if every rep failed** → the campaign records a failed cell, never a silent zero.
|
|
80
|
-
- `weights` (partial) selects-and-weights named dimensions; default is uniform.
|
|
81
|
-
|
|
82
|
-
## Config reference (all `SelfImproveOptions`, all optional unless noted)
|
|
83
|
-
|
|
84
|
-
| Field | Default | When to set |
|
|
85
|
-
|---|---|---|
|
|
86
|
-
| `scenarios` | — (required) | your corpus |
|
|
87
|
-
| `agent` | — (required) | your dispatch under a surface |
|
|
88
|
-
| `judge` | — (required) | `buildEnsembleJudge` or a `JudgeConfig` |
|
|
89
|
-
| `baselineSurface` | — (required) | the surface the loop optimizes; start `''` |
|
|
90
|
-
| `mutationPrimitives` | gepaProposer's own | your optimization levers (additive directives) |
|
|
91
|
-
| `proposer` | `gepaProposer` | pass `evolutionaryProposer({ mutator })` for blind addendum rotation |
|
|
92
|
-
| `gate` | `defaultProductionGate` (Δ 0.05) | `paretoSignificanceGate` for multi-objective; tune `deltaThreshold` for your rubric scale. To prove a held-out lift is real CONTENT and not just added prompt/mount FOOTPRINT, compose `neutralizationGate` (agent-eval >= 0.107.0, from `@tangle-network/agent-eval/campaign`) — a footprint-matched placebo arm. It needs `ctx.neutralizedJudgeScores` from `runImprovementLoop({ neutralize })`; `selfImprove`/this scaffold do not surface that option yet, so wire it at the `runImprovementLoop` level or re-export it in the scaffold first. |
|
|
93
|
-
| `budget` | 3 gens × pop 2, 0.25 holdout | `budget.reps` (replicates → tighter CIs), `budget.promoteTopK`, `budget.holdoutScenarios` (explicit split), `budget.dollars` (cost cap) |
|
|
94
|
-
| `expectUsage` | **`'assert'`** | the fail-loud backend-integrity guard. Leave at `'assert'` for real runs (a stub cell throws); set `'off'` ONLY for a deterministic offline/replay run |
|
|
95
|
-
| `labeledStore` | off | capture every artifact + judge score (the dataset you ship + few-shot corpus); set `captureSource` (default `'eval-run'`) |
|
|
96
|
-
| `analyzeGeneration` | — | the per-generation findings producer (EYES→HANDS) — plug a trace-analyst / HALO to refresh `ctx.findings` each round |
|
|
97
|
-
| `runDir` | `mem://…` (non-durable) | a real path to persist provenance + spans |
|
|
98
|
-
| `hostedTenant` | off | ship eval-run events to a hosted orchestrator |
|
|
99
|
-
| `collectWorkerRecords` | — | return the per-call `RunRecord`s your agent accumulated → real backend-integrity verdict |
|
|
100
|
-
| `onProgress` | — | stream baseline/generation/gate events to a UI |
|
|
101
|
-
|
|
102
|
-
## Fail-loud contract (do not break)
|
|
103
|
-
|
|
104
|
-
- In `agent`, report real cost via `ctx.cost.observe(costUsd, label)` + `ctx.cost.observeTokens(...)`. A dispatch that reports `{0,0}` trips `expectUsage` — that is the honest "ran against a stub" signal; never paper over it.
|
|
105
|
-
- A judge failure is `perDimension: null`, never a fabricated zero.
|
|
106
|
-
- Train and holdout must both be non-empty (`selfImprove` derives the split; supply enough scenarios).
|
|
107
|
-
|
|
108
|
-
## Anti-patterns (these are what this scaffold deletes)
|
|
68
|
+
## Reject Invalid Runs
|
|
109
69
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
70
|
+
- Reject missing or zero usage on real model calls.
|
|
71
|
+
- Record a failed judge as `perDimension: null`; never invent a zero.
|
|
72
|
+
- Require non-empty training and final comparison sets.
|
|
73
|
+
- Never expose final comparison cases to an optimization method.
|
|
74
|
+
- Inspect paired results and complete cost accounting before making a claim.
|
|
113
75
|
|
|
114
|
-
##
|
|
76
|
+
## Then consider
|
|
115
77
|
|
|
116
|
-
|
|
78
|
+
- Use `measurement-validation` when the metric has not separated known-good and known-bad cases.
|
|
79
|
+
- Use `surface-evolution` when the measurement is trusted and one production surface is ready to optimize.
|
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: surface-evolution
|
|
3
|
-
description: Optimize
|
|
3
|
+
description: Optimize one production agent surface with Agent Eval methods, separate data, bounded spend, and measured promotion.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Evolve One Surface
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Optimize one surface that production loads exactly as measured.
|
|
9
|
+
Keep security, approval, authorization, and compliance rules outside the editable surface.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
## Run
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
1. Identify the exact source file or data object that production loads.
|
|
14
|
+
2. Establish training, method-selection, and final comparison cases with no overlap.
|
|
15
|
+
3. Choose one search path:
|
|
16
|
+
- Use `gepaOptimizationMethod(...)` for official GEPA.
|
|
17
|
+
- Use `skillOptOptimizationMethod(...)` for official SkillOpt.
|
|
18
|
+
- Use `externalTextOptimizationMethod(...)` for another maintained optimizer.
|
|
19
|
+
- Use `SurfaceProposer` only when product or Runtime code owns candidate generation.
|
|
20
|
+
4. Pass the chosen `method` or `proposer` to `selfImprove`.
|
|
21
|
+
5. Set explicit dollars, concurrency, and repetitions.
|
|
22
|
+
6. Guard critical dimensions in the release policy.
|
|
23
|
+
7. Pass `neutralize` when added prompt size could create a decorative lift.
|
|
24
|
+
8. Promote only the exact returned winner when `gateDecision` is `ship`.
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
2. **Gate promotion on a held-out split AND a critical-dimension floor.** Never promote a net composite gain that regresses a guarded dimension (safety, hallucination, the regulated invariant). A +10 composite that loses 30 on hallucination is a regression, not a win. **AND gate on a footprint placebo.** A held-out lift proves the candidate beat baseline; it does NOT prove the lift came from the surface's CONTENT rather than the extra prompt/mount FOOTPRINT it added. Compose `neutralizationGate` (from `@tangle-network/agent-eval/campaign`, wired via `runImprovementLoop({ neutralize })`) AFTER the significance gate — `composeGate(heldOutGate({...}), neutralizationGate({...}))` — so a candidate whose content-blanked, footprint-matched twin reproduces more than `maxDecorativeFraction` (default 0.5) of the lift is HELD as decorative, no matter how large or significant its raw lift.
|
|
16
|
-
3. **Budget is a hard ceiling and cost-aware** — skip cells beyond the ceiling, never abort. The user's spend maps to generations × candidates × reps: $0.20 buys one quick generation; $50 buys a wide search with tight CIs.
|
|
17
|
-
4. **Never evolve a frozen surface.** The regulated invariants — human-in-the-loop, the compliance gate, auth/RBAC — are off-limits. Declare exactly what is evolvable; everything else the loop must not touch.
|
|
26
|
+
## Required Checks
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
- Prove the metric rejects a known-bad candidate before spending on search.
|
|
29
|
+
- Prove each compared cell executed and reported usage.
|
|
30
|
+
- Require paired final-case evidence.
|
|
31
|
+
- Reject a winner that regresses any protected dimension.
|
|
32
|
+
- After promotion, compare the live bytes with the selected surface.
|
|
33
|
+
- Repeat the final comparison on fresh cases before claiming a durable gain.
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
Stop when added candidates no longer improve selection results within the budget.
|
|
36
|
+
If failures require new tools, data, or execution behavior, change the architecture instead of continuing prompt search.
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
- How to scope budget to the gap — one generation to confirm a hunch, many to search a wide space?
|
|
25
|
-
- When has it converged or plateaued? If surface-tuning is exhausted and the gap is architectural, escalate — don't keep spending on a surface that's maxed.
|
|
38
|
+
## Then consider
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- **After a promotion, the live agent renders the exact winning surface** — diff the deployed artifact against the promoted one. They must be byte-identical.
|
|
30
|
-
- **The held-out lift reproduces on a fresh run** — a one-shot gain is noise until it repeats.
|
|
31
|
-
- **The gate's rejections are honest** — a held verdict carries a stated reason ("0 valid paired runs", "regressed guarded dimension"), never a silent pass and never a lift over partial data.
|
|
32
|
-
|
|
33
|
-
## Evolves-by
|
|
34
|
-
|
|
35
|
-
Production outcomes of shipped surfaces (did the held-out lift actually hold live?) feed back into the driver's mutation priors and the gate's thresholds. A surface that lifted offline but flatlined live tells you the held-out set wasn't representative — widen it. See `skill-evolution`.
|
|
40
|
+
- Use `measurement-validation` when the metric or final-case split is not yet trustworthy.
|
|
41
|
+
- Use `skill-evolution` when repeated runs expose a durable improvement to this operating procedure.
|
package/README.md
CHANGED
|
@@ -35,15 +35,16 @@ pnpm add @tangle-network/agent-eval @tangle-network/agent-integrations
|
|
|
35
35
|
|
|
36
36
|
| Peer | Required by | Range |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `@tangle-network/agent-eval` | `/eval`, `/eval-campaign`, `/profile`, `/knowledge` |
|
|
39
|
-
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` |
|
|
38
|
+
| `@tangle-network/agent-eval` | `/eval`, `/eval-campaign`, `/profile`, `/knowledge` | `0.133.2` |
|
|
39
|
+
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `0.107.4` |
|
|
40
40
|
| `@tangle-network/agent-integrations` | `/integrations` | `>=0.44.0` |
|
|
41
|
-
| `@tangle-network/agent-interface` | `/interactions`, `/chat-store`, `/harness` |
|
|
42
|
-
| `@tangle-network/sandbox` | `/sandbox
|
|
43
|
-
| `@tangle-network/agent-knowledge` | `/knowledge-loop` |
|
|
44
|
-
| `@tangle-network/agent-profile-materialize` | `/skills-placement` |
|
|
41
|
+
| `@tangle-network/agent-interface` | `/interactions`, `/chat-store`, `/harness` | `0.36.0` |
|
|
42
|
+
| `@tangle-network/sandbox` | `/sandbox` | `0.15.1` |
|
|
43
|
+
| `@tangle-network/agent-knowledge` | `/knowledge-loop` | `6.1.4` |
|
|
44
|
+
| `@tangle-network/agent-profile-materialize` | `/skills-placement` | `0.9.0` |
|
|
45
|
+
| `@tangle-network/sandbox-ui` | `/brand`, `/studio-react`, `/work-product-react` | `0.90.1` |
|
|
45
46
|
|
|
46
|
-
All of these except `agent-eval`, `agent-integrations`, and `agent-interface` are declared **optional** peers, so a product that never imports the subpath installs nothing.
|
|
47
|
+
All of these except `agent-eval`, `agent-integrations`, and `agent-interface` are declared **optional** peers, so a product that never imports the subpath installs nothing.
|
|
47
48
|
|
|
48
49
|
Modules that import no engine package (`/tools`, `/web`, `/crypto`, `/redact`, `/stream`, `/billing`, `/tangle` — the last two take their client as a structural contract) need no peers.
|
|
49
50
|
|
|
@@ -4,7 +4,7 @@ import { ToolDetailRenderers, ChatUiMessage } from '../web-react/index.js';
|
|
|
4
4
|
import '../contract-CEewO6DI.js';
|
|
5
5
|
import '@tangle-network/agent-interface';
|
|
6
6
|
import '../plans/index.js';
|
|
7
|
-
import '../parts-
|
|
7
|
+
import '../parts-fyPPdDdK.js';
|
|
8
8
|
import '../types-DB82fktc.js';
|
|
9
9
|
import '../agent-activity-C8ZG0F0M.js';
|
|
10
10
|
import '../flow-types-CJxEmaRy.js';
|
|
@@ -12,7 +12,7 @@ import '../queue-vRI0Qx3X.js';
|
|
|
12
12
|
import '../sandbox-terminal-ChNEdHF8.js';
|
|
13
13
|
import '../catalog/index.js';
|
|
14
14
|
import '../harness/index.js';
|
|
15
|
-
import '../attachment-validation-
|
|
15
|
+
import '../attachment-validation-Dvc_Livy.js';
|
|
16
16
|
import '../stream-normalizer-DnuqkZvw.js';
|
|
17
17
|
|
|
18
18
|
/**
|
package/dist/assistant/index.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
ChatMessages,
|
|
4
4
|
ModelPicker,
|
|
5
5
|
ProviderLogo
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-PRKSYTMQ.js";
|
|
7
7
|
import "../chunk-FBVLEGEG.js";
|
|
8
8
|
import "../chunk-YTMKRL3L.js";
|
|
9
9
|
import "../chunk-GEYACSFW.js";
|
|
10
10
|
import "../chunk-HCOROIRT.js";
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-BI6NKSO4.js";
|
|
12
|
+
import "../chunk-C3SRFZGL.js";
|
|
13
13
|
import "../chunk-ZVEEWGDK.js";
|
|
14
14
|
import "../chunk-5EPIPT4V.js";
|
|
15
15
|
import "../chunk-3ZK5IJSW.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as ChatMessagePart, N as ChatTurnRequestPayload, O as ChatTurnPartInput, P as ChatTurnFilePartInput, b as ChatAttachmentPart, a as ChatAttachmentKind, g as ChatMentionPart } from '../parts-
|
|
2
|
-
export { Q as ChatAttachmentInput, f as ChatMentionKind, R as ChatTurnInputError, T as ChatTurnTextPartInput, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, M as FileMention, Y as FileMentionsToPartsOptions, Z as INLINE_PARTS_MAX_BYTES, _ as MENTION_MAX_COUNT, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, a9 as SandboxMentionPathCheck, aa as assertPromptPartsWithinCap, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, af as formatBytes, ag as mediaTypeForMentionPath, ah as mentionKindForPath, ai as parseChatTurnParts, aj as parseFileMentions, ak as promptPartsByteSize, al as validateSandboxMentionPath } from '../parts-
|
|
3
|
-
export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-
|
|
1
|
+
import { C as ChatMessagePart, N as ChatTurnRequestPayload, O as ChatTurnPartInput, P as ChatTurnFilePartInput, b as ChatAttachmentPart, a as ChatAttachmentKind, g as ChatMentionPart } from '../parts-fyPPdDdK.js';
|
|
2
|
+
export { Q as ChatAttachmentInput, f as ChatMentionKind, R as ChatTurnInputError, T as ChatTurnTextPartInput, U as DISPATCH_MAX_MEDIA_PARTS, V as DISPATCH_MAX_PARTS, W as DISPATCH_REQUEST_MAX_BYTES, X as DISPATCH_STRUCTURAL_RESERVE_BYTES, M as FileMention, Y as FileMentionsToPartsOptions, Z as INLINE_PARTS_MAX_BYTES, _ as MENTION_MAX_COUNT, $ as ProducerErrorEvent, a0 as ProducerNoticeEvent, a1 as ProducerPassthroughEvent, a2 as ProducerPassthroughEventType, a3 as ProducerReasoningEvent, a4 as ProducerTextEvent, a5 as ProducerToolCallEvent, a6 as ProducerToolResultEvent, a7 as ProducerUsageEvent, a8 as ProducerWireEvent, a9 as SandboxMentionPathCheck, aa as assertPromptPartsWithinCap, ab as base64WireLen, ac as buildMentionPromptBlock, ad as chatTurnRequestInit, ae as fileMentionsToParts, af as formatBytes, ag as mediaTypeForMentionPath, ah as mentionKindForPath, ai as parseChatTurnParts, aj as parseFileMentions, ak as promptPartsByteSize, al as validateSandboxMentionPath } from '../parts-fyPPdDdK.js';
|
|
3
|
+
export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-Dvc_Livy.js';
|
|
4
4
|
import { ChatTurnIdentity, ChatTurnProducer } from '@tangle-network/agent-runtime';
|
|
5
5
|
import { InteractionAnswerRoute, InteractionAnswerRouteOptions } from '../interactions/index.js';
|
|
6
6
|
import { PersistedChatMessageForTurn } from '../stream/index.js';
|
|
@@ -1262,7 +1262,8 @@ interface CreateUploadRouteOptions {
|
|
|
1262
1262
|
inlineMaxBytes?: number;
|
|
1263
1263
|
/** Hard per-file cap. Default {@link UPLOAD_MAX_FILE_BYTES}. */
|
|
1264
1264
|
maxFileBytes?: number;
|
|
1265
|
-
/**
|
|
1265
|
+
/** Absolute workspace directory for path-ref files.
|
|
1266
|
+
* Default `'/workspace/uploads'`. */
|
|
1266
1267
|
uploadDir?: string;
|
|
1267
1268
|
}
|
|
1268
1269
|
/** One uploaded file, ready for the composer chip and the turn body. */
|
|
@@ -1526,9 +1527,9 @@ declare function createAttachmentUploadRoute(options: CreateAttachmentUploadRout
|
|
|
1526
1527
|
* blocks); each attachment or mention becomes one media part. An attachment
|
|
1527
1528
|
* (read from the product store via the injected reader) draws the inline byte
|
|
1528
1529
|
* budget first; a mention (read from the LIVE box) takes what is left. A file
|
|
1529
|
-
* inlines as a `data:` URI when it fits the remaining budget, otherwise
|
|
1530
|
-
*
|
|
1531
|
-
* media part is deduped by its resolved absolute path. This module only
|
|
1530
|
+
* inlines as a `data:` URI when it fits the remaining budget, otherwise uses
|
|
1531
|
+
* the in-box file reference supported by the current sandbox prompt contract.
|
|
1532
|
+
* Every media part is deduped by its resolved absolute path. This module only
|
|
1532
1533
|
* produces the parts array; the caller decides when a turn dispatches parts
|
|
1533
1534
|
* instead of a plain string.
|
|
1534
1535
|
*
|
|
@@ -1565,6 +1566,12 @@ type SandboxMentionReadOutcome = {
|
|
|
1565
1566
|
type ReadSandboxMentionFn = (box: SandboxExecChannel, absolutePath: string, options: {
|
|
1566
1567
|
readBytes: boolean;
|
|
1567
1568
|
}) => Promise<SandboxMentionReadOutcome>;
|
|
1569
|
+
/**
|
|
1570
|
+
* Convert the browser-safe chat attachment contract into the current sandbox
|
|
1571
|
+
* prompt contract. Images carry exactly one URL or path; generic files carry a
|
|
1572
|
+
* filename and URL.
|
|
1573
|
+
*/
|
|
1574
|
+
declare function normalizeChatPromptForSandbox(prompt: string | readonly ChatTurnPartInput[]): string | PromptInputPart[];
|
|
1568
1575
|
/** Build input parameters for dispatching chat message parts including text, attachments, mentions, and history */
|
|
1569
1576
|
interface BuildDispatchPartsInput {
|
|
1570
1577
|
text: string;
|
|
@@ -1583,7 +1590,7 @@ interface BuildDispatchPartsInput {
|
|
|
1583
1590
|
/** The product's workspace/tenant key, passed to `readAttachment`. */
|
|
1584
1591
|
scopeId: string;
|
|
1585
1592
|
/** Maps an attachment's store-relative path to the in-box absolute path a
|
|
1586
|
-
*
|
|
1593
|
+
* local media reference uses (same style as `fileMentionsToParts`'s
|
|
1587
1594
|
* `resolvePath`). */
|
|
1588
1595
|
resolveAttachmentPath: (path: string) => string;
|
|
1589
1596
|
/** Maps a mention's workspace-relative path to its in-box absolute path.
|
|
@@ -1593,7 +1600,7 @@ interface BuildDispatchPartsInput {
|
|
|
1593
1600
|
/** The turn's already-ensured box — required when `mentions` is non-empty
|
|
1594
1601
|
* (mention bytes are read from the live box, not the store). */
|
|
1595
1602
|
box?: SandboxExecChannel;
|
|
1596
|
-
/** Force every media part to a
|
|
1603
|
+
/** Force every media part to a local file reference, skipping all inlining. */
|
|
1597
1604
|
forcePath?: boolean;
|
|
1598
1605
|
/** REQUIRED store reader for attachment content — no default (the product
|
|
1599
1606
|
* owns its store; see {@link ReadAttachmentFn}). */
|
|
@@ -1693,4 +1700,4 @@ interface PromoteAgentFilePartOptions {
|
|
|
1693
1700
|
/** Promote a part of an agent file with optional byte limits and MIME type detection */
|
|
1694
1701
|
declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
|
|
1695
1702
|
|
|
1696
|
-
export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, promoteAgentFilePart, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, summarizeFailoverReason, withDurableChatProjection };
|
|
1703
|
+
export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, normalizeChatPromptForSandbox, promoteAgentFilePart, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, summarizeFailoverReason, withDurableChatProjection };
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createSandboxFileIndexRoute,
|
|
12
12
|
sanitizeAttachmentFileName,
|
|
13
13
|
sniffBinary
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-BI6NKSO4.js";
|
|
15
15
|
import {
|
|
16
16
|
coalesceDeltas,
|
|
17
17
|
createBufferedTurnTap,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
promptPartsByteSize,
|
|
56
56
|
toChatMessageParts,
|
|
57
57
|
validateSandboxMentionPath
|
|
58
|
-
} from "../chunk-
|
|
58
|
+
} from "../chunk-C3SRFZGL.js";
|
|
59
59
|
import "../chunk-ZVEEWGDK.js";
|
|
60
60
|
import {
|
|
61
61
|
asRecord,
|
|
@@ -93,7 +93,7 @@ import {
|
|
|
93
93
|
flattenHistory,
|
|
94
94
|
readSandboxBinaryBytes,
|
|
95
95
|
statSandboxFileSize
|
|
96
|
-
} from "../chunk-
|
|
96
|
+
} from "../chunk-2ZSSOYXP.js";
|
|
97
97
|
import "../chunk-LWSJK546.js";
|
|
98
98
|
import "../chunk-CQZSAR77.js";
|
|
99
99
|
import "../chunk-ICOHEZK6.js";
|
|
@@ -310,8 +310,9 @@ function validateTurnBody(body, maxInlinePartBytes) {
|
|
|
310
310
|
const content = rawContent.trim();
|
|
311
311
|
const fileParts = parseChatTurnParts(body.parts);
|
|
312
312
|
const mentions = parseFileMentions(body.mentions);
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
const hasAttachments = Array.isArray(body.attachments) && body.attachments.length > 0;
|
|
314
|
+
if (!content && fileParts.length === 0 && mentions.length === 0 && !hasAttachments) {
|
|
315
|
+
throw new ChatTurnInputError("Missing content (send text, parts, mentions, attachments, or any combination)");
|
|
315
316
|
}
|
|
316
317
|
assertPromptPartsWithinCap(fileParts, maxInlinePartBytes);
|
|
317
318
|
let turnId;
|
|
@@ -1693,6 +1694,14 @@ function withDurableChatProjection(producer, projection, log = (message, meta) =
|
|
|
1693
1694
|
// src/chat-routes/upload.ts
|
|
1694
1695
|
var UPLOAD_INLINE_MAX_BYTES = 700 * 1024;
|
|
1695
1696
|
var UPLOAD_MAX_FILE_BYTES = 8 * 1024 * 1024;
|
|
1697
|
+
var DEFAULT_UPLOAD_DIRECTORY = "/workspace/uploads";
|
|
1698
|
+
function normalizeUploadDirectory(value) {
|
|
1699
|
+
const withoutTrailingSlash = value.trim().replace(/\/+$/, "");
|
|
1700
|
+
if (!withoutTrailingSlash.startsWith("/") || withoutTrailingSlash.length === 0) return null;
|
|
1701
|
+
const segments = withoutTrailingSlash.slice(1).split("/");
|
|
1702
|
+
if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) return null;
|
|
1703
|
+
return `/${segments.join("/")}`;
|
|
1704
|
+
}
|
|
1696
1705
|
function sanitizeUploadFilename(name) {
|
|
1697
1706
|
const base = name.split(/[\\/]/).pop() ?? "file";
|
|
1698
1707
|
const safe = base.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^\.+/, "_");
|
|
@@ -1716,7 +1725,7 @@ function createUploadRoute(options) {
|
|
|
1716
1725
|
const auth = await options.authorize({ request });
|
|
1717
1726
|
if (!auth.ok) return auth.response;
|
|
1718
1727
|
const sink = auth.sink ?? null;
|
|
1719
|
-
const
|
|
1728
|
+
const configuredUploadDir = auth.uploadDir ?? options.uploadDir ?? DEFAULT_UPLOAD_DIRECTORY;
|
|
1720
1729
|
let form;
|
|
1721
1730
|
try {
|
|
1722
1731
|
form = await request.formData();
|
|
@@ -1767,6 +1776,14 @@ function createUploadRoute(options) {
|
|
|
1767
1776
|
`${name} is ${file.size}B, over the ${inlineMaxBytes}B inline cap, and no sandbox is available to hold it`
|
|
1768
1777
|
);
|
|
1769
1778
|
}
|
|
1779
|
+
const uploadDir = normalizeUploadDirectory(configuredUploadDir);
|
|
1780
|
+
if (!uploadDir) {
|
|
1781
|
+
return uploadError(
|
|
1782
|
+
500,
|
|
1783
|
+
"INVALID_UPLOAD_DIRECTORY",
|
|
1784
|
+
'uploadDir must be an absolute path without empty, "." or ".." segments'
|
|
1785
|
+
);
|
|
1786
|
+
}
|
|
1770
1787
|
const path = `${uploadDir}/${id}-${name}`;
|
|
1771
1788
|
const base64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()));
|
|
1772
1789
|
await sink.write(path, base64, { encoding: "base64" });
|
|
@@ -2179,6 +2196,7 @@ function createAttachmentUploadRoute(options) {
|
|
|
2179
2196
|
}
|
|
2180
2197
|
|
|
2181
2198
|
// src/chat-routes/dispatch-parts.ts
|
|
2199
|
+
import { pathToFileURL } from "url";
|
|
2182
2200
|
function byteLen(value) {
|
|
2183
2201
|
return new TextEncoder().encode(value).length;
|
|
2184
2202
|
}
|
|
@@ -2196,10 +2214,58 @@ async function readSandboxMention(box, absolutePath, options) {
|
|
|
2196
2214
|
}
|
|
2197
2215
|
function violatesUrlPathXor(part) {
|
|
2198
2216
|
if (part.type === "text") return false;
|
|
2199
|
-
const hasUrl = typeof part.url === "string" && part.url.startsWith("data:");
|
|
2200
|
-
const hasPath = typeof part.path === "string" && part.path.startsWith("/");
|
|
2217
|
+
const hasUrl = typeof part.url === "string" && (part.url.startsWith("data:") || part.url.startsWith("file://"));
|
|
2218
|
+
const hasPath = "path" in part && typeof part.path === "string" && part.path.startsWith("/");
|
|
2219
|
+
if (part.type === "file") return !hasUrl || hasPath;
|
|
2201
2220
|
return hasUrl === hasPath;
|
|
2202
2221
|
}
|
|
2222
|
+
function sandboxFileUrl(absolutePath) {
|
|
2223
|
+
if (!absolutePath.startsWith("/")) return void 0;
|
|
2224
|
+
try {
|
|
2225
|
+
return pathToFileURL(absolutePath).href;
|
|
2226
|
+
} catch {
|
|
2227
|
+
return void 0;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
function normalizeChatPromptForSandbox(prompt) {
|
|
2231
|
+
if (typeof prompt === "string") return prompt;
|
|
2232
|
+
return prompt.map((part) => {
|
|
2233
|
+
if (part.type === "text") return part;
|
|
2234
|
+
if ("content" in part) {
|
|
2235
|
+
throw new ChatTurnInputError("Sandbox prompt parts do not accept inline content; provide a URL or path");
|
|
2236
|
+
}
|
|
2237
|
+
const url = typeof part.url === "string" && part.url.length > 0 ? part.url : void 0;
|
|
2238
|
+
const path = typeof part.path === "string" && part.path.length > 0 ? part.path : void 0;
|
|
2239
|
+
if (Boolean(url) === Boolean(path)) {
|
|
2240
|
+
throw new ChatTurnInputError(`Sandbox ${part.type} parts require exactly one URL or path`);
|
|
2241
|
+
}
|
|
2242
|
+
if (path && !path.startsWith("/")) {
|
|
2243
|
+
throw new ChatTurnInputError(`Sandbox ${part.type} paths must be absolute: ${path}`);
|
|
2244
|
+
}
|
|
2245
|
+
if (part.type === "image") {
|
|
2246
|
+
return {
|
|
2247
|
+
type: "image",
|
|
2248
|
+
...part.filename ? { filename: part.filename } : {},
|
|
2249
|
+
...part.mediaType ? { mediaType: part.mediaType } : {},
|
|
2250
|
+
...url ? { url } : { path }
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
const filename = part.filename?.trim();
|
|
2254
|
+
if (!filename) {
|
|
2255
|
+
throw new ChatTurnInputError("Sandbox file parts require a filename");
|
|
2256
|
+
}
|
|
2257
|
+
const fileUrl = url ?? sandboxFileUrl(path);
|
|
2258
|
+
if (!fileUrl) {
|
|
2259
|
+
throw new ChatTurnInputError(`Sandbox file paths must be absolute: ${path}`);
|
|
2260
|
+
}
|
|
2261
|
+
return {
|
|
2262
|
+
type: "file",
|
|
2263
|
+
filename,
|
|
2264
|
+
...part.mediaType ? { mediaType: part.mediaType } : {},
|
|
2265
|
+
url: fileUrl
|
|
2266
|
+
};
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2203
2269
|
function readResultToBase64(read) {
|
|
2204
2270
|
if (typeof read.base64 === "string") return read.base64;
|
|
2205
2271
|
if (read.bytes) return bytesToBase64(read.bytes);
|
|
@@ -2270,7 +2336,11 @@ async function buildDispatchParts(input) {
|
|
|
2270
2336
|
parts.push(inlinePart);
|
|
2271
2337
|
runningInline += cost;
|
|
2272
2338
|
} else {
|
|
2273
|
-
|
|
2339
|
+
const url = sandboxFileUrl(absPath);
|
|
2340
|
+
if (!url) {
|
|
2341
|
+
return { succeeded: false, error: `resolved attachment path must be absolute: ${absPath}` };
|
|
2342
|
+
}
|
|
2343
|
+
parts.push({ type: "file", filename: attachment.name, mediaType: fileMediaType, url });
|
|
2274
2344
|
}
|
|
2275
2345
|
}
|
|
2276
2346
|
if (mentions.length > 0 && !input.box) {
|
|
@@ -2315,9 +2385,15 @@ async function buildDispatchParts(input) {
|
|
|
2315
2385
|
continue;
|
|
2316
2386
|
}
|
|
2317
2387
|
}
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2388
|
+
if (isImage && mediaType) {
|
|
2389
|
+
parts.push({ type: "image", filename: mention.name, mediaType, path: absPath });
|
|
2390
|
+
} else {
|
|
2391
|
+
const url = sandboxFileUrl(absPath);
|
|
2392
|
+
if (!url) {
|
|
2393
|
+
return { succeeded: false, error: `resolved mention path must be absolute: ${absPath}` };
|
|
2394
|
+
}
|
|
2395
|
+
parts.push({ type: "file", filename: mention.name, url });
|
|
2396
|
+
}
|
|
2321
2397
|
}
|
|
2322
2398
|
for (const part of parts) {
|
|
2323
2399
|
if (violatesUrlPathXor(part)) {
|
|
@@ -2379,6 +2455,7 @@ export {
|
|
|
2379
2455
|
isDraftContentEvent,
|
|
2380
2456
|
mediaTypeForMentionPath,
|
|
2381
2457
|
mentionKindForPath,
|
|
2458
|
+
normalizeChatPromptForSandbox,
|
|
2382
2459
|
parseChatTurnParts,
|
|
2383
2460
|
parseFileMentions,
|
|
2384
2461
|
promoteAgentFilePart,
|