diff --git a/internal/guard/app/server/policy.go b/internal/guard/app/server/policy.go index 585104d..5b91071 100644 --- a/internal/guard/app/server/policy.go +++ b/internal/guard/app/server/policy.go @@ -39,16 +39,6 @@ func (p staticPolicyConfigProvider) ActivePolicyConfig(context.Context) (guardpo return p.config, nil } -func NewRiskPolicyProvider() RiskPolicyProvider { - return NewRiskPolicyProviderWithJudge(nil) -} - -func NewRiskPolicyProviderWithJudge(localJudge judge.Judge) RiskPolicyProvider { - return NewRiskPolicyProviderWithOptions(RiskPolicyProviderOptions{ - Judge: localJudge, - }) -} - func NewRiskPolicyProviderWithOptions(opts RiskPolicyProviderOptions) RiskPolicyProvider { configProvider := opts.PolicyConfigProvider if configProvider == nil { diff --git a/internal/guard/app/server/server.go b/internal/guard/app/server/server.go index cc162f2..697d90f 100644 --- a/internal/guard/app/server/server.go +++ b/internal/guard/app/server/server.go @@ -93,21 +93,6 @@ func NewServerWithOptions(store *sqlite.Store, opts Options) (*Server, error) { }), policyStore, opts) } -// NewServerWithPolicy creates a Guard server with an injected policy provider. -// A nil interface uses the default local risk policy; callers must not pass a -// typed-nil provider because it still satisfies the PolicyProvider interface. -func NewServerWithPolicy(store *sqlite.Store, policy PolicyProvider) (*Server, error) { - policyStore, err := openPolicyStoreForSQLite(store) - if err != nil { - return nil, err - } - return NewServerWithPolicyConfig(store, policy, policyStore) -} - -func NewServerWithPolicyConfig(store *sqlite.Store, policy PolicyProvider, policyStore *policyconfig.Store) (*Server, error) { - return NewServerWithPolicyConfigAndOptions(store, policy, policyStore, Options{}) -} - func NewServerWithPolicyConfigAndOptions(store *sqlite.Store, policy PolicyProvider, policyStore *policyconfig.Store, opts Options) (*Server, error) { if policyStore == nil { var err error @@ -461,10 +446,6 @@ func openPolicyStoreForSQLite(store *sqlite.Store) (*policyconfig.Store, error) return policyconfig.Open(filepath.Dir(store.Path())) } -func OpenDefaultServer(dbPath string) (*Server, func() error, error) { - return OpenDefaultServerWithOptions(dbPath, Options{}) -} - func OpenDefaultServerWithOptions(dbPath string, opts Options) (*Server, func() error, error) { store, err := sqlite.OpenStore(dbPath) if err != nil { diff --git a/internal/guard/app/server/server_test.go b/internal/guard/app/server/server_test.go index df14c7e..9ae27f1 100644 --- a/internal/guard/app/server/server_test.go +++ b/internal/guard/app/server/server_test.go @@ -29,18 +29,22 @@ func newTestServer(t *testing.T, store *sqlite.Store) *Server { func newTestServerWithPolicy(t *testing.T, store *sqlite.Store, policy PolicyProvider) *Server { t.Helper() - server, err := NewServerWithPolicy(store, policy) + policyStore, err := openPolicyStoreForSQLite(store) if err != nil { - t.Fatalf("NewServerWithPolicy() error = %v", err) + t.Fatalf("openPolicyStoreForSQLite() error = %v", err) + } + server, err := NewServerWithPolicyConfigAndOptions(store, policy, policyStore, Options{}) + if err != nil { + t.Fatalf("NewServerWithPolicyConfigAndOptions() error = %v", err) } return server } func newTestServerWithPolicyConfig(t *testing.T, store *sqlite.Store, policyStore *policyconfig.Store) *Server { t.Helper() - server, err := NewServerWithPolicyConfig(store, NewRiskPolicyProvider(), policyStore) + server, err := NewServerWithPolicyConfigAndOptions(store, nil, policyStore, Options{}) if err != nil { - t.Fatalf("NewServerWithPolicyConfig() error = %v", err) + t.Fatalf("NewServerWithPolicyConfigAndOptions() error = %v", err) } return server } diff --git a/internal/guard/store/sqlite/ledger.go b/internal/guard/store/sqlite/ledger.go index 1728676..0b42da4 100644 --- a/internal/guard/store/sqlite/ledger.go +++ b/internal/guard/store/sqlite/ledger.go @@ -179,7 +179,7 @@ func ledgerCursor(actions []LedgerRecord) (*LedgerCursor, error) { if updatedAtKey == "" || actionID == "" { return nil, nil } - updatedAt, err := parseLedgerTimestamp(updatedAtKey) + updatedAt, err := ParseLedgerTimestamp(updatedAtKey) if err != nil { return nil, err } @@ -309,7 +309,7 @@ func normalizeLedgerRecord(record LedgerRecord) { } func normalizeLedgerTimeValue(column, value string, fallback time.Time) any { - if parsed, err := parseLedgerTimestamp(value); err == nil { + if parsed, err := ParseLedgerTimestamp(value); err == nil { return parsed.UTC().Format(time.RFC3339Nano) } if isRequiredLedgerTimeColumn(column) { @@ -321,7 +321,7 @@ func normalizeLedgerTimeValue(column, value string, fallback time.Time) any { func ledgerRecordFallbackTime(record LedgerRecord) time.Time { for _, column := range []string{"updated_at", "created_at"} { value, _ := record[column].(string) - if parsed, err := parseLedgerTimestamp(value); err == nil { + if parsed, err := ParseLedgerTimestamp(value); err == nil { return parsed.UTC() } } @@ -347,7 +347,9 @@ func isRequiredLedgerTimeColumn(column string) bool { return column == "created_at" || column == "updated_at" } -func parseLedgerTimestamp(value string) (time.Time, error) { +// ParseLedgerTimestamp accepts the RFC3339Nano timestamps we write today plus +// the legacy layouts that may still exist in SQLite rows or persisted cursors. +func ParseLedgerTimestamp(value string) (time.Time, error) { if parsed, err := time.Parse(time.RFC3339Nano, value); err == nil { return parsed, nil } @@ -370,7 +372,7 @@ func ledgerTimestampCursorKeyFromTime(value time.Time) string { func ledgerTimestampCursorKeyFromValues(values ...string) string { for _, value := range values { - parsed, err := parseLedgerTimestamp(value) + parsed, err := ParseLedgerTimestamp(value) if err == nil { return ledgerTimestampCursorKeyFromTime(parsed) } diff --git a/internal/hook/domain.go b/internal/hook/domain.go index de12a7e..2ad5a16 100644 --- a/internal/hook/domain.go +++ b/internal/hook/domain.go @@ -1,6 +1,10 @@ package hook -import "strings" +import ( + "strings" + + "github.com/kontext-security/kontext-cli/internal/guard/decision" +) type HookName string @@ -30,13 +34,13 @@ func (h HookName) CanBlock() bool { return h == HookPreToolUse } -type Decision string - const ( - DecisionAllow Decision = "allow" - DecisionDeny Decision = "deny" + DecisionAllow = decision.Allow + DecisionDeny = decision.Deny ) +type Decision = decision.Decision + func NormalizeDecision(value string) (Decision, bool) { switch strings.ToLower(strings.TrimSpace(value)) { case string(DecisionAllow): diff --git a/internal/managedstream/stream.go b/internal/managedstream/stream.go index 679065b..53895a7 100644 --- a/internal/managedstream/stream.go +++ b/internal/managedstream/stream.go @@ -127,7 +127,7 @@ func Flush(ctx context.Context, opts Options) error { var updatedAfter *time.Time if state.UpdatedAfter != "" { - parsed, err := parseStateUpdatedAfter(state.UpdatedAfter) + parsed, err := sqlite.ParseLedgerTimestamp(state.UpdatedAfter) if err != nil { return fmt.Errorf("parse managed stream state: %w", err) } @@ -325,23 +325,6 @@ func saveCursor(statePath string, batch sqlite.LedgerBatch) error { }) } -func parseStateUpdatedAfter(value string) (time.Time, error) { - if parsed, err := time.Parse(time.RFC3339Nano, value); err == nil { - return parsed, nil - } - for _, layout := range []string{ - "2006-01-02T15:04:05.999999999", - "2006-01-02T15:04:05", - "2006-01-02 15:04:05.999999999", - "2006-01-02 15:04:05", - } { - if parsed, err := time.Parse(layout, value); err == nil { - return parsed, nil - } - } - return time.Time{}, fmt.Errorf("invalid timestamp %q", value) -} - func responseBodySummary(body io.Reader) string { data, err := io.ReadAll(io.LimitReader(body, maxErrorBodyBytes+1)) if err != nil { diff --git a/internal/managedstream/stream_test.go b/internal/managedstream/stream_test.go index 4198f91..5966495 100644 --- a/internal/managedstream/stream_test.go +++ b/internal/managedstream/stream_test.go @@ -447,12 +447,12 @@ func TestParseStateUpdatedAfterAcceptsLegacyTimestampFormats(t *testing.T) { "2026-06-08T12:20:07.853885", "2026-06-08 12:20:07.853885", } { - parsed, err := parseStateUpdatedAfter(value) + parsed, err := sqlite.ParseLedgerTimestamp(value) if err != nil { - t.Fatalf("parseStateUpdatedAfter(%q) error = %v", value, err) + t.Fatalf("ParseLedgerTimestamp(%q) error = %v", value, err) } if got := parsed.UTC().Format(time.RFC3339Nano); got != "2026-06-08T12:20:07.853885Z" { - t.Fatalf("parseStateUpdatedAfter(%q) = %q", value, got) + t.Fatalf("ParseLedgerTimestamp(%q) = %q", value, got) } } } diff --git a/internal/run/run.go b/internal/run/run.go index da2333f..ab0beb5 100644 --- a/internal/run/run.go +++ b/internal/run/run.go @@ -46,11 +46,6 @@ type Options struct { Args []string } -// Start preserves the historical managed-session entry point. -func Start(ctx context.Context, opts Options) error { - return StartManaged(ctx, opts) -} - // StartManaged launches an agent with a hosted managed Kontext session. func StartManaged(ctx context.Context, opts Options) error { diagnostics := diagnostic.New(os.Stderr, opts.Verbose || diagnostic.EnabledFromEnv())