@poncho-ai/harness 0.50.0 → 0.50.2
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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +50 -0
- package/dist/index.js +6 -5
- package/package.json +1 -1
- package/src/harness.ts +16 -5
- package/src/upload-store.ts +13 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.50.
|
|
2
|
+
> @poncho-ai/harness@0.50.2 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
|
|
3
3
|
> node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[embed-docs] Generated poncho-docs.ts with 4 topics
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mESM[39m Build start
|
|
11
11
|
[32mESM[39m [1mdist/isolate-BNQ6P3HI.js [22m[32m51.41 KB[39m
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[32m530.
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m530.76 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 181ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 5658ms
|
|
16
16
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m89.28 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.50.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#133](https://github.com/cesr/poncho-ai/pull/133) [`60e21c8`](https://github.com/cesr/poncho-ai/commit/60e21c8c18054d2824cf7364b71dabfd07574b48) Thanks [@cesr](https://github.com/cesr)! - harness: don't inject the "interrupted by a time limit" bridge when resuming from tool results
|
|
8
|
+
|
|
9
|
+
A taskless `run()` (continuation checkpoint, or `continueFromToolResult` —
|
|
10
|
+
e.g. resuming after an approval gate) injected a synthetic user message
|
|
11
|
+
telling the model its turn was "interrupted by a time limit ... continue
|
|
12
|
+
EXACTLY from where you left off, do not re-summarize." That bridge is only
|
|
13
|
+
appropriate when the model was actually cut off mid-response (the last
|
|
14
|
+
message is an `assistant` turn, which some providers also reject as a
|
|
15
|
+
conversation-ending message).
|
|
16
|
+
|
|
17
|
+
When the last message is a `tool` result — which is the case for every
|
|
18
|
+
`continueFromToolResult` resume — the conversation already ends in a
|
|
19
|
+
provider-valid user `tool_result` block and the model continues from the
|
|
20
|
+
results naturally. Injecting the bridge there was a bug: after a normal
|
|
21
|
+
approval resolution the model was told its turn had been killed by a time
|
|
22
|
+
limit, causing it to distrust and re-derive context (re-reading the VFS,
|
|
23
|
+
concluding it had "hallucinated" data it had legitimately loaded). The
|
|
24
|
+
bridge is now only added when the last message is an `assistant` turn, and
|
|
25
|
+
its wording no longer hard-codes "time limit" (max-steps checkpoints use
|
|
26
|
+
the same path).
|
|
27
|
+
|
|
28
|
+
## 0.50.1
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- [#131](https://github.com/cesr/poncho-ai/pull/131) [`9e0ccd8`](https://github.com/cesr/poncho-ai/commit/9e0ccd8307ab056f94b7efc3e25a0cb71ee75625) Thanks [@cesr](https://github.com/cesr)! - harness: strip `poncho-upload://` scheme in `S3UploadStore.get` / `.delete`
|
|
33
|
+
|
|
34
|
+
`createUploadStore({ provider: "s3" })` wraps `S3UploadStore` in
|
|
35
|
+
`CachedUploadStore`, whose `put` returns a `poncho-upload://<key>` ref
|
|
36
|
+
and stores the underlying S3 object at the bare `<key>`. On read,
|
|
37
|
+
`CachedUploadStore.get` checks an in-memory cache (10-minute TTL); on
|
|
38
|
+
miss it falls through to `S3UploadStore.get(<ref>)`. Pre-fix, the S3
|
|
39
|
+
store treated the scheme-prefixed ref as a literal S3 key and hit the
|
|
40
|
+
backend with `poncho-upload://<key>` — guaranteed `NoSuchKey`.
|
|
41
|
+
|
|
42
|
+
In practice this meant a chat message with an attached image worked on
|
|
43
|
+
the turn it was uploaded (cache hit) and then started showing as
|
|
44
|
+
"[Attached file: … — file is no longer available]" on every follow-up
|
|
45
|
+
turn ~10 minutes later (cache miss → S3 NoSuchKey → outer catch in the
|
|
46
|
+
harness resolver). The same path worked for the local-fs store, which
|
|
47
|
+
strips the scheme in both `get` and `delete`.
|
|
48
|
+
|
|
49
|
+
`S3UploadStore.get` now strips the scheme before issuing
|
|
50
|
+
`GetObjectCommand`. `S3UploadStore.delete` already stripped `https://`
|
|
51
|
+
and now strips `poncho-upload://` too.
|
|
52
|
+
|
|
3
53
|
## 0.50.0
|
|
4
54
|
|
|
5
55
|
### Minor Changes
|
package/dist/index.js
CHANGED
|
@@ -2612,16 +2612,17 @@ var S3UploadStore = class {
|
|
|
2612
2612
|
}
|
|
2613
2613
|
return Buffer.from(await response.arrayBuffer());
|
|
2614
2614
|
}
|
|
2615
|
+
const key = urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME) ? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length) : urlOrKey;
|
|
2615
2616
|
await this.ensureClient();
|
|
2616
2617
|
const result = await this.client.send(
|
|
2617
|
-
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key:
|
|
2618
|
+
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key: key })
|
|
2618
2619
|
);
|
|
2619
|
-
if (!result.Body) throw new Error(`uploads: empty body for S3 key ${
|
|
2620
|
+
if (!result.Body) throw new Error(`uploads: empty body for S3 key ${key}`);
|
|
2620
2621
|
return Buffer.from(await result.Body.transformToByteArray());
|
|
2621
2622
|
}
|
|
2622
2623
|
async delete(urlOrKey) {
|
|
2623
2624
|
await this.ensureClient();
|
|
2624
|
-
const key = urlOrKey.startsWith("https://") ? new URL(urlOrKey).pathname.slice(1) : urlOrKey;
|
|
2625
|
+
const key = urlOrKey.startsWith("https://") ? new URL(urlOrKey).pathname.slice(1) : urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME) ? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length) : urlOrKey;
|
|
2625
2626
|
await this.client.send(
|
|
2626
2627
|
new this.s3Sdk.DeleteObjectCommand({ Bucket: this.bucket, Key: key })
|
|
2627
2628
|
);
|
|
@@ -10440,10 +10441,10 @@ ${this.skillFingerprint}`;
|
|
|
10440
10441
|
}
|
|
10441
10442
|
} else {
|
|
10442
10443
|
const lastMsg = messages[messages.length - 1];
|
|
10443
|
-
if (lastMsg && lastMsg.role
|
|
10444
|
+
if (lastMsg && lastMsg.role === "assistant") {
|
|
10444
10445
|
messages.push({
|
|
10445
10446
|
role: "user",
|
|
10446
|
-
content: "[System: Your previous turn was interrupted
|
|
10447
|
+
content: "[System: Your previous turn was interrupted before you finished. Your partial response above is already visible to the user. Continue EXACTLY from where you left off \u2014 do NOT restart, re-summarize, or repeat any content you already produced. If you were mid-sentence or mid-table, continue that sentence or table. Proceed directly with the next action or output.]",
|
|
10447
10448
|
metadata: { timestamp: now(), id: randomUUID5() }
|
|
10448
10449
|
});
|
|
10449
10450
|
}
|
package/package.json
CHANGED
package/src/harness.ts
CHANGED
|
@@ -2388,14 +2388,25 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2388
2388
|
});
|
|
2389
2389
|
}
|
|
2390
2390
|
} else {
|
|
2391
|
-
//
|
|
2392
|
-
//
|
|
2393
|
-
//
|
|
2391
|
+
// Taskless run: either a genuine continuation checkpoint (the model
|
|
2392
|
+
// was cut off mid-response by a soft-deadline / max-steps boundary)
|
|
2393
|
+
// or a tool-result resume — `continueFromToolResult`, e.g. after an
|
|
2394
|
+
// approval gate — whose last message is the tool results.
|
|
2395
|
+
//
|
|
2396
|
+
// Only the first case needs a bridge: ending on an assistant message
|
|
2397
|
+
// is invalid for some providers (Anthropic), and the model must be
|
|
2398
|
+
// told to continue rather than restart. A `tool` last message converts
|
|
2399
|
+
// to a user-role tool_result block, so the conversation already ends
|
|
2400
|
+
// "with a user message" and the model continues from the results
|
|
2401
|
+
// naturally. Injecting the bridge after tool results was a bug: it
|
|
2402
|
+
// told the model — after a normal approval resolution — that its turn
|
|
2403
|
+
// had been "interrupted by a time limit" and to not re-summarize,
|
|
2404
|
+
// which made it distrust and re-derive context it already had.
|
|
2394
2405
|
const lastMsg = messages[messages.length - 1];
|
|
2395
|
-
if (lastMsg && lastMsg.role
|
|
2406
|
+
if (lastMsg && lastMsg.role === "assistant") {
|
|
2396
2407
|
messages.push({
|
|
2397
2408
|
role: "user",
|
|
2398
|
-
content: "[System: Your previous turn was interrupted
|
|
2409
|
+
content: "[System: Your previous turn was interrupted before you finished. Your partial response above is already visible to the user. Continue EXACTLY from where you left off — do NOT restart, re-summarize, or repeat any content you already produced. If you were mid-sentence or mid-table, continue that sentence or table. Proceed directly with the next action or output.]",
|
|
2399
2410
|
metadata: { timestamp: now(), id: randomUUID() },
|
|
2400
2411
|
});
|
|
2401
2412
|
}
|
package/src/upload-store.ts
CHANGED
|
@@ -349,11 +349,19 @@ export class S3UploadStore implements UploadStore {
|
|
|
349
349
|
}
|
|
350
350
|
return Buffer.from(await response.arrayBuffer());
|
|
351
351
|
}
|
|
352
|
+
// Strip the `poncho-upload://` scheme — LocalUploadStore does this for
|
|
353
|
+
// both get/delete but S3 used to treat the full URI as a literal S3
|
|
354
|
+
// key, so resolving a persisted file part (data: "poncho-upload://…")
|
|
355
|
+
// on a follow-up turn 404'd against R2 and the harness emitted a
|
|
356
|
+
// "file is no longer available" placeholder.
|
|
357
|
+
const key = urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME)
|
|
358
|
+
? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length)
|
|
359
|
+
: urlOrKey;
|
|
352
360
|
await this.ensureClient();
|
|
353
361
|
const result = await this.client.send(
|
|
354
|
-
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key:
|
|
362
|
+
new this.s3Sdk.GetObjectCommand({ Bucket: this.bucket, Key: key }),
|
|
355
363
|
);
|
|
356
|
-
if (!result.Body) throw new Error(`uploads: empty body for S3 key ${
|
|
364
|
+
if (!result.Body) throw new Error(`uploads: empty body for S3 key ${key}`);
|
|
357
365
|
return Buffer.from(await result.Body.transformToByteArray());
|
|
358
366
|
}
|
|
359
367
|
|
|
@@ -361,7 +369,9 @@ export class S3UploadStore implements UploadStore {
|
|
|
361
369
|
await this.ensureClient();
|
|
362
370
|
const key = urlOrKey.startsWith("https://")
|
|
363
371
|
? new URL(urlOrKey).pathname.slice(1)
|
|
364
|
-
: urlOrKey
|
|
372
|
+
: urlOrKey.startsWith(PONCHO_UPLOAD_SCHEME)
|
|
373
|
+
? urlOrKey.slice(PONCHO_UPLOAD_SCHEME.length)
|
|
374
|
+
: urlOrKey;
|
|
365
375
|
await this.client.send(
|
|
366
376
|
new this.s3Sdk.DeleteObjectCommand({ Bucket: this.bucket, Key: key }),
|
|
367
377
|
);
|