@agent-native/core 0.168.6 → 0.168.9
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +8 -1
- package/corpus/templates/calendar/actions/delete-event.ts +34 -18
- package/corpus/templates/calendar/actions/delete-events.ts +13 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +22 -3
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +19 -0
- package/corpus/templates/calendar/server/lib/google-api.ts +18 -1
- package/corpus/templates/clips/actions/finalize-meeting.ts +16 -0
- package/corpus/templates/clips/actions/list-meetings.ts +1 -0
- package/corpus/templates/clips/actions/list-recordings.ts +14 -0
- package/corpus/templates/clips/actions/sync-calendars.ts +10 -29
- package/corpus/templates/clips/app/components/library/recording-card.tsx +2 -2
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +20 -3
- package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +12 -5
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +28 -12
- package/corpus/templates/clips/desktop/src/lib/meeting-stop.ts +16 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +19 -9
- package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +5 -0
- package/corpus/templates/factory/AGENTS.md +12 -22
- package/corpus/templates/factory/README.md +11 -8
- package/corpus/templates/factory/actions/approve-factory-item.ts +35 -8
- package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +24 -9
- package/corpus/templates/factory/actions/create-factory.ts +211 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +29 -5
- package/corpus/templates/factory/actions/get-factory-graph.ts +1 -1
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -11
- package/corpus/templates/factory/actions/get-triage-config.ts +13 -12
- package/corpus/templates/factory/actions/get-triage-item.ts +28 -6
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +33 -17
- package/corpus/templates/factory/actions/ingest-github-observation.ts +10 -4
- package/corpus/templates/factory/actions/list-factory-audit.ts +11 -4
- package/corpus/templates/factory/actions/list-factory-automations.ts +34 -12
- package/corpus/templates/factory/actions/list-triage-items.ts +11 -3
- package/corpus/templates/factory/actions/list-triage-rules.ts +11 -4
- package/corpus/templates/factory/actions/poll-github-sources.ts +24 -10
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +26 -11
- package/corpus/templates/factory/actions/poll-slack-channel.ts +31 -13
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +59 -9
- package/corpus/templates/factory/actions/record-triage-feedback.ts +14 -3
- package/corpus/templates/factory/actions/run-factory-automation.ts +12 -2
- package/corpus/templates/factory/actions/save-factory-automation.ts +82 -21
- package/corpus/templates/factory/actions/save-factory-graph.ts +10 -1
- package/corpus/templates/factory/actions/save-triage-config.ts +90 -36
- package/corpus/templates/factory/actions/save-triage-rule.ts +16 -3
- package/corpus/templates/factory/actions/start-builder-for-item.ts +28 -20
- package/corpus/templates/factory/actions/suggest-factory-rules.ts +11 -4
- package/corpus/templates/factory/actions/view-screen.ts +22 -30
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +7 -2
- package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +7 -22
- package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +260 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +2 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -3
- package/corpus/templates/factory/app/i18n/en-US.ts +18 -1
- package/corpus/templates/factory/app/routes/factory.tsx +78 -62
- package/corpus/templates/factory/app/routes/{factory-settings.tsx → new-factory.tsx} +12 -9
- package/corpus/templates/factory/server/db/schema.ts +18 -3
- package/corpus/templates/factory/server/factory-graph/contracts.ts +24 -0
- package/corpus/templates/factory/server/factory-graph/store.ts +16 -9
- package/corpus/templates/factory/server/lib/factory-automation-plan.ts +136 -0
- package/corpus/templates/factory/server/lib/factory-automation-repair.ts +31 -0
- package/corpus/templates/factory/server/lib/factory-scope.ts +416 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +11 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -2
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +80 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +142 -43
- package/corpus/templates/factory/server/triage/audit.ts +4 -0
- package/corpus/templates/factory/server/triage/ids.ts +4 -0
- package/dist/cli/native-dependencies.js +4 -1
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +9 -4
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/chat-first/apps-rail.js +6 -6
- package/dist/client/chat-first/primary-nav.js +14 -6
- package/dist/client/integrations/IntegrationsPanel.d.ts +15 -0
- package/dist/client/integrations/IntegrationsPanel.js +29 -21
- package/dist/client/integrations/index.d.ts +1 -1
- package/dist/client/integrations/index.js +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/ssr-handler.js +19 -1
- package/package.json +1 -1
|
@@ -294,6 +294,86 @@ const migrations = [
|
|
|
294
294
|
ALTER TABLE factory_config ADD COLUMN builder_slack_user_id TEXT;
|
|
295
295
|
`,
|
|
296
296
|
},
|
|
297
|
+
{
|
|
298
|
+
version: 20,
|
|
299
|
+
name: "factory-runtime-factory-id-columns",
|
|
300
|
+
sql: `
|
|
301
|
+
ALTER TABLE factory_config ADD COLUMN factory_id TEXT;
|
|
302
|
+
ALTER TABLE factory_items ADD COLUMN factory_id TEXT;
|
|
303
|
+
ALTER TABLE factory_rules ADD COLUMN factory_id TEXT;
|
|
304
|
+
ALTER TABLE factory_decisions ADD COLUMN factory_id TEXT;
|
|
305
|
+
ALTER TABLE factory_runs ADD COLUMN factory_id TEXT;
|
|
306
|
+
ALTER TABLE factory_feedback ADD COLUMN factory_id TEXT;
|
|
307
|
+
ALTER TABLE factory_audit_events ADD COLUMN factory_id TEXT;
|
|
308
|
+
`,
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
version: 21,
|
|
312
|
+
name: "factory-runtime-factory-id-backfill",
|
|
313
|
+
sql: {},
|
|
314
|
+
run: async () => {
|
|
315
|
+
const { getDbExec } = await import("@agent-native/core/db");
|
|
316
|
+
const exec = getDbExec();
|
|
317
|
+
const defaultFactoryId = "product-feedback";
|
|
318
|
+
const tables = [
|
|
319
|
+
"factory_config",
|
|
320
|
+
"factory_items",
|
|
321
|
+
"factory_rules",
|
|
322
|
+
"factory_decisions",
|
|
323
|
+
"factory_runs",
|
|
324
|
+
"factory_feedback",
|
|
325
|
+
"factory_audit_events",
|
|
326
|
+
] as const;
|
|
327
|
+
for (const table of tables) {
|
|
328
|
+
await exec.execute({
|
|
329
|
+
sql: `UPDATE ${table} SET factory_id = ? WHERE factory_id IS NULL OR factory_id = ''`,
|
|
330
|
+
args: [defaultFactoryId],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const configRows = await exec.execute({
|
|
334
|
+
sql: `SELECT id, org_id FROM factory_config WHERE factory_id = ?`,
|
|
335
|
+
args: [defaultFactoryId],
|
|
336
|
+
});
|
|
337
|
+
for (const row of configRows.rows ?? []) {
|
|
338
|
+
const orgId = String(row.org_id ?? "");
|
|
339
|
+
const id = String(row.id ?? "");
|
|
340
|
+
if (!orgId || id.includes(":")) continue;
|
|
341
|
+
const nextId = `${orgId}:${defaultFactoryId}`;
|
|
342
|
+
await exec.execute({
|
|
343
|
+
sql: `UPDATE factory_config SET id = ? WHERE id = ? AND org_id = ?`,
|
|
344
|
+
args: [nextId, id, orgId],
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
version: 22,
|
|
351
|
+
name: "factory-items-org-factory-dedupe-index",
|
|
352
|
+
sql: `
|
|
353
|
+
CREATE UNIQUE INDEX IF NOT EXISTS factory_items_org_factory_dedupe_idx
|
|
354
|
+
ON factory_items (org_id, factory_id, dedupe_key);
|
|
355
|
+
CREATE INDEX IF NOT EXISTS factory_items_org_factory_status_idx
|
|
356
|
+
ON factory_items (org_id, factory_id, status, updated_at);
|
|
357
|
+
CREATE INDEX IF NOT EXISTS factory_audit_events_org_factory_created_idx
|
|
358
|
+
ON factory_audit_events (org_id, factory_id, created_at);
|
|
359
|
+
`,
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
version: 23,
|
|
363
|
+
name: "factory-items-drop-org-dedupe-index",
|
|
364
|
+
sql: `
|
|
365
|
+
DROP INDEX IF EXISTS factory_items_org_dedupe_idx;
|
|
366
|
+
`,
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
version: 24,
|
|
370
|
+
name: "factory-config-org-slack-channel-unique",
|
|
371
|
+
sql: `
|
|
372
|
+
CREATE UNIQUE INDEX IF NOT EXISTS factory_config_org_slack_channel_idx
|
|
373
|
+
ON factory_config (org_id, slack_channel_id)
|
|
374
|
+
WHERE slack_channel_id IS NOT NULL AND slack_channel_id != '';
|
|
375
|
+
`,
|
|
376
|
+
},
|
|
297
377
|
];
|
|
298
378
|
|
|
299
379
|
export const runFactoryMigrations = runMigrations(migrations, {
|
|
@@ -3,6 +3,7 @@ import { notify } from "@agent-native/core/notifications";
|
|
|
3
3
|
import { resolveOrgIdForEmail } from "@agent-native/core/org";
|
|
4
4
|
import {
|
|
5
5
|
organizationResourceOwner,
|
|
6
|
+
resourceDeleteByPath,
|
|
6
7
|
resourceGetByPath,
|
|
7
8
|
resourcePut,
|
|
8
9
|
resourcePutIfCurrent,
|
|
@@ -12,10 +13,21 @@ import {
|
|
|
12
13
|
defineNitroPlugin,
|
|
13
14
|
runWithRequestContext,
|
|
14
15
|
} from "@agent-native/core/server";
|
|
16
|
+
import { listAutomationDefinitions } from "@agent-native/core/triggers";
|
|
15
17
|
import { and, eq, isNull, lt, ne, or } from "drizzle-orm";
|
|
16
18
|
|
|
17
19
|
import { getDb } from "../db/index.js";
|
|
18
20
|
import { triageConfig } from "../db/schema.js";
|
|
21
|
+
import { repairFactoryAutomationsFromConfig } from "../lib/factory-automation-repair.js";
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_FACTORY_ID,
|
|
24
|
+
factoryAutomationJobPath,
|
|
25
|
+
factoryConfigRowId,
|
|
26
|
+
patchAutomationResource,
|
|
27
|
+
readAutomationFactoryId,
|
|
28
|
+
readFactoryIdFromAutomationPath,
|
|
29
|
+
readTriageConfigRow,
|
|
30
|
+
} from "../lib/factory-scope.js";
|
|
19
31
|
import {
|
|
20
32
|
repairSlackFeedbackPrompt,
|
|
21
33
|
SLACK_HANDOFF_INSTRUCTION,
|
|
@@ -57,31 +69,23 @@ async function notifyFactoryAutomationFailure(
|
|
|
57
69
|
if (
|
|
58
70
|
(event.status !== "error" && event.status !== "interrupted") ||
|
|
59
71
|
!event.orgId ||
|
|
60
|
-
!event.path.startsWith("jobs/factory-")
|
|
72
|
+
(!event.path.startsWith("jobs/factory-") &&
|
|
73
|
+
!event.path.startsWith("jobs/factories/"))
|
|
61
74
|
) {
|
|
62
75
|
return;
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
const db = getDb();
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
eq(triageConfig.id, event.orgId),
|
|
77
|
-
eq(triageConfig.orgId, event.orgId),
|
|
78
|
-
),
|
|
79
|
-
)
|
|
80
|
-
.limit(1)
|
|
81
|
-
)[0];
|
|
82
|
-
if (!config || config.alertsEnabled !== 1) return;
|
|
83
|
-
|
|
84
|
-
const recipient = (config.alertEmail || config.ownerEmail || "")
|
|
79
|
+
const factoryId =
|
|
80
|
+
readFactoryIdFromAutomationPath(event.path) ?? DEFAULT_FACTORY_ID;
|
|
81
|
+
const config = await readTriageConfigRow(db, event.orgId, factoryId);
|
|
82
|
+
if (!config || config.automationFailureAlertsEnabled !== 1) return;
|
|
83
|
+
|
|
84
|
+
const recipient = (
|
|
85
|
+
config.automationFailureAlertEmail ||
|
|
86
|
+
config.ownerEmail ||
|
|
87
|
+
""
|
|
88
|
+
)
|
|
85
89
|
.trim()
|
|
86
90
|
.toLowerCase();
|
|
87
91
|
if (!recipient) return;
|
|
@@ -92,6 +96,7 @@ async function notifyFactoryAutomationFailure(
|
|
|
92
96
|
const alertKey = `${event.automation}\n${error}`.slice(0, 700);
|
|
93
97
|
const now = new Date();
|
|
94
98
|
const cutoff = new Date(now.getTime() - FAILURE_ALERT_COOLDOWN_MS);
|
|
99
|
+
const configRowId = factoryConfigRowId(event.orgId, factoryId);
|
|
95
100
|
const claimed = await db
|
|
96
101
|
.update(triageConfig)
|
|
97
102
|
.set({
|
|
@@ -100,7 +105,7 @@ async function notifyFactoryAutomationFailure(
|
|
|
100
105
|
})
|
|
101
106
|
.where(
|
|
102
107
|
and(
|
|
103
|
-
eq(triageConfig.id,
|
|
108
|
+
eq(triageConfig.id, configRowId),
|
|
104
109
|
eq(triageConfig.orgId, event.orgId),
|
|
105
110
|
eq(triageConfig.automationFailureAlertsEnabled, 1),
|
|
106
111
|
or(
|
|
@@ -116,7 +121,7 @@ async function notifyFactoryAutomationFailure(
|
|
|
116
121
|
|
|
117
122
|
const url = factoryPublicUrl();
|
|
118
123
|
const debugTarget = url
|
|
119
|
-
? `${url}/factory?tab=automations`
|
|
124
|
+
? `${url}/factory?factoryId=${encodeURIComponent(factoryId)}&tab=automations`
|
|
120
125
|
: "Factory > Automations";
|
|
121
126
|
const details = [
|
|
122
127
|
`Automation: ${event.automation}`,
|
|
@@ -440,10 +445,31 @@ function frontmatterField(content: string, key: string): string | undefined {
|
|
|
440
445
|
return value.replace(/^(\"|')|((\"|')$)/g, "");
|
|
441
446
|
}
|
|
442
447
|
|
|
448
|
+
function automationFactoryScopeInstruction(factoryId: string): string {
|
|
449
|
+
return `This automation runs for factory \`${factoryId}\`. Pass \`factoryId: "${factoryId}"\` on every Factory triage, poll, and config action in this run.`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function repairAutomationFactoryScopeInstruction(
|
|
453
|
+
content: string,
|
|
454
|
+
factoryId: string,
|
|
455
|
+
): string {
|
|
456
|
+
if (content.includes(`Pass \`factoryId: "${factoryId}"\``)) {
|
|
457
|
+
return content;
|
|
458
|
+
}
|
|
459
|
+
const end = content.indexOf("\n---", 4);
|
|
460
|
+
if (end === -1) {
|
|
461
|
+
return `${automationFactoryScopeInstruction(factoryId)}\n\n${content.trim()}\n`;
|
|
462
|
+
}
|
|
463
|
+
const insertAt = end + 4;
|
|
464
|
+
return `${content.slice(0, insertAt)}\n\n${automationFactoryScopeInstruction(factoryId)}\n${content.slice(insertAt)}`;
|
|
465
|
+
}
|
|
466
|
+
|
|
443
467
|
function automationContent(
|
|
444
468
|
ownerEmail: string,
|
|
445
469
|
orgId: string,
|
|
470
|
+
factoryId: string,
|
|
446
471
|
seed: AutomationSeed,
|
|
472
|
+
enabled = true,
|
|
447
473
|
): string {
|
|
448
474
|
const body = syncManagedReviewSkillAlignment(
|
|
449
475
|
seed.body.trim(),
|
|
@@ -451,17 +477,20 @@ function automationContent(
|
|
|
451
477
|
);
|
|
452
478
|
return `---
|
|
453
479
|
schedule: "${seed.schedule}"
|
|
454
|
-
${seed.timezone ? `timezone: ${seed.timezone}\n` : ""}enabled: true
|
|
480
|
+
${seed.timezone ? `timezone: ${seed.timezone}\n` : ""}enabled: ${enabled ? "true" : "false"}
|
|
455
481
|
triggerType: schedule
|
|
456
482
|
domain: factory
|
|
457
483
|
appId: factory
|
|
458
484
|
orgId: ${orgId}
|
|
485
|
+
factoryId: ${factoryId}
|
|
459
486
|
createdBy: ${ownerEmail}
|
|
460
487
|
runAs: creator
|
|
461
488
|
model: ${seed.model}
|
|
462
489
|
maxIterations: ${seed.maxIterations}
|
|
463
490
|
maxRunInputTokens: ${seed.maxRunInputTokens}
|
|
464
491
|
---
|
|
492
|
+
${automationFactoryScopeInstruction(factoryId)}
|
|
493
|
+
|
|
465
494
|
${body.trim()}
|
|
466
495
|
`;
|
|
467
496
|
}
|
|
@@ -480,20 +509,26 @@ async function disableLegacyObserver(): Promise<void> {
|
|
|
480
509
|
}
|
|
481
510
|
}
|
|
482
511
|
|
|
483
|
-
async function
|
|
512
|
+
export async function ensureFactoryAutomations(
|
|
484
513
|
ownerEmail: string,
|
|
485
514
|
orgId: string,
|
|
515
|
+
factoryId: string,
|
|
516
|
+
options?: { enabled?: boolean; enabledNames?: ReadonlySet<string> },
|
|
486
517
|
): Promise<void> {
|
|
487
518
|
const owner = organizationResourceOwner(orgId);
|
|
519
|
+
const defaultEnabled = options?.enabled ?? false;
|
|
488
520
|
await Promise.all(
|
|
489
521
|
AUTOMATION_SEEDS.map(async (seed) => {
|
|
490
|
-
const
|
|
522
|
+
const enabled = options?.enabledNames
|
|
523
|
+
? options.enabledNames.has(seed.name)
|
|
524
|
+
: defaultEnabled;
|
|
525
|
+
const path = factoryAutomationJobPath(factoryId, seed.name);
|
|
491
526
|
const existing = await resourceGetByPath(owner, path);
|
|
492
527
|
if (!existing) {
|
|
493
528
|
await resourcePut(
|
|
494
529
|
owner,
|
|
495
530
|
path,
|
|
496
|
-
automationContent(ownerEmail, orgId, seed),
|
|
531
|
+
automationContent(ownerEmail, orgId, factoryId, seed, enabled),
|
|
497
532
|
"text/markdown",
|
|
498
533
|
);
|
|
499
534
|
return;
|
|
@@ -507,6 +542,7 @@ async function ensureOrganizationAutomations(
|
|
|
507
542
|
repaired = setFrontmatterField(repaired, "domain", "factory");
|
|
508
543
|
repaired = setFrontmatterField(repaired, "appId", "factory");
|
|
509
544
|
repaired = setFrontmatterField(repaired, "orgId", orgId);
|
|
545
|
+
repaired = setFrontmatterField(repaired, "factoryId", factoryId);
|
|
510
546
|
repaired = setFrontmatterField(repaired, "createdBy", ownerEmail);
|
|
511
547
|
repaired = setFrontmatterField(repaired, "runAs", "creator");
|
|
512
548
|
if (!frontmatterField(repaired, "model")) {
|
|
@@ -552,6 +588,7 @@ async function ensureOrganizationAutomations(
|
|
|
552
588
|
if (seed.name === "factory-slack-feedback") {
|
|
553
589
|
repaired = repairSlackFeedbackPrompt(repaired);
|
|
554
590
|
}
|
|
591
|
+
repaired = repairAutomationFactoryScopeInstruction(repaired, factoryId);
|
|
555
592
|
if (repaired === existing.content) return;
|
|
556
593
|
|
|
557
594
|
const updated = await resourcePutIfCurrent({
|
|
@@ -572,33 +609,95 @@ async function ensureOrganizationAutomations(
|
|
|
572
609
|
);
|
|
573
610
|
}
|
|
574
611
|
|
|
612
|
+
export async function syncFactoryAutomationEnabledStates(
|
|
613
|
+
ownerEmail: string,
|
|
614
|
+
orgId: string,
|
|
615
|
+
factoryId: string,
|
|
616
|
+
enabledNames: readonly string[],
|
|
617
|
+
): Promise<void> {
|
|
618
|
+
const enabledSet = new Set(enabledNames);
|
|
619
|
+
const definitions = await listAutomationDefinitions(
|
|
620
|
+
{ userEmail: ownerEmail, orgId, appId: "factory" },
|
|
621
|
+
"organization",
|
|
622
|
+
);
|
|
623
|
+
await Promise.all(
|
|
624
|
+
definitions
|
|
625
|
+
.filter(
|
|
626
|
+
(definition) =>
|
|
627
|
+
readAutomationFactoryId(
|
|
628
|
+
definition.meta,
|
|
629
|
+
definition.resource.content,
|
|
630
|
+
) === factoryId,
|
|
631
|
+
)
|
|
632
|
+
.map(async (definition) => {
|
|
633
|
+
const resource = await resourceGetByPath(
|
|
634
|
+
definition.resource.owner,
|
|
635
|
+
definition.resource.path,
|
|
636
|
+
);
|
|
637
|
+
if (!resource) return;
|
|
638
|
+
const leafName =
|
|
639
|
+
definition.resource.path.match(/([^/]+)\.md$/)?.[1] ??
|
|
640
|
+
definition.name;
|
|
641
|
+
const enabled = enabledSet.has(leafName);
|
|
642
|
+
const content = patchAutomationResource(resource.content, { enabled });
|
|
643
|
+
if (content === resource.content) return;
|
|
644
|
+
const updated = await resourcePutIfCurrent({
|
|
645
|
+
owner: definition.resource.owner,
|
|
646
|
+
path: definition.resource.path,
|
|
647
|
+
content,
|
|
648
|
+
mimeType: "text/markdown",
|
|
649
|
+
expectedId: resource.id,
|
|
650
|
+
expectedUpdatedAt: resource.updatedAt,
|
|
651
|
+
expectedContent: resource.content,
|
|
652
|
+
});
|
|
653
|
+
if (!updated) {
|
|
654
|
+
console.warn(
|
|
655
|
+
`[factory-scheduler-job] skipped enabled-state sync for ${definition.resource.path}: the resource changed concurrently`,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}),
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export async function removeFactoryAutomationResources(
|
|
663
|
+
orgId: string,
|
|
664
|
+
factoryId: string,
|
|
665
|
+
): Promise<void> {
|
|
666
|
+
const owner = organizationResourceOwner(orgId);
|
|
667
|
+
await Promise.all(
|
|
668
|
+
AUTOMATION_SEEDS.map(async (seed) => {
|
|
669
|
+
await resourceDeleteByPath(
|
|
670
|
+
owner,
|
|
671
|
+
factoryAutomationJobPath(factoryId, seed.name),
|
|
672
|
+
);
|
|
673
|
+
}),
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
575
677
|
async function ensureDefaultTriageConfig(
|
|
576
678
|
ownerEmail: string,
|
|
577
679
|
orgId: string,
|
|
578
680
|
): Promise<void> {
|
|
579
681
|
const db = getDb();
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
.where(and(eq(triageConfig.id, orgId), eq(triageConfig.orgId, orgId)))
|
|
587
|
-
.limit(1)
|
|
588
|
-
)[0];
|
|
589
|
-
const repository = defaultRepository();
|
|
590
|
-
// Existing rows are operator-owned. An empty repository plus disabled
|
|
591
|
-
// polling can be an intentional choice, so do not infer bootstrap state.
|
|
592
|
-
if (existing) return;
|
|
682
|
+
const factoryId = DEFAULT_FACTORY_ID;
|
|
683
|
+
const existing = await readTriageConfigRow(db, orgId, factoryId);
|
|
684
|
+
if (existing) {
|
|
685
|
+
await repairFactoryAutomationsFromConfig(ownerEmail, orgId, factoryId);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
593
688
|
const now = new Date().toISOString();
|
|
689
|
+
const repository = defaultRepository();
|
|
690
|
+
const pollingEnabled = 1;
|
|
691
|
+
const githubPollingEnabled = defaultGithubPollingEnabled() ? 1 : 0;
|
|
594
692
|
await db.insert(triageConfig).values({
|
|
595
|
-
id: orgId,
|
|
693
|
+
id: factoryConfigRowId(orgId, factoryId),
|
|
694
|
+
factoryId,
|
|
596
695
|
slackWorkspace: "primary",
|
|
597
696
|
slackChannelId: DEFAULT_SLACK_CHANNEL_ID,
|
|
598
697
|
slackChannelName: DEFAULT_SLACK_CHANNEL_NAME,
|
|
599
698
|
builderSlackUserId: null,
|
|
600
|
-
pollingEnabled
|
|
601
|
-
githubPollingEnabled
|
|
699
|
+
pollingEnabled,
|
|
700
|
+
githubPollingEnabled,
|
|
602
701
|
sentryPollingEnabled: 0,
|
|
603
702
|
lastSlackTs: null,
|
|
604
703
|
slackHistoryCursor: null,
|
|
@@ -612,6 +711,7 @@ async function ensureDefaultTriageConfig(
|
|
|
612
711
|
ownerEmail,
|
|
613
712
|
orgId,
|
|
614
713
|
});
|
|
714
|
+
await repairFactoryAutomationsFromConfig(ownerEmail, orgId, factoryId);
|
|
615
715
|
}
|
|
616
716
|
|
|
617
717
|
async function ensureSchedulerJobs(): Promise<void> {
|
|
@@ -636,7 +736,6 @@ async function ensureSchedulerJobs(): Promise<void> {
|
|
|
636
736
|
orgId = existingConfig.orgId?.trim() || existingConfig.id;
|
|
637
737
|
}
|
|
638
738
|
await ensureDefaultTriageConfig(ownerEmail, orgId);
|
|
639
|
-
await ensureOrganizationAutomations(ownerEmail, orgId);
|
|
640
739
|
await disableLegacyObserver();
|
|
641
740
|
}
|
|
642
741
|
|
|
@@ -21,6 +21,7 @@ export interface FactoryAuditInput {
|
|
|
21
21
|
action: string;
|
|
22
22
|
kind: FactoryAuditKind;
|
|
23
23
|
status?: FactoryAuditStatus;
|
|
24
|
+
factoryId?: string | null;
|
|
24
25
|
itemId?: string | null;
|
|
25
26
|
source?: string | null;
|
|
26
27
|
sourceUrl?: string | null;
|
|
@@ -55,8 +56,10 @@ export async function recordFactoryAudit(
|
|
|
55
56
|
context: ActionRunContext | undefined,
|
|
56
57
|
identity: { userEmail: string; orgId: string },
|
|
57
58
|
input: FactoryAuditInput,
|
|
59
|
+
factoryId?: string | null,
|
|
58
60
|
): Promise<void> {
|
|
59
61
|
if (context?.caller !== "automation" || !context.runId) return;
|
|
62
|
+
const resolvedFactoryId = factoryId ?? input.factoryId ?? null;
|
|
60
63
|
await getDb()
|
|
61
64
|
.insert(factoryAuditEvents)
|
|
62
65
|
.values({
|
|
@@ -64,6 +67,7 @@ export async function recordFactoryAudit(
|
|
|
64
67
|
automationRunId: context.runId,
|
|
65
68
|
automationThreadId: context.threadId ?? null,
|
|
66
69
|
automationName: context.automation?.triggerName ?? null,
|
|
70
|
+
factoryId: resolvedFactoryId,
|
|
67
71
|
itemId: input.itemId ?? null,
|
|
68
72
|
source: input.source ?? null,
|
|
69
73
|
sourceUrl: input.sourceUrl ?? null,
|
|
@@ -13,9 +13,13 @@ export function itemDedupeKey(
|
|
|
13
13
|
headSha?: string;
|
|
14
14
|
},
|
|
15
15
|
organizationId = "",
|
|
16
|
+
factoryId = "",
|
|
16
17
|
): string {
|
|
18
|
+
const factorySegment =
|
|
19
|
+
factoryId && factoryId !== "product-feedback" ? factoryId : "";
|
|
17
20
|
return stableId(
|
|
18
21
|
organizationId,
|
|
22
|
+
factorySegment,
|
|
19
23
|
input.source,
|
|
20
24
|
input.externalId,
|
|
21
25
|
input.repository ?? "",
|
|
@@ -58,7 +58,10 @@ function probeBetterSqlite3(packageDir) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
export function checkNativeDependencies(fromDirectory = corePackageDirectory()) {
|
|
61
|
-
const packageDir = resolvePackageDirectory(BETTER_SQLITE3, fromDirectory)
|
|
61
|
+
const packageDir = resolvePackageDirectory(BETTER_SQLITE3, fromDirectory) ??
|
|
62
|
+
(path.resolve(fromDirectory) === corePackageDirectory()
|
|
63
|
+
? null
|
|
64
|
+
: resolvePackageDirectory(BETTER_SQLITE3, corePackageDirectory()));
|
|
62
65
|
if (!packageDir) {
|
|
63
66
|
return { status: "absent", packageName: BETTER_SQLITE3 };
|
|
64
67
|
}
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* <AgentChatSurface mode="page" className="h-screen" />
|
|
22
22
|
*/
|
|
23
23
|
import React from "react";
|
|
24
|
+
/** Start loading the desktop chat surface before a sidebar is opened. */
|
|
25
|
+
export declare function preloadAgentChatSurface(): Promise<void>;
|
|
24
26
|
import type { AgentChatSurfaceKind } from "./agent-chat-adapter.js";
|
|
25
27
|
import type { AssistantChatProps } from "./AssistantChat.js";
|
|
26
28
|
import type { MultiTabAssistantChatHeaderProps, MultiTabAssistantChatProps } from "./MultiTabAssistantChat.js";
|
|
@@ -223,6 +225,8 @@ export interface AgentSidebarProps {
|
|
|
223
225
|
chatViewTransitionHandoff?: boolean;
|
|
224
226
|
/** Namespace for persisted chat state. Use the same key as AgentChatHome. */
|
|
225
227
|
storageKey?: string;
|
|
228
|
+
/** Restore the previously active chat thread on mount. Default: true. */
|
|
229
|
+
restoreActiveThread?: boolean;
|
|
226
230
|
/** Namespace for the persisted open/closed preference. Defaults to storageKey. */
|
|
227
231
|
openStorageKey?: string;
|
|
228
232
|
/** API base URL used by the chat surface. */
|
|
@@ -262,7 +266,7 @@ export interface AgentSidebarProps {
|
|
|
262
266
|
* Wraps app content with a toggleable agent sidebar.
|
|
263
267
|
* Use AgentToggleButton in your header to open/close it.
|
|
264
268
|
*/
|
|
265
|
-
export declare function AgentSidebar({ children, enabled, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, chatViewTransitionHandoff, storageKey, openStorageKey, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning, onFullscreenRequest, scope, isolateHistoryByScope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding, }: AgentSidebarProps): React.JSX.Element;
|
|
269
|
+
export declare function AgentSidebar({ children, enabled, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, chatViewTransitionHandoff, storageKey, openStorageKey, restoreActiveThread, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning, onFullscreenRequest, scope, isolateHistoryByScope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding, }: AgentSidebarProps): React.JSX.Element;
|
|
266
270
|
/**
|
|
267
271
|
* Focus the agent chat composer input.
|
|
268
272
|
* Opens the sidebar if closed, then focuses the text input.
|
|
@@ -36,9 +36,14 @@ import { ShareButton } from "./sharing/ShareButton.js";
|
|
|
36
36
|
// assistant-ui + zod block schemas) so it is NOT in the static import closure of
|
|
37
37
|
// every page. The header/tab chrome renders immediately; chat streams in once the
|
|
38
38
|
// chunk lands (~650-700 KB gzip saved from the critical path).
|
|
39
|
-
const
|
|
39
|
+
const loadMultiTabAssistantChat = () => import("./MultiTabAssistantChat.js").then((m) => ({
|
|
40
40
|
default: m.MultiTabAssistantChat,
|
|
41
|
-
}))
|
|
41
|
+
}));
|
|
42
|
+
const MultiTabAssistantChatLazy = lazy(loadMultiTabAssistantChat);
|
|
43
|
+
/** Start loading the desktop chat surface before a sidebar is opened. */
|
|
44
|
+
export function preloadAgentChatSurface() {
|
|
45
|
+
return loadMultiTabAssistantChat().then(() => undefined);
|
|
46
|
+
}
|
|
42
47
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
43
48
|
import { Link, useLocation, useNavigate } from "react-router";
|
|
44
49
|
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
@@ -1418,7 +1423,7 @@ export function AgentChatSurface({ mode = "panel", className, defaultMode = "cha
|
|
|
1418
1423
|
* Wraps app content with a toggleable agent sidebar.
|
|
1419
1424
|
* Use AgentToggleButton in your header to open/close it.
|
|
1420
1425
|
*/
|
|
1421
|
-
export function AgentSidebar({ children, enabled = true, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openStorageKey, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar = true, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning = false, onFullscreenRequest, scope, isolateHistoryByScope = false, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, }) {
|
|
1426
|
+
export function AgentSidebar({ children, enabled = true, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, availableAgents, availableModels, modelListLoading, selectedAgent, onAgentChange, onConnectLocalRuntime, onConnectProvider, runtime, adapterReloadKey, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openStorageKey, restoreActiveThread = true, apiUrl, agentChatSurface, desktopIdentityUnauthenticated, desktopIdentityAuthenticated, showTabBar = true, suppressInlineOpenApp, composerPlaceholder, openOnChatRunning = false, onFullscreenRequest, scope, isolateHistoryByScope = false, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, }) {
|
|
1422
1427
|
const staticHostedHarnessEnabled = isHostedHarnessConfigured(injectedAgentNativeConfig().harness);
|
|
1423
1428
|
const hostedHarnessQuery = useActionQuery("get-hosted-harness-config", undefined, { enabled: staticHostedHarnessEnabled });
|
|
1424
1429
|
const hostedHarnessStatus = hostedHarnessQuery.data;
|
|
@@ -2062,7 +2067,7 @@ export function AgentSidebar({ children, enabled = true, emptyStateText = "How c
|
|
|
2062
2067
|
? "desktop"
|
|
2063
2068
|
: undefined, "data-agent-sidebar-layout": panelLayout, "data-agent-sidebar-position": effectivePosition, "data-agent-native-hosted-harness-ui": hostedHarnessUi ? "desktop" : undefined, "data-agent-sidebar-state": panelOpen ? "open" : "closed", "data-agent-sidebar-per-app-chat": isPerAppChatSidebar ? "true" : undefined, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, "data-agent-sidebar-chat-handoff": chatViewTransitionHandoff ? "true" : undefined, style: chatViewTransition
|
|
2064
2069
|
? getAgentChatViewTransitionStyle(panelStyle)
|
|
2065
|
-
: panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, availableAgents: effectiveAvailableAgents, availableModels: availableModels, modelListLoading: modelListLoading, selectedAgent: effectiveSelectedAgent, onAgentChange: effectiveOnAgentChange, hostedHarness: hostedHarnessEnabled, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, runtime: runtime, adapterReloadKey: adapterReloadKey, threadFooterSlot: threadFooterSlot, apiUrl: apiUrl, agentChatSurface: agentChatSurface, desktopIdentityUnauthenticated: desktopIdentityUnauthenticated, desktopIdentityAuthenticated: desktopIdentityAuthenticated, showTabBar: effectiveShowTabBar, suppressInlineOpenApp: suppressInlineOpenApp, composerPlaceholder: composerPlaceholder, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, onFullViewRequest: onFullscreenRequest, storageKey: storageKey, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: effectivePosition, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
|
|
2070
|
+
: panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, availableAgents: effectiveAvailableAgents, availableModels: availableModels, modelListLoading: modelListLoading, selectedAgent: effectiveSelectedAgent, onAgentChange: effectiveOnAgentChange, hostedHarness: hostedHarnessEnabled, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, runtime: runtime, adapterReloadKey: adapterReloadKey, threadFooterSlot: threadFooterSlot, apiUrl: apiUrl, agentChatSurface: agentChatSurface, desktopIdentityUnauthenticated: desktopIdentityUnauthenticated, desktopIdentityAuthenticated: desktopIdentityAuthenticated, showTabBar: effectiveShowTabBar, suppressInlineOpenApp: suppressInlineOpenApp, composerPlaceholder: composerPlaceholder, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, onFullViewRequest: onFullscreenRequest, storageKey: storageKey, restoreActiveThread: restoreActiveThread, scope: scope, isolateHistoryByScope: isolateHistoryByScope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: effectivePosition, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
|
|
2066
2071
|
const drawerPlaceholder = wideDrawerEnabled && !presentationMode && panelOpen ? (_jsx("div", { "aria-hidden": "true", className: "agent-sidebar-drawer-placeholder shrink-0", "data-agent-sidebar-placeholder": "true", style: { width: drawerPlaceholderWidth + 1 } })) : null;
|
|
2067
2072
|
return (_jsx(AgentSidebarOnboardingContext.Provider, { value: true, children: _jsxs(RealtimeVoiceModeProvider, { browserTabId: browserTabId, children: [showFirstRunOnboarding && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsxs("div", { className: "agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden", "data-agent-sidebar-position": effectivePosition, "data-agent-native-hosted-harness-ui": hostedHarnessUi ? "desktop" : undefined, "data-agent-native-hosted-chat": isPerAppChatHosted ? "true" : undefined, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: [isMobile &&
|
|
2068
2073
|
!isPerAppChatHosted &&
|
|
@@ -32,7 +32,7 @@ export { GuidedQuestionFlow, useGuidedQuestionFlow, askUserQuestion, formatGuide
|
|
|
32
32
|
export { useChatThreads, type ChatThreadScope, type ChatThreadSnapshot, type ChatThreadSummary, type ChatThreadData, type ChatThreadShareLink, type ChatThreadShareState, type UseChatThreadsOptions, } from "../use-chat-threads.js";
|
|
33
33
|
export { ChatHistoryList, type ChatHistoryItem, type ChatHistorySection, type ChatHistoryListProps, } from "../chat/ChatHistoryList.js";
|
|
34
34
|
export { AgentChatHome, type AgentChatHomeProps } from "../AgentChatHome.js";
|
|
35
|
-
export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, type AgentChatSurfaceMode, type AgentChatSurfaceProps, type AgentPanelProps, type AgentSidebarProps, } from "../AgentPanel.js";
|
|
35
|
+
export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, preloadAgentChatSurface, type AgentChatSurfaceMode, type AgentChatSurfaceProps, type AgentPanelProps, type AgentSidebarProps, } from "../AgentPanel.js";
|
|
36
36
|
export { AgentTabsPage, ConnectionsTab, type AgentPageExtraTabContext, type AgentPageExtraTabFactory, type AgentTabsPageProps, } from "../agent-page/AgentTabsPage.js";
|
|
37
37
|
export type { AgentPageScope, AgentPageTabProps } from "../agent-page/types.js";
|
|
38
38
|
export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, type AgentChatHomeHandoffOptions, type AgentChatViewTransition, type AgentChatViewTransitionOptions, } from "../chat-view-transition.js";
|
|
@@ -32,7 +32,7 @@ export { GuidedQuestionFlow, useGuidedQuestionFlow, askUserQuestion, formatGuide
|
|
|
32
32
|
export { useChatThreads, } from "../use-chat-threads.js";
|
|
33
33
|
export { ChatHistoryList, } from "../chat/ChatHistoryList.js";
|
|
34
34
|
export { AgentChatHome } from "../AgentChatHome.js";
|
|
35
|
-
export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, } from "../AgentPanel.js";
|
|
35
|
+
export { AgentChatSurface, AgentPanel, AgentSidebar, AgentToggleButton, focusAgentChat, preloadAgentChatSurface, } from "../AgentPanel.js";
|
|
36
36
|
export { AgentTabsPage, ConnectionsTab, } from "../agent-page/AgentTabsPage.js";
|
|
37
37
|
export { AGENT_CHAT_HOME_HANDOFF_TTL_MS, AGENT_CHAT_VIEW_TRANSITION_CLASS, AGENT_CHAT_VIEW_TRANSITION_NAME, consumeAgentChatHomeHandoff, getAgentChatViewTransitionStyle, isAgentChatHomeHandoffActive, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, startAgentChatViewTransition, supportsAgentChatViewTransition, } from "../chat-view-transition.js";
|
|
38
38
|
export { useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, } from "../use-agent-chat-home-handoff.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
2
|
+
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
3
3
|
import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, IconTrash, } from "@tabler/icons-react";
|
|
4
4
|
import { memo, useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { CHAT_FIRST_DEFAULT_APP_IDS, orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
|
|
@@ -133,11 +133,11 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, default
|
|
|
133
133
|
persistLayout({ ...layout, orderedIds: nextOrder });
|
|
134
134
|
}
|
|
135
135
|
if (collapsed) {
|
|
136
|
-
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "flex flex-col items-center gap-1 px-1.5 pt-2", "aria-label": copy("workspaceApps"), children: [loading && apps.length === 0
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
return (_jsx(TooltipProvider, { children: _jsxs("section", { "data-chat-first-apps-rail": true, className: "flex flex-col items-center gap-1 px-1.5 pt-2", "aria-label": copy("workspaceApps"), children: [loading && apps.length === 0
|
|
137
|
+
? [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "size-9 rounded-md" }, index)))
|
|
138
|
+
: visibleApps.map((app) => (_jsxs(Tooltip, { delayDuration: 0, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md", activeAppId === app.id
|
|
139
|
+
? "text-sidebar-foreground"
|
|
140
|
+
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), children: _jsx(ChatFirstRailAppIcon, { app: app, activeAppId: activeAppId, renderIcon: renderIcon }) }) }), _jsx(TooltipContent, { side: "right", children: app.name })] }, app.id))), onOpenAllApps ? (_jsxs(Tooltip, { delayDuration: 0, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) }) }), _jsx(TooltipContent, { side: "right", children: copy("allApps") })] })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }) }));
|
|
141
141
|
}
|
|
142
142
|
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, defaultAppIds: defaultAppIds, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onRemoveApp: onRemoveApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-2 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
143
143
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
2
3
|
import { IconClock, IconPlus, IconPlugConnected, IconSearch, } from "@tabler/icons-react";
|
|
3
4
|
import { defaultChatFirstCopy } from "./copy.js";
|
|
4
5
|
export function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOpenScheduled, onSearch, activeTab, collapsed = false, stickyNewChat = false, copy = defaultChatFirstCopy, }) {
|
|
@@ -10,15 +11,22 @@ export function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOp
|
|
|
10
11
|
: tab === "integrations"
|
|
11
12
|
? copy("integrations")
|
|
12
13
|
: copy("scheduled");
|
|
13
|
-
const renderTab = (tab, content, onSelect, className) =>
|
|
14
|
+
const renderTab = (tab, content, onSelect, className) => {
|
|
15
|
+
const label = tabLabel(tab);
|
|
16
|
+
const control = (_jsx("button", { type: "button", role: "tab", "aria-selected": activeTab === tab, "aria-label": collapsed || tab === "new-chat" ? label : undefined, className: [tabClassName(tab), className].filter(Boolean).join(" "), onClick: onSelect, children: content }));
|
|
17
|
+
return collapsed ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: control }), _jsx(TooltipContent, { side: "right", children: label })] })) : (control);
|
|
18
|
+
};
|
|
14
19
|
const newChatTab = onNewChat
|
|
15
20
|
? renderTab("new-chat", _jsxs(_Fragment, { children: [_jsx(IconPlus, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: collapsed ? "sr-only" : undefined, children: copy("newChat") })] }), onNewChat, "code-agents-primary-new-chat")
|
|
16
21
|
: null;
|
|
17
22
|
const integrationsTab = renderTab("integrations", _jsxs(_Fragment, { children: [_jsx(IconPlugConnected, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: collapsed ? "sr-only" : undefined, children: copy("integrations") })] }), onOpenIntegrations);
|
|
18
23
|
const scheduledTab = renderTab("scheduled", _jsxs(_Fragment, { children: [_jsx(IconClock, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: collapsed ? "sr-only" : undefined, children: copy("scheduled") })] }), onOpenScheduled);
|
|
19
|
-
const searchAction = onSearch
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const searchAction = onSearch
|
|
25
|
+
? (() => {
|
|
26
|
+
const label = copy("search");
|
|
27
|
+
const control = (_jsxs("button", { type: "button", "aria-label": collapsed ? label : undefined, className: `mt-px flex h-8 w-full items-center gap-2 rounded-md text-[13px] font-medium text-sidebar-foreground/80 transition-[background-color,color] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${collapsed ? "justify-center px-0" : "px-2"}`, onClick: onSearch, children: [_jsx(IconSearch, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: collapsed ? "sr-only" : undefined, children: label })] }));
|
|
28
|
+
return collapsed ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: control }), _jsx(TooltipContent, { side: "right", children: label })] })) : (control);
|
|
29
|
+
})()
|
|
30
|
+
: null;
|
|
31
|
+
return (_jsx(TooltipProvider, { delayDuration: 0, children: stickyNewChat ? (_jsxs(_Fragment, { children: [newChatTab ? (_jsx("div", { className: "code-agents-primary-new-chat-shell", children: newChatTab })) : null, _jsxs("div", { className: "code-agents-nav-list", "aria-label": "Agent navigation", children: [_jsxs("div", { role: "tablist", "aria-label": "Primary navigation", className: "grid gap-px", children: [integrationsTab, scheduledTab] }), searchAction] })] })) : (_jsxs("div", { children: [_jsxs("div", { role: "tablist", "aria-label": "Primary navigation", className: "grid gap-px", children: [newChatTab, integrationsTab, scheduledTab] }), searchAction] })) }));
|
|
24
32
|
}
|