@namzu/sdk 58.1.0 → 59.0.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/CHANGELOG.md +30 -0
- package/dist/account/binding.d.ts +20 -0
- package/dist/account/binding.d.ts.map +1 -0
- package/dist/account/binding.js +27 -0
- package/dist/account/binding.js.map +1 -0
- package/dist/account/index.d.ts +9 -0
- package/dist/account/index.d.ts.map +1 -0
- package/dist/account/index.js +5 -0
- package/dist/account/index.js.map +1 -0
- package/dist/account/lock.d.ts +33 -0
- package/dist/account/lock.d.ts.map +1 -0
- package/dist/account/lock.js +170 -0
- package/dist/account/lock.js.map +1 -0
- package/dist/account/registry.d.ts +21 -0
- package/dist/account/registry.d.ts.map +1 -0
- package/dist/account/registry.js +340 -0
- package/dist/account/registry.js.map +1 -0
- package/dist/account/types.d.ts +124 -0
- package/dist/account/types.d.ts.map +1 -0
- package/dist/account/types.js +35 -0
- package/dist/account/types.js.map +1 -0
- package/dist/pals/communication/ingress-schema.d.ts +38 -38
- package/dist/peers/record.d.ts +98 -0
- package/dist/peers/record.d.ts.map +1 -1
- package/dist/peers/record.js +17 -0
- package/dist/peers/record.js.map +1 -1
- package/dist/peers/registry.d.ts.map +1 -1
- package/dist/peers/registry.js +2 -2
- package/dist/peers/registry.js.map +1 -1
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +25 -2
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/token-budget.d.ts.map +1 -1
- package/dist/provider/token-budget.js +9 -1
- package/dist/provider/token-budget.js.map +1 -1
- package/dist/public-runtime.d.ts +1 -0
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +1 -0
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/query/executor.d.ts +7 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +10 -2
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +7 -7
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +7 -7
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +5 -3
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/tools/text-window.d.ts.map +1 -1
- package/dist/tools/text-window.js +4 -3
- package/dist/tools/text-window.js.map +1 -1
- package/dist/types/errors/catalog.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +6 -0
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/provider/errors.d.ts +7 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +27 -3
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/session/records.d.ts +215 -1
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +13 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/utils/copy-string.d.ts +10 -0
- package/dist/utils/copy-string.d.ts.map +1 -0
- package/dist/utils/copy-string.js +12 -0
- package/dist/utils/copy-string.js.map +1 -0
- package/package.json +1 -1
- package/src/account/binding.ts +43 -0
- package/src/account/index.ts +32 -0
- package/src/account/lock.ts +184 -0
- package/src/account/registry.ts +413 -0
- package/src/account/types.ts +137 -0
- package/src/peers/record.ts +18 -0
- package/src/peers/registry.ts +2 -2
- package/src/provider/errors.ts +27 -1
- package/src/provider/token-budget.ts +9 -3
- package/src/public-runtime.ts +17 -0
- package/src/public-types.ts +15 -0
- package/src/runtime/query/executor.ts +10 -2
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +7 -7
- package/src/runtime/query/tool-output-budget.ts +8 -4
- package/src/tools/text-window.ts +4 -3
- package/src/types/errors/catalog.ts +6 -0
- package/src/types/provider/errors.ts +40 -3
- package/src/types/session/records.ts +16 -0
- package/src/utils/copy-string.ts +11 -0
package/src/public-types.ts
CHANGED
|
@@ -800,6 +800,21 @@ export type {
|
|
|
800
800
|
ResolvePeerRuntimeDirOptions,
|
|
801
801
|
} from './peers/dir.js'
|
|
802
802
|
export type { PeerRecord } from './peers/record.js'
|
|
803
|
+
export type { AccountBoundRecord } from './types/session/records.js'
|
|
804
|
+
export type {
|
|
805
|
+
Account,
|
|
806
|
+
AccountBoundReason,
|
|
807
|
+
AccountCredentialReaders,
|
|
808
|
+
AccountId,
|
|
809
|
+
AccountLockOptions,
|
|
810
|
+
AccountLockSyncOptions,
|
|
811
|
+
AccountRegistry,
|
|
812
|
+
AccountRegistryOptions,
|
|
813
|
+
AccountSource,
|
|
814
|
+
BoundAccount,
|
|
815
|
+
NewAccount,
|
|
816
|
+
ResolvedAccountCredential,
|
|
817
|
+
} from './account/index.js'
|
|
803
818
|
export type {
|
|
804
819
|
ListLivePeersOptions,
|
|
805
820
|
PeerLivenessOptions,
|
|
@@ -13,6 +13,7 @@ import { buildProbeContext } from '../../probe/context.js'
|
|
|
13
13
|
import { ProbeVetoError } from '../../probe/errors.js'
|
|
14
14
|
import { probe as defaultProbeRegistry } from '../../probe/registry.js'
|
|
15
15
|
import type { ProbeEnforcement } from '../../probe/registry.js'
|
|
16
|
+
import { ToolResultHalted } from '../../registry/tool/screen.js'
|
|
16
17
|
import type { ActivityStore } from '../../store/activity/memory.js'
|
|
17
18
|
import {
|
|
18
19
|
effectiveBashWorkingDirectory,
|
|
@@ -3131,11 +3132,16 @@ export class ToolExecutor {
|
|
|
3131
3132
|
}
|
|
3132
3133
|
|
|
3133
3134
|
/**
|
|
3134
|
-
* One execution attempt, with a throw materialized as an error result
|
|
3135
|
+
* One execution attempt, with a throw materialized as an error result —
|
|
3136
|
+
* except `ToolResultHalted`, which this deliberately lets through.
|
|
3135
3137
|
*
|
|
3136
3138
|
* an unhandled throw from `tools.execute(...)` used to
|
|
3137
3139
|
* propagate up to `result.ts` as `turn_failed` without emitting a
|
|
3138
|
-
* terminal `tool_completed`, leaving UI cards stuck in `executing`.
|
|
3140
|
+
* terminal `tool_completed`, leaving UI cards stuck in `executing`. That
|
|
3141
|
+
* is still right for a tool that merely broke. It is wrong for a screen
|
|
3142
|
+
* that said `halt`: `screen.ts` throws `ToolResultHalted` instead of
|
|
3143
|
+
* returning it so a catch this generic cannot flatten it into a failed
|
|
3144
|
+
* `ToolResult` the model reads and works around.
|
|
3139
3145
|
*
|
|
3140
3146
|
* The return is the full `ToolResult`, not a narrowed literal: the
|
|
3141
3147
|
* narrow version silently DROPPED `content`, so a tool returning an
|
|
@@ -3151,6 +3157,8 @@ export class ToolExecutor {
|
|
|
3151
3157
|
try {
|
|
3152
3158
|
return await this.executeWithDeadline(toolName, input, toolContext, prepared)
|
|
3153
3159
|
} catch (err) {
|
|
3160
|
+
if (err instanceof ToolResultHalted) throw err
|
|
3161
|
+
|
|
3154
3162
|
const message = toErrorMessage(err)
|
|
3155
3163
|
this.log.warn('Tool execution threw', {
|
|
3156
3164
|
[NAMZU.TURN_ID]: this.config.turnId,
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* line and a `breaker_open` event, no sentence); a pass the provider forces
|
|
9
9
|
* by rejecting the prompt is not gated, so the reactive ladder still runs.
|
|
10
10
|
* - Three compactions in a row, each followed by the history regaining all
|
|
11
|
-
* space shed within three iterations:
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* space shed within three iterations: conversation growth refills the space
|
|
12
|
+
* freed. Even small outputs can do this when a summary leaves little room.
|
|
13
|
+
* The turn stops, because carrying on buys the same wall.
|
|
14
14
|
*
|
|
15
15
|
* Off unless `compactionConfig.circuitBreaker` is true: stopping compaction,
|
|
16
16
|
* and above all stopping a turn, is not something a host had before.
|
|
@@ -49,12 +49,12 @@ export const COMPACTION_REFILL_ITERATIONS = 3
|
|
|
49
49
|
export const COMPACTION_REFILL_LIMIT = 3
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
52
|
+
* Shown once when the turn stops on refills. Explain context pressure without
|
|
53
|
+
* inferring the size of any individual output; suggest steps that preserve
|
|
54
|
+
* the conversation.
|
|
55
55
|
*/
|
|
56
56
|
export const COMPACTION_REFILL_MESSAGE =
|
|
57
|
-
'The context filled up again right after being compacted, three times in a row.
|
|
57
|
+
'The context filled up again right after being compacted, three times in a row. Compaction may have left too little room, even for small outputs. Check the configured model context window; limit tool output or read files in smaller parts.'
|
|
58
58
|
|
|
59
59
|
export class CompactionBreaker {
|
|
60
60
|
private failures = 0
|
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
import { digest, spillManifest } from '../../store/evidence/format.js'
|
|
5
|
+
import { copyString } from '../../utils/copy-string.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Model-visible size cap for a single tool result.
|
|
@@ -304,8 +305,9 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
|
|
|
304
305
|
if (preview !== undefined && retained?.integrity) {
|
|
305
306
|
const condensed = [preview, recovery].filter(Boolean).join('\n\n')
|
|
306
307
|
if (effectiveTextBudget === undefined || condensed.length <= effectiveTextBudget) {
|
|
308
|
+
const visible = withNotice(condensed)
|
|
307
309
|
return {
|
|
308
|
-
output:
|
|
310
|
+
output: effectiveTextBudget === undefined ? visible : copyString(visible),
|
|
309
311
|
originalLength,
|
|
310
312
|
truncated: true,
|
|
311
313
|
spillPath,
|
|
@@ -318,8 +320,10 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
|
|
|
318
320
|
}
|
|
319
321
|
|
|
320
322
|
return {
|
|
321
|
-
output:
|
|
322
|
-
|
|
323
|
+
output: copyString(
|
|
324
|
+
withNotice(
|
|
325
|
+
boundedPreview(output, effectiveTextBudget ?? textBudget, opts.toolName, recovery),
|
|
326
|
+
),
|
|
323
327
|
),
|
|
324
328
|
originalLength,
|
|
325
329
|
truncated: true,
|
|
@@ -471,7 +475,7 @@ function previewForMessageBudget(
|
|
|
471
475
|
spilledAt,
|
|
472
476
|
].join('\n')
|
|
473
477
|
return {
|
|
474
|
-
output,
|
|
478
|
+
output: copyString(output),
|
|
475
479
|
originalLength: item.output.length,
|
|
476
480
|
spillPath: (spilledAt.split('\n')[0] as string).slice(SPILL_MARKER.length + 1),
|
|
477
481
|
}
|
package/src/tools/text-window.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolTextWindow } from '../types/tool/presentation.js'
|
|
2
|
+
import { copyString } from '../utils/copy-string.js'
|
|
2
3
|
|
|
3
4
|
/** Lines kept from the start and from the end of a long text. */
|
|
4
5
|
export const WINDOW_HEAD_LINES = 80
|
|
@@ -36,7 +37,7 @@ export function windowText(
|
|
|
36
37
|
const lines = body.length === 0 ? [] : body.split('\n')
|
|
37
38
|
const totalLines = lines.length
|
|
38
39
|
if (totalLines <= WINDOW_HEAD_LINES + WINDOW_TAIL_LINES && body.length <= WINDOW_HALF_CHARS * 2) {
|
|
39
|
-
return { head: body, totalLines, totalBytes }
|
|
40
|
+
return { head: copyString(body), totalLines, totalBytes }
|
|
40
41
|
}
|
|
41
42
|
const headLines = lines.slice(0, WINDOW_HEAD_LINES)
|
|
42
43
|
const tailLines =
|
|
@@ -47,8 +48,8 @@ export function windowText(
|
|
|
47
48
|
const tail = clipTail(tailLines.join('\n'), WINDOW_HALF_CHARS)
|
|
48
49
|
const omittedLines = totalLines - headLines.length - tailLines.length
|
|
49
50
|
return {
|
|
50
|
-
head,
|
|
51
|
-
...(tail ? { tail } : {}),
|
|
51
|
+
head: copyString(head),
|
|
52
|
+
...(tail ? { tail: copyString(tail) } : {}),
|
|
52
53
|
...(omittedLines > 0 ? { omittedLines } : {}),
|
|
53
54
|
totalLines,
|
|
54
55
|
totalBytes,
|
|
@@ -164,6 +164,12 @@ export const DEFAULT_ERROR_RULES: readonly ErrorCatalogRule[] = [
|
|
|
164
164
|
message: 'The provider rejected the credentials for this turn.',
|
|
165
165
|
hint: 'Check that the API key is set, is not expired, and belongs to the account the model is billed to. Retrying will not help until it changes.',
|
|
166
166
|
},
|
|
167
|
+
{
|
|
168
|
+
id: 'provider.usage_limit',
|
|
169
|
+
when: (facts) => facts.code === 'usage_limit',
|
|
170
|
+
message: 'The account has used up its allowance until the limit resets.',
|
|
171
|
+
hint: 'Waiting inside this turn cannot help. Continue on another account or provider, or resume after the reset time the provider reported.',
|
|
172
|
+
},
|
|
167
173
|
{
|
|
168
174
|
id: 'provider.rate_limit',
|
|
169
175
|
when: (facts) => facts.code === 'rate_limit' || facts.status === 429,
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
export type ProviderErrorCode =
|
|
16
16
|
/** 429. Back off; honour `retryAfterMs` when the server sent one. */
|
|
17
17
|
| 'rate_limit'
|
|
18
|
+
/**
|
|
19
|
+
* The account's usage allowance is spent until a later reset, as opposed to
|
|
20
|
+
* a momentary burst limit. Waiting inside this turn cannot help, so it is
|
|
21
|
+
* not retried; a host may offer another account instead.
|
|
22
|
+
*/
|
|
23
|
+
| 'usage_limit'
|
|
18
24
|
/** 529 / 503 — the model is up but saturated. Back off. */
|
|
19
25
|
| 'overloaded'
|
|
20
26
|
/** Any other 5xx. Back off. */
|
|
@@ -487,7 +493,10 @@ const STRUCTURAL_CODES: Readonly<Record<string, ProviderErrorCode>> = {
|
|
|
487
493
|
string_above_max_length: 'context_length_exceeded',
|
|
488
494
|
rate_limit_exceeded: 'rate_limit',
|
|
489
495
|
rate_limit_error: 'rate_limit',
|
|
490
|
-
insufficient_quota: '
|
|
496
|
+
insufficient_quota: 'usage_limit',
|
|
497
|
+
usage_limit_reached: 'usage_limit',
|
|
498
|
+
usage_limit_exceeded: 'usage_limit',
|
|
499
|
+
billing_error: 'usage_limit',
|
|
491
500
|
overloaded_error: 'overloaded',
|
|
492
501
|
content_filter: 'content_filter',
|
|
493
502
|
content_policy_violation: 'content_filter',
|
|
@@ -497,6 +506,24 @@ const STRUCTURAL_CODES: Readonly<Record<string, ProviderErrorCode>> = {
|
|
|
497
506
|
model_not_found: 'not_found',
|
|
498
507
|
}
|
|
499
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Is this exact provider code, or the provider's own complaint, a spent
|
|
511
|
+
* allowance? The codes are the vendors' identifiers (`insufficient_quota`,
|
|
512
|
+
* `usage_limit_reached`, `usage_limit_exceeded`, `billing_error`); the
|
|
513
|
+
* sentences are the two vendors that word it only in prose: an exhausted
|
|
514
|
+
* prepaid balance and an exhausted plan allowance.
|
|
515
|
+
*/
|
|
516
|
+
const USAGE_LIMIT_DETAIL: readonly RegExp[] = [
|
|
517
|
+
/credit balance is too low/i,
|
|
518
|
+
/exceeded your current quota/i,
|
|
519
|
+
/usage limit (?:has been )?(?:reached|exceeded)/i,
|
|
520
|
+
]
|
|
521
|
+
|
|
522
|
+
export function isUsageLimitSignal(code: string | undefined, detail: string | undefined): boolean {
|
|
523
|
+
if (code !== undefined && STRUCTURAL_CODES[code.toLowerCase()] === 'usage_limit') return true
|
|
524
|
+
return detail !== undefined && USAGE_LIMIT_DETAIL.some((re) => re.test(detail))
|
|
525
|
+
}
|
|
526
|
+
|
|
500
527
|
function codeFromStructure(err: unknown): ProviderErrorCode | undefined {
|
|
501
528
|
for (const link of causeChain(err)) {
|
|
502
529
|
for (const field of [link.code, link.type, (link.error as { type?: unknown })?.type]) {
|
|
@@ -556,6 +583,8 @@ function classifyFromProviderRequestError(
|
|
|
556
583
|
const candidate = err as Error & {
|
|
557
584
|
kind?: unknown
|
|
558
585
|
providerId?: unknown
|
|
586
|
+
providerCode?: unknown
|
|
587
|
+
detail?: unknown
|
|
559
588
|
status?: unknown
|
|
560
589
|
retryAfterMs?: unknown
|
|
561
590
|
shouldRetry?: unknown
|
|
@@ -563,15 +592,23 @@ function classifyFromProviderRequestError(
|
|
|
563
592
|
if (typeof candidate.kind !== 'string') return undefined
|
|
564
593
|
const mapped = KIND_TO_CODE[candidate.kind]
|
|
565
594
|
if (!mapped) return undefined
|
|
595
|
+
// A throttle or an invalid request that says the allowance is spent is not
|
|
596
|
+
// a burst limit: resending the same request cannot succeed.
|
|
597
|
+
const spent =
|
|
598
|
+
(candidate.kind === 'throttle' || candidate.kind === 'bad_request') &&
|
|
599
|
+
isUsageLimitSignal(
|
|
600
|
+
typeof candidate.providerCode === 'string' ? candidate.providerCode : undefined,
|
|
601
|
+
typeof candidate.detail === 'string' ? candidate.detail : undefined,
|
|
602
|
+
)
|
|
566
603
|
|
|
567
604
|
const retryAfterMs =
|
|
568
605
|
typeof candidate.retryAfterMs === 'number' ? candidate.retryAfterMs : readRetryAfterMs(err, now)
|
|
569
606
|
|
|
570
607
|
return new ProviderError({
|
|
571
|
-
code: mapped.code,
|
|
608
|
+
code: spent ? 'usage_limit' : mapped.code,
|
|
572
609
|
message: err.message,
|
|
573
610
|
// The provider saying "do not resend" beats the kind's own verdict.
|
|
574
|
-
retryable: candidate.shouldRetry === false ? false : mapped.retryable,
|
|
611
|
+
retryable: spent || candidate.shouldRetry === false ? false : mapped.retryable,
|
|
575
612
|
cause: err,
|
|
576
613
|
...(typeof candidate.providerId === 'string'
|
|
577
614
|
? { providerId: candidate.providerId }
|
|
@@ -576,6 +576,19 @@ export const BudgetBoundRecordSchema = inTurn('budget_bound', {
|
|
|
576
576
|
accountId: text.min(1),
|
|
577
577
|
})
|
|
578
578
|
|
|
579
|
+
/**
|
|
580
|
+
* The account a session's provider calls run under. Written when a session is
|
|
581
|
+
* bound to an account other than the provider's `default`, and again on each
|
|
582
|
+
* switch; never inside an open turn's work, only between turns. Replay reads
|
|
583
|
+
* the last one. A build that predates this record refuses a log holding it, so
|
|
584
|
+
* a session that never leaves the default account never writes one.
|
|
585
|
+
*/
|
|
586
|
+
export const AccountBoundRecordSchema = recordSchema('account_bound', {
|
|
587
|
+
accountId: text.min(1),
|
|
588
|
+
provider: text.min(1),
|
|
589
|
+
reason: z.enum(['start', 'user_switch', 'limit_switch']),
|
|
590
|
+
})
|
|
591
|
+
|
|
579
592
|
export const LogRepairedRecordSchema = recordSchema('log_repaired', {
|
|
580
593
|
truncatedBytes: positive,
|
|
581
594
|
lastGoodSeq: positive,
|
|
@@ -612,6 +625,7 @@ const RECORD_ONLY_SCHEMAS = {
|
|
|
612
625
|
child_session_ended: ChildSessionEndedRecordSchema,
|
|
613
626
|
audit: AuditRecordSchema,
|
|
614
627
|
budget_bound: BudgetBoundRecordSchema,
|
|
628
|
+
account_bound: AccountBoundRecordSchema,
|
|
615
629
|
log_repaired: LogRepairedRecordSchema,
|
|
616
630
|
} as const
|
|
617
631
|
|
|
@@ -982,6 +996,7 @@ export type CompactionRecord = z.infer<typeof CompactionRecordSchema>
|
|
|
982
996
|
export type ChildSessionEndedRecord = z.infer<typeof ChildSessionEndedRecordSchema>
|
|
983
997
|
export type AuditRecord = z.infer<typeof AuditRecordSchema>
|
|
984
998
|
export type BudgetBoundRecord = z.infer<typeof BudgetBoundRecordSchema>
|
|
999
|
+
export type AccountBoundRecord = z.infer<typeof AccountBoundRecordSchema>
|
|
985
1000
|
export type LogRepairedRecord = z.infer<typeof LogRepairedRecordSchema>
|
|
986
1001
|
|
|
987
1002
|
export type SessionRecordOnly =
|
|
@@ -998,6 +1013,7 @@ export type SessionRecordOnly =
|
|
|
998
1013
|
| ChildSessionEndedRecord
|
|
999
1014
|
| AuditRecord
|
|
1000
1015
|
| BudgetBoundRecord
|
|
1016
|
+
| AccountBoundRecord
|
|
1001
1017
|
| LogRepairedRecord
|
|
1002
1018
|
|
|
1003
1019
|
/** One line of a session log. */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Give retained text its own backing storage instead of keeping a source slice
|
|
3
|
+
* alive. Call only after bounding the text: copying a whole tool result would
|
|
4
|
+
* briefly double its memory. UTF-16 preserves every JavaScript code unit,
|
|
5
|
+
* including lone surrogates that a UTF-8 round trip would replace.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export function copyString(text: string): string {
|
|
10
|
+
return Buffer.from(text, 'utf16le').toString('utf16le')
|
|
11
|
+
}
|