@squidcloud/cli 1.0.488 → 1.0.490
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/index.js +187 -33
- package/dist/resources/claude/skills/squid-development/SKILL.md +2 -2
- package/dist/resources/claude/skills/squid-development/reference/ai.md +37 -13
- package/dist/resources/claude/skills/squid-development/reference/chat-widget.md +1 -1
- package/dist/resources/claude/skills/squid-development/reference/client.md +7 -2
- package/dist/resources/claude/skills/squid-development/reference/console.md +2 -1
- package/dist/resources/claude/skills/squid-development/reference/openai.md +1 -1
- package/dist/resources/claude/skills/squid-integrations/SKILL.md +5 -3
- package/dist/resources/claude/skills/squid-integrations/reference/connector-functions.md +1 -1
- package/dist/resources/claude/skills/squid-react-development/SKILL.md +21 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -32174,7 +32174,8 @@ module.exports = function toBuffer(data, encoding) {
|
|
|
32174
32174
|
var __webpack_unused_export__;
|
|
32175
32175
|
|
|
32176
32176
|
__webpack_unused_export__ = ({ value: true });
|
|
32177
|
-
__webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.h3 = exports.VF = __webpack_unused_export__ = exports.F3 = exports.eS = exports.Eh = exports.dH = exports.mz = exports.Pq = exports.lZ = exports.I1 = exports.xV = exports.Kq = exports.oL = exports.hp = exports.AI_PROVIDER_TYPES = __webpack_unused_export__ = void 0;
|
|
32177
|
+
__webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.h3 = exports.VF = __webpack_unused_export__ = exports.F3 = exports.eS = exports.Eh = exports.dH = exports.mz = exports.Pq = exports.lZ = exports.I1 = exports.xV = exports.Kq = exports.oL = exports.hp = exports.MD = exports.AI_PROVIDER_TYPES = __webpack_unused_export__ = void 0;
|
|
32178
|
+
__webpack_unused_export__ = isFileCapableProviderType;
|
|
32178
32179
|
__webpack_unused_export__ = isVendorAiChatModelName;
|
|
32179
32180
|
__webpack_unused_export__ = isAiEmbeddingsModelName;
|
|
32180
32181
|
__webpack_unused_export__ = isIntegrationEmbeddingModelSpec;
|
|
@@ -32182,7 +32183,7 @@ __webpack_unused_export__ = isIntegrationModelSpec;
|
|
|
32182
32183
|
/**
|
|
32183
32184
|
* @category AI
|
|
32184
32185
|
*/
|
|
32185
|
-
__webpack_unused_export__ = ['cohere', 'none'];
|
|
32186
|
+
__webpack_unused_export__ = ['cohere', 'voyage', 'none'];
|
|
32186
32187
|
/** List of available AI provider types. See AiProviderType. */
|
|
32187
32188
|
exports.AI_PROVIDER_TYPES = [
|
|
32188
32189
|
'anthropic',
|
|
@@ -32204,35 +32205,47 @@ exports.AI_PROVIDER_TYPES = [
|
|
|
32204
32205
|
'vertex',
|
|
32205
32206
|
'external', // This is a technicality, referring to user-defined providers.
|
|
32206
32207
|
];
|
|
32208
|
+
/**
|
|
32209
|
+
* The AI providers that support file upload operations, and so are the only ones that resolve a
|
|
32210
|
+
* pre-uploaded file id. Declared as the list, with the type derived from it, so the two cannot drift:
|
|
32211
|
+
* a provider added to one is added to both.
|
|
32212
|
+
*/
|
|
32213
|
+
exports.MD = [
|
|
32214
|
+
'openai',
|
|
32215
|
+
'gemini',
|
|
32216
|
+
'anthropic',
|
|
32217
|
+
'grok',
|
|
32218
|
+
];
|
|
32219
|
+
/**
|
|
32220
|
+
* True when the provider resolves pre-uploaded file ids. Every other provider — the integration-backed
|
|
32221
|
+
* and user-defined ones, which report no vendor identity of their own — ignores `fileIds` outright, so
|
|
32222
|
+
* for them the ids are inert rather than wrong.
|
|
32223
|
+
*/
|
|
32224
|
+
function isFileCapableProviderType(type) {
|
|
32225
|
+
return exports.MD.includes(type);
|
|
32226
|
+
}
|
|
32207
32227
|
/**
|
|
32208
32228
|
* Public OpenAI chat model names (active models only).
|
|
32209
32229
|
* @category AI
|
|
32210
32230
|
*/
|
|
32211
|
-
exports.hp = [
|
|
32212
|
-
'gpt-5.4-mini',
|
|
32213
|
-
'gpt-5.4-nano',
|
|
32214
|
-
'gpt-5.5',
|
|
32215
|
-
'gpt-5.5-pro',
|
|
32216
|
-
'gpt-5.6-sol',
|
|
32217
|
-
'gpt-5.6-terra',
|
|
32218
|
-
'gpt-5.6-luna',
|
|
32219
|
-
];
|
|
32231
|
+
exports.hp = ['gpt-5.5-pro', 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'];
|
|
32220
32232
|
/**
|
|
32221
32233
|
* Public Gemini chat model names (active models only).
|
|
32222
32234
|
* @category AI
|
|
32223
32235
|
*/
|
|
32224
|
-
exports.oL = ['gemini-3.1-pro', 'gemini-3.
|
|
32236
|
+
exports.oL = ['gemini-3.1-pro', 'gemini-3.7-flash', 'gemini-3.5-flash-lite'];
|
|
32225
32237
|
/**
|
|
32226
32238
|
* Public Grok chat model names (active models only).
|
|
32227
32239
|
* @category AI
|
|
32228
32240
|
*/
|
|
32229
|
-
exports.Kq = ['grok-4.
|
|
32241
|
+
exports.Kq = ['grok-4.6', 'grok-4-1-fast-reasoning', 'grok-4-1-fast-non-reasoning'];
|
|
32230
32242
|
/**
|
|
32231
32243
|
* Public Anthropic chat model names (active models only).
|
|
32232
32244
|
* @category AI
|
|
32233
32245
|
*/
|
|
32234
32246
|
exports.xV = [
|
|
32235
32247
|
'claude-fable-5',
|
|
32248
|
+
'claude-fable-5-1',
|
|
32236
32249
|
'claude-haiku-4-5-20251001',
|
|
32237
32250
|
'claude-opus-5',
|
|
32238
32251
|
'claude-sonnet-5',
|
|
@@ -32258,7 +32271,7 @@ exports.lZ = ['text-embedding-3-small'];
|
|
|
32258
32271
|
/**
|
|
32259
32272
|
* @category AI
|
|
32260
32273
|
*/
|
|
32261
|
-
exports.Pq = ['voyage-3-large'];
|
|
32274
|
+
exports.Pq = ['voyage-3-large', 'voyage-4-large', 'voyage-4'];
|
|
32262
32275
|
/**
|
|
32263
32276
|
* @category AI
|
|
32264
32277
|
*/
|
|
@@ -32380,6 +32393,7 @@ exports.CONNECTOR_IDS = [
|
|
|
32380
32393
|
'cotomi',
|
|
32381
32394
|
'essentials',
|
|
32382
32395
|
'freshdesk',
|
|
32396
|
+
'generic_site_ingester',
|
|
32383
32397
|
'github',
|
|
32384
32398
|
'google_calendar',
|
|
32385
32399
|
'google_drive',
|
|
@@ -32410,6 +32424,7 @@ var __webpack_unused_export__;
|
|
|
32410
32424
|
|
|
32411
32425
|
__webpack_unused_export__ = ({ value: true });
|
|
32412
32426
|
exports.EL = exports.y4 = exports.q7 = exports.lO = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.INTEGRATION_TYPES = __webpack_unused_export__ = void 0;
|
|
32427
|
+
__webpack_unused_export__ = isIntegrationDisabled;
|
|
32413
32428
|
__webpack_unused_export__ = isBuiltInIntegrationId;
|
|
32414
32429
|
/** @internal */
|
|
32415
32430
|
__webpack_unused_export__ = 'ai_agents';
|
|
@@ -32447,11 +32462,13 @@ exports.INTEGRATION_TYPES = [
|
|
|
32447
32462
|
'descope',
|
|
32448
32463
|
'documentdb',
|
|
32449
32464
|
'azure-entra-external-id',
|
|
32465
|
+
'azure-entra-id',
|
|
32450
32466
|
'dynamodb',
|
|
32451
32467
|
'elasticsearch',
|
|
32452
32468
|
'firebase_auth',
|
|
32453
32469
|
'firestore',
|
|
32454
32470
|
'gcs',
|
|
32471
|
+
'generic_site_ingester',
|
|
32455
32472
|
'github',
|
|
32456
32473
|
'google_calendar',
|
|
32457
32474
|
'google_docs',
|
|
@@ -32532,6 +32549,7 @@ __webpack_unused_export__ = [
|
|
|
32532
32549
|
'descope',
|
|
32533
32550
|
'firebase_auth',
|
|
32534
32551
|
'azure-entra-external-id',
|
|
32552
|
+
'azure-entra-id',
|
|
32535
32553
|
];
|
|
32536
32554
|
/**
|
|
32537
32555
|
* Auth integration types that can OAuth-protect an MCP server (see `McpOAuthOptions` and
|
|
@@ -32558,6 +32576,10 @@ exports.q7 = 'built_in_queue';
|
|
|
32558
32576
|
* @category
|
|
32559
32577
|
*/
|
|
32560
32578
|
exports.y4 = 'built_in_storage';
|
|
32579
|
+
/** Returns true when the integration is explicitly disabled. An absent or null `enabled` means enabled. */
|
|
32580
|
+
function isIntegrationDisabled(integration) {
|
|
32581
|
+
return integration.enabled === false;
|
|
32582
|
+
}
|
|
32561
32583
|
/** Integration IDs used for built-in integrations by Squid. */
|
|
32562
32584
|
exports.EL = [
|
|
32563
32585
|
exports.lO,
|
|
@@ -33049,13 +33071,15 @@ exports.sQ = void 0;
|
|
|
33049
33071
|
__webpack_unused_export__ = isKotlinPath;
|
|
33050
33072
|
__webpack_unused_export__ = getEnvironmentPrefix;
|
|
33051
33073
|
exports.getApplicationUrl = getApplicationUrl;
|
|
33074
|
+
__webpack_unused_export__ = getQueryKotlinServiceUrl;
|
|
33052
33075
|
exports.sQ = [
|
|
33053
33076
|
'application',
|
|
33054
|
-
'auth',
|
|
33055
33077
|
'mutation',
|
|
33056
|
-
'native-query',
|
|
33057
33078
|
'query',
|
|
33058
33079
|
// Note: every `/ws/*` WebSocket path is served by the TypeScript core (port 8000), never Kotlin.
|
|
33080
|
+
// Note: `native-query` is absent on purpose — the TypeScript core answers it and forwards the
|
|
33081
|
+
// integration types it has no implementation for to Kotlin itself, via
|
|
33082
|
+
// {@link getQueryKotlinServiceUrl}.
|
|
33059
33083
|
];
|
|
33060
33084
|
/**
|
|
33061
33085
|
* Returns true if the given path is served by Kotlin core in local dev (port 8001). All `/ws/*`
|
|
@@ -33101,6 +33125,26 @@ function getApplicationUrl(environmentPrefix, appId, path, appIdPlaceholder, env
|
|
|
33101
33125
|
const cleanPath = path.replace(/^\/+/, '');
|
|
33102
33126
|
return cleanPath ? `${origin}/${cleanPath}` : origin;
|
|
33103
33127
|
}
|
|
33128
|
+
/**
|
|
33129
|
+
* Returns the URL of [path] on the `core-query-kotlin` Service, addressed from inside the cluster.
|
|
33130
|
+
*
|
|
33131
|
+
* Naming the Service rather than building the public URL with {@link getApplicationUrl} keeps a
|
|
33132
|
+
* purely intra-cluster call off the external load balancer, which is what the Kotlin core's own
|
|
33133
|
+
* `getApplicationUrlInternal` already does for the hop in the other direction; and it makes the call
|
|
33134
|
+
* independent of where ingress sends [path], so moving that route cannot turn a forward into a
|
|
33135
|
+
* request back to this core.
|
|
33136
|
+
*
|
|
33137
|
+
* The Service is shared and not per-app, so the application is identified by the
|
|
33138
|
+
* `SquidHeaders.APP_ID` header rather than by the hostname. Local development has no cluster DNS and
|
|
33139
|
+
* keeps the per-app host on Kotlin's port.
|
|
33140
|
+
*/
|
|
33141
|
+
function getQueryKotlinServiceUrl(environmentPrefix, appId, path) {
|
|
33142
|
+
if (isLocal(environmentPrefix)) {
|
|
33143
|
+
return getApplicationUrl(environmentPrefix, appId, path, undefined, undefined, true);
|
|
33144
|
+
}
|
|
33145
|
+
const cleanPath = path.replace(/^\/+/, '');
|
|
33146
|
+
return `http://core-query-kotlin.default.svc.cluster.local:8001/${cleanPath}`;
|
|
33147
|
+
}
|
|
33104
33148
|
function isLocal(regionPrefix) {
|
|
33105
33149
|
return /^local/.test(regionPrefix);
|
|
33106
33150
|
}
|
|
@@ -34455,6 +34499,20 @@ const SIGINT_STAGING_WAIT_MILLIS = 10 * time_units_1.MILLIS_PER_SECOND;
|
|
|
34455
34499
|
const DEFAULT_TIMEOUT_MINUTES = 120;
|
|
34456
34500
|
/** Interval between `bulk/getJob` polls while waiting for a job to reach a terminal state. */
|
|
34457
34501
|
const POLL_INTERVAL_MILLIS = 10 * time_units_1.MILLIS_PER_SECOND;
|
|
34502
|
+
/**
|
|
34503
|
+
* How old the last successful poll may be for the timeout classification to trust it. A status older
|
|
34504
|
+
* than this (every recent poll failed) says nothing about the job NOW, so the timeout falls back to the
|
|
34505
|
+
* conservative failure exit instead of waving a stale "still draining" snapshot through.
|
|
34506
|
+
*/
|
|
34507
|
+
const STALE_STATUS_TOLERANCE_MILLIS = 3 * POLL_INTERVAL_MILLIS;
|
|
34508
|
+
/**
|
|
34509
|
+
* How recently a poll must have observed an ACTIVE provider batch for a timeout to defer when NO work
|
|
34510
|
+
* is queued: the no-pending grace for the tick-long gap between one batch's reconciliation and the
|
|
34511
|
+
* next stage's submission. With `requestsPending > 0` behind an observed batch the deferral holds
|
|
34512
|
+
* regardless of this window (queued work is stalled-but-alive under cluster load); a job past this
|
|
34513
|
+
* window with nothing pending is wedged and takes the failure exit.
|
|
34514
|
+
*/
|
|
34515
|
+
const ACTIVE_BATCH_RECENCY_MILLIS = 5 * time_units_1.MILLIS_PER_MINUTE;
|
|
34458
34516
|
/** Extensions ingested by default when `--extensions` is not provided. */
|
|
34459
34517
|
const DEFAULT_EXTENSIONS = ['pdf', 'docx', 'txt', 'md', 'html', 'csv', 'xlsx', 'xls', 'xlsm', 'xlsb', 'pptx'];
|
|
34460
34518
|
/** Job states that end a bulk-ingestion job's lifecycle (`BulkIngestionJobState` in core). */
|
|
@@ -34465,6 +34523,7 @@ const UNKNOWN_JOB_STATUS = {
|
|
|
34465
34523
|
state: 'unknown',
|
|
34466
34524
|
counts: { files: 0, finalized: 0, failed: 0, requestsPending: 0, requestsSubmitted: 0 },
|
|
34467
34525
|
providerBatchIds: [],
|
|
34526
|
+
activeProviderBatchCount: 0,
|
|
34468
34527
|
files: [],
|
|
34469
34528
|
};
|
|
34470
34529
|
/**
|
|
@@ -34733,9 +34792,41 @@ onStagingStarted) {
|
|
|
34733
34792
|
console.log(`${batchLabel}: ${duplicates.length} file(s) skipped as content this knowledge base already holds.`);
|
|
34734
34793
|
}
|
|
34735
34794
|
activeJobIds.add(jobId);
|
|
34736
|
-
const { finalStatus, timedOut } = await waitForJobCompletion(client, jobId, batchLabel, timeoutMillis, isInterrupted);
|
|
34795
|
+
const { finalStatus, timedOut, statusStale, lastActiveBatchAtMillis } = await waitForJobCompletion(client, jobId, batchLabel, timeoutMillis, isInterrupted);
|
|
34737
34796
|
activeJobIds.delete(jobId);
|
|
34738
|
-
|
|
34797
|
+
// A duplicate found by the deferred extraction pass (a presigned object's bytes are first hashed
|
|
34798
|
+
// there) reports as a failed file with `errorCode: 'DUPLICATE_CONTENT'` — the same skip a
|
|
34799
|
+
// staging-time rejection is, so fold it into the duplicates the summary reconciles by name.
|
|
34800
|
+
const deferredDuplicates = (finalStatus?.files ?? [])
|
|
34801
|
+
.filter(file => file.errorCode === 'DUPLICATE_CONTENT')
|
|
34802
|
+
.map(file => file.title);
|
|
34803
|
+
if (deferredDuplicates.length > 0) {
|
|
34804
|
+
console.log(`${batchLabel}: ${deferredDuplicates.length} file(s) skipped as content this knowledge base already holds.`);
|
|
34805
|
+
}
|
|
34806
|
+
return {
|
|
34807
|
+
batchIndex,
|
|
34808
|
+
batchCount,
|
|
34809
|
+
jobId,
|
|
34810
|
+
uploadFailures,
|
|
34811
|
+
duplicates: [...duplicates, ...deferredDuplicates],
|
|
34812
|
+
finalStatus,
|
|
34813
|
+
timedOut,
|
|
34814
|
+
// Only a FRESH status may waive the timeout, and only on OBSERVED batch activity: an active batch
|
|
34815
|
+
// on the final poll; one seen within the recency window; or one seen at ANY point of this wait
|
|
34816
|
+
// while queued work remains (`requestsPending > 0`) — inter-stage stalls scale with cluster load
|
|
34817
|
+
// (the submit pass visits ~20 running jobs per 15s tick, and an app at its in-flight batch cap
|
|
34818
|
+
// waits for a sibling to drain), so pending-work-behind-an-observed-batch is stalled-but-alive,
|
|
34819
|
+
// not wedged. A job with batches long drained and NOTHING pending takes the failure exit. The
|
|
34820
|
+
// job's `transport` is deliberately NOT consulted: it is 'batch' from staging for every large
|
|
34821
|
+
// job, batches or none, so it cannot say whether anything is draining NOW.
|
|
34822
|
+
deferredInFlight: timedOut &&
|
|
34823
|
+
!statusStale &&
|
|
34824
|
+
finalStatus.state === 'running' &&
|
|
34825
|
+
((finalStatus.activeProviderBatchCount ?? 0) > 0 ||
|
|
34826
|
+
(lastActiveBatchAtMillis > 0 &&
|
|
34827
|
+
(Date.now() - lastActiveBatchAtMillis < ACTIVE_BATCH_RECENCY_MILLIS ||
|
|
34828
|
+
finalStatus.counts.requestsPending > 0))),
|
|
34829
|
+
};
|
|
34739
34830
|
}
|
|
34740
34831
|
/** Mints presigned upload URLs for a batch, chunking requests to the server's per-call cap. */
|
|
34741
34832
|
async function createUploadUrlsForBatch(client, fileNames) {
|
|
@@ -34793,7 +34884,10 @@ async function uploadFile(filePath, upload, stagedObjectKey) {
|
|
|
34793
34884
|
async function waitForJobCompletion(client, jobId, batchLabel, timeoutMillis, isInterrupted) {
|
|
34794
34885
|
const deadline = Date.now() + timeoutMillis;
|
|
34795
34886
|
let status;
|
|
34887
|
+
let statusAtMillis = 0;
|
|
34888
|
+
let lastActiveBatchAtMillis = 0;
|
|
34796
34889
|
let isFirstPoll = true;
|
|
34890
|
+
let deferralAnnounced = false;
|
|
34797
34891
|
while (!isInterrupted() && Date.now() < deadline && (!status || !TERMINAL_JOB_STATES.has(status.state))) {
|
|
34798
34892
|
if (!isFirstPoll) {
|
|
34799
34893
|
await sleep(POLL_INTERVAL_MILLIS);
|
|
@@ -34801,6 +34895,17 @@ async function waitForJobCompletion(client, jobId, batchLabel, timeoutMillis, is
|
|
|
34801
34895
|
isFirstPoll = false;
|
|
34802
34896
|
try {
|
|
34803
34897
|
status = await client.getJob(jobId);
|
|
34898
|
+
statusAtMillis = Date.now();
|
|
34899
|
+
if ((status.activeProviderBatchCount ?? 0) > 0) {
|
|
34900
|
+
lastActiveBatchAtMillis = statusAtMillis;
|
|
34901
|
+
if (!deferralAnnounced) {
|
|
34902
|
+
deferralAnnounced = true;
|
|
34903
|
+
console.log(`\n${batchLabel}: job ${jobId} submitted a provider batch — half-price processing whose tail can ` +
|
|
34904
|
+
`reach 24 hours, possibly past --timeoutMinutes. A timed-out wait leaves the job running ` +
|
|
34905
|
+
`without failing the run; the job finishes server-side, and re-runs are idempotent ` +
|
|
34906
|
+
`(already-staged content skips as duplicate).`);
|
|
34907
|
+
}
|
|
34908
|
+
}
|
|
34804
34909
|
printProgress(batchLabel, jobId, status);
|
|
34805
34910
|
}
|
|
34806
34911
|
catch (error) {
|
|
@@ -34810,13 +34915,18 @@ async function waitForJobCompletion(client, jobId, batchLabel, timeoutMillis, is
|
|
|
34810
34915
|
process.stdout.write('\n');
|
|
34811
34916
|
if (!status) {
|
|
34812
34917
|
console.warn(`${batchLabel}: could not retrieve job ${jobId}'s status before the timeout.`);
|
|
34813
|
-
return { finalStatus: UNKNOWN_JOB_STATUS, timedOut: true };
|
|
34918
|
+
return { finalStatus: UNKNOWN_JOB_STATUS, timedOut: true, statusStale: true, lastActiveBatchAtMillis: 0 };
|
|
34814
34919
|
}
|
|
34815
34920
|
const timedOut = !TERMINAL_JOB_STATES.has(status.state) && !isInterrupted();
|
|
34816
34921
|
if (timedOut) {
|
|
34817
34922
|
console.warn(`${batchLabel}: job ${jobId} did not finish within the timeout; it may still be running server-side.`);
|
|
34818
34923
|
}
|
|
34819
|
-
return {
|
|
34924
|
+
return {
|
|
34925
|
+
finalStatus: status,
|
|
34926
|
+
timedOut,
|
|
34927
|
+
statusStale: Date.now() - statusAtMillis > STALE_STATUS_TOLERANCE_MILLIS,
|
|
34928
|
+
lastActiveBatchAtMillis,
|
|
34929
|
+
};
|
|
34820
34930
|
}
|
|
34821
34931
|
/** Best-effort cancellation of every job still active when the process is interrupted. */
|
|
34822
34932
|
async function cancelActiveJobs(client, activeJobIds) {
|
|
@@ -34832,7 +34942,13 @@ async function cancelActiveJobs(client, activeJobIds) {
|
|
|
34832
34942
|
}
|
|
34833
34943
|
function printProgress(batchLabel, jobId, status) {
|
|
34834
34944
|
const { counts, state } = status;
|
|
34835
|
-
|
|
34945
|
+
// Deferred duplicates sit inside counts.failed but are skips to the caller (see printSummary) — shown
|
|
34946
|
+
// raw, the live line says "1 failed" on a job the summary then reports as 0 failed, 1 skipped.
|
|
34947
|
+
const duplicateSkips = status.files.filter(file => file.errorCode === 'DUPLICATE_CONTENT').length;
|
|
34948
|
+
const failed = Math.max(0, counts.failed - duplicateSkips);
|
|
34949
|
+
const skippedPart = duplicateSkips > 0 ? `, ${duplicateSkips} skipped` : '';
|
|
34950
|
+
const line = `${batchLabel} job ${jobId}: ${counts.finalized}/${counts.files} finalized, ` +
|
|
34951
|
+
`${failed} failed${skippedPart}, state=${state}`;
|
|
34836
34952
|
process.stdout.write(`\r${line}${' '.repeat(Math.max(0, PROGRESS_LINE_WIDTH - line.length))}`);
|
|
34837
34953
|
}
|
|
34838
34954
|
/** Narrows a `FileUploadResult` to one that uploaded successfully (has a `stagedObjectKey`, no `error`). */
|
|
@@ -34855,7 +34971,13 @@ async function printDryRun(dir, files, batchSize) {
|
|
|
34855
34971
|
}
|
|
34856
34972
|
function printSummary(totalFiles, results) {
|
|
34857
34973
|
const totalFinalized = results.reduce((sum, result) => sum + (result.finalStatus?.counts.finalized ?? 0), 0);
|
|
34858
|
-
|
|
34974
|
+
// Deferred duplicates sit inside counts.failed but are reported on the "Skipped as duplicate" line
|
|
34975
|
+
// (runBatch folds them into `duplicates`), so subtract them here or the two lines double-count.
|
|
34976
|
+
const totalServerFailed = results.reduce((sum, result) => {
|
|
34977
|
+
const failed = result.finalStatus?.counts.failed ?? 0;
|
|
34978
|
+
const duplicateFailures = (result.finalStatus?.files ?? []).filter(file => file.errorCode === 'DUPLICATE_CONTENT').length;
|
|
34979
|
+
return sum + Math.max(0, failed - duplicateFailures);
|
|
34980
|
+
}, 0);
|
|
34859
34981
|
const totalUploadFailed = results.reduce((sum, result) => sum + result.uploadFailures.length, 0);
|
|
34860
34982
|
const totalDuplicates = results.reduce((sum, result) => sum + result.duplicates.length, 0);
|
|
34861
34983
|
const totalStagedButFailed = results.reduce((sum, result) => sum + (result.stagingError ? (result.stagedCount ?? 0) : 0), 0);
|
|
@@ -34872,6 +34994,17 @@ function printSummary(totalFiles, results) {
|
|
|
34872
34994
|
if (totalStagedButFailed > 0) {
|
|
34873
34995
|
console.log(`Staged but job failed: ${totalStagedButFailed} (uploaded objects remain; auto-expire in 24h)`);
|
|
34874
34996
|
}
|
|
34997
|
+
const totalStillDeferred = results.reduce((sum, result) => result.deferredInFlight
|
|
34998
|
+
? sum +
|
|
34999
|
+
Math.max(0, (result.finalStatus?.counts.files ?? 0) -
|
|
35000
|
+
(result.finalStatus?.counts.finalized ?? 0) -
|
|
35001
|
+
(result.finalStatus?.counts.failed ?? 0))
|
|
35002
|
+
: sum, 0);
|
|
35003
|
+
if (totalStillDeferred > 0) {
|
|
35004
|
+
// Reconciles the summary the same way the duplicates line does: these files are neither finalized
|
|
35005
|
+
// nor failed, and without this line they read as silently missing from a run that exits 0.
|
|
35006
|
+
console.log(`Still processing: ${totalStillDeferred} (queued or draining server-side; the job finishes there)`);
|
|
35007
|
+
}
|
|
34875
35008
|
for (const result of results) {
|
|
34876
35009
|
const label = `batch ${result.batchIndex + 1}/${result.batchCount}`;
|
|
34877
35010
|
if (result.stagingError) {
|
|
@@ -34887,12 +35020,20 @@ function printSummary(totalFiles, results) {
|
|
|
34887
35020
|
console.log(` ${label}: no job created (all uploads failed)`);
|
|
34888
35021
|
continue;
|
|
34889
35022
|
}
|
|
34890
|
-
const state = result.
|
|
34891
|
-
?
|
|
34892
|
-
: result.
|
|
34893
|
-
|
|
35023
|
+
const state = result.deferredInFlight
|
|
35024
|
+
? 'running — work still queued or draining server-side (batch tail can reach 24h); re-runs are idempotent'
|
|
35025
|
+
: result.timedOut
|
|
35026
|
+
? `${result.finalStatus?.state ?? 'unknown'} (timed out waiting)`
|
|
35027
|
+
: result.finalStatus?.state;
|
|
35028
|
+
// Deferred duplicates are excluded from BOTH the failed count and the per-file failure lines: they
|
|
35029
|
+
// are already named on the "skipped" lines below, and a clean idempotent re-run must not read as
|
|
35030
|
+
// "0/1 finalized, 1 failed" while exiting 0 — the CLI tells one story about a skip.
|
|
35031
|
+
const deferredDuplicateCount = result.finalStatus?.files.filter(file => file.errorCode === 'DUPLICATE_CONTENT').length ?? 0;
|
|
35032
|
+
const failedCount = Math.max(0, (result.finalStatus?.counts.failed ?? 0) - deferredDuplicateCount);
|
|
35033
|
+
console.log(` ${label}: job ${result.jobId} — ${result.finalStatus?.counts.finalized ?? 0}/${result.finalStatus?.counts.files ?? 0} finalized, ${failedCount} failed [${state}]`);
|
|
34894
35034
|
// Per-file failure reasons, straight from the job-status response (BulkIngestionFileStatus).
|
|
34895
|
-
const failedFiles = result.finalStatus?.files.filter(file => file.status === 'failed') ??
|
|
35035
|
+
const failedFiles = result.finalStatus?.files.filter(file => file.status === 'failed' && file.errorCode !== 'DUPLICATE_CONTENT') ??
|
|
35036
|
+
[];
|
|
34896
35037
|
for (const failedFile of failedFiles) {
|
|
34897
35038
|
console.log(` - ${failedFile.title}: ${failedFile.errorMessage ?? 'unknown error'}`);
|
|
34898
35039
|
}
|
|
@@ -34904,11 +35045,21 @@ function printSummary(totalFiles, results) {
|
|
|
34904
35045
|
}
|
|
34905
35046
|
}
|
|
34906
35047
|
function hasBatchFailed(result) {
|
|
34907
|
-
if (result.uploadFailures.length > 0
|
|
35048
|
+
if (result.uploadFailures.length > 0)
|
|
34908
35049
|
return true;
|
|
34909
35050
|
if (!result.finalStatus)
|
|
34910
35051
|
return true;
|
|
34911
|
-
|
|
35052
|
+
// Deferred duplicates are skips, not failures: an idempotent re-run over an already-ingested
|
|
35053
|
+
// directory must exit 0, exactly as it does when staging rejects the duplicates synchronously.
|
|
35054
|
+
const duplicateFailures = result.finalStatus.files.filter(file => file.errorCode === 'DUPLICATE_CONTENT').length;
|
|
35055
|
+
const nonDuplicateFailed = result.finalStatus.counts.failed - duplicateFailures;
|
|
35056
|
+
// A wait-budget expiry on a still-draining provider batch is not a failure BY ITSELF: the pending
|
|
35057
|
+
// files will finish server-side, and exiting 1 would report success-in-progress as an error to every
|
|
35058
|
+
// unattended corpus load. Files the job has ALREADY failed still fail the run — a deferral must never
|
|
35059
|
+
// hide them behind exit 0. Every other timeout shape fails the run outright.
|
|
35060
|
+
if (result.timedOut)
|
|
35061
|
+
return result.deferredInFlight ? nonDuplicateFailed > 0 : true;
|
|
35062
|
+
return result.finalStatus.state !== 'completed' || nonDuplicateFailed > 0;
|
|
34912
35063
|
}
|
|
34913
35064
|
/** Recursively walks `dir`, skipping symlinks and dotfiles/dot-directories, filtered to `extensions`. */
|
|
34914
35065
|
async function collectFiles(dir, extensions) {
|
|
@@ -35504,6 +35655,9 @@ const process_utils_1 = __webpack_require__(8251);
|
|
|
35504
35655
|
const report_utils_1 = __webpack_require__(3066);
|
|
35505
35656
|
const version_check_1 = __webpack_require__(4827);
|
|
35506
35657
|
const ORG = 'squid-cloud-samples';
|
|
35658
|
+
// Audit and fund are extra registry round-trips that tell the user nothing about a project the
|
|
35659
|
+
// CLI just generated, and the audit request alone can take minutes.
|
|
35660
|
+
const NPM_INSTALL_COMMAND = 'npm install --no-audit --no-fund';
|
|
35507
35661
|
async function initSample(consoleRegion, isOnPremConsole, dirPath, appId, apiKey, environmentId, squidDeveloperId, region, templateName, verbose = false) {
|
|
35508
35662
|
(0, global_utils_1.enableDebugLogs)(verbose || (0, global_utils_1.isDebugEnabled)());
|
|
35509
35663
|
(0, logpipes_1.installConsoleOverrides)(enable_debug_decorator_utils_1.debugLogFilterPipe);
|
|
@@ -35552,18 +35706,18 @@ async function initSample(consoleRegion, isOnPremConsole, dirPath, appId, apiKey
|
|
|
35552
35706
|
let monorepo = false;
|
|
35553
35707
|
try {
|
|
35554
35708
|
await promises_1.default.access(path_1.default.resolve(dirPath, 'package.json'));
|
|
35555
|
-
await (0, shell_runner_1.runInShell)(
|
|
35709
|
+
await (0, shell_runner_1.runInShell)(NPM_INSTALL_COMMAND, verbose, path_1.default.resolve(dirPath));
|
|
35556
35710
|
}
|
|
35557
35711
|
catch { }
|
|
35558
35712
|
try {
|
|
35559
35713
|
await promises_1.default.access(path_1.default.resolve(dirPath, 'backend'));
|
|
35560
|
-
await (0, shell_runner_1.runInShell)(
|
|
35714
|
+
await (0, shell_runner_1.runInShell)(NPM_INSTALL_COMMAND, verbose, path_1.default.resolve(dirPath, 'backend'));
|
|
35561
35715
|
monorepo = true;
|
|
35562
35716
|
}
|
|
35563
35717
|
catch { }
|
|
35564
35718
|
try {
|
|
35565
35719
|
await promises_1.default.access(path_1.default.resolve(dirPath, 'frontend'));
|
|
35566
|
-
await (0, shell_runner_1.runInShell)(
|
|
35720
|
+
await (0, shell_runner_1.runInShell)(NPM_INSTALL_COMMAND, verbose, path_1.default.resolve(dirPath, 'frontend'));
|
|
35567
35721
|
}
|
|
35568
35722
|
catch { }
|
|
35569
35723
|
const hasEnvars = appId && region && apiKey && environmentId && squidDeveloperId;
|
|
@@ -40729,7 +40883,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"seek-bzip","version":"1.0.6",
|
|
|
40729
40883
|
(module) {
|
|
40730
40884
|
|
|
40731
40885
|
"use strict";
|
|
40732
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@squidcloud/cli","version":"1.0.
|
|
40886
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@squidcloud/cli","version":"1.0.490","description":"The Squid CLI","main":"dist/index.js","scripts":{"start":"node dist/index.js","start-ts":"ts-node -r tsconfig-paths/register src/index.ts","prebuild":"rimraf dist","build":"webpack --mode=production","build:dev":"webpack --mode=development","lint":"eslint","link":"npm run build && chmod 755 dist/index.js && npm link","watch":"webpack --watch","deploy":"npm run build && npm pack --silent | xargs -I {} mv {} package.tgz && npm install -g package.tgz && rm -rf package.tgz","publish:public":"npm run build && npm publish --access public"},"files":["dist/**/*"],"bin":{"squid":"dist/index.js"},"keywords":[],"author":"","license":"ISC","engines":{"node":">=18.0.0"},"dependencies":{"@squidcloud/local-backend":"^1.0.490","adm-zip":"^0.5.16","copy-webpack-plugin":"^14.0.0","decompress":"^4.2.1","logpipes":"^1.11.0","nodemon":"^3.1.9","terser-webpack-plugin":"^5.5.0","ts-loader":"^9.5.1","ts-node":"^10.9.2","tsconfig-paths":"^4.2.0","tsconfig-paths-webpack-plugin":"^4.1.0","webpack":"^5.106.2","zip-webpack-plugin":"^4.0.1"},"devDependencies":{"@types/adm-zip":"^0.5.7","@types/decompress":"^4.2.7","terminal-link":"^3.0.0"}}');
|
|
40733
40887
|
|
|
40734
40888
|
/***/ }
|
|
40735
40889
|
|
|
@@ -22,8 +22,8 @@ Squid is a backend-as-a-service platform that provides:
|
|
|
22
22
|
## Feature-Specific Guidance
|
|
23
23
|
|
|
24
24
|
- **[client.md](reference/client.md)** → client SDK, initialization, setup, auth, login, tokens, OAuth, appId, region, environmentId, apiKey, authProvider, getToken, setAuthProvider, Squid client, frontend, collection, executeFunction, executeFunctionWithHeaders, getWebhookUrl, externalAuth, saveAuthCode, getAccessToken, storage, uploadFile, downloadUrl, queues, produce, consume, distributed locks, acquireLock, withLock, web, aiSearch, allowedDomains, getUrlContent, createShortUrl, jobs, getJob, awaitJob, startJob, completeJob, failJob, events, squid.events(), emit, TriggerEvent, LangGraph, squid.langGraph(), invoke, invokeAsync, resume, getState, deleteThread, observability, metrics, reportMetric, queryMetrics, setMetricAnnotations, notifications, publishNotification, observeNotifications
|
|
25
|
-
- **[console.md](reference/console.md)** → Squid Console, web UI, organizations, applications, AI Studio, knowledge bases, knowledge graph toggle, integrations, monitoring, logs, API keys, secrets, testing, debugging, profile settings, management API keys
|
|
26
|
-
- **[ai.md](reference/ai.md)** → AI agents, chat, ask, askWithAnnotations, askAsync, askWithVoiceResponse, transcribeAndChat, transcribeAndAsk, knowledge bases, RAG, embeddings, image generation, audio, transcription, text-to-speech, TTS, connectedAgents, connectedIntegrations, connectedKnowledgeBases, @aiFunction, @secureAiAgent, @secureAiQuery, memory, memoryOptions, metricAnnotations, voiceOptions, OpenAI, Anthropic, Gemini, Grok, DALL-E, Whisper, MCP, @mcpServer, @mcpTool, agent as MCP server, updateMcpServer, generateMcpDescriptions, agent API key, executeAiQuery, executeAiApiCall, extraction, createPdf, upsert agent, listAgents, listChatModels, listFunctions, spreadsheet understanding, querySpreadsheetsWithAi, searchMode, keyword search, xlsx, csv, grep, literal scan, knowledge graph, GraphRAG, graphRag, graph search, queryGraph, getGraphStatus, rebuildGraph, exploreGraph, searchWithGraphContext, graphOptions, graphFilter, underConcept, entityTypes, queryKnowledgeGraph, bulk ingestion, bulkUpsertContexts, createBulkUploadUrls, observeBulkIngestionJob, listContextsPage, generateMetadataFieldDescriptions
|
|
25
|
+
- **[console.md](reference/console.md)** → Squid Console, web UI, organizations, applications, AI Studio, knowledge bases, knowledge graph toggle, extraction method dropdown, integrations, monitoring, logs, API keys, secrets, testing, debugging, profile settings, management API keys
|
|
26
|
+
- **[ai.md](reference/ai.md)** → AI agents, chat, ask, askWithAnnotations, askAsync, askWithVoiceResponse, transcribeAndChat, transcribeAndAsk, knowledge bases, RAG, embeddings, image generation, audio, transcription, text-to-speech, TTS, connectedAgents, connectedIntegrations, connectedKnowledgeBases, @aiFunction, @secureAiAgent, @secureAiQuery, memory, memoryOptions, metricAnnotations, voiceOptions, OpenAI, Anthropic, Gemini, Grok, DALL-E, Whisper, MCP, @mcpServer, @mcpTool, agent as MCP server, updateMcpServer, generateMcpDescriptions, agent API key, executeAiQuery, executeAiApiCall, extraction, createPdf, upsert agent, listAgents, listChatModels, listFunctions, spreadsheet understanding, querySpreadsheetsWithAi, searchMode, keyword search, xlsx, csv, grep, literal scan, knowledge graph, GraphRAG, graphRag, graph search, queryGraph, getGraphStatus, rebuildGraph, exploreGraph, searchWithGraphContext, graphOptions, graphFilter, underConcept, entityTypes, queryKnowledgeGraph, bulk ingestion, bulkUpsertContexts, createBulkUploadUrls, observeBulkIngestionJob, listContextsPage, generateMetadataFieldDescriptions, extraction method, preferredExtractionMethod, legacy_with_llm, mistral_ocr, amazon_textract, azure_document_intelligence, page screenshot, image description, extractImages
|
|
27
27
|
- **[chat-widget.md](reference/chat-widget.md)** → AI chat widget, @squidcloud/react-chat-widget, squid-chat-widget, squid-chat-widget-with-fab-button, embeddable chat, custom API webhook, AI query mode, chain-of-thought, status updates, session management, agentContext, memoryOptions, squid-ai-custom-api-url, squid-ai-agent-chat-options, FAB button, theming, CSS variables, localization, RTL, menu items slots, suggested prompts, voice transcription, error formatting, authentication, squid-auth-provider, onChange events
|
|
28
28
|
- **[databases.md](reference/databases.md)** → collections, documents, queries, subscriptions, snapshots, insert, update, delete, CRUD, real-time, dereference, pagination, transactions, query operators, eq, neq, gt, gte, lt, lte, like, in, nin, arrayIncludesSome, arrayIncludesAll, sortBy, limit, join queries, OR queries, @trigger, native queries, SQL, MongoDB, Elasticsearch, incrementInPath, decrementInPath, watch changes, doc(), docId, docIds, query.delete(), delete by query, projectFields, field projection, __docId__, __id
|
|
29
29
|
- **[backend.md](reference/backend.md)** → SquidService, @executable, @webhook, @trigger, TriggerRequest, @scheduler, @limits, rate limiting, quotas, decorators, backend functions, WebhookRequest, CronExpression, cron, file handling, SquidFile, getUserAuth, isAuthenticated, assertIsAuthenticated, createWebhookResponse, this.squid, getSquid, getPassiveSquid, this.secrets, @clientConnectionStateHandler, @onQueueMessage, QueueMessageRequest, queue message handler, server-side queue consumer, @eventHandler, event handler, workspace, CLI, squid init, squid start, squid deploy, squid build, squid kb-upload, printBundle, project structure, multiple services, service architecture, squidInject, cross-service communication
|
|
@@ -59,10 +59,10 @@ import {
|
|
|
59
59
|
|
|
60
60
|
| Provider | `*_CHAT_MODEL_NAMES` |
|
|
61
61
|
|---|---|
|
|
62
|
-
| OpenAI | `gpt-5.
|
|
62
|
+
| OpenAI | `gpt-5.5-pro`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` |
|
|
63
63
|
| Anthropic | `claude-fable-5`, `claude-haiku-4-5-20251001`, `claude-opus-5`, `claude-sonnet-5` |
|
|
64
|
-
| Gemini | `gemini-3.1-pro`, `gemini-3.
|
|
65
|
-
| Grok | `grok-4.
|
|
64
|
+
| Gemini | `gemini-3.1-pro`, `gemini-3.7-flash`, `gemini-3.5-flash-lite` |
|
|
65
|
+
| Grok | `grok-4.6`, `grok-4-1-fast-reasoning`, `grok-4-1-fast-non-reasoning` |
|
|
66
66
|
|
|
67
67
|
This list moves. **Don't hardcode a model name from this table into user code** — call
|
|
68
68
|
`squid.ai().listChatModels()`, which also returns the app's custom integration models
|
|
@@ -72,7 +72,7 @@ This list moves. **Don't hardcode a model name from this table into user code**
|
|
|
72
72
|
**Model Categories:**
|
|
73
73
|
- **Chat Models**: Used for AI Agents, AI Query, etc. (OpenAI, Anthropic, Gemini, Grok)
|
|
74
74
|
- **Embedding Models**: Used for Knowledge Bases (OpenAI, Voyage)
|
|
75
|
-
- **Image Generation Models**: Used for image creation (
|
|
75
|
+
- **Image Generation Models**: Used for image creation (OpenAI `gpt-image-*`, Stable Diffusion, Flux)
|
|
76
76
|
- **Audio Models**: Transcription (Whisper, GPT-4o) and Text-to-Speech (TTS-1, GPT-4o-mini-tts)
|
|
77
77
|
|
|
78
78
|
## AI Agents
|
|
@@ -115,7 +115,7 @@ await myAgent.upsert({
|
|
|
115
115
|
isPublic: false, // Whether the agent is publicly accessible
|
|
116
116
|
auditLog: true, // Enable audit logging for compliance
|
|
117
117
|
options: {
|
|
118
|
-
model: 'gpt-5.
|
|
118
|
+
model: 'gpt-5.6-terra', // or 'claude-sonnet-5', 'gemini-3.7-flash' - see Supported Models
|
|
119
119
|
instructions: 'You are a helpful customer support assistant. Be concise and professional.',
|
|
120
120
|
temperature: 0.7
|
|
121
121
|
}
|
|
@@ -128,7 +128,7 @@ console.log(agentInfo.id, agentInfo.description, agentInfo.options.model);
|
|
|
128
128
|
// Update specific properties
|
|
129
129
|
await myAgent.updateInstructions('You are a technical support specialist.');
|
|
130
130
|
await myAgent.updateModel('claude-sonnet-5');
|
|
131
|
-
await myAgent.updateGuardrails(
|
|
131
|
+
await myAgent.updateGuardrails({ disableProfanity: true, disablePii: true });
|
|
132
132
|
|
|
133
133
|
// Delete an agent
|
|
134
134
|
await myAgent.delete();
|
|
@@ -386,14 +386,18 @@ const chatObs = agent.chat('What is your return policy?', {
|
|
|
386
386
|
connectedKnowledgeBases: [{ knowledgeBaseId: 'kb1', description: 'When to use this KB' }],
|
|
387
387
|
|
|
388
388
|
// Model & generation
|
|
389
|
-
model: 'gpt-5.
|
|
389
|
+
model: 'gpt-5.6-terra', // Override agent's default model
|
|
390
390
|
temperature: 0.7,
|
|
391
391
|
maxTokens: 4000,
|
|
392
392
|
maxOutputTokens: 2000,
|
|
393
393
|
instructions: 'Additional instructions for this request only',
|
|
394
394
|
responseFormat: 'json_object', // or 'text'
|
|
395
395
|
verbosity: 'medium', // 'low' | 'medium' | 'high' (OpenAI only)
|
|
396
|
-
reasoningEffort: 'high', // 'minimal' | 'low' | 'medium' | 'high'
|
|
396
|
+
reasoningEffort: 'high', // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'ultra'
|
|
397
|
+
// (for reasoning models). On an `openai_compatible` integration the value
|
|
398
|
+
// is sent to the endpoint as written, so a level outside this list reaches
|
|
399
|
+
// it too — OpenAI's Chat Completions API, for one, serves function tools on
|
|
400
|
+
// a gpt-5.6 model only with reasoning off outright (`'none'`).
|
|
397
401
|
|
|
398
402
|
// Context & RAG
|
|
399
403
|
disableContext: false,
|
|
@@ -404,7 +408,13 @@ const chatObs = agent.chat('What is your return policy?', {
|
|
|
404
408
|
},
|
|
405
409
|
|
|
406
410
|
// Guardrails & quotas
|
|
407
|
-
guardrails:
|
|
411
|
+
guardrails: {
|
|
412
|
+
disableProfanity: true,
|
|
413
|
+
offTopicAnswers: true,
|
|
414
|
+
professionalTone: true,
|
|
415
|
+
disablePii: true,
|
|
416
|
+
custom: 'Never reveal internal case numbers'
|
|
417
|
+
},
|
|
408
418
|
quotas: {
|
|
409
419
|
maxAiCallStackSize: 5
|
|
410
420
|
},
|
|
@@ -433,7 +443,7 @@ const chatObs = agent.chat('What is your return policy?', {
|
|
|
433
443
|
useCodeInterpreter: 'llm', // 'none' | 'llm' (OpenAI/Gemini only)
|
|
434
444
|
executionPlanOptions: {
|
|
435
445
|
enabled: true,
|
|
436
|
-
model: 'gpt-5.
|
|
446
|
+
model: 'gpt-5.6-terra',
|
|
437
447
|
reasoningEffort: 'high',
|
|
438
448
|
allowClarificationQuestions: false
|
|
439
449
|
},
|
|
@@ -530,6 +540,12 @@ await kb.upsertContexts([
|
|
|
530
540
|
{ contextId: 'doc-2', text: '...' }
|
|
531
541
|
], optionalFiles);
|
|
532
542
|
|
|
543
|
+
// File contexts can pick how the document is extracted — see "Document Extraction Methods" below
|
|
544
|
+
await kb.upsertContexts(
|
|
545
|
+
[{ contextId: 'report-1', type: 'file', preferredExtractionMethod: 'legacy_with_llm' }],
|
|
546
|
+
[reportPdfFile]
|
|
547
|
+
);
|
|
548
|
+
|
|
533
549
|
// Search with prompt
|
|
534
550
|
const results = await kb.searchContextsWithPrompt({
|
|
535
551
|
prompt: 'How do I reset password?',
|
|
@@ -784,6 +800,14 @@ await kb.bulkUpsertContexts(uploads.map(u => ({ type: 'file', stagedObjectKey: u
|
|
|
784
800
|
The CLI wraps this whole flow: `squid kb-upload --dir ./docs --knowledgeBase my-kb` (see
|
|
785
801
|
[backend.md](backend.md#cli-commands)).
|
|
786
802
|
|
|
803
|
+
### Document Extraction Methods
|
|
804
|
+
|
|
805
|
+
File contexts (and `squid.extraction()` calls) accept `preferredExtractionMethod: 'mistral_ocr' | 'legacy' | 'legacy_with_llm' | 'amazon_textract' | 'azure_document_intelligence'`. Default: the deployment's `defaultDocExtractionMethod` (usually `mistral_ocr`). The OCR providers (`mistral_ocr`, `amazon_textract`, `azure_document_intelligence`) are available only where the deployment configures them; `legacy` and `legacy_with_llm` run in-process and are always available. The same option applies to `upsertContexts`, `bulkUpsertContexts`, and the extraction client alike.
|
|
806
|
+
|
|
807
|
+
- `legacy` — reads the PDF's text layer (pdfjs) and extracts embedded images; no external service, but a scanned page yields nothing (there is no text layer to read). Console label: **Basic**.
|
|
808
|
+
- `legacy_with_llm` — the same text extraction, plus a screenshot of each page described by a vision model as a *complement* to the stored page text: figures, charts, layout, handwriting, and scanned regions the text layer cannot carry, without restating the page's prose. Notably, vector-drawn figures/graphs are not embedded images, so `legacy` cannot see them at all; the page screenshot (like provider OCR) is what catches them. Each description lands as its own chunk beside the page's text chunk, with page provenance; a page whose screenshot adds nothing beyond its text stores no extra chunk. Costs one vision call per page, so ingestion of long documents is slower and pricier than `legacy`. Console label: **Basic + page understanding**.
|
|
809
|
+
- `extractImages` (default `true` for file contexts) gates image handling for every method, including the `legacy_with_llm` page screenshots; `imageMinSizePixels` (default 100) filters small embedded images; `imageExtractionModel` overrides the model used for image/screenshot descriptions.
|
|
810
|
+
|
|
787
811
|
### Spreadsheet Understanding
|
|
788
812
|
|
|
789
813
|
Spreadsheet files (`.csv`, `.tsv`, `.xlsx`, `.xlsm`, `.xls`, `.xlsb`) uploaded as file context take a dedicated ingestion lane: instead of chunking raw cell text, Squid extracts the workbook structure (sheets, headers, hidden sheets; charts and pivot tables where the format exposes them — absent for CSV/TSV and `.xls`, pivots also absent for `.xlsb`) and embeds a generated whole-workbook summary, so search results describe what a workbook contains.
|
|
@@ -818,10 +842,10 @@ REST API: [AI Image API](https://docs.getsquid.ai/reference-docs/api/#tag/AI-Ima
|
|
|
818
842
|
### Image Generation
|
|
819
843
|
|
|
820
844
|
```typescript
|
|
821
|
-
// Generate image
|
|
845
|
+
// Generate image - `modelName` is required
|
|
822
846
|
const imageUrl = await squid.ai().image().generate(
|
|
823
847
|
'A futuristic city',
|
|
824
|
-
{ size: '1024x1024', quality: '
|
|
848
|
+
{ modelName: 'gpt-image-1', size: '1024x1024', quality: 'high' }
|
|
825
849
|
);
|
|
826
850
|
|
|
827
851
|
// Remove background
|
|
@@ -894,7 +918,7 @@ const settings = await aiClient.getApplicationAiSettings();
|
|
|
894
918
|
|
|
895
919
|
// Set application AI settings
|
|
896
920
|
await aiClient.setApplicationAiSettings({
|
|
897
|
-
defaultModel: 'gpt-5.
|
|
921
|
+
defaultModel: 'gpt-5.6-terra',
|
|
898
922
|
// ... other settings
|
|
899
923
|
});
|
|
900
924
|
|
|
@@ -763,7 +763,7 @@ declare namespace JSX {
|
|
|
763
763
|
squid-ai-instructions="Be concise and professional."
|
|
764
764
|
squid-ai-functions="getOrderStatus,lookupAccount"
|
|
765
765
|
squid-ai-connected-agents='[{"agentId":"billing-agent","description":"Handles billing questions"}]'
|
|
766
|
-
squid-ai-override-model="gpt-5.
|
|
766
|
+
squid-ai-override-model="gpt-5.6-terra"
|
|
767
767
|
squid-ai-temperature="0.7"
|
|
768
768
|
squid-ai-max-tokens="2000"
|
|
769
769
|
squid-ai-agent-chat-options='{
|
|
@@ -77,7 +77,7 @@ Every Squid application includes the **Essentials Connector** - a built-in integ
|
|
|
77
77
|
| **Web Utilities** | `squid.web()` | AI-powered web search, URL content extraction, short URLs | [Web section](#web) |
|
|
78
78
|
| **AI Agents** | `squid.ai().agent()` | Chat with built-in or custom AI agents | [ai.md](ai.md) |
|
|
79
79
|
| **Knowledge Bases** | `squid.ai().knowledgeBase()` | RAG with semantic, keyword, and knowledge-graph search plus reranking | [ai.md](ai.md) |
|
|
80
|
-
| **Image Generation** | `squid.ai().image()` | Generate images with
|
|
80
|
+
| **Image Generation** | `squid.ai().image()` | Generate images with `gpt-image-*`, Stable Diffusion, or Flux | [ai.md](ai.md) |
|
|
81
81
|
| **Audio** | `squid.ai().audio()` | Transcription and text-to-speech | [ai.md](ai.md) |
|
|
82
82
|
| **PDF/Extraction** | `squid.extraction()` | Create PDFs, extract data from documents | [Extraction section](#extraction) |
|
|
83
83
|
| **Observability** | `squid.observability` | Report and query custom metrics | [Observability section](#observability--metrics) |
|
|
@@ -389,7 +389,12 @@ const extractedData = await extraction.extractDataFromDocumentFile(
|
|
|
389
389
|
{ name: 'total', type: 'number', description: 'Total amount' },
|
|
390
390
|
{ name: 'date', type: 'date', description: 'Invoice date' }
|
|
391
391
|
]
|
|
392
|
-
}
|
|
392
|
+
},
|
|
393
|
+
// 'mistral_ocr' | 'legacy' | 'legacy_with_llm' | 'amazon_textract' | 'azure_document_intelligence'.
|
|
394
|
+
// Default: the deployment's configured method (usually 'mistral_ocr'). Semantics: see the
|
|
395
|
+
// "Document Extraction Methods" section in ai.md.
|
|
396
|
+
preferredExtractionMethod: 'legacy_with_llm',
|
|
397
|
+
extractImages: true // default true; also gates legacy_with_llm's page screenshots
|
|
393
398
|
}
|
|
394
399
|
);
|
|
395
400
|
|
|
@@ -73,6 +73,7 @@ The AI Studio provides visual tools for building and testing AI agents:
|
|
|
73
73
|
|
|
74
74
|
### Managing Content
|
|
75
75
|
- Upload documents (PDF, text, markdown, etc.)
|
|
76
|
+
- Pick the **Extraction Method** in the add-knowledge dialog: **Basic** (`legacy`, in-process text extraction) and **Basic + page understanding** (`legacy_with_llm`, adds an LLM description of each page's screenshot) are always offered; OCR providers (**Mistral OCR**, **Amazon Textract**, **Azure AI Document Intelligence**) appear when the deployment configures them, with the deployment default marked **(Default)**
|
|
76
77
|
- View and edit contexts
|
|
77
78
|
- Search and preview indexed content
|
|
78
79
|
- Monitor indexing status
|
|
@@ -156,7 +157,7 @@ const agent = squid.ai().agent('my-agent');
|
|
|
156
157
|
await agent.upsert({
|
|
157
158
|
description: 'Customer support agent',
|
|
158
159
|
options: {
|
|
159
|
-
model: 'gpt-
|
|
160
|
+
model: 'gpt-5.6-terra',
|
|
160
161
|
instructions: 'You are a helpful support agent...'
|
|
161
162
|
}
|
|
162
163
|
});
|
|
@@ -7,7 +7,7 @@ OpenAI-specific features in Squid. For general AI usage, see [ai.md](ai.md).
|
|
|
7
7
|
```typescript
|
|
8
8
|
import {
|
|
9
9
|
OPENAI_CHAT_MODEL_NAMES, // Chat models
|
|
10
|
-
OPENAI_IMAGE_MODEL_NAMES, //
|
|
10
|
+
OPENAI_IMAGE_MODEL_NAMES, // gpt-image-*
|
|
11
11
|
OPENAI_AUDIO_TRANSCRIPTION_MODEL_NAMES, // Whisper
|
|
12
12
|
OPENAI_AUDIO_CREATE_SPEECH_MODEL_NAMES, // TTS
|
|
13
13
|
OPENAI_EMBEDDINGS_MODEL_NAMES, // Embeddings
|
|
@@ -194,9 +194,11 @@ const content = await squid.web().getUrlContent('https://example.com');
|
|
|
194
194
|
// Short URLs
|
|
195
195
|
const shortUrl = await squid.web().createShortUrl('https://long-url.com/path');
|
|
196
196
|
|
|
197
|
-
// Data Extraction from documents
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
// Data Extraction from documents. Optional preferredExtractionMethod:
|
|
198
|
+
// 'mistral_ocr' | 'legacy' | 'legacy_with_llm' | 'amazon_textract' | 'azure_document_intelligence'
|
|
199
|
+
// (see the squid-development skill's ai.md "Document Extraction Methods")
|
|
200
|
+
const data = await squid.extraction().extractDataFromDocumentUrl(url);
|
|
201
|
+
const data = await squid.extraction().extractDataFromDocumentFile(file, { preferredExtractionMethod: 'legacy_with_llm' });
|
|
200
202
|
|
|
201
203
|
// PDF Creation
|
|
202
204
|
const pdf = await squid.extraction().createPdf(htmlContent);
|
|
@@ -12,7 +12,7 @@ Each Squid SaaS connector provides built-in AI functions that AI agents can call
|
|
|
12
12
|
await squid.ai().agent('my-agent').upsert({
|
|
13
13
|
description: 'Support agent',
|
|
14
14
|
options: {
|
|
15
|
-
model: 'gpt-5.
|
|
15
|
+
model: 'gpt-5.6-terra',
|
|
16
16
|
instructions: 'You are a support agent that can search tickets and send messages.'
|
|
17
17
|
}
|
|
18
18
|
});
|
|
@@ -164,12 +164,13 @@ function TaskList({ userId }: { userId: string }) {
|
|
|
164
164
|
const tasksCollection = useCollection<Task>('tasks');
|
|
165
165
|
const query = tasksCollection.query().where('userId', '==', userId);
|
|
166
166
|
|
|
167
|
-
const { loading, data, error } = useQuery(query, {
|
|
167
|
+
const { loading, data, error, enabled } = useQuery(query, {
|
|
168
168
|
enabled: !!userId, // Only run when userId is set
|
|
169
169
|
subscribe: true, // Real-time updates (default)
|
|
170
170
|
initialData: [], // Initial data before first load
|
|
171
171
|
}, [userId]); // Re-subscribe when userId changes
|
|
172
172
|
|
|
173
|
+
if (!enabled) return <div>No user selected</div>; // Check before `loading`
|
|
173
174
|
if (loading) return <div>Loading...</div>;
|
|
174
175
|
if (error) return <div>Error: {error.message}</div>;
|
|
175
176
|
|
|
@@ -201,9 +202,20 @@ interface QueryType<T> {
|
|
|
201
202
|
loading: boolean;
|
|
202
203
|
data: Array<T>;
|
|
203
204
|
error: any;
|
|
205
|
+
enabled: boolean; // Mirrors options.enabled
|
|
204
206
|
}
|
|
205
207
|
```
|
|
206
208
|
|
|
209
|
+
**`loading` stays `true` while the query is disabled.** A disabled query never subscribes, so it
|
|
210
|
+
never resolves — gate on `enabled` first, or `if (loading)` renders a spinner forever:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const { loading, data, error, enabled } = useQuery(query, { enabled: !!userId }, [userId]);
|
|
214
|
+
|
|
215
|
+
if (!enabled) return <SelectUserPrompt />; // Not "loading" - just not running
|
|
216
|
+
if (loading) return <Spinner />;
|
|
217
|
+
```
|
|
218
|
+
|
|
207
219
|
**Single Snapshot vs Real-time:**
|
|
208
220
|
```typescript
|
|
209
221
|
// Real-time updates (subscribe: true) - default
|
|
@@ -940,7 +952,7 @@ function AdvancedFeatures() {
|
|
|
940
952
|
const agent = squid.ai().agent('new-agent');
|
|
941
953
|
await agent.upsert({
|
|
942
954
|
description: 'My new agent',
|
|
943
|
-
options: { model: 'gpt-
|
|
955
|
+
options: { model: 'gpt-5.6-terra' },
|
|
944
956
|
});
|
|
945
957
|
};
|
|
946
958
|
|
|
@@ -1054,13 +1066,18 @@ const { data } = useQuery(
|
|
|
1054
1066
|
### 2. Use `enabled` for Conditional Fetching
|
|
1055
1067
|
|
|
1056
1068
|
```typescript
|
|
1057
|
-
// GOOD: Prevent unnecessary queries
|
|
1058
|
-
const { data } = useQuery(query, { enabled: !!userId }, [userId]);
|
|
1069
|
+
// GOOD: Prevent unnecessary queries, and distinguish "disabled" from "loading"
|
|
1070
|
+
const { data, loading, enabled } = useQuery(query, { enabled: !!userId }, [userId]);
|
|
1071
|
+
if (!enabled) return <EmptyState />; // `loading` is still true here
|
|
1072
|
+
if (loading) return <Spinner />;
|
|
1059
1073
|
|
|
1060
1074
|
// BAD: Query runs even when userId is null
|
|
1061
1075
|
const { data } = useQuery(query, {}, [userId]);
|
|
1062
1076
|
```
|
|
1063
1077
|
|
|
1078
|
+
Only `useQuery` returns `enabled`. For the other hooks, track the same condition yourself rather
|
|
1079
|
+
than relying on `loading` to go false while they are disabled.
|
|
1080
|
+
|
|
1064
1081
|
### 3. Provide Type Parameters
|
|
1065
1082
|
|
|
1066
1083
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squidcloud/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.490",
|
|
4
4
|
"description": "The Squid CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=18.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@squidcloud/local-backend": "^1.0.
|
|
31
|
+
"@squidcloud/local-backend": "^1.0.490",
|
|
32
32
|
"adm-zip": "^0.5.16",
|
|
33
33
|
"copy-webpack-plugin": "^14.0.0",
|
|
34
34
|
"decompress": "^4.2.1",
|